瀏覽代碼

http2: remove support for Go 1.8 and earlier

Go's policy is to only support the past two releases (which is
currently Go 1.11 and Go 1.10). But because App Engine was stuck on Go
1.6 and Go 1.8 for so long, we kept kinda supporting Go 1.6 anyway,
even though we didn't actively test it with any CI system.

But that led to code getting disgusting and full of too many
+build-tagged files and indirection, as this change shows.

So, remove Go 1.8, Go 1.7, and Go 1.6 support. We still "support" Go
1.9 for now, even though it's also not actively tested.

Fixes golang/go#26302

Change-Id: I4aa5793173e50ffcd67be52a464492ca48fc9a23
Reviewed-on: https://go-review.googlesource.com/c/145677
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
Brad Fitzpatrick 7 年之前
父節點
當前提交
22700d5518

+ 0 - 82
http2/configure_transport.go

@@ -1,82 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build go1.6
-
-package http2
-
-import (
-	"crypto/tls"
-	"fmt"
-	"net/http"
-)
-
-func configureTransport(t1 *http.Transport) (*Transport, error) {
-	connPool := new(clientConnPool)
-	t2 := &Transport{
-		ConnPool: noDialClientConnPool{connPool},
-		t1:       t1,
-	}
-	connPool.t = t2
-	if err := registerHTTPSProtocol(t1, noDialH2RoundTripper{t2}); err != nil {
-		return nil, err
-	}
-	if t1.TLSClientConfig == nil {
-		t1.TLSClientConfig = new(tls.Config)
-	}
-	if !strSliceContains(t1.TLSClientConfig.NextProtos, "h2") {
-		t1.TLSClientConfig.NextProtos = append([]string{"h2"}, t1.TLSClientConfig.NextProtos...)
-	}
-	if !strSliceContains(t1.TLSClientConfig.NextProtos, "http/1.1") {
-		t1.TLSClientConfig.NextProtos = append(t1.TLSClientConfig.NextProtos, "http/1.1")
-	}
-	upgradeFn := func(authority string, c *tls.Conn) http.RoundTripper {
-		addr := authorityAddr("https", authority)
-		if used, err := connPool.addConnIfNeeded(addr, t2, c); err != nil {
-			go c.Close()
-			return erringRoundTripper{err}
-		} else if !used {
-			// Turns out we don't need this c.
-			// For example, two goroutines made requests to the same host
-			// at the same time, both kicking off TCP dials. (since protocol
-			// was unknown)
-			go c.Close()
-		}
-		return t2
-	}
-	if m := t1.TLSNextProto; len(m) == 0 {
-		t1.TLSNextProto = map[string]func(string, *tls.Conn) http.RoundTripper{
-			"h2": upgradeFn,
-		}
-	} else {
-		m["h2"] = upgradeFn
-	}
-	return t2, nil
-}
-
-// registerHTTPSProtocol calls Transport.RegisterProtocol but
-// converting panics into errors.
-func registerHTTPSProtocol(t *http.Transport, rt noDialH2RoundTripper) (err error) {
-	defer func() {
-		if e := recover(); e != nil {
-			err = fmt.Errorf("%v", e)
-		}
-	}()
-	t.RegisterProtocol("https", rt)
-	return nil
-}
-
-// noDialH2RoundTripper is a RoundTripper which only tries to complete the request
-// if there's already has a cached connection to the host.
-// (The field is exported so it can be accessed via reflect from net/http; tested
-// by TestNoDialH2RoundTripperType)
-type noDialH2RoundTripper struct{ *Transport }
-
-func (rt noDialH2RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
-	res, err := rt.Transport.RoundTrip(req)
-	if isNoCachedConnError(err) {
-		return nil, http.ErrSkipAltProtocol
-	}
-	return res, err
-}

+ 0 - 2
http2/databuffer_test.go

