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 <iant@golang.org>
diff --git a/http2/go111.go b/http2/go111.go
index e38ea29..9749dc0 100644
--- a/http2/go111.go
+++ b/http2/go111.go
@@ -6,6 +6,8 @@
 
 package http2
 
+import "net/textproto"
+
 func traceHasWroteHeaderField(trace *clientTrace) bool {
 	return trace != nil && trace.WroteHeaderField != nil
 }
@@ -15,3 +17,10 @@
 		trace.WroteHeaderField(k, []string{v})
 	}
 }
+
+func traceGot1xxResponseFunc(trace *clientTrace) func(int, textproto.MIMEHeader) error {
+	if trace != nil {
+		return trace.Got1xxResponse
+	}
+	return nil
+}
diff --git a/http2/not_go111.go b/http2/not_go111.go
index d036b01..0df34e6 100644
--- a/http2/not_go111.go
+++ b/http2/not_go111.go
@@ -6,6 +6,12 @@
 
 package http2
 
+import "net/textproto"
+
 func traceHasWroteHeaderField(trace *clientTrace) bool { return false }
 
 func traceWroteHeaderField(trace *clientTrace, k, v string) {}
+
+func traceGot1xxResponseFunc(trace *clientTrace) func(int, textproto.MIMEHeader) error {
+	return nil
+}
diff --git a/http2/transport.go b/http2/transport.go
index 671c187..dc89cbc 100644
--- a/http2/transport.go
+++ b/http2/transport.go
@@ -21,6 +21,7 @@
 	mathrand "math/rand"
 	"net"
 	"net/http"
+	"net/textproto"
 	"sort"
 	"strconv"
 	"strings"
@@ -212,9 +213,10 @@
 	done chan struct{} // closed when stream remove from cc.streams map; close calls guarded by cc.mu
 
 	// owned by clientConnReadLoop:
-	firstByte    bool // got the first response byte
-	pastHeaders  bool // got first MetaHeadersFrame (actual headers)
-	pastTrailers bool // got optional second MetaHeadersFrame (trailers)
+	firstByte    bool  // got the first response byte
+	pastHeaders  bool  // got first MetaHeadersFrame (actual headers)
+	pastTrailers bool  // got optional second MetaHeadersFrame (trailers)
+	num1xx       uint8 // number of 1xx responses seen
 
 	trailer    http.Header  // accumulated trailers
 	resTrailer *http.Header // client's Response.Trailer
@@ -238,6 +240,17 @@
 	}
 }
 
+var got1xxFuncForTests func(int, textproto.MIMEHeader) error
+
+// get1xxTraceFunc returns the value of request's httptrace.ClientTrace.Got1xxResponse func,
+// if any. It returns nil if not set or if the Go version is too old.
+func (cs *clientStream) get1xxTraceFunc() func(int, textproto.MIMEHeader) error {
+	if fn := got1xxFuncForTests; fn != nil {
+		return fn
+	}
+	return traceGot1xxResponseFunc(cs.trace)
+}
+
 // awaitRequestCancel waits for the user to cancel a request, its context to
 // expire, or for the request to be done (any way it might be removed from the
 // cc.streams map: peer reset, successful completion, TCP connection breakage,
@@ -1734,8 +1747,7 @@
 // is the detail.
 //
 // As a special case, handleResponse may return (nil, nil) to skip the
