fix: give covers their own 10 MiB byte cap (#71) (#112)

Co-authored-by: Sulthan Zaki <sultankiki05@gmail.com>
Co-committed-by: Sulthan Zaki <sultankiki05@gmail.com>
This commit was merged in pull request #112.
This commit is contained in:
2026-08-17 12:06:14 +07:00
committed by sulthan
parent 3ac865cd08
commit 550b258c59
3 changed files with 382 additions and 6 deletions
+14 -5
View File
@@ -47,6 +47,15 @@ func fetchCoverBytes(ctx context.Context, cover string, browser BrowserCoverFetc
// inject it to exercise hostile DNS results without touching the live network.
type CoverResolver func(context.Context, string) ([]netip.Addr, error)
// maxCoverBytes caps one cover, separately from the series-page maxBodyBytes:
// a cover is a bounded binary asset, not a text page, and 4 MiB rejected 12%
// of asurascans covers measured 2026-08-17 (p90 4.52 MB, max 8.57 MB — two of
// the three over-cap files were JPEGs, not the animated GIF of issue #71).
// 10 MiB is ~18% headroom over that worst case and matches the GitHub and
// Discord image limits; see docs/research/gif-maximum-byte-size.md. GIF itself
// has no maximum size, so this number is policy, not format.
const maxCoverBytes = 10 << 20
// TLSCoverFetcher retrieves image bytes with the standard HTTPS client. Unlike
// TLSFetcher, it does not need a browser fingerprint: cover hosts are public
// CDNs and the response is accepted only after the destination gate passes.
@@ -152,15 +161,15 @@ func (f *TLSCoverFetcher) Fetch(ctx context.Context, sourceURL string) ([]byte,
if !ok {
return nil, "", fmt.Errorf("fetch cover: unsupported content type %q", raw)
}
if resp.ContentLength > maxBodyBytes {
return nil, "", fmt.Errorf("fetch cover: response exceeds %d bytes", maxBodyBytes)
if resp.ContentLength > maxCoverBytes {
return nil, "", fmt.Errorf("fetch cover: response exceeds %d bytes", maxCoverBytes)
}
body, err := io.ReadAll(io.LimitReader(resp.Body, maxBodyBytes+1))
body, err := io.ReadAll(io.LimitReader(resp.Body, maxCoverBytes+1))
if err != nil {
return nil, "", fmt.Errorf("read cover: %w", err)
}
if len(body) > maxBodyBytes {
return nil, "", fmt.Errorf("fetch cover: response exceeds %d bytes", maxBodyBytes)
if len(body) > maxCoverBytes {
return nil, "", fmt.Errorf("fetch cover: response exceeds %d bytes", maxCoverBytes)
}
return body, contentType, nil
}