Source file src/net/http/http2client.go

     1  // Copyright 2026 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build !nethttpomithttp2 && !nethttpomithttp2client
     6  
     7  package http
     8  
     9  import (
    10  	"context"
    11  	"crypto/tls"
    12  	"errors"
    13  	"net"
    14  	"net/http/internal/http2"
    15  	"time"
    16  
    17  	_ "unsafe" // for go:linkname
    18  )
    19  
    20  // This file connects the net/http Transport to the HTTP/2 client
    21  // in the net/http/internal/http2 package. It is omitted when
    22  // the nethttpomithttp2 or nethttpomithttp2client build tag is set.
    23  
    24  const omitHTTP2Client = false
    25  
    26  type http2Transport = http2.Transport
    27  
    28  // http2ExternalTransportConfig is an HTTP/2 configuration provided by x/net/http2.
    29  //
    30  // When a x/net/http2.Transport wraps a net/http.Transport, we need to support the user
    31  // setting configuration settings on the x/net Transport:
    32  //
    33  //	tr1 := &http.Transport{}
    34  //	tr2 := http2.ConfigureTransports(t1)
    35  //
    36  //	// This setting needs to affect tr1:
    37  //	tr2.MaxHeaderListSize = 10000
    38  //
    39  // We handle this by having http2.ConfigureTransports pass us an http2ExternalTransportConfig,
    40  // which we can use to query the current state of the http2.Transport.
    41  type http2ExternalTransportConfig interface {
    42  	// Various configuration settings:
    43  	HTTP2Config() HTTP2Config
    44  	DisableCompression() bool
    45  	MaxHeaderListSize() int64
    46  	IdleConnTimeout() time.Duration
    47  
    48  	// ConnFromContext is used to pass a net.Conn to Transport.NewClientConn
    49  	// via a context value. See Transport.http2NewClientConnFromContext.
    50  	ConnFromContext(context.Context) net.Conn
    51  
    52  	// DialFromContext is used to dial new connections, overriding Transport.DialContext etc.
    53  	// This is used when the user calls x/net/http2.Transport.RoundTrip directly,
    54  	// in which case the historical behavior is to use the http2.Transport's dial functions.
    55  	DialFromContext(ctx context.Context, network, addr string) (net.Conn, error)
    56  
    57  	// ExternalRoundTrip reports whether Transport.RoundTrip should call the
    58  	// external transport's RoundTrip. This is used when x/net/http2.Transport.ConnPool
    59  	// is set, in which case the user-provided ClientConnPool has taken responsibility
    60  	// for picking a connection to use.
    61  	ExternalRoundTrip() bool
    62  
    63  	// RoundTrip performs a round trip.
    64  	// It should only be used when ExternalRoundTrip requests it.
    65  	RoundTrip(*Request) (*Response, error)
    66  
    67  	// Registered is called to report successful registration of the config.
    68  	Registered(*Transport)
    69  }
    70  
    71  func (t *Transport) configureHTTP2(protocols Protocols) {
    72  	if t.TLSClientConfig == nil {
    73  		t.TLSClientConfig = &tls.Config{}
    74  	}
    75  	if t.HTTP2 == nil {
    76  		t.HTTP2 = &HTTP2Config{}
    77  	}
    78  	t2 := http2.NewTransport(transportConfig{t})
    79  	t.h2Transport = t2
    80  
    81  	t.registerProtocol("https", http2RoundTripper{t2, true})
    82  	if t.TLSNextProto == nil {
    83  		t.TLSNextProto = make(map[string]func(authority string, c *tls.Conn) RoundTripper)
    84  	}
    85  	// Historically, the presence of a TLSNextProto["h2"] key has been the signal to
    86  	// enable/disable HTTP/2 support. Set a value in the map, but we'll never use it.
    87  	t.TLSNextProto["h2"] = func(authority string, c *tls.Conn) RoundTripper {
    88  		return http2ErringRoundTripper{
    89  			errors.New("unexpected use of stub RoundTripper"),
    90  		}
    91  	}
    92  
    93  	// Server.ServeTLS clones the tls.Config before modifying it.
    94  	// Transport doesn't. We may want to make the two consistent some day.
    95  	//
    96  	// http2configureTransport will have already set NextProtos, but adjust it again
    97  	// here to remove HTTP/1.1 if the user has disabled it.
    98  	t.TLSClientConfig.NextProtos = adjustNextProtos(t.TLSClientConfig.NextProtos, protocols)
    99  }
   100  
   101  type http2ErringRoundTripper struct{ err error }
   102  
   103  func (rt http2ErringRoundTripper) RoundTripErr() error                   { return rt.err }
   104  func (rt http2ErringRoundTripper) RoundTrip(*Request) (*Response, error) { return nil, rt.err }
   105  
   106  func http2RoundTrip(req *Request, rt func(*http2.ClientRequest) (*http2.ClientResponse, error)) (*Response, error) {
   107  	resp := &Response{}
   108  	cresp, err := rt(&http2.ClientRequest{
   109  		Context:       req.Context(),
   110  		Method:        req.Method,
   111  		URL:           req.URL,
   112  		Header:        http2.Header(req.Header),
   113  		Trailer:       http2.Header(req.Trailer),
   114  		Body:          req.Body,
   115  		Host:          req.Host,
   116  		GetBody:       req.GetBody,
   117  		ContentLength: req.ContentLength,
   118  		Cancel:        req.Cancel,
   119  		Close:         req.Close,
   120  		ResTrailer:    (*http2.Header)(&resp.Trailer),
   121  	})
   122  	if err != nil {
   123  		return nil, err
   124  	}
   125  	resp.Status = cresp.Status + " " + StatusText(cresp.StatusCode)
   126  	resp.StatusCode = cresp.StatusCode
   127  	resp.Proto = "HTTP/2.0"
   128  	resp.ProtoMajor = 2
   129  	resp.ProtoMinor = 0
   130  	resp.ContentLength = cresp.ContentLength
   131  	resp.Uncompressed = cresp.Uncompressed
   132  	resp.Header = Header(cresp.Header)
   133  	resp.Trailer = Header(cresp.Trailer)
   134  	resp.Body = cresp.Body
   135  	resp.TLS = cresp.TLS
   136  	resp.Request = req
   137  	return resp, nil
   138  }
   139  
   140  // http2AddConn adds nc to the HTTP/2 connection pool.
   141  func (t *Transport) http2AddConn(scheme, authority string, nc net.Conn) (RoundTripper, error) {
   142  	if t.h2Transport == nil {
   143  		return nil, errors.ErrUnsupported
   144  	}
   145  	err := t.h2Transport.AddConn(scheme, authority, nc)
   146  	if err != nil {
   147  		return nil, err
   148  	}
   149  	return http2RoundTripper{t.h2Transport, false}, nil
   150  }
   151  
   152  // http2NewClientConn creates an HTTP/2 genericClientConn (used to implement ClientConn) from nc.
   153  // The connection is not added to the HTTP/2 connection pool.
   154  func (t *Transport) http2NewClientConn(nc net.Conn, internalStateHook func()) (genericClientConn, error) {
   155  	if t.h2Transport == nil {
   156  		return nil, errors.ErrUnsupported
   157  	}
   158  	cc, err := t.h2Transport.NewClientConn(nc, internalStateHook)
   159  	if err != nil {
   160  		return nil, err
   161  	}
   162  	return http2ClientConn{cc}, nil
   163  }
   164  
   165  // http2NewClientConnFromContext creates a *ClientConn from a net.Conn.
   166  //
   167  // Transport.NewClientConn takes an address and dials a new net.Conn.
   168  // We don't currently provide a simple way for the user to provide a net.Conn and get a
   169  // *ClientConn out of it (although we do let the user provide their own Transport.DialContext,
   170  // which can be used to effectively do this).
   171  //
   172  // x/net/http2.Transport.NewClientConn, in contrast, requires the user to provide a net.Conn.
   173  // To support implementing the x/net/http2 NewClientConn in terms of a net/http.Transport,
   174  // we permit x/net/http2 to pass us a net.Conn via a context key.
   175  //
   176  // http2NewClientConnFromContext handles extracting the net.Conn from the Context
   177  // (when present) and creating a *ClientConn from it.
   178  func (t *Transport) http2NewClientConnFromContext(ctx context.Context) (*ClientConn, error) {
   179  	if t.h2Config == nil {
   180  		return nil, errors.ErrUnsupported
   181  	}
   182  	nc := t.h2Config.ConnFromContext(ctx)
   183  	if nc == nil {
   184  		return nil, errors.ErrUnsupported
   185  	}
   186  	if t.h2Transport == nil {
   187  		return nil, errors.New("http: Transport does not support HTTP/2")
   188  	}
   189  	cc := &ClientConn{}
   190  	gc, err := t.http2NewClientConn(nc, cc.maybeRunStateHook)
   191  	if err != nil {
   192  		return nil, err
   193  	}
   194  	cc.stateHookMu.Lock()
   195  	defer cc.stateHookMu.Unlock()
   196  	cc.cc = gc
   197  	cc.lastAvailable = gc.Available()
   198  	return cc, nil
   199  }
   200  
   201  // http2ExternalDial creates a new HTTP/2 connection,
   202  // using the x/net/http2.Transport's dial functions.
   203  //
   204  // This is used when the user has called x/net/http2.Transport.RoundTrip.
   205  // If the RoundTrip needs to create a new connection,
   206  // the historical behavior is for it to use the http2.Transport's DialTLS or DialTLSContext
   207  // functions, and not any dial functions on the http.Transport.
   208  func (t *Transport) http2ExternalDial(ctx context.Context, cm connectMethod) (RoundTripper, error) {
   209  	if t.h2Config == nil {
   210  		return nil, errors.ErrUnsupported
   211  	}
   212  	nc, err := t.h2Config.DialFromContext(ctx, "tcp", cm.targetAddr)
   213  	if err != nil {
   214  		return nil, err
   215  	}
   216  	return t.http2AddConn(cm.targetScheme, cm.targetAddr, nc)
   217  }
   218  
   219  type http2RoundTripper struct {
   220  	t                *http2.Transport
   221  	mapCachedConnErr bool
   222  }
   223  
   224  func (rt http2RoundTripper) RoundTrip(req *Request) (*Response, error) {
   225  	resp, err := http2RoundTrip(req, rt.t.RoundTrip)
   226  	if err != nil {
   227  		if rt.mapCachedConnErr && http2isNoCachedConnError(err) {
   228  			err = ErrSkipAltProtocol
   229  		}
   230  		return nil, err
   231  	}
   232  	return resp, nil
   233  }
   234  
   235  type http2ClientConn struct {
   236  	http2.NetHTTPClientConn
   237  }
   238  
   239  func (cc http2ClientConn) RoundTrip(req *Request) (*Response, error) {
   240  	return http2RoundTrip(req, cc.NetHTTPClientConn.RoundTrip)
   241  }
   242  
   243  // transportConfig implements the http2.TransportConfig interface,
   244  // providing the net/http Transport's configuration to the HTTP/2 implementation.
   245  //
   246  // When an x/net/http2 Transport has provided a configuration (see http2ExternalTransportConfig),
   247  // the transportConfig merges the x/net/http2 and net/http Transport configurations.
   248  type transportConfig struct {
   249  	t *Transport
   250  }
   251  
   252  func (t transportConfig) MaxResponseHeaderBytes() int64        { return t.t.MaxResponseHeaderBytes }
   253  func (t transportConfig) DisableKeepAlives() bool              { return t.t.DisableKeepAlives }
   254  func (t transportConfig) ExpectContinueTimeout() time.Duration { return t.t.ExpectContinueTimeout }
   255  func (t transportConfig) ResponseHeaderTimeout() time.Duration { return t.t.ResponseHeaderTimeout }
   256  
   257  func (t transportConfig) MaxHeaderListSize() int64 {
   258  	if t.t.h2Config != nil {
   259  		return t.t.h2Config.MaxHeaderListSize()
   260  	}
   261  	return 0
   262  }
   263  
   264  func (t transportConfig) DisableCompression() bool {
   265  	if t.t.h2Config != nil && t.t.h2Config.DisableCompression() {
   266  		return true
   267  	}
   268  	return t.t.DisableCompression
   269  }
   270  
   271  func (t transportConfig) IdleConnTimeout() time.Duration {
   272  	// Unlike most config settings, historically IdleConnTimeout prefers the
   273  	// http2.Transport's setting over the http.Transport.
   274  	if t.t.h2Config != nil {
   275  		if timeout := t.t.h2Config.IdleConnTimeout(); timeout != 0 {
   276  			return timeout
   277  		}
   278  	}
   279  	return t.t.IdleConnTimeout
   280  }
   281  
   282  func (t transportConfig) HTTP2Config() http2.Config {
   283  	return mergeHTTP2Config(t.t.HTTP2, t.t.h2Config)
   284  }
   285  
   286  // transportFromH1Transport provides a way for HTTP/2 tests to extract
   287  // the http2.Transport from an http.Transport.
   288  //
   289  //go:linkname transportFromH1Transport net/http/internal/http2_test.transportFromH1Transport
   290  func transportFromH1Transport(t *Transport) any {
   291  	t.nextProtoOnce.Do(t.onceSetNextProtoDefaults)
   292  	return t.h2Transport
   293  }
   294  

View as plain text