author | ayanamist
<ayanamist@gmail.com> 2016-05-17 22:54:12 UTC |
committer | Andrew Gerrand
<adg@golang.org> 2016-05-25 21:22:51 UTC |
parent | 0c607074acd38c5f23d1344dfe74c977464d1257 |
http2/configure_transport.go | +1 | -1 |
http2/transport.go | +12 | -4 |
http2/transport_test.go | +45 | -0 |
diff --git a/http2/configure_transport.go b/http2/configure_transport.go index d87ba0f..4f720f5 100644 --- a/http2/configure_transport.go +++ b/http2/configure_transport.go @@ -32,7 +32,7 @@ func configureTransport(t1 *http.Transport) (*Transport, error) { t1.TLSClientConfig.NextProtos = append(t1.TLSClientConfig.NextProtos, "http/1.1") } upgradeFn := func(authority string, c *tls.Conn) http.RoundTripper { - addr := authorityAddr(authority) + addr := authorityAddr("https", authority) if used, err := connPool.addConnIfNeeded(addr, t2, c); err != nil { go c.Close() return erringRoundTripper{err} diff --git a/http2/transport.go b/http2/transport.go index 2ae7437..b42eae7 100644 --- a/http2/transport.go +++ b/http2/transport.go @@ -77,6 +77,10 @@ type Transport struct { // uncompressed. DisableCompression bool + // AllowHTTP, if true, permits HTTP/2 requests using the insecure, + // plain-text "http" scheme. Note that this does not enable h2c support. + AllowHTTP bool + // MaxHeaderListSize is the http2 SETTINGS_MAX_HEADER_LIST_SIZE to // send in the initial settings frame. It is how many bytes // of response headers are allow. Unlike the http2 spec, zero here @@ -275,20 +279,24 @@ func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) { // authorityAddr returns a given authority (a host/IP, or host:port / ip:port) // and returns a host:port. The port 443 is added if needed. -func authorityAddr(authority string) (addr string) { +func authorityAddr(scheme string, authority string) (addr string) { if _, _, err := net.SplitHostPort(authority); err == nil { return authority } - return net.JoinHostPort(authority, "443") + port := "443" + if scheme == "http" { + port = "80" + } + return net.JoinHostPort(authority, port) } // RoundTripOpt is like RoundTrip, but takes options. func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Response, error) { - if req.URL.Scheme != "https" { + if !(req.URL.Scheme == "https" || (req.URL.Scheme == "http" && t.AllowHTTP)) { return nil, errors.New("http2: unsupported scheme") } - addr := authorityAddr(req.URL.Host) + addr := authorityAddr(req.URL.Scheme, req.URL.Host) for { cc, err := t.connPool().GetClientConn(req, addr) if err != nil { diff --git a/http2/transport_test.go b/http2/transport_test.go index 7bba6a7..62c7ab1 100644 --- a/http2/transport_test.go +++ b/http2/transport_test.go @@ -53,6 +53,51 @@ func TestTransportExternal(t *testing.T) { res.Write(os.Stdout) } +func startH2cServer(t *testing.T) net.Listener { + h2Server := &Server{} + l := newLocalListener(t) + go func() { + conn, err := l.Accept() + if err != nil { + t.Error(err) + return + } + h2Server.ServeConn(conn, &ServeConnOpts{Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Hello, %v", r.URL.Path) + })}) + }() + return l +} + +func TestTransportH2c(t *testing.T) { + l := startH2cServer(t) + defer l.Close() + req, err := http.NewRequest("GET", "http://"+l.Addr().String()+"/foobar", nil) + if err != nil { + t.Fatal(err) + } + tr := &Transport{ + AllowHTTP: true, + DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) { + return net.Dial(network, addr) + }, + } + res, err := tr.RoundTrip(req) + if err != nil { + t.Fatal(err) + } + if res.ProtoMajor != 2 { + t.Fatal("proto not h2c") + } + body, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + if got, want := string(body), "Hello, /foobar"; got != want { + t.Fatalf("response got %v, want %v", got, want) + } +} + func TestTransport(t *testing.T) { const body = "sup" st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {