author | Brad Fitzpatrick
<bradfitz@golang.org> 2016-04-05 16:55:11 UTC |
committer | Brad Fitzpatrick
<bradfitz@golang.org> 2016-04-06 03:57:22 UTC |
parent | 318395d8b12f5dd0f1b7cd0fbb95195f49acb0f9 |
http2/server.go | +33 | -0 |
http2/server_test.go | +63 | -0 |
diff --git a/http2/server.go b/http2/server.go index 5f62b49..3a46db6 100644 --- a/http2/server.go +++ b/http2/server.go @@ -1437,6 +1437,8 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error { if f.Truncated { // Their header list was too long. Send a 431 error. handler = handleHeaderListTooLong + } else if err := checkValidHTTP2Request(req); err != nil { + handler = new400Handler(err) } go sc.runHandler(rw, req, handler) @@ -2180,3 +2182,34 @@ func foreachHeaderElement(v string, fn func(string)) { } } } + +// From http://httpwg.org/specs/rfc7540.html#rfc.section.8.1.2.2 +var connHeaders = []string{ + "Connection", + "Keep-Alive", + "Proxy-Connection", + "Transfer-Encoding", + "Upgrade", +} + +// checkValidHTTP2Request checks whether req is a valid HTTP/2 request, +// per RFC 7540 Section 8.1.2.2. +// The returned error is reported to users. +func checkValidHTTP2Request(req *http.Request) error { + for _, h := range connHeaders { + if _, ok := req.Header[h]; ok { + return fmt.Errorf("request header %q is not valid in HTTP/2", h) + } + } + te := req.Header["Te"] + if len(te) > 0 && (len(te) > 1 || (te[0] != "trailers" && te[0] != "")) { + return errors.New(`request header "TE" may only be "trailers" in HTTP/2`) + } + return nil +} + +func new400Handler(err error) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + http.Error(w, err.Error(), http.StatusBadRequest) + } +} diff --git a/http2/server_test.go b/http2/server_test.go index c2df355..2e6375d 100644 --- a/http2/server_test.go +++ b/http2/server_test.go @@ -3156,6 +3156,27 @@ func TestServerHandleCustomConn(t *testing.T) { } } +// golang.org/issue/14214 +func TestServer_Rejects_ConnHeaders(t *testing.T) { + testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error { + t.Errorf("should not get to Handler") + return nil + }, func(st *serverTester) { + st.bodylessReq1("connection", "foo") + hf := st.wantHeaders() + goth := st.decodeHeader(hf.HeaderBlockFragment()) + wanth := [][2]string{ + {":status", "400"}, + {"content-type", "text/plain; charset=utf-8"}, + {"x-content-type-options", "nosniff"}, + {"content-length", "51"}, + } + if !reflect.DeepEqual(goth, wanth) { + t.Errorf("Got headers %v; want %v", goth, wanth) + } + }) +} + type hpackEncoder struct { enc *hpack.Encoder buf bytes.Buffer @@ -3179,3 +3200,45 @@ func (he *hpackEncoder) encodeHeaderRaw(t *testing.T, headers ...string) []byte } return he.buf.Bytes() } + +func TestCheckValidHTTP2Request(t *testing.T) { + tests := []struct { + req *http.Request + want error + }{ + { + req: &http.Request{Header: http.Header{"Te": {"trailers"}}}, + want: nil, + }, + { + req: &http.Request{Header: http.Header{"Te": {"trailers", "bogus"}}}, + want: errors.New(`request header "TE" may only be "trailers" in HTTP/2`), + }, + { + req: &http.Request{Header: http.Header{"Foo": {""}}}, + want: nil, + }, + { + req: &http.Request{Header: http.Header{"Connection": {""}}}, + want: errors.New(`request header "Connection" is not valid in HTTP/2`), + }, + { + req: &http.Request{Header: http.Header{"Proxy-Connection": {""}}}, + want: errors.New(`request header "Proxy-Connection" is not valid in HTTP/2`), + }, + { + req: &http.Request{Header: http.Header{"Keep-Alive": {""}}}, + want: errors.New(`request header "Keep-Alive" is not valid in HTTP/2`), + }, + { + req: &http.Request{Header: http.Header{"Upgrade": {""}}}, + want: errors.New(`request header "Upgrade" is not valid in HTTP/2`), + }, + } + for i, tt := range tests { + got := checkValidHTTP2Request(tt.req) + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("%d. checkValidHTTP2Request = %v; want %v", i, got, tt.want) + } + } +}