@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build go1.7
-
 package http2
 
 import (

+ 7 - 4
http2/go111.go

@@ -6,19 +6,22 @@
 
 package http2
 
-import "net/textproto"
+import (
+	"net/http/httptrace"
+	"net/textproto"
+)
 
-func traceHasWroteHeaderField(trace *clientTrace) bool {
+func traceHasWroteHeaderField(trace *httptrace.ClientTrace) bool {
 	return trace != nil && trace.WroteHeaderField != nil
 }
 
-func traceWroteHeaderField(trace *clientTrace, k, v string) {
+func traceWroteHeaderField(trace *httptrace.ClientTrace, k, v string) {
 	if trace != nil && trace.WroteHeaderField != nil {
 		trace.WroteHeaderField(k, []string{v})
 	}
 }
 
-func traceGot1xxResponseFunc(trace *clientTrace) func(int, textproto.MIMEHeader) error {
+func traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.MIMEHeader) error {
 	if trace != nil {
 		return trace.Got1xxResponse
 	}

+ 0 - 16
http2/go16.go

@@ -1,16 +0,0 @@
-// Copyright 2016 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build go1.6
-
-package http2
-
-import (
-	"net/http"
-	"time"
-)
-
-func transportExpectContinueTimeout(t1 *http.Transport) time.Duration {
-	return t1.ExpectContinueTimeout
-}

+ 0 - 121
http2/go17.go

@@ -1,121 +0,0 @@
-// Copyright 2016 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build go1.7
-
-package http2
-
-import (
-	"context"
-	"net"
-	"net/http"
-	"net/http/httptrace"
-	"time"
-)
-
-type contextContext interface {
-	context.Context
-}
-
-var errCanceled = context.Canceled
-
-func serverConnBaseContext(c net.Conn, opts *ServeConnOpts) (ctx contextContext, cancel func()) {
-	ctx, cancel = context.WithCancel(context.Background())
-	ctx = context.WithValue(ctx, http.LocalAddrContextKey, c.LocalAddr())
-	if hs := opts.baseConfig(); hs != nil {
-		ctx = context.WithValue(ctx, http.ServerContextKey, hs)
-	}
-	return
-}
-
-func contextWithCancel(ctx contextContext) (_ contextContext, cancel func()) {
-	return context.WithCancel(ctx)
-}
-
-func requestWithContext(req *http.Request, ctx contextContext) *http.Request {
-	return req.WithContext(ctx)
-}
-
-type clientTrace httptrace.ClientTrace
-
-func reqContext(r *http.Request) context.Context { return r.Context() }
-
-func (t *Transport) idleConnTimeout() time.Duration {
-	if t.t1 != nil {
-		return t.t1.IdleConnTimeout
-	}
-	return 0
-}
-
-func setResponseUncompressed(res *http.Response) { res.Uncompressed = true }
-
-func traceGetConn(req *http.Request, hostPort string) {
-	trace := httptrace.ContextClientTrace(req.Context())
-	if trace == nil || trace.GetConn == nil {
-		return
-	}
-	trace.GetConn(hostPort)
-}
-
-func traceGotConn(req *http.Request, cc *ClientConn) {
-	trace := httptrace.ContextClientTrace(req.Context())
-	if trace == nil || trace.GotConn == nil {
-		return
-	}
-	ci := httptrace.GotConnInfo{Conn: cc.tconn}
-	cc.mu.Lock()
-	ci.Reused = cc.nextStreamID > 1
-	ci.WasIdle = len(cc.streams) == 0 && ci.Reused
-	if ci.WasIdle && !cc.lastActive.IsZero() {
-		ci.IdleTime = time.Now().Sub(cc.lastActive)
-	}
-	cc.mu.Unlock()
-
-	trace.GotConn(ci)
-}
-
-func traceWroteHeaders(trace *clientTrace) {
-	if trace != nil && trace.WroteHeaders != nil {
-		trace.WroteHeaders()
-	}
-}
-
-func traceGot100Continue(trace *clientTrace) {
-	if trace != nil && trace.Got100Continue != nil {
-		trace.Got100Continue()
-	}
-}
-
-func traceWait100Continue(trace *clientTrace) {
-	if trace != nil && trace.Wait100Continue != nil {
-		trace.Wait100Continue()
-	}
-}
-
-func traceWroteRequest(trace *clientTrace, err error) {
-	if trace != nil && trace.WroteRequest != nil {
-		trace.WroteRequest(httptrace.WroteRequestInfo{Err: err})
-	}
-}
-
-func traceFirstResponseByte(trace *clientTrace) {
-	if trace != nil && trace.GotFirstResponseByte != nil {
-		trace.GotFirstResponseByte()
-	}
-}
-
-func requestTrace(req *http.Request) *clientTrace {
-	trace := httptrace.ContextClientTrace(req.Context())
-	return (*clientTrace)(trace)
-}
-
-// Ping sends a PING frame to the server and waits for the ack.
-func (cc *ClientConn) Ping(ctx context.Context) error {
-	return cc.ping(ctx)
-}
-
-// Shutdown gracefully closes the client connection, waiting for running streams to complete.
-func (cc *ClientConn) Shutdown(ctx context.Context) error {
-	return cc.shutdown(ctx)
-}

+ 0 - 36
http2/go17_not18.go

@@ -1,36 +0,0 @@
-// Copyright 2016 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build go1.7,!go1.8
-
-package http2
-
-import "crypto/tls"
-
-// temporary copy of Go 1.7's private tls.Config.clone:
-func cloneTLSConfig(c *tls.Config) *tls.Config {
-	return &tls.Config{
-		Rand:                        c.Rand,
-		Time:                        c.Time,
-		Certificates:                c.Certificates,
-		NameToCertificate:           c.NameToCertificate,
-		GetCertificate:              c.GetCertificate,
-		RootCAs:                     c.RootCAs,
-		NextProtos:                  c.NextProtos,
-		ServerName:                  c.ServerName,
-		ClientAuth:                  c.ClientAuth,
-		ClientCAs:                   c.ClientCAs,
-		InsecureSkipVerify:          c.InsecureSkipVerify,
-		CipherSuites:                c.CipherSuites,
-		PreferServerCipherSuites:    c.PreferServerCipherSuites,
-		SessionTicketsDisabled:      c.SessionTicketsDisabled,
-		SessionTicketKey:            c.SessionTicketKey,
-		ClientSessionCache:          c.ClientSessionCache,
-		MinVersion:                  c.MinVersion,
-		MaxVersion:                  c.MaxVersion,
-		CurvePreferences:            c.CurvePreferences,
-		DynamicRecordSizingDisabled: c.DynamicRecordSizingDisabled,
-		Renegotiation:               c.Renegotiation,
-	}
-}

+ 0 - 56
http2/go18.go

@@ -1,56 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build go1.8
-
-package http2
-
-import (
-	"crypto/tls"
-	"io"
-	"net/http"
-)
-
-func cloneTLSConfig(c *tls.Config) *tls.Config {
-	c2 := c.Clone()
-	c2.GetClientCertificate = c.GetClientCertificate // golang.org/issue/19264
-	return c2
-}
-
-var _ http.Pusher = (*responseWriter)(nil)
-
-// Push implements http.Pusher.
-func (w *responseWriter) Push(target string, opts *http.PushOptions) error {
-	internalOpts := pushOptions{}
-	if opts != nil {
-		internalOpts.Method = opts.Method
-		internalOpts.Header = opts.Header
-	}
-	return w.push(target, internalOpts)
-}
-
-func configureServer18(h1 *http.Server, h2 *Server) error {
-	if h2.IdleTimeout == 0 {
-		if h1.IdleTimeout != 0 {
-			h2.IdleTimeout = h1.IdleTimeout
-		} else {
-			h2.IdleTimeout = h1.ReadTimeout
-		}
-	}
-	return nil
-}
-
-func shouldLogPanic(panicValue interface{}) bool {
-	return panicValue != nil && panicValue != http.ErrAbortHandler
-}
-
-func reqGetBody(req *http.Request) func() (io.ReadCloser, error) {
-	return req.GetBody
-}
-
-func reqBodyIsNoBody(body io.ReadCloser) bool {
-	return body == http.NoBody
-}
-
-func go18httpNoBody() io.ReadCloser { return http.NoBody } // for tests only

+ 0 - 79
http2/go18_test.go

@@ -1,79 +0,0 @@
-// Copyright 2016 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build go1.8
-
-package http2
-
-import (
-	"crypto/tls"
-	"net/http"
-	"testing"
-	"time"
-)
-
-// Tests that http2.Server.IdleTimeout is initialized from
-// http.Server.{Idle,Read}Timeout. http.Server.IdleTimeout was
-// added in Go 1.8.
-func TestConfigureServerIdleTimeout_Go18(t *testing.T) {
-	const timeout = 5 * time.Second
-	const notThisOne = 1 * time.Second
-
-	// With a zero http2.Server, verify that it copies IdleTimeout:
-	{
-		s1 := &http.Server{
-			IdleTimeout: timeout,
-			ReadTimeout: notThisOne,
-		}
-		s2 := &Server{}
-		if err := ConfigureServer(s1, s2); err != nil {
-			t.Fatal(err)
-		}
-		if s2.IdleTimeout != timeout {
-			t.Errorf("s2.IdleTimeout = %v; want %v", s2.IdleTimeout, timeout)
-		}
-	}
-
-	// And that it falls back to ReadTimeout:
-	{
-		s1 := &http.Server{
-			ReadTimeout: timeout,
-		}
-		s2 := &Server{}
-		if err := ConfigureServer(s1, s2); err != nil {
-			t.Fatal(err)
-		}
-		if s2.IdleTimeout != timeout {
-			t.Errorf("s2.IdleTimeout = %v; want %v", s2.IdleTimeout, timeout)
-		}
-	}
-
-	// Verify that s1's IdleTimeout doesn't overwrite an existing setting:
-	{
-		s1 := &http.Server{
-			IdleTimeout: notThisOne,
-		}
-		s2 := &Server{
-			IdleTimeout: timeout,
-		}
-		if err := ConfigureServer(s1, s2); err != nil {
-			t.Fatal(err)
-		}
-		if s2.IdleTimeout != timeout {
-			t.Errorf("s2.IdleTimeout = %v; want %v", s2.IdleTimeout, timeout)
-		}
-	}
-}
-
-func TestCertClone(t *testing.T) {
-	c := &tls.Config{
-		GetClientCertificate: func(*tls.CertificateRequestInfo) (*tls.Certificate, error) {
-			panic("shouldn't be called")
-		},
-	}
-	c2 := cloneTLSConfig(c)
-	if c2.GetClientCertificate == nil {
-		t.Error("GetClientCertificate is nil")
-	}
-}

+ 0 - 16
http2/go19.go

@@ -1,16 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build go1.9
-
-package http2
-
-import (
-	"net/http"
-)
-
-func configureServer19(s *http.Server, conf *Server) error {
-	s.RegisterOnShutdown(conf.state.startGracefulShutdown)
-	return nil
-}

+ 0 - 59
http2/go19_test.go

@@ -1,59 +0,0 @@
-// Copyright 2017 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build go1.9
-
-package http2
-
-import (
-	"context"
-	"net/http"
-	"reflect"
-	"testing"
-	"time"
-)
-
-func TestServerGracefulShutdown(t *testing.T) {
-	var st *serverTester
-	handlerDone := make(chan struct{})
-	st = newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
-		defer close(handlerDone)
-		go st.ts.Config.Shutdown(context.Background())
-
-		ga := st.wantGoAway()
-		if ga.ErrCode != ErrCodeNo {
-			t.Errorf("GOAWAY error = %v; want ErrCodeNo", ga.ErrCode)
-		}
-		if ga.LastStreamID != 1 {
-			t.Errorf("GOAWAY LastStreamID = %v; want 1", ga.LastStreamID)
-		}
-
-		w.Header().Set("x-foo", "bar")
-	})
-	defer st.Close()
-
-	st.greet()
-	st.bodylessReq1()
-
-	select {
-	case <-handlerDone:
-	case <-time.After(5 * time.Second):
-		t.Fatalf("server did not shutdown?")
-	}
-	hf := st.wantHeaders()
-	goth := st.decodeHeader(hf.HeaderBlockFragment())
-	wanth := [][2]string{
-		{":status", "200"},
-		{"x-foo", "bar"},
-		{"content-length", "0"},
-	}
-	if !reflect.DeepEqual(goth, wanth) {
-		t.Errorf("Got headers %v; want %v", goth, wanth)
-	}
-
-	n, err := st.cc.Read([]byte{0})
-	if n != 0 || err == nil {
-		t.Errorf("Read = %v, %v; want 0, non-nil", n, err)
-	}
-}

+ 53 - 0
http2/http2_test.go

@@ -225,3 +225,56 @@ func waitErrCondition(waitFor, checkEvery time.Duration, fn func() error) error
 	}
 	return err
 }
