// Copyright (c) 2015-2022 MinIO, Inc. // // # This file is part of MinIO Object Storage stack // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . package http import ( "context" "crypto/tls" "crypto/x509" "net" "net/http" "syscall" "time" "github.com/minio/pkg/certs" "github.com/rs/dnscache" ) // tlsClientSessionCacheSize is the cache size for client sessions. var tlsClientSessionCacheSize = 100 // ConnSettings - contains connection settings. type ConnSettings struct { // If this is non-nil, DNSCache and DialTimeout are ignored. DialContext func(ctx context.Context, network, addr string) (net.Conn, error) // Dial settings, used if DialContext is nil. DNSCache *dnscache.Resolver DialTimeout time.Duration // TLS Settings RootCAs *x509.CertPool CipherSuites []uint16 CurvePreferences []tls.CurveID // HTTP2 EnableHTTP2 bool } func (s ConnSettings) getDefaultTransport() *http.Transport { dialContext := s.DialContext if dialContext == nil { dialContext = DialContextWithDNSCache(s.DNSCache, NewInternodeDialContext(s.DialTimeout)) } tlsClientConfig := tls.Config{ RootCAs: s.RootCAs, CipherSuites: s.CipherSuites, CurvePreferences: s.CurvePreferences, ClientSessionCache: tls.NewLRUClientSessionCache(tlsClientSessionCacheSize), } // For more details about various values used here refer // https://golang.org/pkg/net/http/#Transport documentation tr := &http.Transport{ Proxy: http.ProxyFromEnvironment, DialContext: dialContext, MaxIdleConnsPerHost: 1024, WriteBufferSize: 32 << 10, // 32KiB moving up from 4KiB default ReadBufferSize: 32 << 10, // 32KiB moving up from 4KiB default IdleConnTimeout: 15 * time.Second, ResponseHeaderTimeout: 15 * time.Minute, // Conservative timeout is the default (for MinIO internode) TLSHandshakeTimeout: 10 * time.Second, ExpectContinueTimeout: 10 * time.Second, TLSClientConfig: &tlsClientConfig, ForceAttemptHTTP2: s.EnableHTTP2, // Go net/http automatically unzip if content-type is // gzip disable this feature, as we are always interested // in raw stream. DisableCompression: true, } // https://github.com/golang/go/issues/23559 // https://github.com/golang/go/issues/42534 // https://github.com/golang/go/issues/43989 // https://github.com/golang/go/issues/33425 // https://github.com/golang/go/issues/29246 // if tlsConfig != nil { // trhttp2, _ := http2.ConfigureTransports(tr) // if trhttp2 != nil { // // ReadIdleTimeout is the timeout after which a health check using ping // // frame will be carried out if no frame is received on the // // connection. 5 minutes is sufficient time for any idle connection. // trhttp2.ReadIdleTimeout = 5 * time.Minute // // PingTimeout is the timeout after which the connection will be closed // // if a response to Ping is not received. // trhttp2.PingTimeout = dialTimeout // // DisableCompression, if true, prevents the Transport from // // requesting compression with an "Accept-Encoding: gzip" // trhttp2.DisableCompression = true // } // } return tr } // NewInternodeHTTPTransport returns transport for internode MinIO connections. func (s ConnSettings) NewInternodeHTTPTransport() func() http.RoundTripper { tr := s.getDefaultTransport() // Settings specific to internode requests. tr.TLSHandshakeTimeout = 15 * time.Second tr.ExpectContinueTimeout = 15 * time.Second return func() http.RoundTripper { return tr } } // NewCustomHTTPProxyTransport is used only for proxied requests, specifically // only supports HTTP/1.1 func (s ConnSettings) NewCustomHTTPProxyTransport() func() *http.Transport { s.EnableHTTP2 = false tr := s.getDefaultTransport() // Settings specific to proxied requests. tr.ResponseHeaderTimeout = 30 * time.Minute return func() *http.Transport { return tr } } // NewHTTPTransportWithTimeout allows setting a timeout for response headers func (s ConnSettings) NewHTTPTransportWithTimeout(timeout time.Duration) *http.Transport { tr := s.getDefaultTransport() // Settings specific to this transport. tr.ResponseHeaderTimeout = timeout return tr } // NewHTTPTransportWithClientCerts returns a new http configuration used for // communicating with client cert authentication. func (s ConnSettings) NewHTTPTransportWithClientCerts(ctx context.Context, clientCert, clientKey string) (*http.Transport, error) { transport := s.NewHTTPTransportWithTimeout(1 * time.Minute) if clientCert != "" && clientKey != "" { c, err := certs.NewManager(ctx, clientCert, clientKey, tls.LoadX509KeyPair) if err != nil { return nil, err } if c != nil { c.UpdateReloadDuration(10 * time.Second) c.ReloadOnSignal(syscall.SIGHUP) // allow reloads upon SIGHUP transport.TLSClientConfig.GetClientCertificate = c.GetClientCertificate } } return transport, nil } // NewRemoteTargetHTTPTransport returns a new http configuration // used while communicating with the remote replication targets. func (s ConnSettings) NewRemoteTargetHTTPTransport() func() *http.Transport { tr := s.getDefaultTransport() tr.TLSHandshakeTimeout = 5 * time.Second tr.ExpectContinueTimeout = 5 * time.Second tr.ResponseHeaderTimeout = 0 return func() *http.Transport { return tr } }