launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/environs/simplestreams/datasource.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package simplestreams
     5  
     6  import (
     7  	"fmt"
     8  	"io"
     9  	"net/http"
    10  	"strings"
    11  
    12  	"launchpad.net/juju-core/errors"
    13  	"launchpad.net/juju-core/utils"
    14  )
    15  
    16  // A DataSource retrieves simplestreams metadata.
    17  type DataSource interface {
    18  	// Fetch loads the data at the specified relative path. It returns a reader from which
    19  	// the data can be retrieved as well as the full URL of the file. The full URL is typically
    20  	// used in log messages to help diagnose issues accessing the data.
    21  	Fetch(path string) (io.ReadCloser, string, error)
    22  	// URL returns the full URL of the path, as applicable to this datasource.
    23  	// This method is used primarily for logging purposes.
    24  	URL(path string) (string, error)
    25  	// SetAllowRetry sets the flag which determines if the datasource will retry fetching the metadata
    26  	// if it is not immediately available.
    27  	SetAllowRetry(allow bool)
    28  }
    29  
    30  // SSLHostnameVerification is used as a switch for when a given provider might
    31  // use self-signed credentials and we should not try to verify the hostname on
    32  // the TLS/SSL certificates
    33  type SSLHostnameVerification bool
    34  
    35  const (
    36  	// VerifySSLHostnames ensures we verify the hostname on the certificate
    37  	// matches the host we are connecting and is signed
    38  	VerifySSLHostnames = SSLHostnameVerification(true)
    39  	// NoVerifySSLHostnames informs us to skip verifying the hostname
    40  	// matches a valid certificate
    41  	NoVerifySSLHostnames = SSLHostnameVerification(false)
    42  )
    43  
    44  // A urlDataSource retrieves data from an HTTP URL.
    45  type urlDataSource struct {
    46  	baseURL              string
    47  	hostnameVerification SSLHostnameVerification
    48  }
    49  
    50  // NewURLDataSource returns a new datasource reading from the specified baseURL.
    51  func NewURLDataSource(baseURL string, verify SSLHostnameVerification) DataSource {
    52  	return &urlDataSource{
    53  		baseURL:              baseURL,
    54  		hostnameVerification: verify,
    55  	}
    56  }
    57  
    58  func (u *urlDataSource) GoString() string {
    59  	return fmt.Sprintf("urlDataSource(%q)", u.baseURL)
    60  }
    61  
    62  // urlJoin returns baseURL + relpath making sure to have a '/' inbetween them
    63  // This doesn't try to do anything fancy with URL query or parameter bits
    64  // It also doesn't use path.Join because that normalizes slashes, and you need
    65  // to keep both slashes in 'http://'.
    66  func urlJoin(baseURL, relpath string) string {
    67  	if strings.HasSuffix(baseURL, "/") {
    68  		return baseURL + relpath
    69  	}
    70  	return baseURL + "/" + relpath
    71  }
    72  
    73  // Fetch is defined in simplestreams.DataSource.
    74  func (h *urlDataSource) Fetch(path string) (io.ReadCloser, string, error) {
    75  	dataURL := urlJoin(h.baseURL, path)
    76  	// dataURL can be http:// or file://
    77  	client := http.DefaultClient
    78  	if !h.hostnameVerification {
    79  		client = utils.GetNonValidatingHTTPClient()
    80  	}
    81  	resp, err := client.Get(dataURL)
    82  	if err != nil {
    83  		logger.Debugf("Got error requesting %q: %v", dataURL, err)
    84  		return nil, dataURL, errors.NotFoundf("invalid URL %q", dataURL)
    85  	}
    86  	if resp.StatusCode == http.StatusNotFound {
    87  		return nil, dataURL, errors.NotFoundf("cannot find URL %q", dataURL)
    88  	}
    89  	if resp.StatusCode == http.StatusUnauthorized {
    90  		return nil, dataURL, errors.Unauthorizedf("unauthorised access to URL %q", dataURL)
    91  	}
    92  	if resp.StatusCode != http.StatusOK {
    93  		return nil, dataURL, fmt.Errorf("cannot access URL %q, %q", dataURL, resp.Status)
    94  	}
    95  	return resp.Body, dataURL, nil
    96  }
    97  
    98  // URL is defined in simplestreams.DataSource.
    99  func (h *urlDataSource) URL(path string) (string, error) {
   100  	return urlJoin(h.baseURL, path), nil
   101  }
   102  
   103  // SetAllowRetry is defined in simplestreams.DataSource.
   104  func (h *urlDataSource) SetAllowRetry(allow bool) {
   105  	// This is a NOOP for url datasources.
   106  }