in plugin/source/http/http.go [64:184]
func (s *Source) DownloadToPath(dlPath string) (err error) {
// set up start/end events
s.logger.WriteEvent(go2chef.NewEvent("HTTP_DOWNLOAD_STARTED", TypeName, s.URL))
defer func() {
event := "HTTP_DOWNLOAD_COMPLETE"
if err != nil {
event = "HTTP_DOWNLOAD_FAILURE"
}
s.logger.WriteEvent(go2chef.NewEvent(event, TypeName, s.URL))
}()
tlsConf, err := certs.TLS.GetTLSClientConf()
if err != nil {
return err
}
// Use the client from GlobalConfig so we can get any configured CAs
c := &http.Client{
Transport: &http.Transport{
TLSClientConfig: tlsConf,
},
}
if ex, err := go2chef.PathExists(dlPath); err != nil {
return err
} else if !ex {
s.logger.Debugf(1, "creating download directory %s", dlPath)
if err := os.MkdirAll(dlPath, 0755); err != nil {
return err
}
}
s.logger.Debugf(1, "%s: 1", s.Name())
req, err := http.NewRequest(s.Method, s.URL, nil)
if err != nil {
return err
}
resp, err := c.Do(req)
if err != nil {
return err
}
defer func() { _ = resp.Body.Close() }()
s.logger.Debugf(1, "%s: HTTP %s %s => %d %s", s.Name(), s.Method, s.URL, resp.StatusCode, http.StatusText(resp.StatusCode))
if !s.checkStatusCode(resp) {
return fmt.Errorf("non-matching status code: %d", resp.StatusCode)
}
tmpfile, err := temp.File("", "go2chef-src-http-*")
defer func() { _ = tmpfile.Close() }()
if err != nil {
return err
}
if _, err = io.Copy(tmpfile, resp.Body); err != nil {
return err
}
/*
FILENAME DETERMINATION
Filenames are determined like so:
- Start with the basename of the request URL path
- Check if config["output_filename"] is set and use it if so
- If not, check if the Content-Disposition has a download filename set and use that if so
*/
outputFilename := path.Base(req.URL.Path)
if s.OutputFilename != "" {
outputFilename = dlPath
} else {
_, params, err := mime.ParseMediaType(resp.Header.Get("Content-Disposition"))
if err == nil {
if fn, ok := params["filename"]; ok {
outputFilename = fn
}
}
}
outputPath := filepath.Join(dlPath, outputFilename)
if s.SHA256 != "" {
s.logger.Debugf(1, "%s: sha256 was provided, validating %s", s.Name(), outputPath)
fileHash, err := hashfile.SHA256(tmpfile.Name())
if err != nil {
return err
}
s.logger.Debugf(1, "%s: calculated hash %s", s.Name(), fileHash)
s.logger.Debugf(1, "%s: provided hash %s", s.Name(), s.SHA256)
// If the hash doesn't match what is provided, return an error.
if fileHash != s.SHA256 {
return errors.New("sha256 hashes do not match")
}
}
if s.Archive {
/*
ARCHIVE MODE: If the request is for an archive (using `{"archive": true}` in config) then
decompress that archive into the destination.
*/
_ = tmpfile.Close()
s.logger.Debugf(1, "%s: archive mode enabled, extracting %s to %s", s.Name(), tmpfile.Name(), dlPath)
extFilename := filepath.Join(filepath.Dir(tmpfile.Name()), outputFilename)
if err := os.Rename(tmpfile.Name(), extFilename); err != nil {
s.logger.Errorf("failed to relocate output")
return err
}
if err := archiver.Unarchive(extFilename, dlPath); err != nil {
return err
}
} else {
/*
FILE MODE: If the request isn't for an archive (default), then just close the temp file
and move to the output path.
*/
s.logger.Debugf(1, "%s: direct download to %s, rename to %s", s.Name(), tmpfile.Name(), outputPath)
_ = tmpfile.Close()
return os.Rename(tmpfile.Name(), outputPath)
}
return nil
}