minio/internal/http/transports.go
2022-12-12 20:31:21 -08:00

178 lines
5.8 KiB
Go

// 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 <http://www.gnu.org/licenses/>.
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.Minute
tr.ExpectContinueTimeout = 15 * time.Minute
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
}
}