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 }