refactor: extract httpfetcher package from imgcache (#43)
All checks were successful
check / check (push) Successful in 4s

Extracts the HTTP fetcher concern out of `internal/imgcache/` into its own `internal/httpfetcher/` package, per the plan in [issue #39](#39).

This is one of four planned extractions; only the fetcher is moved here so the diff stays reviewable. The remaining three (signature, magic, urlparser) will land in separate PRs.

## What moved

From `internal/imgcache/fetcher.go` and `internal/imgcache/mock_fetcher.go` into `internal/httpfetcher/`:

- `HTTPFetcher` type, its `New` constructor, and `Config` (formerly `FetcherConfig`) with `DefaultConfig`
- SSRF-safe dialer, `validateURL`, `isPrivateIP`, `isLocalhost`, `extractHost`
- Per-host connection semaphore (rate limiting) and `limitedReader`
- Content-type validation (`isAllowedContentType`, `detectContentTypeFromPath`)
- All related error values (`ErrSSRFBlocked`, `ErrUpstreamError`, `ErrUpstreamTimeout`, `ErrPayloadTooLarge`, `ErrDisallowedContentType`)
- All related constants (`DefaultFetchTimeout`, `DefaultMaxPayloadBytes`, `DefaultMaxConnectionsPerHost`, etc.)
- The `Fetcher` interface and `FetchResult` type (moved here to keep the import edge one-way: `imgcache` depends on `httpfetcher`, never the reverse)
- `MockFetcher` test helper

## Renames (no stuttering)

- `NewHTTPFetcher` → `httpfetcher.New`
- `FetcherConfig` → `httpfetcher.Config`
- `DefaultFetcherConfig` → `httpfetcher.DefaultConfig`
- `NewMockFetcher` → `httpfetcher.NewMock`

The `ServiceConfig.FetcherConfig` field name is retained — it describes what kind of config the field holds (not a stutter).

## Behavior

Pure refactor. No behavior changes. All existing tests pass; unit tests for the new package are included.

`docker build .` passes (fmt-check, lint, test, build).

refs #39

Co-authored-by: clawbot <clawbot@eeqj.de>
Co-authored-by: Jeffrey Paul <sneak@noreply.example.org>
Reviewed-on: #43
Co-authored-by: clawbot <clawbot@noreply.example.org>
Co-committed-by: clawbot <clawbot@noreply.example.org>
This commit was merged in pull request #43.
This commit is contained in:
2026-07-25 12:26:18 +02:00
committed by Jeffrey Paul
parent 504afea4f8
commit b6e9ac2a93
12 changed files with 414 additions and 74 deletions

View File

@@ -9,6 +9,7 @@ import (
"time"
_ "modernc.org/sqlite"
"sneak.berlin/go/pixa/internal/httpfetcher"
)
func setupTestDB(t *testing.T) *sql.DB {
@@ -152,7 +153,7 @@ func TestCache_StoreAndLookup(t *testing.T) {
// Store source content
sourceContent := []byte("fake jpeg data")
fetchResult := &FetchResult{
fetchResult := &httpfetcher.FetchResult{
ContentType: "image/jpeg",
Headers: map[string][]string{"Content-Type": {"image/jpeg"}},
}