internal/storage/storageutil/client.go (88 lines of code) (raw):

// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package storageutil import ( "crypto/tls" "fmt" "net/http" "strings" "time" "github.com/googlecloudplatform/gcsfuse/v2/cfg" "github.com/googlecloudplatform/gcsfuse/v2/internal/auth" "golang.org/x/net/context" "golang.org/x/oauth2" ) const urlSchemeSeparator = "://" type StorageClientConfig struct { /** Common client parameters. */ // ClientProtocol decides the go-sdk client to create. ClientProtocol cfg.Protocol UserAgent string CustomEndpoint string KeyFile string TokenUrl string ReuseTokenFromUrl bool MaxRetrySleep time.Duration RetryMultiplier float64 /** HTTP client parameters. */ MaxConnsPerHost int MaxIdleConnsPerHost int MaxRetryAttempts int HttpClientTimeout time.Duration ExperimentalEnableJsonRead bool AnonymousAccess bool /** Grpc client parameters. */ GrpcConnPoolSize int // Enabling new API flow for HNS bucket. EnableHNS bool ReadStallRetryConfig cfg.ReadStallGcsRetriesConfig } func CreateHttpClient(storageClientConfig *StorageClientConfig) (httpClient *http.Client, err error) { var transport *http.Transport // Using http1 makes the client more performant. if storageClientConfig.ClientProtocol == cfg.HTTP1 { transport = &http.Transport{ Proxy: http.ProxyFromEnvironment, MaxConnsPerHost: storageClientConfig.MaxConnsPerHost, MaxIdleConnsPerHost: storageClientConfig.MaxIdleConnsPerHost, // This disables HTTP/2 in transport. TLSNextProto: make( map[string]func(string, *tls.Conn) http.RoundTripper, ), } } else { // For http2, change in MaxConnsPerHost doesn't affect the performance. transport = &http.Transport{ Proxy: http.ProxyFromEnvironment, DisableKeepAlives: true, MaxConnsPerHost: storageClientConfig.MaxConnsPerHost, ForceAttemptHTTP2: true, } } if storageClientConfig.AnonymousAccess { // UserAgent will not be added if authentication is disabled. // Bypassing authentication prevents the creation of an HTTP transport // because it requires a token source. // Setting a dummy token would conflict with the "WithoutAuthentication" option. // While the "WithUserAgent" option could set a custom User-Agent, it's incompatible // with the "WithHTTPClient" option, preventing the direct injection of a user agent // when authentication is skipped. httpClient = &http.Client{ Timeout: storageClientConfig.HttpClientTimeout, } } else { var tokenSrc oauth2.TokenSource tokenSrc, err = CreateTokenSource(storageClientConfig) if err != nil { err = fmt.Errorf("while fetching tokenSource: %w", err) return } // Custom http client for Go Client. httpClient = &http.Client{ Transport: &oauth2.Transport{ Base: transport, Source: tokenSrc, }, Timeout: storageClientConfig.HttpClientTimeout, } // Setting UserAgent through RoundTripper middleware httpClient.Transport = &userAgentRoundTripper{ wrapped: httpClient.Transport, UserAgent: storageClientConfig.UserAgent, } } return httpClient, err } // It creates the token-source from the provided // key-file or using ADC search order (https://cloud.google.com/docs/authentication/application-default-credentials#order). func CreateTokenSource(storageClientConfig *StorageClientConfig) (tokenSrc oauth2.TokenSource, err error) { return auth.GetTokenSource(context.Background(), storageClientConfig.KeyFile, storageClientConfig.TokenUrl, storageClientConfig.ReuseTokenFromUrl) } // StripScheme strips the scheme part of given url. func StripScheme(url string) string { // Don't strip off the scheme part for google-internal schemes. if strings.HasPrefix(url, "dns:///") || strings.HasPrefix(url, "google-c2p:///") || strings.HasPrefix(url, "google:///") { return url } if strings.Contains(url, urlSchemeSeparator) { url = strings.SplitN(url, urlSchemeSeparator, 2)[1] } return url }