+
+// Tests that http2.Server.IdleTimeout is initialized from
+// http.Server.{Idle,Read}Timeout. http.Server.IdleTimeout was
+// added in Go 1.8.
+func TestConfigureServerIdleTimeout_Go18(t *testing.T) {
+	const timeout = 5 * time.Second
+	const notThisOne = 1 * time.Second
+
+	// With a zero http2.Server, verify that it copies IdleTimeout:
+	{
+		s1 := &http.Server{
+			IdleTimeout: timeout,
+			ReadTimeout: notThisOne,
+		}
+		s2 := &Server{}
+		if err := ConfigureServer(s1, s2); err != nil {
+			t.Fatal(err)
+		}
+		if s2.IdleTimeout != timeout {
+			t.Errorf("s2.IdleTimeout = %v; want %v", s2.IdleTimeout, timeout)
+		}
+	}
+
+	// And that it falls back to ReadTimeout:
+	{
+		s1 := &http.Server{
+			ReadTimeout: timeout,
+		}
+		s2 := &Server{}
+		if err := ConfigureServer(s1, s2); err != nil {
+			t.Fatal(err)
+		}
+		if s2.IdleTimeout != timeout {
+			t.Errorf("s2.IdleTimeout = %v; want %v", s2.IdleTimeout, timeout)
+		}
+	}
+
+	// Verify that s1's IdleTimeout doesn't overwrite an existing setting:
+	{
+		s1 := &http.Server{
+			IdleTimeout: notThisOne,
+		}
+		s2 := &Server{
+			IdleTimeout: timeout,
+		}
+		if err := ConfigureServer(s1, s2); err != nil {
+			t.Fatal(err)
+		}
+		if s2.IdleTimeout != timeout {
+			t.Errorf("s2.IdleTimeout = %v; want %v", s2.IdleTimeout, timeout)
+		}
+	}
+}

