refactor(httpcache): sentinel errors for Fetch failure modes

ErrUnexpectedStatus, ErrEmptyResponse, ErrSaveMeta are exposed so
callers can branch with errors.Is. Messages remain descriptive (status
code, URL, Path) via %w wrapping.
This commit is contained in:
AJ ONeal 2026-04-20 17:01:04 -06:00
parent ba64018838
commit 159cf2d4d3
No known key found for this signature in database

View File

@ -3,6 +3,7 @@ package httpcache
import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
"net"
@ -13,6 +14,21 @@ import (
"time"
)
// Sentinel errors returned by Fetch; wrap with errors.Is to branch on the
// failure mode. The wrapped error always includes the URL or Path context.
var (
// ErrUnexpectedStatus is returned when the server replies with a
// non-200, non-304 response.
ErrUnexpectedStatus = errors.New("unexpected response status")
// ErrEmptyResponse is returned when a 200 response body is zero bytes.
ErrEmptyResponse = errors.New("empty response body")
// ErrSaveMeta is returned when the .meta sidecar cannot be written
// after a successful body download (updated is still true).
ErrSaveMeta = errors.New("save meta sidecar")
)
// BasicAuth returns an HTTP Basic Authorization header value:
// "Basic " + base64(user:pass). Pair with the "Authorization" header in
// Cacher.Header.
@ -191,7 +207,7 @@ func (c *Cacher) Fetch() (updated bool, err error) {
return false, nil
}
if resp.StatusCode != http.StatusOK {
return false, fmt.Errorf("unexpected status %d fetching %s", resp.StatusCode, c.URL)
return false, fmt.Errorf("%w %d fetching %s", ErrUnexpectedStatus, resp.StatusCode, c.URL)
}
if err := os.MkdirAll(filepath.Dir(c.Path), 0o755); err != nil {
@ -210,7 +226,7 @@ func (c *Cacher) Fetch() (updated bool, err error) {
}
if n == 0 {
os.Remove(tmp)
return false, fmt.Errorf("empty response from %s", c.URL)
return false, fmt.Errorf("%w from %s", ErrEmptyResponse, c.URL)
}
if err := os.Rename(tmp, c.Path); err != nil {
os.Remove(tmp)
@ -224,7 +240,7 @@ func (c *Cacher) Fetch() (updated bool, err error) {
c.lastMod = lm
}
if err := c.saveMeta(); err != nil {
return true, fmt.Errorf("save meta for %s: %w", c.Path, err)
return true, fmt.Errorf("%w for %s: %w", ErrSaveMeta, c.Path, err)
}
return true, nil