github.com/netdata/go.d.plugin@v0.58.1/pkg/web/client.go (about)

     1  // SPDX-License-Identifier: GPL-3.0-or-later
     2  
     3  package web
     4  
     5  import (
     6  	"errors"
     7  	"fmt"
     8  	"net"
     9  	"net/http"
    10  	"net/url"
    11  
    12  	"github.com/netdata/go.d.plugin/pkg/tlscfg"
    13  )
    14  
    15  // ErrRedirectAttempted indicates that a redirect occurred.
    16  var ErrRedirectAttempted = errors.New("redirect")
    17  
    18  // Client is the configuration of the HTTP client.
    19  // This structure is not intended to be used directly as part of a module's configuration.
    20  // Supported configuration file formats: YAML.
    21  type Client struct {
    22  	// Timeout specifies a time limit for requests made by this Client.
    23  	// Default (zero value) is no timeout. Must be set before http.Client creation.
    24  	Timeout Duration `yaml:"timeout"`
    25  
    26  	// NotFollowRedirect specifies the policy for handling redirects.
    27  	// Default (zero value) is std http package default policy (stop after 10 consecutive requests).
    28  	NotFollowRedirect bool `yaml:"not_follow_redirects"`
    29  
    30  	// ProxyURL specifies the URL of the proxy to use. An empty string means use the environment variables
    31  	// HTTP_PROXY, HTTPS_PROXY and NO_PROXY (or the lowercase versions thereof) to get the URL.
    32  	ProxyURL string `yaml:"proxy_url"`
    33  
    34  	// TLSConfig specifies the TLS configuration.
    35  	tlscfg.TLSConfig `yaml:",inline"`
    36  }
    37  
    38  // NewHTTPClient returns a new *http.Client given a Client configuration and an error if any.
    39  func NewHTTPClient(cfg Client) (*http.Client, error) {
    40  	tlsConfig, err := tlscfg.NewTLSConfig(cfg.TLSConfig)
    41  	if err != nil {
    42  		return nil, fmt.Errorf("error on creating TLS config: %v", err)
    43  	}
    44  
    45  	if cfg.ProxyURL != "" {
    46  		if _, err := url.Parse(cfg.ProxyURL); err != nil {
    47  			return nil, fmt.Errorf("error on parsing proxy URL '%s': %v", cfg.ProxyURL, err)
    48  		}
    49  	}
    50  
    51  	d := &net.Dialer{Timeout: cfg.Timeout.Duration}
    52  
    53  	transport := &http.Transport{
    54  		Proxy:               proxyFunc(cfg.ProxyURL),
    55  		TLSClientConfig:     tlsConfig,
    56  		DialContext:         d.DialContext,
    57  		TLSHandshakeTimeout: cfg.Timeout.Duration,
    58  	}
    59  
    60  	return &http.Client{
    61  		Timeout:       cfg.Timeout.Duration,
    62  		Transport:     transport,
    63  		CheckRedirect: redirectFunc(cfg.NotFollowRedirect),
    64  	}, nil
    65  }
    66  
    67  func redirectFunc(notFollowRedirect bool) func(req *http.Request, via []*http.Request) error {
    68  	if follow := !notFollowRedirect; follow {
    69  		return nil
    70  	}
    71  	return func(_ *http.Request, _ []*http.Request) error { return ErrRedirectAttempted }
    72  }
    73  
    74  func proxyFunc(rawProxyURL string) func(r *http.Request) (*url.URL, error) {
    75  	if rawProxyURL == "" {
    76  		return http.ProxyFromEnvironment
    77  	}
    78  	proxyURL, _ := url.Parse(rawProxyURL)
    79  	return http.ProxyURL(proxyURL)
    80  }