internal/httptransport/transport.go (86 lines of code) (raw):

package httptransport import ( "context" "crypto/tls" "crypto/x509" "net" "net/http" "os" "sync" "time" "gitlab.com/gitlab-org/labkit/log" "gitlab.com/gitlab-org/gitlab-pages/internal/config" ) const ( // DefaultTTFBTimeout is the timeout used in the meteredRoundTripper // when calling http.Transport.RoundTrip. The request will be cancelled // if the response takes longer than this. DefaultTTFBTimeout = 15 * time.Second ) var ( sysPoolOnce = &sync.Once{} sysPool *x509.CertPool // only overridden by transport_darwin.go loadExtraCerts = func() {} // DefaultTransport can be used with http.Client with TLS and certificates DefaultTransport = NewTransport() ) // Transport wraps a RoundTripper so it can be extended and modified outside of this package type Transport interface { http.RoundTripper RegisterProtocol(scheme string, rt http.RoundTripper) } // NewTransport initializes an http.Transport with a custom dialer that includes TLS Root CAs. // It sets default connection values such as timeouts and max idle connections. func NewTransport() *http.Transport { return &http.Transport{ DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) { var dialer tls.Dialer dialer.Config = &tls.Config{ RootCAs: pool(), MinVersion: tls.VersionTLS12, } return dialer.DialContext(ctx, network, addr) }, Proxy: http.ProxyFromEnvironment, // overrides the DefaultMaxIdleConnsPerHost = 2 MaxIdleConns: 100, MaxIdleConnsPerHost: 100, IdleConnTimeout: 90 * time.Second, // Set more timeouts https://gitlab.com/gitlab-org/gitlab-pages/-/issues/495 TLSHandshakeTimeout: 10 * time.Second, ResponseHeaderTimeout: 15 * time.Second, ExpectContinueTimeout: 15 * time.Second, } } // NewTransportWithClientCert creates a new http.Transport with the provided client certificate configuration. // It sets up the TLS configuration with the provided CA files and client certificates. // The transport is configured with default connection values such as timeouts and max idle connections. func NewTransportWithClientCert(clientCfg config.HTTPClientCfg) *http.Transport { certPool := pool() for _, caFile := range clientCfg.CAFiles { cert, err := os.ReadFile(caFile) if err == nil { certPool.AppendCertsFromPEM(cert) } else { log.WithError(err).WithField("ca-file", caFile).Error("reading CA file") } } tlsConfig := &tls.Config{ RootCAs: certPool, MinVersion: tls.VersionTLS12, // set MinVersion to fix gosec: G402 } tlsConfig.MinVersion = clientCfg.MinVersion tlsConfig.MaxVersion = clientCfg.MaxVersion if clientCfg.Cert != nil { tlsConfig.Certificates = []tls.Certificate{*clientCfg.Cert} } t := NewTransport() t.DialTLSContext = func(ctx context.Context, network, addr string) (net.Conn, error) { var dialer tls.Dialer dialer.Config = tlsConfig return dialer.DialContext(ctx, network, addr) } t.TLSClientConfig = tlsConfig return t } // This is here because macOS does not support the SSL_CERT_FILE and // SSL_CERT_DIR environment variables. We have arranged things to read // SSL_CERT_FILE and SSL_CERT_DIR as late as possible to avoid conflicts // with file descriptor passing at startup. func pool() *x509.CertPool { sysPoolOnce.Do(loadPool) return sysPool } func loadPool() { var err error // Always load the system cert pool sysPool, err = x509.SystemCertPool() if err != nil { log.WithError(err).Error("failed to load system cert pool for http client") return } // Go does not load SSL_CERT_FILE and SSL_CERT_DIR on darwin systems so we need to // load them manually in OSX. See https://golang.org/src/crypto/x509/root_unix.go loadExtraCerts() }