Source file src/net/http/http2.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
     6  
     7  package http
     8  
     9  import (
    10  	"context"
    11  	"crypto/tls"
    12  	"errors"
    13  	"io"
    14  	"log"
    15  	"net"
    16  	"net/http/internal/http2"
    17  	"time"
    18  
    19  	_ "unsafe" // for go:linkname
    20  )
    21  
    22  // net/http supports HTTP/2 by default, but this support is removed when
    23  // the nethttpomithttp2 build tag is set.
    24  //
    25  // HTTP/2 support is provided by the net/http/internal/http2 package.
    26  //
    27  // This file (http2.go) connects net/http to the http2 package.
    28  // Since http imports http2, to avoid an import cycle we need to
    29  // translate http package types (e.g., Request) into the equivalent
    30  // http2 package types (e.g., http2.ClientRequest).
    31  //
    32  // The golang.org/x/net/http2 package is the original source of truth for
    33  // the HTTP/2 implementation. At this time, users may still import that
    34  // package and register its implementation on a net/http Transport or Server.
    35  // However, the x/net package is no longer synchronized with std.
    36  
    37  func init() {
    38  	// NoBody and LocalAddrContextKey need to have the same value
    39  	// in the http and http2 packages.
    40  	//
    41  	// We can't define these values in net/http/internal,
    42  	// because their concrete types are part of the net/http API and
    43  	// moving them causes API checker failures.
    44  	// Override the http2 package versions at init time instead.
    45  	http2.LocalAddrContextKey = LocalAddrContextKey
    46  	http2.NoBody = NoBody
    47  }
    48  
    49  type http2Server = http2.Server
    50  type http2Transport = http2.Transport
    51  
    52  func (s *Server) configureHTTP2() {
    53  	h2srv := &http2.Server{}
    54  
    55  	// Historically, we've configured the HTTP/2 idle timeout in this fashion:
    56  	// Set once at configuration time.
    57  	if s.IdleTimeout != 0 {
    58  		s.h2IdleTimeout = s.IdleTimeout
    59  	} else {
    60  		s.h2IdleTimeout = s.ReadTimeout
    61  	}
    62  
    63  	if s.TLSConfig == nil {
    64  		s.TLSConfig = &tls.Config{}
    65  	}
    66  	s.nextProtoErr = h2srv.Configure(http2ServerConfig{s}, s.TLSConfig)
    67  	if s.nextProtoErr != nil {
    68  		return
    69  	}
    70  
    71  	s.RegisterOnShutdown(h2srv.GracefulShutdown)
    72  
    73  	if s.TLSNextProto == nil {
    74  		s.TLSNextProto = make(map[string]func(*Server, *tls.Conn, Handler))
    75  	}
    76  	// Historically, the presence of a TLSNextProto["h2"] key has been the signal to
    77  	// enable/disable HTTP/2 support. Set a value in the map, but we'll never use it.
    78  	s.TLSNextProto["h2"] = func(hs *Server, c *tls.Conn, h Handler) {
    79  		c.Close()
    80  	}
    81  
    82  	s.h2 = h2srv
    83  }
    84  
    85  func (s *Server) setHTTP2Config(conf http2ExternalServerConfig) {
    86  	if s.h2Config != nil {
    87  		panic("http: HTTP/2 Server already registered")
    88  	}
    89  	s.h2Config = conf
    90  	s.h2Config.ServeConnFunc(s.serveHTTP2Conn)
    91  }
    92  
    93  func (s *Server) serveHTTP2Conn(ctx context.Context, nc net.Conn, h Handler, sawClientPreface bool, upgradeReq *Request, settings []byte) {
    94  	s.setupHTTP2_ServeTLS()
    95  	var serverUpgradeReq *http2.ServerRequest
    96  	if upgradeReq != nil {
    97  		serverUpgradeReq = http2ServerRequestFromRequest(upgradeReq)
    98  	}
    99  	s.h2.ServeConn(nc, &http2.ServeConnOpts{
   100  		Context:          ctx,
   101  		Handler:          http2Handler{h},
   102  		BaseConfig:       http2ServerConfig{s},
   103  		SawClientPreface: sawClientPreface,
   104  		UpgradeRequest:   serverUpgradeReq,
   105  		Settings:         settings,
   106  	})
   107  }
   108  
   109  func http2ServerRequestFromRequest(req *Request) *http2.ServerRequest {
   110  	return &http2.ServerRequest{
   111  		Context:       req.Context(),
   112  		Proto:         req.Proto,
   113  		ProtoMajor:    req.ProtoMajor,
   114  		ProtoMinor:    req.ProtoMinor,
   115  		Method:        req.Method,
   116  		URL:           req.URL,
   117  		Header:        http2.Header(req.Header),
   118  		Trailer:       http2.Header(req.Trailer),
   119  		Body:          req.Body,
   120  		Host:          req.Host,
   121  		ContentLength: req.ContentLength,
   122  		RemoteAddr:    req.RemoteAddr,
   123  		RequestURI:    req.RequestURI,
   124  		TLS:           req.TLS,
   125  		MultipartForm: req.MultipartForm,
   126  	}
   127  }
   128  
   129  type http2Handler struct {
   130  	h Handler
   131  }
   132  
   133  func (h http2Handler) ServeHTTP(w *http2.ResponseWriter, req *http2.ServerRequest) {
   134  	h.h.ServeHTTP(http2ResponseWriter{w}, &Request{
   135  		ctx:           req.Context,
   136  		Proto:         "HTTP/2.0",
   137  		ProtoMajor:    2,
   138  		ProtoMinor:    0,
   139  		Method:        req.Method,
   140  		URL:           req.URL,
   141  		Header:        Header(req.Header),
   142  		RequestURI:    req.RequestURI,
   143  		Trailer:       Header(req.Trailer),
   144  		Body:          req.Body,
   145  		Host:          req.Host,
   146  		ContentLength: req.ContentLength,
   147  		RemoteAddr:    req.RemoteAddr,
   148  		TLS:           req.TLS,
   149  		MultipartForm: req.MultipartForm,
   150  	})
   151  }
   152  
   153  type http2ResponseWriter struct {
   154  	*http2.ResponseWriter
   155  }
   156  
   157  // Optional http.ResponseWriter interfaces implemented.
   158  var (
   159  	_ CloseNotifier   = http2ResponseWriter{}
   160  	_ Flusher         = http2ResponseWriter{}
   161  	_ io.StringWriter = http2ResponseWriter{}
   162  )
   163  
   164  func (w http2ResponseWriter) Flush()            { w.ResponseWriter.FlushError() }
   165  func (w http2ResponseWriter) FlushError() error { return w.ResponseWriter.FlushError() }
   166  
   167  func (w http2ResponseWriter) Header() Header { return Header(w.ResponseWriter.Header()) }
   168  
   169  func (w http2ResponseWriter) Push(target string, opts *PushOptions) error {
   170  	var (
   171  		method string
   172  		header http2.Header
   173  	)
   174  	if opts != nil {
   175  		method = opts.Method
   176  		header = http2.Header(opts.Header)
   177  	}
   178  	err := w.ResponseWriter.Push(target, method, header)
   179  	if err == http2.ErrNotSupported {
   180  		err = ErrNotSupported
   181  	}
   182  	return err
   183  }
   184  
   185  type http2ServerConfig struct {
   186  	s *Server
   187  }
   188  
   189  func (s http2ServerConfig) MaxHeaderBytes() int { return s.s.MaxHeaderBytes }
   190  func (s http2ServerConfig) ConnState(c net.Conn, st http2.ConnState) {
   191  	if s.s.ConnState != nil {
   192  		s.s.ConnState(c, ConnState(st))
   193  	}
   194  }
   195  func (s http2ServerConfig) DoKeepAlives() bool             { return s.s.doKeepAlives() }
   196  func (s http2ServerConfig) WriteTimeout() time.Duration    { return s.s.WriteTimeout }
   197  func (s http2ServerConfig) SendPingTimeout() time.Duration { return s.s.ReadTimeout }
   198  func (s http2ServerConfig) ErrorLog() *log.Logger          { return s.s.ErrorLog }
   199  func (s http2ServerConfig) ReadTimeout() time.Duration     { return s.s.ReadTimeout }
   200  func (s http2ServerConfig) DisableClientPriority() bool    { return s.s.DisableClientPriority }
   201  
   202  func (s http2ServerConfig) IdleTimeout() time.Duration {
   203  	if s.s.h2Config != nil {
   204  		return s.s.h2Config.IdleTimeout()
   205  	}
   206  	return s.s.h2IdleTimeout
   207  }
   208  
   209  func (s http2ServerConfig) HTTP2Config() http2.Config {
   210  	return mergeHTTP2Config(s.s.HTTP2, s.s.h2Config)
   211  }
   212  
   213  // http2ExternalServerConfig is an HTTP/2 configuration provided by x/net/http2.
   214  //
   215  // When a x/net/http2.Server wraps a net/http.Server, we need to support the user
   216  // setting configuration settings on the x/net Server:
   217  //
   218  //	s1 := &http.Server{}
   219  //	s2 := &http2.Server{}
   220  //	http2.ConfigureServer(s1, s2)
   221  //
   222  //	// This setting needs to affect s1:
   223  //	s2.MaxReadFrameSize = 10000
   224  //
   225  // We handle this by having http2.ConfigureServer pass us an http2ExternalServerConfig
   226  // (see http.Server.Serve) which we can use to query the current state of the http2.Server.
   227  type http2ExternalServerConfig interface {
   228  	// Various configuration settings:
   229  	HTTP2Config() HTTP2Config
   230  	IdleTimeout() time.Duration
   231  
   232  	// ServeConnFunc provides a function to the x/net/http2.Server which it
   233  	// can use to serve a new connection.
   234  	ServeConnFunc(func(ctx context.Context, nc net.Conn, h Handler, sawClientPreface bool, upgradeReq *Request, settings []byte))
   235  }
   236  
   237  // http2ExternalTransportConfig is an HTTP/2 configuration provided by x/net/http2.
   238  //
   239  // When a x/net/http2.Transport wraps a net/http.Transport, we need to support the user
   240  // setting configuration settings on the x/net Transport:
   241  //
   242  //	tr1 := &http.Transport{}
   243  //	tr2 := http2.ConfigureTransports(t1)
   244  //
   245  //	// This setting needs to affect tr1:
   246  //	tr2.MaxHeaderListSize = 10000
   247  //
   248  // We handle this by having http2.ConfigureTransports pass us an http2ExternalTransportConfig,
   249  // which we can use to query the current state of the http2.Transport.
   250  type http2ExternalTransportConfig interface {
   251  	// Various configuration settings:
   252  	HTTP2Config() HTTP2Config
   253  	DisableCompression() bool
   254  	MaxHeaderListSize() int64
   255  	IdleConnTimeout() time.Duration
   256  
   257  	// ConnFromContext is used to pass a net.Conn to Transport.NewClientConn
   258  	// via a context value. See Transport.http2NewClientConnFromContext.
   259  	ConnFromContext(context.Context) net.Conn
   260  
   261  	// DialFromContext is used to dial new connections, overriding Transport.DialContext etc.
   262  	// This is used when the user calls x/net/http2.Transport.RoundTrip directly,
   263  	// in which case the historical behavior is to use the http2.Transport's dial functions.
   264  	DialFromContext(ctx context.Context, network, addr string) (net.Conn, error)
   265  
   266  	// ExternalRoundTrip reports whether Transport.RoundTrip should call the
   267  	// external transport's RoundTrip. This is used when x/net/http2.Transport.ConnPool
   268  	// is set, in which case the user-provided ClientConnPool has taken responsibility
   269  	// for picking a connection to use.
   270  	ExternalRoundTrip() bool
   271  
   272  	// RoundTrip performs a round trip.
   273  	// It should only be used when ExternalRoundTrip requests it.
   274  	RoundTrip(*Request) (*Response, error)
   275  
   276  	// Registered is called to report successful registration of the config.
   277  	Registered(*Transport)
   278  }
   279  
   280  func (t *Transport) configureHTTP2(protocols Protocols) {
   281  	if t.TLSClientConfig == nil {
   282  		t.TLSClientConfig = &tls.Config{}
   283  	}
   284  	if t.HTTP2 == nil {
   285  		t.HTTP2 = &HTTP2Config{}
   286  	}
   287  	t2 := http2.NewTransport(transportConfig{t})
   288  	t2.AllowHTTP = true
   289  	t.h2Transport = t2
   290  
   291  	t.registerProtocol("https", http2RoundTripper{t2, true})
   292  	if t.TLSNextProto == nil {
   293  		t.TLSNextProto = make(map[string]func(authority string, c *tls.Conn) RoundTripper)
   294  	}
   295  	// Historically, the presence of a TLSNextProto["h2"] key has been the signal to
   296  	// enable/disable HTTP/2 support. Set a value in the map, but we'll never use it.
   297  	t.TLSNextProto["h2"] = func(authority string, c *tls.Conn) RoundTripper {
   298  		return http2ErringRoundTripper{
   299  			errors.New("unexpected use of stub RoundTripper"),
   300  		}
   301  	}
   302  
   303  	// Auto-configure the http2.Transport's MaxHeaderListSize from
   304  	// the http.Transport's MaxResponseHeaderBytes. They don't
   305  	// exactly mean the same thing, but they're close.
   306  	if limit1 := t.MaxResponseHeaderBytes; limit1 != 0 && t2.MaxHeaderListSize == 0 {
   307  		const h2max = 1<<32 - 1
   308  		if limit1 >= h2max {
   309  			t2.MaxHeaderListSize = h2max
   310  		} else {
   311  			t2.MaxHeaderListSize = uint32(limit1)
   312  		}
   313  	}
   314  
   315  	// Server.ServeTLS clones the tls.Config before modifying it.
   316  	// Transport doesn't. We may want to make the two consistent some day.
   317  	//
   318  	// http2configureTransport will have already set NextProtos, but adjust it again
   319  	// here to remove HTTP/1.1 if the user has disabled it.
   320  	t.TLSClientConfig.NextProtos = adjustNextProtos(t.TLSClientConfig.NextProtos, protocols)
   321  }
   322  
   323  type http2ErringRoundTripper struct{ err error }
   324  
   325  func (rt http2ErringRoundTripper) RoundTripErr() error                   { return rt.err }
   326  func (rt http2ErringRoundTripper) RoundTrip(*Request) (*Response, error) { return nil, rt.err }
   327  
   328  func http2RoundTrip(req *Request, rt func(*http2.ClientRequest) (*http2.ClientResponse, error)) (*Response, error) {
   329  	resp := &Response{}
   330  	cresp, err := rt(&http2.ClientRequest{
   331  		Context:       req.Context(),
   332  		Method:        req.Method,
   333  		URL:           req.URL,
   334  		Header:        http2.Header(req.Header),
   335  		Trailer:       http2.Header(req.Trailer),
   336  		Body:          req.Body,
   337  		Host:          req.Host,
   338  		GetBody:       req.GetBody,
   339  		ContentLength: req.ContentLength,
   340  		Cancel:        req.Cancel,
   341  		Close:         req.Close,
   342  		ResTrailer:    (*http2.Header)(&resp.Trailer),
   343  	})
   344  	if err != nil {
   345  		return nil, err
   346  	}
   347  	resp.Status = cresp.Status + " " + StatusText(cresp.StatusCode)
   348  	resp.StatusCode = cresp.StatusCode
   349  	resp.Proto = "HTTP/2.0"
   350  	resp.ProtoMajor = 2
   351  	resp.ProtoMinor = 0
   352  	resp.ContentLength = cresp.ContentLength
   353  	resp.Uncompressed = cresp.Uncompressed
   354  	resp.Header = Header(cresp.Header)
   355  	resp.Trailer = Header(cresp.Trailer)
   356  	resp.Body = cresp.Body
   357  	resp.TLS = cresp.TLS
   358  	resp.Request = req
   359  	return resp, nil
   360  }
   361  
   362  // http2AddConn adds nc to the HTTP/2 connection pool.
   363  func (t *Transport) http2AddConn(scheme, authority string, nc net.Conn) (RoundTripper, error) {
   364  	if t.h2Transport == nil {
   365  		return nil, errors.ErrUnsupported
   366  	}
   367  	err := t.h2Transport.AddConn(scheme, authority, nc)
   368  	if err != nil {
   369  		return nil, err
   370  	}
   371  	return http2RoundTripper{t.h2Transport, false}, nil
   372  }
   373  
   374  // http2NewClientConn creates an HTTP/2 genericClientConn (used to implement ClientConn) from nc.
   375  // The connection is not added to the HTTP/2 connection pool.
   376  func (t *Transport) http2NewClientConn(nc net.Conn, internalStateHook func()) (genericClientConn, error) {
   377  	if t.h2Transport == nil {
   378  		return nil, errors.ErrUnsupported
   379  	}
   380  	cc, err := t.h2Transport.NewClientConn(nc, internalStateHook)
   381  	if err != nil {
   382  		return nil, err
   383  	}
   384  	return http2ClientConn{cc}, nil
   385  }
   386  
   387  // http2NewClientConnFromContext creates a *ClientConn from a net.Conn.
   388  //
   389  // Transport.NewClientConn takes an address and dials a new net.Conn.
   390  // We don't currently provide a simple way for the user to provide a net.Conn and get a
   391  // *ClientConn out of it (although we do let the user provide their own Transport.DialContext,
   392  // which can be used to effectively do this).
   393  //
   394  // x/net/http2.Transport.NewClientConn, in contrast, requires the user to provide a net.Conn.
   395  // To support implementing the x/net/http2 NewClientConn in terms of a net/http.Transport,
   396  // we permit x/net/http2 to pass us a net.Conn via a context key.
   397  //
   398  // http2NewClientConnFromContext handles extracting the net.Conn from the Context
   399  // (when present) and creating a *ClientConn from it.
   400  func (t *Transport) http2NewClientConnFromContext(ctx context.Context) (*ClientConn, error) {
   401  	if t.h2Config == nil {
   402  		return nil, errors.ErrUnsupported
   403  	}
   404  	nc := t.h2Config.ConnFromContext(ctx)
   405  	if nc == nil {
   406  		return nil, errors.ErrUnsupported
   407  	}
   408  	if t.h2Transport == nil {
   409  		return nil, errors.New("http: Transport does not support HTTP/2")
   410  	}
   411  	cc := &ClientConn{}
   412  	gc, err := t.http2NewClientConn(nc, cc.maybeRunStateHook)
   413  	if err != nil {
   414  		return nil, err
   415  	}
   416  	cc.stateHookMu.Lock()
   417  	defer cc.stateHookMu.Unlock()
   418  	cc.cc = gc
   419  	cc.lastAvailable = gc.Available()
   420  	return cc, nil
   421  }
   422  
   423  // http2ExternalDial creates a new HTTP/2 connection,
   424  // using the x/net/http2.Transport's dial functions.
   425  //
   426  // This is used when the user has called x/net/http2.Transport.RoundTrip.
   427  // If the RoundTrip needs to create a new connection,
   428  // the historical behavior is for it to use the http2.Transport's DialTLS or DialTLSContext
   429  // functions, and not any dial functions on the http.Transport.
   430  func (t *Transport) http2ExternalDial(ctx context.Context, cm connectMethod) (RoundTripper, error) {
   431  	if t.h2Config == nil {
   432  		return nil, errors.ErrUnsupported
   433  	}
   434  	nc, err := t.h2Config.DialFromContext(ctx, "tcp", cm.targetAddr)
   435  	if err != nil {
   436  		return nil, err
   437  	}
   438  	return t.http2AddConn(cm.targetScheme, cm.targetAddr, nc)
   439  }
   440  
   441  type http2RoundTripper struct {
   442  	t                *http2.Transport
   443  	mapCachedConnErr bool
   444  }
   445  
   446  func (rt http2RoundTripper) RoundTrip(req *Request) (*Response, error) {
   447  	resp, err := http2RoundTrip(req, rt.t.RoundTrip)
   448  	if err != nil {
   449  		if rt.mapCachedConnErr && http2isNoCachedConnError(err) {
   450  			err = ErrSkipAltProtocol
   451  		}
   452  		return nil, err
   453  	}
   454  	return resp, nil
   455  }
   456  
   457  type http2ClientConn struct {
   458  	http2.NetHTTPClientConn
   459  }
   460  
   461  func (cc http2ClientConn) RoundTrip(req *Request) (*Response, error) {
   462  	return http2RoundTrip(req, cc.NetHTTPClientConn.RoundTrip)
   463  }
   464  
   465  // transportConfig implements the http2.TransportConfig interface,
   466  // providing the net/http Transport's configuration to the HTTP/2 implementation.
   467  //
   468  // When an x/net/http2 Transport has provided a configuration (see http2ExternalTransportConfig),
   469  // the transportConfig merges the x/net/http2 and net/http Transport configurations.
   470  type transportConfig struct {
   471  	t *Transport
   472  }
   473  
   474  func (t transportConfig) MaxResponseHeaderBytes() int64        { return t.t.MaxResponseHeaderBytes }
   475  func (t transportConfig) DisableKeepAlives() bool              { return t.t.DisableKeepAlives }
   476  func (t transportConfig) ExpectContinueTimeout() time.Duration { return t.t.ExpectContinueTimeout }
   477  func (t transportConfig) ResponseHeaderTimeout() time.Duration { return t.t.ResponseHeaderTimeout }
   478  
   479  func (t transportConfig) MaxHeaderListSize() int64 {
   480  	if t.t.h2Config != nil {
   481  		return t.t.h2Config.MaxHeaderListSize()
   482  	}
   483  	return 0
   484  }
   485  
   486  func (t transportConfig) DisableCompression() bool {
   487  	if t.t.h2Config != nil && t.t.h2Config.DisableCompression() {
   488  		return true
   489  	}
   490  	return t.t.DisableCompression
   491  }
   492  
   493  func (t transportConfig) IdleConnTimeout() time.Duration {
   494  	// Unlike most config settings, historically IdleConnTimeout prefers the
   495  	// http2.Transport's setting over the http.Transport.
   496  	if t.t.h2Config != nil {
   497  		if timeout := t.t.h2Config.IdleConnTimeout(); timeout != 0 {
   498  			return timeout
   499  		}
   500  	}
   501  	return t.t.IdleConnTimeout
   502  }
   503  
   504  type http2Configer interface {
   505  	HTTP2Config() HTTP2Config
   506  }
   507  
   508  func mergeHTTP2Config(c1 *HTTP2Config, confer http2Configer) http2.Config {
   509  	if c1 == nil && confer == nil {
   510  		return http2.Config{}
   511  	}
   512  	var c http2.Config
   513  	if c1 != nil {
   514  		c = (http2.Config)(*c1)
   515  	}
   516  	var c2 HTTP2Config
   517  	if confer != nil {
   518  		c2 = confer.HTTP2Config()
   519  	}
   520  	if c.MaxConcurrentStreams == 0 {
   521  		c.MaxConcurrentStreams = c2.MaxConcurrentStreams
   522  	}
   523  	if c2.StrictMaxConcurrentRequests {
   524  		c.StrictMaxConcurrentRequests = true
   525  	}
   526  	if c.MaxDecoderHeaderTableSize == 0 {
   527  		c.MaxDecoderHeaderTableSize = c2.MaxDecoderHeaderTableSize
   528  	}
   529  	if c.MaxEncoderHeaderTableSize == 0 {
   530  		c.MaxEncoderHeaderTableSize = c2.MaxEncoderHeaderTableSize
   531  	}
   532  	if c.MaxReadFrameSize == 0 {
   533  		c.MaxReadFrameSize = c2.MaxReadFrameSize
   534  	}
   535  	if c.MaxReceiveBufferPerConnection == 0 {
   536  		c.MaxReceiveBufferPerConnection = c2.MaxReceiveBufferPerConnection
   537  	}
   538  	if c.MaxReceiveBufferPerStream == 0 {
   539  		c.MaxReceiveBufferPerStream = c2.MaxReceiveBufferPerStream
   540  	}
   541  	if c.SendPingTimeout == 0 {
   542  		c.SendPingTimeout = c2.SendPingTimeout
   543  	}
   544  	if c.PingTimeout == 0 {
   545  		c.PingTimeout = c2.PingTimeout
   546  	}
   547  	if c.WriteByteTimeout == 0 {
   548  		c.WriteByteTimeout = c2.WriteByteTimeout
   549  	}
   550  	if c2.PermitProhibitedCipherSuites {
   551  		c.PermitProhibitedCipherSuites = true
   552  	}
   553  	if c.CountError == nil {
   554  		c.CountError = c2.CountError
   555  	}
   556  	return c
   557  }
   558  
   559  func (t transportConfig) HTTP2Config() http2.Config {
   560  	return mergeHTTP2Config(t.t.HTTP2, t.t.h2Config)
   561  }
   562  
   563  // transportFromH1Transport provides a way for HTTP/2 tests to extract
   564  // the http2.Transport from an http.Transport.
   565  //
   566  //go:linkname transportFromH1Transport net/http/internal/http2_test.transportFromH1Transport
   567  func transportFromH1Transport(t *Transport) any {
   568  	t.nextProtoOnce.Do(t.onceSetNextProtoDefaults)
   569  	return t.h2Transport
   570  }
   571  

View as plain text