-// frame (currently only used for 100 expect continue). This special
-// case is going away after Issue 13851 is fixed.
+// frame (currently only used for 1xx responses).
 func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFrame) (*http.Response, error) {
 	if f.Truncated {
 		return nil, errResponseHeaderListSize
@@ -1750,16 +1762,38 @@
 		return nil, errors.New("malformed response from server: malformed non-numeric status pseudo header")
 	}
 
-	if statusCode == 100 {
-		traceGot100Continue(cs.trace)
-		if cs.on100 != nil {
-			cs.on100() // forces any write delay timer to fire
+	header := make(http.Header)
+	var trailerValue string
+	for _, hf := range f.RegularFields() {
+		key := http.CanonicalHeaderKey(hf.Name)
+		if key == "Trailer" {
+			trailerValue = hf.Value
+		} else {
+			header[key] = append(header[key], hf.Value)
+		}
+	}
+
+	if statusCode >= 100 && statusCode <= 199 {
+		cs.num1xx++
+		const max1xxResponses = 5 // arbitrary bound on number of informational responses, same as net/http
+		if cs.num1xx > max1xxResponses {
+			return nil, errors.New("http2: too many 1xx informational responses")
+		}
+		if fn := cs.get1xxTraceFunc(); fn != nil {
+			if err := fn(statusCode, textproto.MIMEHeader(header)); err != nil {
+				return nil, err
+			}
+		}
+		if statusCode == 100 {
+			traceGot100Continue(cs.trace)
+			if cs.on100 != nil {
+				cs.on100() // forces any write delay timer to fire
+			}
 		}
 		cs.pastHeaders = false // do it all again
 		return nil, nil
 	}
 
-	header := make(http.Header)
 	res := &http.Response{
 		Proto:      "HTTP/2.0",
 		ProtoMajor: 2,
@@ -1767,20 +1801,15 @@
 		StatusCode: statusCode,
 		Status:     status + " " + http.StatusText(statusCode),
 	}
-	for _, hf := range f.RegularFields() {
-		key := http.CanonicalHeaderKey(hf.Name)
-		if key == "Trailer" {
-			t := res.Trailer
-			if t == nil {
-				t = make(http.Header)
-				res.Trailer = t
-			}
-			foreachHeaderElement(hf.Value, func(v string) {
-				t[http.CanonicalHeaderKey(v)] = nil
-			})
-		} else {
-			header[key] = append(header[key], hf.Value)
+	if trailerValue != "" {
+		t := res.Trailer
+		if t == nil {
+			t = make(http.Header)
+			res.Trailer = t
 		}
+		foreachHeaderElement(trailerValue, func(v string) {
+			t[http.CanonicalHeaderKey(v)] = nil
+		})
 	}
 
 	streamEnded := f.StreamEnded()
diff --git a/http2/transport_test.go b/http2/transport_test.go
index afd7969..23cb820 100644
--- a/http2/transport_test.go
+++ b/http2/transport_test.go
@@ -18,6 +18,7 @@
 	"net"
 	"net/http"
 	"net/http/httptest"
+	"net/textproto"
 	"net/url"
 	"os"
 	"reflect"
@@ -1192,6 +1193,77 @@
 	ct.run()
 }
 
+// Issue 26189, Issue 17739: ignore unknown 1xx responses
+func TestTransportUnknown1xx(t *testing.T) {
+	var buf bytes.Buffer
+	defer func() { got1xxFuncForTests = nil }()
+	got1xxFuncForTests = func(code int, header textproto.MIMEHeader) error {
+		fmt.Fprintf(&buf, "code=%d header=%v\n", code, header)
+		return nil
+	}
+
+	ct := newClientTester(t)
+	ct.client = func() error {
+		req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
+		res, err := ct.tr.RoundTrip(req)
+		if err != nil {
+			return fmt.Errorf("RoundTrip: %v", err)
+		}
+		defer res.Body.Close()
+		if res.StatusCode != 204 {
+			return fmt.Errorf("status code = %v; want 204", res.StatusCode)
+		}
+		want := `code=110 header=map[Foo-Bar:[110]]
+code=111 header=map[Foo-Bar:[111]]
+code=112 header=map[Foo-Bar:[112]]
+code=113 header=map[Foo-Bar:[113]]
+code=114 header=map[Foo-Bar:[114]]
+`
+		if got := buf.String(); got != want {
+			t.Errorf("Got trace:\n%s\nWant:\n%s", got, want)
+		}
+		return nil
+	}
+	ct.server = func() error {
+		ct.greet()
+		var buf bytes.Buffer
+		enc := hpack.NewEncoder(&buf)
+
+		for {
+			f, err := ct.fr.ReadFrame()
+			if err != nil {
+				return err
+			}
+			switch f := f.(type) {
+			case *WindowUpdateFrame, *SettingsFrame:
+			case *HeadersFrame:
+				for i := 110; i <= 114; i++ {
+					buf.Reset()
+					enc.WriteField(hpack.HeaderField{Name: ":status", Value: fmt.Sprint(i)})
+					enc.WriteField(hpack.HeaderField{Name: "foo-bar", Value: fmt.Sprint(i)})
+					ct.fr.WriteHeaders(HeadersFrameParam{
+						StreamID:      f.StreamID,
+						EndHeaders:    true,
+						EndStream:     false,
+						BlockFragment: buf.Bytes(),
+					})
+				}
+				buf.Reset()
+				enc.WriteField(hpack.HeaderField{Name: ":status", Value: "204"})
+				ct.fr.WriteHeaders(HeadersFrameParam{
+					StreamID:      f.StreamID,
+					EndHeaders:    true,
+					EndStream:     false,
+					BlockFragment: buf.Bytes(),
+				})
+				return nil
+			}
+		}
+	}
+	ct.run()
+
+}
+
 func TestTransportReceiveUndeclaredTrailer(t *testing.T) {
 	ct := newClientTester(t)
 	ct.client = func() error {