+ 7 - 4
http2/not_go111.go

@@ -6,12 +6,15 @@
 
 package http2
 
-import "net/textproto"
+import (
+	"net/http/httptrace"
+	"net/textproto"
+)
 
-func traceHasWroteHeaderField(trace *clientTrace) bool { return false }
+func traceHasWroteHeaderField(trace *httptrace.ClientTrace) bool { return false }
 
-func traceWroteHeaderField(trace *clientTrace, k, v string) {}
+func traceWroteHeaderField(trace *httptrace.ClientTrace, k, v string) {}
 
-func traceGot1xxResponseFunc(trace *clientTrace) func(int, textproto.MIMEHeader) error {
+func traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.MIMEHeader) error {
 	return nil
 }

+ 0 - 21
http2/not_go16.go

@@ -1,21 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build !go1.6
-
-package http2
-
-import (
-	"net/http"
-	"time"
-)
-
-func configureTransport(t1 *http.Transport) (*Transport, error) {
-	return nil, errTransportVersion
-}
-
-func transportExpectContinueTimeout(t1 *http.Transport) time.Duration {
-	return 0
-
-}

+ 0 - 95
http2/not_go17.go

@@ -1,95 +0,0 @@
-// Copyright 2016 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build !go1.7
-
-package http2
-
-import (
-	"crypto/tls"
-	"errors"
-	"net"
-	"net/http"
-	"time"
-)
-
-type contextContext interface {
-	Done() <-chan struct{}
-	Err() error
-}
-
-var errCanceled = errors.New("canceled")
-
-type fakeContext struct{}
-
-func (fakeContext) Done() <-chan struct{} { return nil }
-func (fakeContext) Err() error            { panic("should not be called") }
-
-func reqContext(r *http.Request) fakeContext {
-	return fakeContext{}
-}
-
-func setResponseUncompressed(res *http.Response) {
-	// Nothing.
-}
-
-type clientTrace struct{}
-
-func requestTrace(*http.Request) *clientTrace { return nil }
-func traceGetConn(*http.Request, string)      {}
-func traceGotConn(*http.Request, *ClientConn) {}
-func traceFirstResponseByte(*clientTrace)     {}
-func traceWroteHeaders(*clientTrace)          {}
-func traceWroteRequest(*clientTrace, error)   {}
-func traceGot100Continue(trace *clientTrace)  {}
-func traceWait100Continue(trace *clientTrace) {}
-
-func nop() {}
-
-func serverConnBaseContext(c net.Conn, opts *ServeConnOpts) (ctx contextContext, cancel func()) {
-	return nil, nop
-}
-
-func contextWithCancel(ctx contextContext) (_ contextContext, cancel func()) {
-	return ctx, nop
-}
-
-func requestWithContext(req *http.Request, ctx contextContext) *http.Request {
-	return req
-}
-
-// temporary copy of Go 1.6's private tls.Config.clone:
-func cloneTLSConfig(c *tls.Config) *tls.Config {
-	return &tls.Config{
-		Rand:                     c.Rand,
-		Time:                     c.Time,
-		Certificates:             c.Certificates,
-		NameToCertificate:        c.NameToCertificate,
-		GetCertificate:           c.GetCertificate,
-		RootCAs:                  c.RootCAs,
-		NextProtos:               c.NextProtos,
-		ServerName:               c.ServerName,
-		ClientAuth:               c.ClientAuth,
-		ClientCAs:                c.ClientCAs,
-		InsecureSkipVerify:       c.InsecureSkipVerify,
-		CipherSuites:             c.CipherSuites,
-		PreferServerCipherSuites: c.PreferServerCipherSuites,
-		SessionTicketsDisabled:   c.SessionTicketsDisabled,
-		SessionTicketKey:         c.SessionTicketKey,
-		ClientSessionCache:       c.ClientSessionCache,
-		MinVersion:               c.MinVersion,
-		MaxVersion:               c.MaxVersion,
-		CurvePreferences:         c.CurvePreferences,
-	}
-}
-
-func (cc *ClientConn) Ping(ctx contextContext) error {
-	return cc.ping(ctx)
-}
-
-func (cc *ClientConn) Shutdown(ctx contextContext) error {
-	return cc.shutdown(ctx)
-}
-
-func (t *Transport) idleConnTimeout() time.Duration { return 0 }

