http2: reject stream self-dependencies
Fix spec bug: Section 5.3.1 says that self-dependencies should be
treated as a stream error of type PROTOCOL_ERROR.
Updates golang/go#16046
Change-Id: I8b5dc8808943dc96aac0c543c7032fa989ab9e0b
Reviewed-on: https://go-review.googlesource.com/31858
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/http2/server.go b/http2/server.go
index 345b08c..50bc112 100644
--- a/http2/server.go
+++ b/http2/server.go
@@ -1473,9 +1473,6 @@
sc.streams[id] = st
sc.writeSched.OpenStream(st.id, OpenStreamOptions{})
- if f.HasPriority() {
- sc.writeSched.AdjustStream(st.id, f.Priority)
- }
sc.curOpenStreams++
if sc.curOpenStreams == 1 {
sc.setConnState(http.StateActive)
@@ -1498,6 +1495,12 @@
// runtime.
return streamError(st.id, ErrCodeRefusedStream)
}
+ if f.HasPriority() {
+ if err := checkPriority(f.StreamID, f.Priority); err != nil {
+ return err
+ }
+ sc.writeSched.AdjustStream(st.id, f.Priority)
+ }
rw, req, err := sc.newWriterAndRequest(st, f)
if err != nil {
@@ -1565,10 +1568,24 @@
return nil
}
+func checkPriority(streamID uint32, p PriorityParam) error {
+ if streamID == p.StreamDep {
+ // Section 5.3.1: "A stream cannot depend on itself. An endpoint MUST treat
+ // this as a stream error (Section 5.4.2) of type PROTOCOL_ERROR."
+ // Section 5.3.3 says that a stream can depend on one of its dependencies,
+ // so it's only self-dependencies that are forbidden.
+ return streamError(streamID, ErrCodeProtocol)
+ }
+ return nil
+}
+
func (sc *serverConn) processPriority(f *PriorityFrame) error {
if sc.inGoAway {
return nil
}
+ if err := checkPriority(f.StreamID, f.PriorityParam); err != nil {
+ return err
+ }
sc.writeSched.AdjustStream(f.StreamID, f.PriorityParam)
return nil
}
diff --git a/http2/server_test.go b/http2/server_test.go
index e7309b9..013abe1 100644
--- a/http2/server_test.go
+++ b/http2/server_test.go
@@ -256,6 +256,12 @@
}
}
+func (st *serverTester) writePriority(id uint32, p PriorityParam) {
+ if err := st.fr.WritePriority(id, p); err != nil {
+ st.t.Fatalf("Error writing PRIORITY: %v", err)
+ }
+}
+
func (st *serverTester) encodeHeaderField(k, v string) {
err := st.hpackEnc.WriteField(hpack.HeaderField{Name: k, Value: v})
if err != nil {
@@ -1481,6 +1487,36 @@
})
}
+// No PRIORITY on stream 0.
+func TestServer_Rejects_Priority0(t *testing.T) {
+ testServerRejectsConn(t, func(st *serverTester) {
+ st.fr.AllowIllegalWrites = true
+ st.writePriority(0, PriorityParam{StreamDep: 1})
+ })
+}
+
+// No HEADERS frame with a self-dependence.
+func TestServer_Rejects_HeadersSelfDependence(t *testing.T) {
+ testServerRejectsStream(t, ErrCodeProtocol, func(st *serverTester) {
+ st.fr.AllowIllegalWrites = true
+ st.writeHeaders(HeadersFrameParam{
+ StreamID: 1,
+ BlockFragment: st.encodeHeader(),
+ EndStream: true,
+ EndHeaders: true,
+ Priority: PriorityParam{StreamDep: 1},
+ })
+ })
+}
+
+// No PRIORTY frame with a self-dependence.
+func TestServer_Rejects_PrioritySelfDependence(t *testing.T) {
+ testServerRejectsStream(t, ErrCodeProtocol, func(st *serverTester) {
+ st.fr.AllowIllegalWrites = true
+ st.writePriority(1, PriorityParam{StreamDep: 1})
+ })
+}
+
func TestServer_Rejects_PushPromise(t *testing.T) {
testServerRejectsConn(t, func(st *serverTester) {
pp := PushPromiseParam{