The public code is SaveAsFile()
. It takes a URL, checksum and authentication info, and downloads the artifact and its hash using HTTP. It saves the content as a file and returns the file's path on success, otherwise an error.
As I'm fairly new to Go I would like to get some feedback on the error handling and on any ways to write this code more elegantly?
https://go.dev/play/p/8i_xZ-EVTvP
import (
"io"
"io/ioutil"
"net/http"
"os"
"strings"
"time"
"github.com/pkg/errors"
)
var errCode = errors.New("bad response status code")
var errFileNotWritten = errors.New("file not written")
var errDigest = errors.New("digest are not identical")
const (
statusMsg = `code: %d, response body:\n%s`
notWritten = `unable to write file %s from Response body`
msgDigestFailure = `target: \"%s\", file: \"%s\")`
msgFileNotClosed = `unable to close file %s`
)
func respBody(url string, user string, pass string) (io.ReadCloser, error) {
client := &http.Client{
Timeout: time.Second * 10,
}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
if user != "" && pass != "" {
req.SetBasicAuth(user, pass)
}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return nil, errors.Wrapf(errCode, statusMsg, resp.StatusCode, resp.Body)
}
return resp.Body, nil
}
func classify(url string, user string, pass string) (string, error) {
const suffix = ".sha512"
rb, err := respBody(url+suffix, user, pass)
if err != nil {
return "", err
}
defer rb.Close()
bodyBytes, err := ioutil.ReadAll(rb)
if err != nil {
return "", err
}
return string(bodyBytes), nil
}
func downFile(url string, user string, pass string) (*os.File, error) {
responseBody, err := respBody(url, user, pass)
if err != nil {
return nil, err
}
defer responseBody.Close()
file, err := ioutil.TempFile("", "*")
if err != nil {
return nil, err
}
written, err := io.Copy(file, responseBody)
if err != nil {
return nil, errors.Wrapf(err, notWritten, file.Name())
} else if written <= 0 {
return nil, errors.Wrapf(errFileNotWritten, notWritten, file.Name())
}
if err := file.Close(); err != nil {
return nil, errors.Wrapf(err, msgFileNotClosed, file.Name())
}
return file, nil
}
func SaveAsFile(url string, digest string, user string, pass string) (string, error) {
file, err := downFile(url, user, pass)
if err != nil {
return "", err
}
if digest != "" {
targetDigest, err := classify(url, user, pass)
if err != nil {
return "", err
}
if strings.Compare(targetDigest, digest) != 0 {
return "", errors.Wrapf(errDigest, msgDigestFailure, targetDigest, digest)
}
}
return file.Name(), nil
}