Skip to content

Commit a45b4ab

Browse files
committed
http2: ignore unknown 1xx responses like HTTP/1
Updates golang/go#26189 (fixes after vendor into std) Updates golang/go#17739 Change-Id: I076cdbb57841b7dbbaa764d11240913bc3a8b05d Reviewed-on: https://go-review.googlesource.com/123615 Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent cffdcf6 commit a45b4ab

File tree

4 files changed

+139
-23
lines changed

4 files changed

+139
-23
lines changed

http2/go111.go

+9
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
package http2
88

9+
import "net/textproto"
10+
911
func traceHasWroteHeaderField(trace *clientTrace) bool {
1012
return trace != nil && trace.WroteHeaderField != nil
1113
}
@@ -15,3 +17,10 @@ func traceWroteHeaderField(trace *clientTrace, k, v string) {
1517
trace.WroteHeaderField(k, []string{v})
1618
}
1719
}
20+
21+
func traceGot1xxResponseFunc(trace *clientTrace) func(int, textproto.MIMEHeader) error {
22+
if trace != nil {
23+
return trace.Got1xxResponse
24+
}
25+
return nil
26+
}

http2/not_go111.go

+6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66

77
package http2
88

9+
import "net/textproto"
10+
911
func traceHasWroteHeaderField(trace *clientTrace) bool { return false }
1012

1113
func traceWroteHeaderField(trace *clientTrace, k, v string) {}
14+
15+
func traceGot1xxResponseFunc(trace *clientTrace) func(int, textproto.MIMEHeader) error {
16+
return nil
17+
}

http2/transport.go

+52-23
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
mathrand "math/rand"
2222
"net"
2323
"net/http"
24+
"net/textproto"
2425
"sort"
2526
"strconv"
2627
"strings"
@@ -212,9 +213,10 @@ type clientStream struct {
212213
done chan struct{} // closed when stream remove from cc.streams map; close calls guarded by cc.mu
213214

214215
// owned by clientConnReadLoop:
215-
firstByte bool // got the first response byte
216-
pastHeaders bool // got first MetaHeadersFrame (actual headers)
217-
pastTrailers bool // got optional second MetaHeadersFrame (trailers)
216+
firstByte bool // got the first response byte
217+
pastHeaders bool // got first MetaHeadersFrame (actual headers)
218+
pastTrailers bool // got optional second MetaHeadersFrame (trailers)
219+
num1xx uint8 // number of 1xx responses seen
218220

219221
trailer http.Header // accumulated trailers
220222
resTrailer *http.Header // client's Response.Trailer
@@ -238,6 +240,17 @@ func awaitRequestCancel(req *http.Request, done <-chan struct{}) error {
238240
}
239241
}
240242

