Browse Source

Require regular headers after pseudo headers.

Brad Fitzpatrick 11 years ago
parent
commit
aeecbd8399
2 changed files with 32 additions and 0 deletions
  1. 9 0
      http2.go
  2. 23 0
      http2_test.go

+ 9 - 0
http2.go

@@ -115,6 +115,7 @@ type serverConn struct {
 	canonHeader       map[string]string // http2-lower-case -> Go-Canonical-Case
 	method, path      string
 	scheme, authority string
+	sawRegularHeader  bool // saw a non-pseudo header already
 	invalidHeader     bool
 
 	// curHeaderStreamID and curStream are non-zero if we're in
@@ -194,6 +195,11 @@ func (sc *serverConn) onNewHeaderField(f hpack.HeaderField) {
 	case !validHeader(f.Name):
 		sc.invalidHeader = true
 	case strings.HasPrefix(f.Name, ":"):
+		if sc.sawRegularHeader {
+			sc.logf("pseudo-header after regular header")
+			sc.invalidHeader = true
+			return
+		}
 		var dst *string
 		switch f.Name {
 		case ":method":
@@ -221,12 +227,14 @@ func (sc *serverConn) onNewHeaderField(f hpack.HeaderField) {
 		}
 		*dst = f.Value
 	case f.Name == "cookie":
+		sc.sawRegularHeader = true
 		if s, ok := sc.header["Cookie"]; ok && len(s) == 1 {
 			s[0] = s[0] + "; " + f.Value
 		} else {
 			sc.header.Add("Cookie", f.Value)
 		}
 	default:
+		sc.sawRegularHeader = true
 		sc.header.Add(sc.canonicalHeader(f.Name), f.Value)
 	}
 }
@@ -426,6 +434,7 @@ func (sc *serverConn) processHeaders(f *HeadersFrame) error {
 	sc.scheme = ""
 	sc.authority = ""
 	sc.invalidHeader = false
+	sc.sawRegularHeader = false
 	sc.curHeaderStreamID = id
 	sc.curStream = st
 	return sc.processHeaderBlockFragment(id, f.HeaderBlockFragment(), f.HeadersEnded())

+ 23 - 0
http2_test.go

@@ -408,6 +408,29 @@ func TestServer_Request_Reject_Pseudo_ExactlyOne(t *testing.T) {
 	testRejectRequest(t, func(st *serverTester) { st.bodylessReq1(":method", "GET", ":method", "POST") })
 }
 
+func TestServer_Request_Reject_Pseudo_AfterRegular(t *testing.T) {
+	// 8.1.2.3 Request Pseudo-Header Fields
+	// "All pseudo-header fields MUST appear in the header block
+	// before regular header fields. Any request or response that
+	// contains a pseudo-header field that appears in a header
+	// block after a regular header field MUST be treated as
+	// malformed (Section 8.1.2.6)."
+	testRejectRequest(t, func(st *serverTester) {
+		var buf bytes.Buffer
+		enc := hpack.NewEncoder(&buf)
+		enc.WriteField(hpack.HeaderField{Name: ":method", Value: "GET"})
+		enc.WriteField(hpack.HeaderField{Name: "regular", Value: "foobar"})
+		enc.WriteField(hpack.HeaderField{Name: ":path", Value: "/"})
+		enc.WriteField(hpack.HeaderField{Name: ":scheme", Value: "https"})
+		st.writeHeaders(HeadersFrameParam{
+			StreamID:      1, // clients send odd numbers
+			BlockFragment: buf.Bytes(),
+			EndStream:     true,
+			EndHeaders:    true,
+		})
+	})
+}
+
 func TestServer_Request_Reject_Pseudo_Missing_path(t *testing.T) {
 	testRejectRequest(t, func(st *serverTester) { st.bodylessReq1(":path", "") })
 }