github.com/OpsMx/go-app-base@v0.0.24/httputil/http.go (about)

     1  // Copyright 2022 OpsMx, Inc
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  // http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package httputil
    16  
    17  import (
    18  	"crypto/tls"
    19  	"net"
    20  	"net/http"
    21  	"time"
    22  
    23  	"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
    24  )
    25  
    26  // ClientConfig defines various timeouts we will want to change.
    27  // All times are in seconds.  If 0, a default will be used.
    28  type ClientConfig struct {
    29  	DialTimeout           int `json:"dialTimeout,omitempty" yaml:"dialTimeout,omitempty"`
    30  	ClientTimeout         int `json:"clientTimeout,omitempty" yaml:"clientTimeout,omitempty"`
    31  	TLSHandshakeTimeout   int `json:"tlsHandshakeTimeout,omitempty" yaml:"tlsHandshakeTimeout,omitempty"`
    32  	ResponseHeaderTimeout int `json:"responseHeaderTimeout,omitempty" yaml:"responseHeaderTimeout,omitempty"`
    33  	MaxIdleConnections    int `json:"maxIdleConnections,omitempty" yaml:"maxIdleConnections,omitempty"`
    34  }
    35  
    36  var defaultTLSConfig *tls.Config
    37  
    38  var defaultClientConfig = &ClientConfig{
    39  	DialTimeout:           15,
    40  	ClientTimeout:         60,
    41  	TLSHandshakeTimeout:   15,
    42  	ResponseHeaderTimeout: 60,
    43  	MaxIdleConnections:    5,
    44  }
    45  
    46  func (c *ClientConfig) applyDefaults() {
    47  	if c.DialTimeout == 0 {
    48  		c.DialTimeout = defaultClientConfig.DialTimeout
    49  	}
    50  	if c.ClientTimeout == 0 {
    51  		c.ClientTimeout = defaultClientConfig.ClientTimeout
    52  	}
    53  	if c.TLSHandshakeTimeout == 0 {
    54  		c.TLSHandshakeTimeout = defaultClientConfig.TLSHandshakeTimeout
    55  	}
    56  	if c.ResponseHeaderTimeout == 0 {
    57  		c.ResponseHeaderTimeout = defaultClientConfig.ResponseHeaderTimeout
    58  	}
    59  	if c.MaxIdleConnections == 0 {
    60  		c.MaxIdleConnections = defaultClientConfig.MaxIdleConnections
    61  	}
    62  }
    63  
    64  // SetClientConfig will replace the current clientConfig for all future clients
    65  // returned by NewHTTPClient().  Generally, this will be set once, and probably
    66  // not changed per connection.  It is not going to be thread-safe, in that
    67  // setting the config and then calling NewHTTPClient() could be a race.
    68  func SetClientConfig(c ClientConfig) {
    69  	if defaultClientConfig == nil {
    70  		defaultClientConfig = &ClientConfig{}
    71  	}
    72  	*defaultClientConfig = c
    73  	defaultClientConfig.applyDefaults()
    74  }
    75  
    76  // SetTLSConfig sets the default TLS configuration used by NewHTTPClient().
    77  // This will generally be set once for adding custom CA roots or other
    78  // configuration used throughout the application.
    79  //
    80  // NewHTTPClient() also allows per-client TLS configuration, if desired.
    81  func SetTLSConfig(tlsconfig *tls.Config) {
    82  	defaultTLSConfig = tlsconfig
    83  }
    84  
    85  // NewHTTPClient returns a new http.Client that is configured with
    86  // sane timeouts, a global TLS configuration, and optionally a per-client
    87  // TLS config.
    88  //
    89  // Generally, the global config will have things like custom CA roots,
    90  // and we will want to trust those for every outgoing conneciton.
    91  // A per-client TLS config would be used where we are talking to a
    92  // specific API, and want to insert our certificates or a custom
    93  // CA root for just that connection.
    94  //
    95  // Future changes should allow merging tls configs, so we can add to
    96  // the base default rather than replace it entirely.
    97  func NewHTTPClient(tlsConfig *tls.Config) *http.Client {
    98  	if tlsConfig == nil {
    99  		tlsConfig = defaultTLSConfig
   100  	}
   101  	dialer := net.Dialer{Timeout: time.Duration(defaultClientConfig.DialTimeout) * time.Second}
   102  	client := &http.Client{
   103  		Timeout: time.Duration(defaultClientConfig.ClientTimeout) * time.Second,
   104  		Transport: otelhttp.NewTransport(&http.Transport{
   105  			Dial:                  dialer.Dial,
   106  			DialContext:           dialer.DialContext,
   107  			TLSHandshakeTimeout:   time.Duration(defaultClientConfig.TLSHandshakeTimeout) * time.Second,
   108  			TLSClientConfig:       tlsConfig,
   109  			ResponseHeaderTimeout: time.Duration(defaultClientConfig.ResponseHeaderTimeout) * time.Second,
   110  			ExpectContinueTimeout: time.Second,
   111  			MaxIdleConns:          defaultClientConfig.MaxIdleConnections,
   112  		}),
   113  		CheckRedirect: func(req *http.Request, via []*http.Request) error {
   114  			return http.ErrUseLastResponse
   115  		},
   116  	}
   117  	return client
   118  }