Browse Source

Require exactly one pseudo header.

Brad Fitzpatrick 11 years ago
parent
commit
832edb1272
2 changed files with 26 additions and 6 deletions
  1. 12 5
      http2.go
  2. 14 1
      http2_test.go

+ 12 - 5
http2.go

@@ -194,15 +194,16 @@ func (sc *serverConn) onNewHeaderField(f hpack.HeaderField) {
 	case !validHeader(f.Name):
 	case !validHeader(f.Name):
 		sc.invalidHeader = true
 		sc.invalidHeader = true
 	case strings.HasPrefix(f.Name, ":"):
 	case strings.HasPrefix(f.Name, ":"):
+		var dst *string
 		switch f.Name {
 		switch f.Name {
 		case ":method":
 		case ":method":
-			sc.method = f.Value
+			dst = &sc.method
 		case ":path":
 		case ":path":
-			sc.path = f.Value
+			dst = &sc.path
 		case ":scheme":
 		case ":scheme":
-			sc.scheme = f.Value
+			dst = &sc.scheme
 		case ":authority":
 		case ":authority":
-			sc.authority = f.Value
+			dst = &sc.authority
 		default:
 		default:
 			// 8.1.2.1 Pseudo-Header Fields
 			// 8.1.2.1 Pseudo-Header Fields
 			// "Endpoints MUST treat a request or response
 			// "Endpoints MUST treat a request or response
@@ -211,8 +212,14 @@ func (sc *serverConn) onNewHeaderField(f hpack.HeaderField) {
 			// 8.1.2.6)."
 			// 8.1.2.6)."
 			sc.logf("invalid pseudo-header %q", f.Name)
 			sc.logf("invalid pseudo-header %q", f.Name)
 			sc.invalidHeader = true
 			sc.invalidHeader = true
+			return
 		}
 		}
-		return
+		if *dst != "" {
+			sc.logf("duplicate pseudo-header %q sent", f.Name)
+			sc.invalidHeader = true
+			return
+		}
+		*dst = f.Value
 	case f.Name == "cookie":
 	case f.Name == "cookie":
 		if s, ok := sc.header["Cookie"]; ok && len(s) == 1 {
 		if s, ok := sc.header["Cookie"]; ok && len(s) == 1 {
 			s[0] = s[0] + "; " + f.Value
 			s[0] = s[0] + "; " + f.Value

+ 14 - 1
http2_test.go

@@ -402,6 +402,12 @@ func TestServer_Request_Reject_Pseudo_Missing_method(t *testing.T) {
 	testRejectRequest(t, func(st *serverTester) { st.bodylessReq1(":method", "") })
 	testRejectRequest(t, func(st *serverTester) { st.bodylessReq1(":method", "") })
 }
 }
 
 
+func TestServer_Request_Reject_Pseudo_ExactlyOne(t *testing.T) {
+	// 8.1.2.3 Request Pseudo-Header Fields
+	// "All HTTP/2 requests MUST include exactly one valid value" ...
+	testRejectRequest(t, func(st *serverTester) { st.bodylessReq1(":method", "GET", ":method", "POST") })
+}
+
 func TestServer_Request_Reject_Pseudo_Missing_path(t *testing.T) {
 func TestServer_Request_Reject_Pseudo_Missing_path(t *testing.T) {
 	testRejectRequest(t, func(st *serverTester) { st.bodylessReq1(":path", "") })
 	testRejectRequest(t, func(st *serverTester) { st.bodylessReq1(":path", "") })
 }
 }
@@ -568,6 +574,7 @@ func (w twriter) Write(p []byte) (n int, err error) {
 // multiple pairs for keys (e.g. "cookie").  The :method, :path, and
 // multiple pairs for keys (e.g. "cookie").  The :method, :path, and
 // :scheme headers default to GET, / and https.
 // :scheme headers default to GET, / and https.
 func encodeHeader(t *testing.T, headers ...string) []byte {
 func encodeHeader(t *testing.T, headers ...string) []byte {
+	pseudoCount := map[string]int{}
 	if len(headers)%2 == 1 {
 	if len(headers)%2 == 1 {
 		panic("odd number of kv args")
 		panic("odd number of kv args")
 	}
 	}
@@ -584,7 +591,13 @@ func encodeHeader(t *testing.T, headers ...string) []byte {
 			keys = append(keys, k)
 			keys = append(keys, k)
 		}
 		}
 		if strings.HasPrefix(k, ":") {
 		if strings.HasPrefix(k, ":") {
-			vals[k] = []string{v}
+			pseudoCount[k]++
+			if pseudoCount[k] == 1 {
+				vals[k] = []string{v}
+			} else {
+				// Allows testing of invalid headers w/ dup pseudo fields.
+				vals[k] = append(vals[k], v)
+			}
 		} else {
 		} else {
 			vals[k] = append(vals[k], v)
 			vals[k] = append(vals[k], v)
 		}
 		}