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

View as plain text