+ 0 - 29
http2/not_go18.go

@@ -1,29 +0,0 @@
-// Copyright 2016 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build !go1.8
-
-package http2
-
-import (
-	"io"
-	"net/http"
-)
-
-func configureServer18(h1 *http.Server, h2 *Server) error {
-	// No IdleTimeout to sync prior to Go 1.8.
-	return nil
-}
-
-func shouldLogPanic(panicValue interface{}) bool {
-	return panicValue != nil
-}
-
-func reqGetBody(req *http.Request) func() (io.ReadCloser, error) {
-	return nil
-}
-
-func reqBodyIsNoBody(io.ReadCloser) bool { return false }
-
-func go18httpNoBody() io.ReadCloser { return nil } // for tests only

+ 0 - 16
http2/not_go19.go

@@ -1,16 +0,0 @@
-// Copyright 2016 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build !go1.9
-
-package http2
-
-import (
-	"net/http"
-)
-
-func configureServer19(s *http.Server, conf *Server) error {
-	// not supported prior to go1.9
-	return nil
-}

+ 28 - 17
http2/server.go

@@ -28,6 +28,7 @@ package http2
 import (
 	"bufio"
 	"bytes"
+	"context"
 	"crypto/tls"
 	"errors"
 	"fmt"
@@ -209,12 +210,14 @@ func ConfigureServer(s *http.Server, conf *Server) error {
 		conf = new(Server)
 	}
 	conf.state = &serverInternalState{activeConns: make(map[*serverConn]struct{})}
-	if err := configureServer18(s, conf); err != nil {
-		return err
-	}
-	if err := configureServer19(s, conf); err != nil {
-		return err
+	if h1, h2 := s, conf; h2.IdleTimeout == 0 {
+		if h1.IdleTimeout != 0 {
+			h2.IdleTimeout = h1.IdleTimeout
+		} else {
+			h2.IdleTimeout = h1.ReadTimeout
+		}
 	}
+	s.RegisterOnShutdown(conf.state.startGracefulShutdown)
 
 	if s.TLSConfig == nil {
 		s.TLSConfig = new(tls.Config)
@@ -435,6 +438,15 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
 	sc.serve()
 }
 
+func serverConnBaseContext(c net.Conn, opts *ServeConnOpts) (ctx context.Context, cancel func()) {
+	ctx, cancel = context.WithCancel(context.Background())
+	ctx = context.WithValue(ctx, http.LocalAddrContextKey, c.LocalAddr())
+	if hs := opts.baseConfig(); hs != nil {
+		ctx = context.WithValue(ctx, http.ServerContextKey, hs)
+	}
+	return
+}
+
 func (sc *serverConn) rejectConn(err ErrCode, debug string) {
 	sc.vlogf("http2: server rejecting conn: %v, %s", err, debug)
 	// ignoring errors. hanging up anyway.
@@ -450,7 +462,7 @@ type serverConn struct {
 	conn             net.Conn
 	bw               *bufferedWriter // writing to conn
 	handler          http.Handler
-	baseCtx          contextContext
+	baseCtx          context.Context
 	framer           *Framer
 	doneServing      chan struct{}          // closed when serverConn.serve ends
 	readFrameCh      chan readFrameResult   // written by serverConn.readFrames
@@ -530,7 +542,7 @@ type stream struct {
 	id        uint32
 	body      *pipe       // non-nil if expecting DATA frames
 	cw        closeWaiter // closed wait stream transitions to closed state
-	ctx       contextContext
+	ctx       context.Context
 	cancelCtx func()
 
 	// owned by serverConn's serve loop:
@@ -1882,7 +1894,7 @@ func (sc *serverConn) newStream(id, pusherID uint32, state streamState) *stream
 		panic("internal error: cannot create stream with id 0")
 	}
 
-	ctx, cancelCtx := contextWithCancel(sc.baseCtx)
+	ctx, cancelCtx := context.WithCancel(sc.baseCtx)
 	st := &stream{
 		sc:        sc,
 		id:        id,
@@ -2048,7 +2060,7 @@ func (sc *serverConn) newWriterAndRequestNoBody(st *stream, rp requestParam) (*r
 		Body:       body,
 		Trailer:    trailer,
 	}
-	req = requestWithContext(req, st.ctx)
+	req = req.WithContext(st.ctx)
 
 	rws := responseWriterStatePool.Get().(*responseWriterState)
 	bwSave := rws.bw
@@ -2076,7 +2088,7 @@ func (sc *serverConn) runHandler(rw *responseWriter, req *http.Request, handler
 				stream: rw.rws.stream,
 			})
 			// Same as net/http:
-			if shouldLogPanic(e) {
+			if e != nil && e != http.ErrAbortHandler {
 				const size = 64 << 10
 				buf := make([]byte, size)
 				buf = buf[:runtime.Stack(buf, false)]
@@ -2638,14 +2650,9 @@ var (
 	ErrPushLimitReached = errors.New("http2: push would exceed peer's SETTINGS_MAX_CONCURRENT_STREAMS")
 )
 
-// pushOptions is the internal version of http.PushOptions, which we
-// cannot include here because it's only defined in Go 1.8 and later.
-type pushOptions struct {
-	Method string
-	Header http.Header
-}
+var _ http.Pusher = (*responseWriter)(nil)
 
-func (w *responseWriter) push(target string, opts pushOptions) error {
+func (w *responseWriter) Push(target string, opts *http.PushOptions) error {
 	st := w.rws.stream
 	sc := st.sc
 	sc.serveG.checkNotOn()
@@ -2656,6 +2663,10 @@ func (w *responseWriter) push(target string, opts pushOptions) error {
 		return ErrRecursivePush
 	}
 
+	if opts == nil {
+		opts = new(http.PushOptions)
+	}
+
 	// Default options.
 	if opts.Method == "" {
 		opts.Method = "GET"

+ 0 - 2
http2/server_push_test.go

@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build go1.8
-
 package http2
 
 import (

+ 45 - 0
http2/server_test.go

@@ -6,6 +6,7 @@ package http2
 
 import (
 	"bytes"
+	"context"
 	"crypto/tls"
 	"errors"
 	"flag"
@@ -3850,3 +3851,47 @@ func TestServer_Headers_HalfCloseRemote(t *testing.T) {
 
 	st.wantRSTStream(1, ErrCodeStreamClosed)
 }
+
+func TestServerGracefulShutdown(t *testing.T) {
+	var st *serverTester
+	handlerDone := make(chan struct{})
+	st = newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
+		defer close(handlerDone)
+		go st.ts.Config.Shutdown(context.Background())
+
+		ga := st.wantGoAway()
+		if ga.ErrCode != ErrCodeNo {
+			t.Errorf("GOAWAY error = %v; want ErrCodeNo", ga.ErrCode)
+		}
+		if ga.LastStreamID != 1 {
+			t.Errorf("GOAWAY LastStreamID = %v; want 1", ga.LastStreamID)
+		}
+
+		w.Header().Set("x-foo", "bar")
+	})
+	defer st.Close()
+
+	st.greet()
+	st.bodylessReq1()
+
+	select {
+	case <-handlerDone:
+	case <-time.After(5 * time.Second):
+		t.Fatalf("server did not shutdown?")
+	}
+	hf := st.wantHeaders()
+	goth := st.decodeHeader(hf.HeaderBlockFragment())
+	wanth := [][2]string{
+		{":status", "200"},
+		{"x-foo", "bar"},
+		{"content-length", "0"},
+	}
+	if !reflect.DeepEqual(goth, wanth) {
+		t.Errorf("Got headers %v; want %v", goth, wanth)
+	}
+
+	n, err := st.cc.Read([]byte{0})
+	if n != 0 || err == nil {
+		t.Errorf("Read = %v, %v; want 0, non-nil", n, err)
+	}
+}

+ 151 - 24
http2/transport.go

@@ -10,6 +10,7 @@ import (
 	"bufio"
 	"bytes"
 	"compress/gzip"
+	"context"
 	"crypto/rand"
 	"crypto/tls"
 	"errors"
@@ -21,6 +22,7 @@ import (
 	mathrand "math/rand"
 	"net"
 	"net/http"
+	"net/http/httptrace"
 	"net/textproto"
 	"sort"
 	"strconv"
@@ -118,16 +120,56 @@ func (t *Transport) disableCompression() bool {
 	return t.DisableCompression || (t.t1 != nil && t.t1.DisableCompression)
 }
 
-var errTransportVersion = errors.New("http2: ConfigureTransport is only supported starting at Go 1.6")
-
 // ConfigureTransport configures a net/http HTTP/1 Transport to use HTTP/2.
-// It requires Go 1.6 or later and returns an error if the net/http package is too old
-// or if t1 has already been HTTP/2-enabled.
+// It returns an error if t1 has already been HTTP/2-enabled.
 func ConfigureTransport(t1 *http.Transport) error {
-	_, err := configureTransport(t1) // in configure_transport.go (go1.6) or not_go16.go
+	_, err := configureTransport(t1)
 	return err
 }
 
+func configureTransport(t1 *http.Transport) (*Transport, error) {
+	connPool := new(clientConnPool)
+	t2 := &Transport{
+		ConnPool: noDialClientConnPool{connPool},
+		t1:       t1,
+	}
+	connPool.t = t2
+	if err := registerHTTPSProtocol(t1, noDialH2RoundTripper{t2}); err != nil {
+		return nil, err
+	}
+	if t1.TLSClientConfig == nil {
+		t1.TLSClientConfig = new(tls.Config)
+	}
+	if !strSliceContains(t1.TLSClientConfig.NextProtos, "h2") {
+		t1.TLSClientConfig.NextProtos = append([]string{"h2"}, t1.TLSClientConfig.NextProtos...)
+	}
+	if !strSliceContains(t1.TLSClientConfig.NextProtos, "http/1.1") {
+		t1.TLSClientConfig.NextProtos = append(t1.TLSClientConfig.NextProtos, "http/1.1")
+	}
+	upgradeFn := func(authority string, c *tls.Conn) http.RoundTripper {
+		addr := authorityAddr("https", authority)
+		if used, err := connPool.addConnIfNeeded(addr, t2, c); err != nil {
+			go c.Close()
+			return erringRoundTripper{err}
+		} else if !used {
+			// Turns out we don't need this c.
+			// For example, two goroutines made requests to the same host
+			// at the same time, both kicking off TCP dials. (since protocol
+			// was unknown)
+			go c.Close()
+		}
+		return t2
+	}
+	if m := t1.TLSNextProto; len(m) == 0 {
+		t1.TLSNextProto = map[string]func(string, *tls.Conn) http.RoundTripper{
+			"h2": upgradeFn,
+		}
+	} else {
+		m["h2"] = upgradeFn
+	}
+	return t2, nil
+}
+
 func (t *Transport) connPool() ClientConnPool {
 	t.connPoolOnce.Do(t.initConnPool)
 	return t.connPoolOrDef
@@ -192,7 +234,7 @@ type ClientConn struct {
 type clientStream struct {
 	cc            *ClientConn
 	req           *http.Request
-	trace         *clientTrace // or nil
+	trace         *httptrace.ClientTrace // or nil
 	ID            uint32
 	resc          chan resAndError
 	bufPipe       pipe // buffered pipe with the flow-controlled response payload
@@ -226,7 +268,7 @@ type clientStream struct {
 // channel to be signaled. A non-nil error is returned only if the request was
 // canceled.
 func awaitRequestCancel(req *http.Request, done <-chan struct{}) error {
-	ctx := reqContext(req)
+	ctx := req.Context()
 	if req.Cancel == nil && ctx.Done() == nil {
 		return nil
 	}
@@ -401,8 +443,8 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Res
 				select {
 				case <-time.After(time.Second * time.Duration(backoff)):
 					continue
-				case <-reqContext(req).Done():
-					return nil, reqContext(req).Err()
+				case <-req.Context().Done():
+					return nil, req.Context().Err()
 				}
 			}
 		}
@@ -439,16 +481,15 @@ func shouldRetryRequest(req *http.Request, err error, afterBodyWrite bool) (*htt
 	}
 	// If the Body is nil (or http.NoBody), it's safe to reuse
 	// this request and its Body.
-	if req.Body == nil || reqBodyIsNoBody(req.Body) {
+	if req.Body == nil || req.Body == http.NoBody {
 		return req, nil
 	}
 
 	// If the request body can be reset back to its original
 	// state via the optional req.GetBody, do that.
-	getBody := reqGetBody(req) // Go 1.8: getBody = req.GetBody
-	if getBody != nil {
+	if req.GetBody != nil {
 		// TODO: consider a req.Body.Close here? or audit that all caller paths do?
-		body, err := getBody()
+		body, err := req.GetBody()
 		if err != nil {
 			return nil, err
 		}
@@ -494,7 +535,7 @@ func (t *Transport) dialClientConn(addr string, singleUse bool) (*ClientConn, er
 func (t *Transport) newTLSConfig(host string) *tls.Config {
 	cfg := new(tls.Config)
 	if t.TLSClientConfig != nil {
-		*cfg = *cloneTLSConfig(t.TLSClientConfig)
+		*cfg = *t.TLSClientConfig.Clone()
 	}
 	if !strSliceContains(cfg.NextProtos, NextProtoTLS) {
 		cfg.NextProtos = append([]string{NextProtoTLS}, cfg.NextProtos...)
@@ -545,7 +586,7 @@ func (t *Transport) expectContinueTimeout() time.Duration {
 	if t.t1 == nil {
 		return 0
 	}
-	return transportExpectContinueTimeout(t.t1)
+	return t.t1.ExpectContinueTimeout
 }
 
 func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) {
@@ -711,8 +752,7 @@ func (cc *ClientConn) closeIfIdle() {
 var shutdownEnterWaitStateHook = func() {}
 
 // Shutdown gracefully close the client connection, waiting for running streams to complete.
-// Public implementation is in go17.go and not_go17.go
-func (cc *ClientConn) shutdown(ctx contextContext) error {
+func (cc *ClientConn) Shutdown(ctx context.Context) error {
 	if err := cc.sendGoAway(); err != nil {
 		return err
 	}
@@ -882,7 +922,7 @@ func checkConnHeaders(req *http.Request) error {
 // req.ContentLength, where 0 actually means zero (not unknown) and -1
 // means unknown.
 func actualContentLength(req *http.Request) int64 {
-	if req.Body == nil || reqBodyIsNoBody(req.Body) {
+	if req.Body == nil || req.Body == http.NoBody {
 		return 0
 	}
 	if req.ContentLength != 0 {
@@ -952,7 +992,7 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
 
 	cs := cc.newStream()
 	cs.req = req
-	cs.trace = requestTrace(req)
+	cs.trace = httptrace.ContextClientTrace(req.Context())
 	cs.requestedGzip = requestedGzip
 	bodyWriter := cc.t.getBodyWriterState(cs, body)
 	cs.on100 = bodyWriter.on100
@@ -990,7 +1030,7 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
 
 	readLoopResCh := cs.resc
 	bodyWritten := false
-	ctx := reqContext(req)
+	ctx := req.Context()
 
 	handleReadLoopResponse := func(re resAndError) (*http.Response, bool, error) {
 		res := re.res
@@ -1416,7 +1456,7 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
 		return nil, errRequestHeaderListSize
 	}
 
-	trace := requestTrace(req)
+	trace := httptrace.ContextClientTrace(req.Context())
 	traceHeaders := traceHasWroteHeaderField(trace)
 
 	// Header list size is ok. Write the headers.
@@ -1839,7 +1879,7 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra
 		res.Header.Del("Content-Length")
 		res.ContentLength = -1
 		res.Body = &gzipReader{body: res.Body}
-		setResponseUncompressed(res)
+		res.Uncompressed = true
 	}
 	return res, nil
 }
@@ -2216,8 +2256,7 @@ func (rl *clientConnReadLoop) processResetStream(f *RSTStreamFrame) error {
 }
 
 // Ping sends a PING frame to the server and waits for the ack.
-// Public implementation is in go17.go and not_go17.go
-func (cc *ClientConn) ping(ctx contextContext) error {
+func (cc *ClientConn) Ping(ctx context.Context) error {
 	c := make(chan struct{})
 	// Generate a random payload
 	var p [8]byte
@@ -2451,3 +2490,91 @@ func (s bodyWriterState) scheduleBodyWrite() {
 func isConnectionCloseRequest(req *http.Request) bool {
 	return req.Close || httpguts.HeaderValuesContainsToken(req.Header["Connection"], "close")
 }
+
+// registerHTTPSProtocol calls Transport.RegisterProtocol but
+// converting panics into errors.
+func registerHTTPSProtocol(t *http.Transport, rt noDialH2RoundTripper) (err error) {
+	defer func() {
+		if e := recover(); e != nil {
+			err = fmt.Errorf("%v", e)
+		}
+	}()
+	t.RegisterProtocol("https", rt)
+	return nil
+}
+
+// noDialH2RoundTripper is a RoundTripper which only tries to complete the request
+// if there's already has a cached connection to the host.
+// (The field is exported so it can be accessed via reflect from net/http; tested
+// by TestNoDialH2RoundTripperType)
+type noDialH2RoundTripper struct{ *Transport }
+
+func (rt noDialH2RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
+	res, err := rt.Transport.RoundTrip(req)
+	if isNoCachedConnError(err) {
+		return nil, http.ErrSkipAltProtocol
+	}
+	return res, err
+}
+
+func (t *Transport) idleConnTimeout() time.Duration {
+	if t.t1 != nil {
+		return t.t1.IdleConnTimeout
+	}
+	return 0
+}
+
+func traceGetConn(req *http.Request, hostPort string) {
+	trace := httptrace.ContextClientTrace(req.Context())
+	if trace == nil || trace.GetConn == nil {
+		return
+	}
+	trace.GetConn(hostPort)
+}
+
+func traceGotConn(req *http.Request, cc *ClientConn) {
+	trace := httptrace.ContextClientTrace(req.Context())
+	if trace == nil || trace.GotConn == nil {
+		return
+	}
+	ci := httptrace.GotConnInfo{Conn: cc.tconn}
+	cc.mu.Lock()
+	ci.Reused = cc.nextStreamID > 1
+	ci.WasIdle = len(cc.streams) == 0 && ci.Reused
+	if ci.WasIdle && !cc.lastActive.IsZero() {
+		ci.IdleTime = time.Now().Sub(cc.lastActive)
+	}
+	cc.mu.Unlock()
+
+	trace.GotConn(ci)
+}
+
+func traceWroteHeaders(trace *httptrace.ClientTrace) {
+	if trace != nil && trace.WroteHeaders != nil {
+		trace.WroteHeaders()
+	}
+}
+
+func traceGot100Continue(trace *httptrace.ClientTrace) {
+	if trace != nil && trace.Got100Continue != nil {
+		trace.Got100Continue()
+	}
+}
+
+func traceWait100Continue(trace *httptrace.ClientTrace) {
+	if trace != nil && trace.Wait100Continue != nil {
+		trace.Wait100Continue()
+	}
+}
+
+func traceWroteRequest(trace *httptrace.ClientTrace, err error) {
+	if trace != nil && trace.WroteRequest != nil {
+		trace.WroteRequest(httptrace.WroteRequestInfo{Err: err})
+	}
+}
+
+func traceFirstResponseByte(trace *httptrace.ClientTrace) {
+	if trace != nil && trace.GotFirstResponseByte != nil {
+		trace.GotFirstResponseByte()
+	}
+}

+ 5 - 8
http2/transport_test.go

@@ -7,6 +7,7 @@ package http2
 import (
 	"bufio"
 	"bytes"
+	"context"
 	"crypto/tls"
 	"errors"
 	"flag"
@@ -31,7 +32,6 @@ import (
 	"testing"
 	"time"
 
-	"golang.org/x/net/context"
 	"golang.org/x/net/http2/hpack"
 )
 
@@ -424,7 +424,7 @@ func TestActualContentLength(t *testing.T) {
 		},
 		// http.NoBody means 0, not -1.
 		3: {
-			req:  &http.Request{Body: go18httpNoBody()},
+			req:  &http.Request{Body: http.NoBody},
 			want: 0,
 		},
 	}
@@ -564,9 +564,6 @@ func TestTransportDialTLS(t *testing.T) {
 func TestConfigureTransport(t *testing.T) {
 	t1 := &http.Transport{}
 	err := ConfigureTransport(t1)
-	if err == errTransportVersion {
-		t.Skip(err)
-	}
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -3784,7 +3781,7 @@ func TestTransportNoBodyMeansNoDATA(t *testing.T) {
 	unblockClient := make(chan bool)
 
 	ct.client = func() error {
-		req, _ := http.NewRequest("GET", "https://dummy.tld/", go18httpNoBody())
+		req, _ := http.NewRequest("GET", "https://dummy.tld/", http.NoBody)
 		ct.tr.RoundTrip(req)
 		<-unblockClient
 		return nil
@@ -4024,8 +4021,8 @@ func testClientConnClose(t *testing.T, closeMode closeMode) {
 	}
 	switch closeMode {
 	case shutdownCancel:
-		if err = cc.Shutdown(canceledCtx); err != errCanceled {
-			t.Errorf("got %v, want %v", err, errCanceled)
+		if err = cc.Shutdown(canceledCtx); err != context.Canceled {
+			t.Errorf("got %v, want %v", err, context.Canceled)
 		}
 		if cc.closing == false {
 			t.Error("expected closing to be true")