]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blobdiff - vendor/github.com/hashicorp/go-getter/get_http.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / go-getter / get_http.go
index d2e28796d8f5ce655fef798a9d71bbe64b02de6d..7c4541c6e95dfe3d72b59439f551ae447a78bdb5 100644 (file)
@@ -1,6 +1,7 @@
 package getter
 
 import (
+       "context"
        "encoding/xml"
        "fmt"
        "io"
@@ -8,9 +9,10 @@ import (
        "net/url"
        "os"
        "path/filepath"
+       "strconv"
        "strings"
 
-       "github.com/hashicorp/go-safetemp"
+       safetemp "github.com/hashicorp/go-safetemp"
 )
 
 // HttpGetter is a Getter implementation that will download from an HTTP
@@ -18,7 +20,7 @@ import (
 //
 // For file downloads, HTTP is used directly.
 //
-// The protocol for downloading a directory from an HTTP endpoing is as follows:
+// The protocol for downloading a directory from an HTTP endpoint is as follows:
 //
 // An HTTP GET request is made to the URL with the additional GET parameter
 // "terraform-get=1". This lets you handle that scenario specially if you
@@ -34,6 +36,8 @@ import (
 // formed URL. The shorthand syntax of "github.com/foo/bar" or relative
 // paths are not allowed.
 type HttpGetter struct {
+       getter
+
        // Netrc, if true, will lookup and use auth information found
        // in the user's netrc file if available.
        Netrc bool
@@ -41,6 +45,12 @@ type HttpGetter struct {
        // Client is the http.Client to use for Get requests.
        // This defaults to a cleanhttp.DefaultClient if left unset.
        Client *http.Client
+
+       // Header contains optional request header fields that should be included
+       // with every HTTP request. Note that the zero value of this field is nil,
+       // and as such it needs to be initialized before use, via something like
+       // make(http.Header).
+       Header http.Header
 }
 
 func (g *HttpGetter) ClientMode(u *url.URL) (ClientMode, error) {
@@ -51,6 +61,7 @@ func (g *HttpGetter) ClientMode(u *url.URL) (ClientMode, error) {
 }
 
 func (g *HttpGetter) Get(dst string, u *url.URL) error {
+       ctx := g.Context()
        // Copy the URL so we can modify it
        var newU url.URL = *u
        u = &newU
@@ -72,10 +83,17 @@ func (g *HttpGetter) Get(dst string, u *url.URL) error {
        u.RawQuery = q.Encode()
 
        // Get the URL
-       resp, err := g.Client.Get(u.String())
+       req, err := http.NewRequest("GET", u.String(), nil)
        if err != nil {
                return err
        }
+
+       req.Header = g.Header
+       resp, err := g.Client.Do(req)
+       if err != nil {
+               return err
+       }
+
        defer resp.Body.Close()
        if resp.StatusCode < 200 || resp.StatusCode >= 300 {
                return fmt.Errorf("bad response code: %d", resp.StatusCode)
@@ -99,57 +117,107 @@ func (g *HttpGetter) Get(dst string, u *url.URL) error {
        // into a temporary directory, then copy over the proper subdir.
        source, subDir := SourceDirSubdir(source)
        if subDir == "" {
-               return Get(dst, source)
+               var opts []ClientOption
+               if g.client != nil {
+                       opts = g.client.Options
+               }
+               return Get(dst, source, opts...)
        }
 
        // We have a subdir, time to jump some hoops
-       return g.getSubdir(dst, source, subDir)
+       return g.getSubdir(ctx, dst, source, subDir)
 }
 
-func (g *HttpGetter) GetFile(dst string, u *url.URL) error {
+func (g *HttpGetter) GetFile(dst string, src *url.URL) error {
+       ctx := g.Context()
        if g.Netrc {
                // Add auth from netrc if we can
-               if err := addAuthFromNetrc(u); err != nil {
+               if err := addAuthFromNetrc(src); err != nil {
                        return err
                }
        }
 
-       if g.Client == nil {
-               g.Client = httpClient
+       // Create all the parent directories if needed
+       if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil {
+               return err
        }
 
-       resp, err := g.Client.Get(u.String())
+       f, err := os.OpenFile(dst, os.O_RDWR|os.O_CREATE, os.FileMode(0666))
        if err != nil {
                return err
        }
-       defer resp.Body.Close()
-       if resp.StatusCode != 200 {
-               return fmt.Errorf("bad response code: %d", resp.StatusCode)
+       defer f.Close()
+
+       if g.Client == nil {
+               g.Client = httpClient
        }
 
-       // Create all the parent directories
-       if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil {
+       var currentFileSize int64
+
+       // We first make a HEAD request so we can check
+       // if the server supports range queries. If the server/URL doesn't
+       // support HEAD requests, we just fall back to GET.
+       req, err := http.NewRequest("HEAD", src.String(), nil)
+       if err != nil {
                return err
        }
+       if g.Header != nil {
+               req.Header = g.Header
+       }
+       headResp, err := g.Client.Do(req)
+       if err == nil && headResp != nil {
+               headResp.Body.Close()
+               if headResp.StatusCode == 200 {
+                       // If the HEAD request succeeded, then attempt to set the range
+                       // query if we can.
+                       if headResp.Header.Get("Accept-Ranges") == "bytes" {
+                               if fi, err := f.Stat(); err == nil {
+                                       if _, err = f.Seek(0, os.SEEK_END); err == nil {
+                                               req.Header.Set("Range", fmt.Sprintf("bytes=%d-", fi.Size()))
+                                               currentFileSize = fi.Size()
+                                               totalFileSize, _ := strconv.ParseInt(headResp.Header.Get("Content-Length"), 10, 64)
+                                               if currentFileSize >= totalFileSize {
+                                                       // file already present
+                                                       return nil
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+       req.Method = "GET"
 
-       f, err := os.Create(dst)
+       resp, err := g.Client.Do(req)
        if err != nil {
                return err
        }
+       switch resp.StatusCode {
+       case http.StatusOK, http.StatusPartialContent:
+               // all good
+       default:
+               resp.Body.Close()
+               return fmt.Errorf("bad response code: %d", resp.StatusCode)
+       }
+
+       body := resp.Body
 
-       n, err := io.Copy(f, resp.Body)
+       if g.client != nil && g.client.ProgressListener != nil {
+               // track download
+               fn := filepath.Base(src.EscapedPath())
+               body = g.client.ProgressListener.TrackProgress(fn, currentFileSize, currentFileSize+resp.ContentLength, resp.Body)
+       }
+       defer body.Close()
+
+       n, err := Copy(ctx, f, body)
        if err == nil && n < resp.ContentLength {
                err = io.ErrShortWrite
        }
-       if err1 := f.Close(); err == nil {
-               err = err1
-       }
        return err
 }
 
 // getSubdir downloads the source into the destination, but with
 // the proper subdir.
-func (g *HttpGetter) getSubdir(dst, source, subDir string) error {
+func (g *HttpGetter) getSubdir(ctx context.Context, dst, source, subDir string) error {
        // Create a temporary directory to store the full source. This has to be
        // a non-existent directory.
        td, tdcloser, err := safetemp.Dir("", "getter")
@@ -158,8 +226,12 @@ func (g *HttpGetter) getSubdir(dst, source, subDir string) error {
        }
        defer tdcloser.Close()
 
+       var opts []ClientOption
+       if g.client != nil {
+               opts = g.client.Options
+       }
        // Download that into the given directory
-       if err := Get(td, source); err != nil {
+       if err := Get(td, source, opts...); err != nil {
                return err
        }
 
@@ -185,7 +257,7 @@ func (g *HttpGetter) getSubdir(dst, source, subDir string) error {
                return err
        }
 
-       return copyDir(dst, sourcePath, false)
+       return copyDir(ctx, dst, sourcePath, false)
 }
 
 // parseMeta looks for the first meta tag in the given reader that