pkg/download/downloader.go (39 lines of code) (raw):
package download
import (
"fmt"
"io"
"net"
"net/http"
"time"
"github.com/pkg/errors"
)
// Downloader describes a method to download files.
type Downloader interface {
// GetRequest returns a new GET request for the resource.
GetRequest() (*http.Request, error)
}
var (
// httpClient is the default client to be used in downloading files from
// Internet. http.Get() uses a client without timeouts (http.DefaultClient)
// so it is dangerous to use it for downloading files from the Internet.
httpClient = &http.Client{
Transport: &http.Transport{
Dial: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
Proxy: http.ProxyFromEnvironment,
TLSHandshakeTimeout: 10 * time.Second,
ResponseHeaderTimeout: 20 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}}
)
// Download retrieves a response body and checks the response status code to see
// if it is 200 OK and then returns the response body. It issues a new request
// every time called. It is caller's responsibility to close the response body.
func Download(d Downloader) (io.ReadCloser, error) {
req, err := d.GetRequest()
if err != nil {
return nil, errors.Wrapf(err, "failed to create the request")
}
resp, err := httpClient.Do(req)
if err != nil {
return nil, errors.Wrapf(err, "http request failed")
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status code: got=%d expected=%d", resp.StatusCode, http.StatusOK)
}
return resp.Body, nil
}