github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/http/transports.go (about)

     1  // Copyright (c) 2015-2022 MinIO, Inc.
     2  //
     3  // This file is part of MinIO Object Storage stack
     4  //
     5  // This program is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Affero General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // This program is distributed in the hope that it will be useful
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU Affero General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Affero General Public License
    16  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package http
    19  
    20  import (
    21  	"context"
    22  	"crypto/tls"
    23  	"crypto/x509"
    24  	"net/http"
    25  	"syscall"
    26  	"time"
    27  
    28  	"github.com/minio/pkg/v2/certs"
    29  )
    30  
    31  // tlsClientSessionCacheSize is the cache size for client sessions.
    32  var tlsClientSessionCacheSize = 100
    33  
    34  // ConnSettings - contains connection settings.
    35  type ConnSettings struct {
    36  	DialContext DialContext // Custom dialContext, DialTimeout is ignored if this is already setup.
    37  	LookupHost  LookupHost  // Custom lookupHost, is nil on containerized deployments.
    38  	DialTimeout time.Duration
    39  
    40  	// TLS Settings
    41  	RootCAs          *x509.CertPool
    42  	CipherSuites     []uint16
    43  	CurvePreferences []tls.CurveID
    44  
    45  	// HTTP2
    46  	EnableHTTP2 bool
    47  
    48  	// TCP Options
    49  	TCPOptions TCPOptions
    50  }
    51  
    52  func (s ConnSettings) getDefaultTransport(maxIdleConnsPerHost int) *http.Transport {
    53  	if maxIdleConnsPerHost <= 0 {
    54  		maxIdleConnsPerHost = 1024
    55  	}
    56  
    57  	dialContext := s.DialContext
    58  	if dialContext == nil {
    59  		dialContext = DialContextWithLookupHost(s.LookupHost, NewInternodeDialContext(s.DialTimeout, s.TCPOptions))
    60  	}
    61  
    62  	tlsClientConfig := tls.Config{
    63  		RootCAs:            s.RootCAs,
    64  		CipherSuites:       s.CipherSuites,
    65  		CurvePreferences:   s.CurvePreferences,
    66  		ClientSessionCache: tls.NewLRUClientSessionCache(tlsClientSessionCacheSize),
    67  	}
    68  
    69  	// For more details about various values used here refer
    70  	// https://golang.org/pkg/net/http/#Transport documentation
    71  	tr := &http.Transport{
    72  		Proxy:                 http.ProxyFromEnvironment,
    73  		DialContext:           dialContext,
    74  		MaxIdleConnsPerHost:   maxIdleConnsPerHost,
    75  		WriteBufferSize:       32 << 10, // 32KiB moving up from 4KiB default
    76  		ReadBufferSize:        32 << 10, // 32KiB moving up from 4KiB default
    77  		IdleConnTimeout:       15 * time.Second,
    78  		ResponseHeaderTimeout: 15 * time.Minute, // Conservative timeout is the default (for MinIO internode)
    79  		TLSHandshakeTimeout:   10 * time.Second,
    80  		TLSClientConfig:       &tlsClientConfig,
    81  		ForceAttemptHTTP2:     s.EnableHTTP2,
    82  		// Go net/http automatically unzip if content-type is
    83  		// gzip disable this feature, as we are always interested
    84  		// in raw stream.
    85  		DisableCompression: true,
    86  	}
    87  
    88  	// https://github.com/golang/go/issues/23559
    89  	// https://github.com/golang/go/issues/42534
    90  	// https://github.com/golang/go/issues/43989
    91  	// https://github.com/golang/go/issues/33425
    92  	// https://github.com/golang/go/issues/29246
    93  	// if tlsConfig != nil {
    94  	// 	trhttp2, _ := http2.ConfigureTransports(tr)
    95  	// 	if trhttp2 != nil {
    96  	// 		// ReadIdleTimeout is the timeout after which a health check using ping
    97  	// 		// frame will be carried out if no frame is received on the
    98  	// 		// connection. 5 minutes is sufficient time for any idle connection.
    99  	// 		trhttp2.ReadIdleTimeout = 5 * time.Minute
   100  	// 		// PingTimeout is the timeout after which the connection will be closed
   101  	// 		// if a response to Ping is not received.
   102  	// 		trhttp2.PingTimeout = dialTimeout
   103  	// 		// DisableCompression, if true, prevents the Transport from
   104  	// 		// requesting compression with an "Accept-Encoding: gzip"
   105  	// 		trhttp2.DisableCompression = true
   106  	// 	}
   107  	// }
   108  
   109  	return tr
   110  }
   111  
   112  // NewInternodeHTTPTransport returns transport for internode MinIO connections.
   113  func (s ConnSettings) NewInternodeHTTPTransport(maxIdleConnsPerHost int) func() http.RoundTripper {
   114  	tr := s.getDefaultTransport(maxIdleConnsPerHost)
   115  
   116  	// Settings specific to internode requests.
   117  	tr.TLSHandshakeTimeout = 15 * time.Second
   118  
   119  	return func() http.RoundTripper {
   120  		return tr
   121  	}
   122  }
   123  
   124  // NewCustomHTTPProxyTransport is used only for proxied requests, specifically
   125  // only supports HTTP/1.1
   126  func (s ConnSettings) NewCustomHTTPProxyTransport() func() *http.Transport {
   127  	s.EnableHTTP2 = false
   128  	tr := s.getDefaultTransport(0)
   129  
   130  	// Settings specific to proxied requests.
   131  	tr.ResponseHeaderTimeout = 30 * time.Minute
   132  
   133  	return func() *http.Transport {
   134  		return tr
   135  	}
   136  }
   137  
   138  // NewHTTPTransportWithTimeout allows setting a timeout for response headers
   139  func (s ConnSettings) NewHTTPTransportWithTimeout(timeout time.Duration) *http.Transport {
   140  	tr := s.getDefaultTransport(0)
   141  
   142  	// Settings specific to this transport.
   143  	tr.ResponseHeaderTimeout = timeout
   144  	return tr
   145  }
   146  
   147  // NewHTTPTransportWithClientCerts returns a new http configuration used for
   148  // communicating with client cert authentication.
   149  func (s ConnSettings) NewHTTPTransportWithClientCerts(ctx context.Context, clientCert, clientKey string) (*http.Transport, error) {
   150  	transport := s.NewHTTPTransportWithTimeout(1 * time.Minute)
   151  	if clientCert != "" && clientKey != "" {
   152  		c, err := certs.NewManager(ctx, clientCert, clientKey, tls.LoadX509KeyPair)
   153  		if err != nil {
   154  			return nil, err
   155  		}
   156  		if c != nil {
   157  			c.UpdateReloadDuration(10 * time.Second)
   158  			c.ReloadOnSignal(syscall.SIGHUP) // allow reloads upon SIGHUP
   159  			transport.TLSClientConfig.GetClientCertificate = c.GetClientCertificate
   160  		}
   161  	}
   162  	return transport, nil
   163  }
   164  
   165  // NewRemoteTargetHTTPTransport returns a new http configuration
   166  // used while communicating with the remote replication targets.
   167  func (s ConnSettings) NewRemoteTargetHTTPTransport(insecure bool) func() *http.Transport {
   168  	tr := s.getDefaultTransport(0)
   169  
   170  	tr.TLSHandshakeTimeout = 10 * time.Second
   171  	tr.ResponseHeaderTimeout = 0
   172  	tr.TLSClientConfig.InsecureSkipVerify = insecure
   173  
   174  	return func() *http.Transport {
   175  		return tr
   176  	}
   177  }