func()

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
}