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  		ContentLength: req.ContentLength,
   117  		Cancel:        req.Cancel,
   118  		Close:         req.Close,
   119  		ResTrailer:    (*http2.Header)(&resp.Trailer),
   120  	})
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  	resp.Status = cresp.Status + " " + StatusText(cresp.StatusCode)
   125  	resp.StatusCode = cresp.StatusCode
   126  	resp.Proto = "HTTP/2.0"
   127  	resp.ProtoMajor = 2
   128  	resp.ProtoMinor = 0
   129  	resp.ContentLength = cresp.ContentLength
   130  	resp.Uncompressed = cresp.Uncompressed
   131  	resp.Header = Header(cresp.Header)
   132  	resp.Trailer = Header(cresp.Trailer)
   133  	resp.Body = cresp.Body
   134  	resp.TLS = cresp.TLS
   135  	resp.Request = req
   136  	return resp, nil
   137  }
   138  
   139  // http2AddConn adds nc to the HTTP/2 connection pool.
   140  func (t *Transport) http2AddConn(scheme, authority string, nc net.Conn) (genericClientConn, error) {
   141  	if t.h2Transport == nil {
   142  		return nil, errors.ErrUnsupported
   143  	}
   144  	err := t.h2Transport.AddConn(scheme, authority, nc)
   145  	if err != nil {
   146  		return nil, err
   147  	}
   148  	return &altClientConn{
   149  		rt:    http2RoundTripper{t.h2Transport, false},
   150  		proto: 2,
   151  	}, nil
   152  }
   153  
   154  // http2NewClientConn creates an HTTP/2 genericClientConn (used to implement ClientConn) from nc.
   155  // The connection is not added to the HTTP/2 connection pool.
   156  func (t *Transport) http2NewClientConn(nc net.Conn, internalStateHook func()) (genericClientConn, error) {
   157  	if t.h2Transport == nil {
   158  		return nil, errors.ErrUnsupported
   159  	}
   160  	cc, err := t.h2Transport.NewClientConn(nc, internalStateHook)
   161  	if err != nil {
   162  		return nil, err
   163  	}
   164  	return externalHTTP2Conn{http2ClientConn{cc}}, nil
   165  }
   166  
   167  // http2NewClientConnFromContext creates a *ClientConn from a net.Conn.
   168  //
   169  // Transport.NewClientConn takes an address and dials a new net.Conn.
   170  // We don't currently provide a simple way for the user to provide a net.Conn and get a
   171  // *ClientConn out of it (although we do let the user provide their own Transport.DialContext,
   172  // which can be used to effectively do this).
   173  //
   174  // x/net/http2.Transport.NewClientConn, in contrast, requires the user to provide a net.Conn.
   175  // To support implementing the x/net/http2 NewClientConn in terms of a net/http.Transport,
   176  // we permit x/net/http2 to pass us a net.Conn via a context key.
   177  //
   178  // http2NewClientConnFromContext handles extracting the net.Conn from the Context
   179  // (when present) and creating a *ClientConn from it.
   180  func (t *Transport) http2NewClientConnFromContext(ctx context.Context) (*ClientConn, error) {
   181  	if t.h2Config == nil {
   182  		return nil, errors.ErrUnsupported
   183  	}
   184  	nc := t.h2Config.ConnFromContext(ctx)
   185  	if nc == nil {
   186  		return nil, errors.ErrUnsupported
   187  	}
   188  	if t.h2Transport == nil {
   189  		return nil, errors.New("http: Transport does not support HTTP/2")
   190  	}
   191  	cc := &ClientConn{}
   192  	gc, err := t.http2NewClientConn(nc, cc.maybeRunStateHook)
   193  	if err != nil {
   194  		return nil, err
   195  	}
   196  	cc.stateHookMu.Lock()
   197  	defer cc.stateHookMu.Unlock()
   198  	cc.cc = gc
   199  	cc.lastAvailable = gc.Available()
   200  	return cc, nil
   201  }
   202  
   203  // http2ExternalDial creates a new HTTP/2 connection,
   204  // using the x/net/http2.Transport's dial functions.
   205  //
   206  // This is used when the user has called x/net/http2.Transport.RoundTrip.
   207  // If the RoundTrip needs to create a new connection,
   208  // the historical behavior is for it to use the http2.Transport's DialTLS or DialTLSContext
   209  // functions, and not any dial functions on the http.Transport.
   210  func (t *Transport) http2ExternalDial(ctx context.Context, cm connectMethod) (genericClientConn, error) {
   211  	if t.h2Config == nil {
   212  		return nil, errors.ErrUnsupported
   213  	}
   214  	nc, err := t.h2Config.DialFromContext(ctx, "tcp", cm.targetAddr)
   215  	if err != nil {
   216  		return nil, err
   217  	}
   218  	return t.http2AddConn(cm.targetScheme, cm.targetAddr, nc)
   219  }
   220  
   221  type http2RoundTripper struct {
   222  	t                *http2.Transport
   223  	mapCachedConnErr bool
   224  }
   225  
   226  func (rt http2RoundTripper) RoundTrip(req *Request) (*Response, error) {
   227  	resp, err := http2RoundTrip(req, rt.t.RoundTrip)
   228  	if err != nil {
   229  		if rt.mapCachedConnErr && http2isNoCachedConnError(err) {
   230  			err = ErrSkipAltProtocol
   231  		}
   232  		return nil, err
   233  	}
   234  	return resp, nil
   235  }
   236  
   237  type http2ClientConn struct {
   238  	http2.NetHTTPClientConn
   239  }
   240  
   241  func (cc http2ClientConn) RoundTrip(req *Request) (*Response, error) {
   242  	return http2RoundTrip(req, cc.NetHTTPClientConn.RoundTrip)
   243  }
   244  
   245  // transportConfig implements the http2.TransportConfig interface,
   246  // providing the net/http Transport's configuration to the HTTP/2 implementation.
   247  //
   248  // When an x/net/http2 Transport has provided a configuration (see http2ExternalTransportConfig),
   249  // the transportConfig merges the x/net/http2 and net/http Transport configurations.
   250  type transportConfig struct {
   251  	t *Transport
   252  }
   253  
   254  func (t transportConfig) MaxResponseHeaderBytes() int64        { return t.t.MaxResponseHeaderBytes }
   255  func (t transportConfig) DisableKeepAlives() bool              { return t.t.DisableKeepAlives }
   256  func (t transportConfig) ExpectContinueTimeout() time.Duration { return t.t.ExpectContinueTimeout }
   257  func (t transportConfig) ResponseHeaderTimeout() time.Duration { return t.t.ResponseHeaderTimeout }
   258  
   259  func (t transportConfig) MaxHeaderListSize() int64 {
   260  	if t.t.h2Config != nil {
   261  		return t.t.h2Config.MaxHeaderListSize()
   262  	}
   263  	return 0
   264  }
   265  
   266  func (t transportConfig) DisableCompression() bool {
   267  	if t.t.h2Config != nil && t.t.h2Config.DisableCompression() {
   268  		return true
   269  	}
   270  	return t.t.DisableCompression
   271  }
   272  
   273  func (t transportConfig) IdleConnTimeout() time.Duration {
   274  	// Unlike most config settings, historically IdleConnTimeout prefers the
   275  	// http2.Transport's setting over the http.Transport.
   276  	if t.t.h2Config != nil {
   277  		if timeout := t.t.h2Config.IdleConnTimeout(); timeout != 0 {
   278  			return timeout
   279  		}
   280  	}
   281  	return t.t.IdleConnTimeout
   282  }
   283  
   284  func (t transportConfig) HTTP2Config() http2.Config {
   285  	return mergeHTTP2Config(t.t.HTTP2, t.t.h2Config)
   286  }
   287  
   288  // transportFromH1Transport provides a way for HTTP/2 tests to extract
   289  // the http2.Transport from an http.Transport.
   290  //
   291  //go:linkname transportFromH1Transport net/http/internal/http2_test.transportFromH1Transport
   292  func transportFromH1Transport(t *Transport) any {
   293  	t.nextProtoOnce.Do(t.onceSetNextProtoDefaults)
   294  	return t.h2Transport
   295  }
   296  

View as plain text