...

Source file src/helm.sh/helm/v3/pkg/getter/httpgetter.go

Documentation: helm.sh/helm/v3/pkg/getter

     1  /*
     2  Copyright The Helm Authors.
     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  
    16  package getter
    17  
    18  import (
    19  	"bytes"
    20  	"crypto/tls"
    21  	"io"
    22  	"net/http"
    23  	"net/url"
    24  	"sync"
    25  
    26  	"github.com/pkg/errors"
    27  
    28  	"helm.sh/helm/v3/internal/tlsutil"
    29  	"helm.sh/helm/v3/internal/urlutil"
    30  	"helm.sh/helm/v3/internal/version"
    31  )
    32  
    33  // HTTPGetter is the default HTTP(/S) backend handler
    34  type HTTPGetter struct {
    35  	opts      options
    36  	transport *http.Transport
    37  	once      sync.Once
    38  }
    39  
    40  // Get performs a Get from repo.Getter and returns the body.
    41  func (g *HTTPGetter) Get(href string, options ...Option) (*bytes.Buffer, error) {
    42  	for _, opt := range options {
    43  		opt(&g.opts)
    44  	}
    45  	return g.get(href)
    46  }
    47  
    48  func (g *HTTPGetter) get(href string) (*bytes.Buffer, error) {
    49  	// Set a helm specific user agent so that a repo server and metrics can
    50  	// separate helm calls from other tools interacting with repos.
    51  	req, err := http.NewRequest(http.MethodGet, href, nil)
    52  	if err != nil {
    53  		return nil, err
    54  	}
    55  
    56  	req.Header.Set("User-Agent", version.GetUserAgent())
    57  	if g.opts.userAgent != "" {
    58  		req.Header.Set("User-Agent", g.opts.userAgent)
    59  	}
    60  
    61  	// Before setting the basic auth credentials, make sure the URL associated
    62  	// with the basic auth is the one being fetched.
    63  	u1, err := url.Parse(g.opts.url)
    64  	if err != nil {
    65  		return nil, errors.Wrap(err, "Unable to parse getter URL")
    66  	}
    67  	u2, err := url.Parse(href)
    68  	if err != nil {
    69  		return nil, errors.Wrap(err, "Unable to parse URL getting from")
    70  	}
    71  
    72  	// Host on URL (returned from url.Parse) contains the port if present.
    73  	// This check ensures credentials are not passed between different
    74  	// services on different ports.
    75  	if g.opts.passCredentialsAll || (u1.Scheme == u2.Scheme && u1.Host == u2.Host) {
    76  		if g.opts.username != "" && g.opts.password != "" {
    77  			req.SetBasicAuth(g.opts.username, g.opts.password)
    78  		}
    79  	}
    80  
    81  	client, err := g.httpClient()
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  
    86  	resp, err := client.Do(req)
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  	defer resp.Body.Close()
    91  	if resp.StatusCode != http.StatusOK {
    92  		return nil, errors.Errorf("failed to fetch %s : %s", href, resp.Status)
    93  	}
    94  
    95  	buf := bytes.NewBuffer(nil)
    96  	_, err = io.Copy(buf, resp.Body)
    97  	return buf, err
    98  }
    99  
   100  // NewHTTPGetter constructs a valid http/https client as a Getter
   101  func NewHTTPGetter(options ...Option) (Getter, error) {
   102  	var client HTTPGetter
   103  
   104  	for _, opt := range options {
   105  		opt(&client.opts)
   106  	}
   107  
   108  	return &client, nil
   109  }
   110  
   111  func (g *HTTPGetter) httpClient() (*http.Client, error) {
   112  	if g.opts.transport != nil {
   113  		return &http.Client{
   114  			Transport: g.opts.transport,
   115  			Timeout:   g.opts.timeout,
   116  		}, nil
   117  	}
   118  
   119  	g.once.Do(func() {
   120  		g.transport = &http.Transport{
   121  			DisableCompression: true,
   122  			Proxy:              http.ProxyFromEnvironment,
   123  		}
   124  	})
   125  
   126  	if (g.opts.certFile != "" && g.opts.keyFile != "") || g.opts.caFile != "" || g.opts.insecureSkipVerifyTLS {
   127  		tlsConf, err := tlsutil.NewClientTLS(g.opts.certFile, g.opts.keyFile, g.opts.caFile, g.opts.insecureSkipVerifyTLS)
   128  		if err != nil {
   129  			return nil, errors.Wrap(err, "can't create TLS config for client")
   130  		}
   131  
   132  		sni, err := urlutil.ExtractHostname(g.opts.url)
   133  		if err != nil {
   134  			return nil, err
   135  		}
   136  		tlsConf.ServerName = sni
   137  
   138  		g.transport.TLSClientConfig = tlsConf
   139  	}
   140  
   141  	if g.opts.insecureSkipVerifyTLS {
   142  		if g.transport.TLSClientConfig == nil {
   143  			g.transport.TLSClientConfig = &tls.Config{
   144  				InsecureSkipVerify: true,
   145  			}
   146  		} else {
   147  			g.transport.TLSClientConfig.InsecureSkipVerify = true
   148  		}
   149  	}
   150  
   151  	client := &http.Client{
   152  		Transport: g.transport,
   153  		Timeout:   g.opts.timeout,
   154  	}
   155  
   156  	return client, nil
   157  }
   158  

View as plain text