243+
var got1xxFuncForTests func(int, textproto.MIMEHeader) error
244+
245+
// get1xxTraceFunc returns the value of request's httptrace.ClientTrace.Got1xxResponse func,
246+
// if any. It returns nil if not set or if the Go version is too old.
247+
func (cs *clientStream) get1xxTraceFunc() func(int, textproto.MIMEHeader) error {
248+
if fn := got1xxFuncForTests; fn != nil {
249+
return fn
250+
}
251+
return traceGot1xxResponseFunc(cs.trace)
252+
}
253+
241254
// awaitRequestCancel waits for the user to cancel a request, its context to
242255
// expire, or for the request to be done (any way it might be removed from the
243256
// cc.streams map: peer reset, successful completion, TCP connection breakage,
@@ -1734,8 +1747,7 @@ func (rl *clientConnReadLoop) processHeaders(f *MetaHeadersFrame) error {
17341747
// is the detail.
17351748
//
17361749
// As a special case, handleResponse may return (nil, nil) to skip the
1737-
// frame (currently only used for 100 expect continue). This special
1738-
// case is going away after Issue 13851 is fixed.
1750+
// frame (currently only used for 1xx responses).
17391751
func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFrame) (*http.Response, error) {
17401752
if f.Truncated {
17411753
return nil, errResponseHeaderListSize
@@ -1750,37 +1762,54 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra
17501762
return nil, errors.New("malformed response from server: malformed non-numeric status pseudo header")
17511763
}
17521764

1753-
if statusCode == 100 {
1754-
traceGot100Continue(cs.trace)
1755-
if cs.on100 != nil {
1756-
cs.on100() // forces any write delay timer to fire
1765+
header := make(http.Header)
1766+
var trailerValue string
1767+
for _, hf := range f.RegularFields() {
1768+
key := http.CanonicalHeaderKey(hf.Name)
1769+
if key == "Trailer" {
1770+
trailerValue = hf.Value
1771+
} else {
1772+
header[key] = append(header[key], hf.Value)
1773+
}
1774+
}
1775+
1776+
if statusCode >= 100 && statusCode <= 199 {
1777+
cs.num1xx++
1778+
const max1xxResponses = 5 // arbitrary bound on number of informational responses, same as net/http
1779+
if cs.num1xx > max1xxResponses {
1780+
return nil, errors.New("http2: too many 1xx informational responses")
1781+
}
1782+
if fn := cs.get1xxTraceFunc(); fn != nil {
1783+
if err := fn(statusCode, textproto.MIMEHeader(header)); err != nil {
1784+
return nil, err
1785+
}
1786+
}
1787+
if statusCode == 100 {
1788+
traceGot100Continue(cs.trace)
1789+
if cs.on100 != nil {
1790+
cs.on100() // forces any write delay timer to fire
1791+
}
17571792
}
17581793
cs.pastHeaders = false // do it all again
17591794
return nil, nil
17601795
}
17611796

1762-
header := make(http.Header)
17631797
res := &http.Response{
17641798
Proto: "HTTP/2.0",
17651799
ProtoMajor: 2,
17661800
Header: header,
17671801
StatusCode: statusCode,
17681802
Status: status + " " + http.StatusText(statusCode),
17691803
}
1770-
for _, hf := range f.RegularFields() {
1771-
key := http.CanonicalHeaderKey(hf.Name)
1772-
if key == "Trailer" {
1773-
t := res.Trailer
1774-
if t == nil {
1775-
t = make(http.Header)
1776-
res.Trailer = t
1777-
}
1778-
foreachHeaderElement(hf.Value, func(v string) {
1779-
t[http.CanonicalHeaderKey(v)] = nil
1780-
})
1781-
} else {
1782-
header[key] = append(header[key], hf.Value)
1804+
if trailerValue != "" {
1805+
t := res.Trailer
1806+
if t == nil {
1807+
t = make(http.Header)
1808+
res.Trailer = t
17831809
}
1810+
foreachHeaderElement(trailerValue, func(v string) {
1811+
t[http.CanonicalHeaderKey(v)] = nil
1812+
})
17841813
}
17851814

17861815
streamEnded := f.StreamEnded()

http2/transport_test.go

+72
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"net"
1919
"net/http"
2020
"net/http/httptest"
21+
"net/textproto"
2122
"net/url"
2223
"os"
2324
"reflect"
@@ -1192,6 +1193,77 @@ func testTransportResPattern(t *testing.T, expect100Continue, resHeader headerTy
11921193
ct.run()
11931194
}
11941195

1196+
// Issue 26189, Issue 17739: ignore unknown 1xx responses
1197+
func TestTransportUnknown1xx(t *testing.T) {
1198+
var buf bytes.Buffer
1199+
defer func() { got1xxFuncForTests = nil }()
1200+
got1xxFuncForTests = func(code int, header textproto.MIMEHeader) error {
1201+
fmt.Fprintf(&buf, "code=%d header=%v\n", code, header)
1202+
return nil
1203+
}
1204+
1205+
ct := newClientTester(t)
1206+
ct.client = func() error {
1207+
req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
1208+
res, err := ct.tr.RoundTrip(req)
1209+
if err != nil {
1210+
return fmt.Errorf("RoundTrip: %v", err)
1211+
}
1212+
defer res.Body.Close()
1213+
if res.StatusCode != 204 {
1214+
return fmt.Errorf("status code = %v; want 204", res.StatusCode)
1215+
}
1216+
want := `code=110 header=map[Foo-Bar:[110]]
1217+
code=111 header=map[Foo-Bar:[111]]
1218+
code=112 header=map[Foo-Bar:[112]]
1219+
code=113 header=map[Foo-Bar:[113]]
1220+
code=114 header=map[Foo-Bar:[114]]
1221+
`
1222+
if got := buf.String(); got != want {
1223+
t.Errorf("Got trace:\n%s\nWant:\n%s", got, want)
1224+
}
1225+
return nil
1226+
}
1227+
ct.server = func() error {
1228+
ct.greet()
1229+
var buf bytes.Buffer
1230+
enc := hpack.NewEncoder(&buf)
1231+
1232+
for {
1233+
f, err := ct.fr.ReadFrame()
1234+
if err != nil {
1235+
return err
1236+
}
1237+
switch f := f.(type) {
1238+
case *WindowUpdateFrame, *SettingsFrame:
1239+
case *HeadersFrame:
1240+
for i := 110; i <= 114; i++ {
1241+
buf.Reset()
1242+
enc.WriteField(hpack.HeaderField{Name: ":status", Value: fmt.Sprint(i)})
1243+
enc.WriteField(hpack.HeaderField{Name: "foo-bar", Value: fmt.Sprint(i)})
1244+
ct.fr.WriteHeaders(HeadersFrameParam{
1245+
StreamID: f.StreamID,
1246+
EndHeaders: true,
1247+
EndStream: false,
1248+
BlockFragment: buf.Bytes(),
1249+
})
1250+
}
1251+
buf.Reset()
1252+
enc.WriteField(hpack.HeaderField{Name: ":status", Value: "204"})
1253+
ct.fr.WriteHeaders(HeadersFrameParam{
1254+
StreamID: f.StreamID,
1255+
EndHeaders: true,
1256+
EndStream: false,
1257+
BlockFragment: buf.Bytes(),
1258+
})
1259+
return nil
1260+
}
1261+
}
1262+
}
1263+
ct.run()
1264+
1265+
}
1266+
11951267
func TestTransportReceiveUndeclaredTrailer(t *testing.T) {
11961268
ct := newClientTester(t)
11971269
ct.client = func() error {

0 commit comments

Comments
 (0)