Files
pkgstash/internal/cache/download.go
T

91 lines
2.0 KiB
Go

package cache
import (
"errors"
"io"
"log/slog"
"net/http"
"os"
)
func (c *Cache) downloadWrangle(relPath string, flight *inFlight, tmpFile *os.File) {
// defer map cleanup and signal client handle done
defer c.cleanupFlight(relPath, flight)
// declare vars outside loop
var err error
// fetch pkgs from mirror with retry logic
for range len(c.cfg.mirrorURLs) {
url := c.nextMirror() + relPath
err = c.downloadToDisk(url, tmpFile)
if err == nil {
break
}
var upstreamErr *UpstreamError
if !errors.As(err, &upstreamErr) {
// network error, transfer interupted, bail
break
}
slog.Warn("mirror failed", "url", url, "status", upstreamErr.StatusCode)
}
if err != nil {
slog.Warn("download error", "err", err)
flight.err = err
removeErr := c.cr.Remove(flight.tmpPath)
if removeErr != nil {
slog.Warn("failed to remove temp file", "path", flight.tmpPath, "err", removeErr)
}
return
}
// mv file to final location
err = c.cr.Rename(flight.tmpPath, relPath)
if err != nil {
removeErr := c.cr.Remove(flight.tmpPath)
if removeErr != nil {
slog.Warn("failed to remove temp file", "path", flight.tmpPath, "err", removeErr)
}
return
}
return
}
func (c *Cache) downloadToDisk(url string, tmpFile *os.File) error {
slog.Info("fetching", "url", url)
// set the user agent
req, err := http.NewRequest("GET", url, nil)
if err != nil {
slog.Error("failed create request", "err", err)
}
req.Header.Set("User-Agent", userAgent)
resp, err := c.client.Do(req)
if err != nil {
slog.Warn("fetch failed", "url", url, "err", err)
return err
}
if resp.StatusCode != 200 {
slog.Info("fetch returned", "url", url, "status", resp.StatusCode)
return &UpstreamError{StatusCode: resp.StatusCode}
}
defer func() {
if closeErr := resp.Body.Close(); closeErr != nil {
err = closeErr
}
}()
_, err = io.Copy(tmpFile, resp.Body)
if err != nil {
return err
}
return nil
}
func (c *Cache) cleanupFlight(key string, f *inFlight) {
c.inFlightMu.Lock()
delete(c.inFlight, key)
c.inFlightMu.Unlock()
close(f.done)
}