author | Brad Fitzpatrick
<bradfitz@golang.org> 2016-05-19 01:21:59 UTC |
committer | Brad Fitzpatrick
<bradfitz@golang.org> 2016-05-19 01:52:22 UTC |
parent | 8aacbecd63e105fa5f07afbd49472fd3e17a19d1 |
http2/server.go | +43 | -2 |
http2/server_test.go | +5 | -5 |
diff --git a/http2/server.go b/http2/server.go index a2b6c4b..57c8276 100644 --- a/http2/server.go +++ b/http2/server.go @@ -1474,6 +1474,12 @@ func (st *stream) processTrailerHeaders(f *MetaHeadersFrame) error { if st.trailer != nil { for _, hf := range f.RegularFields() { key := sc.canonicalHeader(hf.Name) + if !ValidTrailerHeader(key) { + // TODO: send more details to the peer somehow. But http2 has + // no way to send debug data at a stream level. Discuss with + // HTTP folk. + return StreamError{st.id, ErrCodeProtocol} + } st.trailer[key] = append(st.trailer[key], hf.Value) } } @@ -1902,9 +1908,9 @@ func (rws *responseWriterState) hasTrailers() bool { return len(rws.trailers) != // written in the trailers at the end of the response. func (rws *responseWriterState) declareTrailer(k string) { k = http.CanonicalHeaderKey(k) - switch k { - case "Transfer-Encoding", "Content-Length", "Trailer": + if !ValidTrailerHeader(k) { // Forbidden by RFC 2616 14.40. + rws.conn.logf("ignoring invalid trailer %q", k) return } if !strSliceContains(rws.trailers, k) { @@ -2227,3 +2233,38 @@ func new400Handler(err error) http.HandlerFunc { http.Error(w, err.Error(), http.StatusBadRequest) } } + +// ValidTrailerHeader reports whether name is a valid header field name to appear +// in trailers. +// See: http://tools.ietf.org/html/rfc7230#section-4.1.2 +func ValidTrailerHeader(name string) bool { + name = http.CanonicalHeaderKey(name) + if strings.HasPrefix(name, "If-") || badTrailer[name] { + return false + } + return true +} + +var badTrailer = map[string]bool{ + "Authorization": true, + "Cache-Control": true, + "Connection": true, + "Content-Encoding": true, + "Content-Length": true, + "Content-Range": true, + "Content-Type": true, + "Expect": true, + "Host": true, + "Keep-Alive": true, + "Max-Forwards": true, + "Pragma": true, + "Proxy-Authenticate": true, + "Proxy-Authorization": true, + "Proxy-Connection": true, + "Range": true, + "Realm": true, + "Te": true, + "Trailer": true, + "Transfer-Encoding": true, + "Www-Authenticate": true, +} diff --git a/http2/server_test.go b/http2/server_test.go index 15f980b..0907530 100644 --- a/http2/server_test.go +++ b/http2/server_test.go @@ -2660,13 +2660,11 @@ func testServerWritesTrailers(t *testing.T, withFlush bool) { testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error { w.Header().Set("Trailer", "Server-Trailer-A, Server-Trailer-B") w.Header().Add("Trailer", "Server-Trailer-C") - - // TODO: decide if the server should filter these while - // writing the Trailer header in the response. Currently it - // appears net/http doesn't do this for http/1.1 w.Header().Add("Trailer", "Transfer-Encoding, Content-Length, Trailer") // filtered + + // Regular headers: w.Header().Set("Foo", "Bar") - w.Header().Set("Content-Length", "5") + w.Header().Set("Content-Length", "5") // len("Hello") io.WriteString(w, "Hello") if withFlush { @@ -2681,6 +2679,8 @@ func testServerWritesTrailers(t *testing.T, withFlush bool) { // otherwise-invalid "Trailer:" prefix: w.Header().Set("Trailer:Post-Header-Trailer", "hi1") w.Header().Set("Trailer:post-header-trailer2", "hi2") + w.Header().Set("Trailer:Range", "invalid") + w.Header().Set("Trailer:Foo\x01Bogus", "invalid") w.Header().Set("Transfer-Encoding", "should not be included; Forbidden by RFC 2616 14.40") w.Header().Set("Content-Length", "should not be included; Forbidden by RFC 2616 14.40") w.Header().Set("Trailer", "should not be included; Forbidden by RFC 2616 14.40")