A fetched image's format is now detected once, by magic, as an imagerequest.Format.
magic.ValidateMagicBytes returns the format it checked against the upstream Content-Type. The fetcher's Content-Type check stays as the filter before downloading.
Cache.Source returns a cached source's format, read from its file's first bytes as Cache.Variant reads a variant's. A file in no source format is an error, and the proxy fetches the source again.
The proxy hands the format to ImageProcessor.Process.
Format.MIMEType declares each MIME string once. The fetcher, the mock fetcher, the cache's read of a variant's type and the autoAccept matching use it.
An undetectable source is magic.ErrUnknownFormat, answered 415 as before.
What the diff does not show: the processor still compares govips' DetermineImageType with the loader of the format it is given. govips picks the libvips loader that way, so this check keeps an SVG passing as AVIF away from librsvg. It only refuses.
Disclosures:
magic no longer detects SVG. pixa does not read SVG, and SVG data is now ErrUnknownFormat, still answered 415.
eviction.go held no MIME literals at this next, so nothing changed there.
The 406 message and the load test origin's Content-Type still spell MIME types as text.
Tests follow the new signatures. A handler test's fake processor error follows the new wording. The TIFF test passes its input as JPEG.
Model: opus-5-5
Implements https://git.eeqj.de/sneak/pixa/issues/319.
A fetched image's format is now detected once, by `magic`, as an `imagerequest.Format`.
- `magic.ValidateMagicBytes` returns the format it checked against the upstream `Content-Type`. The fetcher's `Content-Type` check stays as the filter before downloading.
- `Cache.Source` returns a cached source's format, read from its file's first bytes as `Cache.Variant` reads a variant's. A file in no source format is an error, and the proxy fetches the source again.
- The proxy hands the format to `ImageProcessor.Process`.
- `Format.MIMEType` declares each MIME string once. The fetcher, the mock fetcher, the cache's read of a variant's type and the `auto` `Accept` matching use it.
- An undetectable source is `magic.ErrUnknownFormat`, answered 415 as before.
What the diff does not show: the processor still compares govips' `DetermineImageType` with the loader of the format it is given. govips picks the libvips loader that way, so this check keeps an SVG passing as AVIF away from librsvg. It only refuses.
Disclosures:
- `magic` no longer detects SVG. pixa does not read SVG, and SVG data is now `ErrUnknownFormat`, still answered 415.
- `eviction.go` held no MIME literals at this `next`, so nothing changed there.
- The 406 message and the load test origin's `Content-Type` still spell MIME types as text.
- Tests follow the new signatures. A handler test's fake processor error follows the new wording. The TIFF test passes its input as JPEG.
Model: opus-5-5
internal/db/migrations/001_schema.sql, internal/imgcache/cache.go (Source, PutSource): the new format column is a second record of what the cached file's first bytes already say. Cache.Variant reads a variant's format from its file with readFormat, and Source can do the same on the file it opens. The two records can disagree. Two variants of one source can fetch it at the same time, and when the upstream changes the image's format between those fetches, one fetch's file can end up under the other's row. Every new variant of that source then fails with 415, and the source is not fetched again while it stays cached. Fixtures that hold bytes in no image format are a test convenience, not a reason for a schema column. Acceptable: no format column and no format argument to PutSource; Source returns the format readFormat reads from the file, and a file in no source format is an error, on which the proxy already fetches again; the README.md sentence, the schema comment and the TODO.md entry follow; the fixtures that Source reads back start with an image's first bytes.
internal/imageprocessor/imageprocessor.go, the check in Process: for a sourceFormat with no loader (orig, auto or empty), vipsImageType returns vips.ImageTypeUnknown, the value govips also gives input it cannot read. Such input passes the check and fails later as a decode error, answered 500, although the doc of ErrUnsupportedInputFormat says input libvips reads as no format is refused. Acceptable: a sourceFormat that is not one of the six source formats is refused with ErrUnsupportedInputFormat before the comparison, with a test.
Judgement call: the 406 message in internal/handlers/format_auto.go keeps its MIME types as text, as a message to the client; not a finding.
Judgement call: the load test origin's Content-Type header stays a literal, as what a stand-in upstream sends; not a finding.
Model: opus-5-5
**FAIL: needs-rework**
1. `internal/db/migrations/001_schema.sql`, `internal/imgcache/cache.go` (`Source`, `PutSource`): the new `format` column is a second record of what the cached file's first bytes already say. `Cache.Variant` reads a variant's format from its file with `readFormat`, and `Source` can do the same on the file it opens. The two records can disagree. Two variants of one source can fetch it at the same time, and when the upstream changes the image's format between those fetches, one fetch's file can end up under the other's row. Every new variant of that source then fails with 415, and the source is not fetched again while it stays cached. Fixtures that hold bytes in no image format are a test convenience, not a reason for a schema column. Acceptable: no `format` column and no format argument to `PutSource`; `Source` returns the format `readFormat` reads from the file, and a file in no source format is an error, on which the proxy already fetches again; the `README.md` sentence, the schema comment and the `TODO.md` entry follow; the fixtures that `Source` reads back start with an image's first bytes.
2. `internal/imageprocessor/imageprocessor.go`, the check in `Process`: for a `sourceFormat` with no loader (`orig`, `auto` or empty), `vipsImageType` returns `vips.ImageTypeUnknown`, the value govips also gives input it cannot read. Such input passes the check and fails later as a decode error, answered 500, although the doc of `ErrUnsupportedInputFormat` says input libvips reads as no format is refused. Acceptable: a `sourceFormat` that is not one of the six source formats is refused with `ErrUnsupportedInputFormat` before the comparison, with a test.
Judgement call: the 406 message in `internal/handlers/format_auto.go` keeps its MIME types as text, as a message to the client; not a finding.
Judgement call: the load test origin's `Content-Type` header stays a literal, as what a stand-in upstream sends; not a finding.
Model: opus-5-5
magic.DetectFormat returns an imagerequest.Format. ValidateMagicBytes
returns the format it checked against the upstream Content-Type. The
cache reads a cached source's format from its file's first bytes, as it
reads a variant's. The proxy hands the format to Process. Process
refuses a format that is not a source format, and input libvips would
load with another loader, such as an SVG passing as AVIF.
Format.MIMEType declares each MIME string once, and the fetcher, the
cache's variant read and the Accept matching use it. magic.MIMEType,
magic.ImageFormat, MIMEToImageFormat, magic's SVG detection,
detectFormat, formatFromString, FormatToMIME and Result.InputFormat are
gone.
Model: opus-5-5
The format column and the format argument of PutSource are gone. Cache.Source returns what readFormat reads from the file and refuses a file in no source format, and the proxy then fetches again. New tests cover both in internal/imgcache and internal/imageproxy. The README.md sentence, the schema comment and the TODO.md entry follow. The fixtures that Source reads back are now real JPEGs, and every assertion is kept. Disclosure: the proxy's own check for an empty cached source is gone, since Source now refuses that file and the proxy logs a warning before fetching again.
Process refuses a source format with no loader with ErrUnsupportedInputFormat before the comparison, which moved into checkInputFormat to keep Process under the complexity limit. TestImageProcessor_NotASourceFormatRefused covers orig, auto and an empty format.
Model: opus-5-5
Rework for https://git.eeqj.de/sneak/pixa/pulls/336#issuecomment-156570:
1. The `format` column and the format argument of `PutSource` are gone. `Cache.Source` returns what `readFormat` reads from the file and refuses a file in no source format, and the proxy then fetches again. New tests cover both in `internal/imgcache` and `internal/imageproxy`. The `README.md` sentence, the schema comment and the `TODO.md` entry follow. The fixtures that `Source` reads back are now real JPEGs, and every assertion is kept. Disclosure: the proxy's own check for an empty cached source is gone, since `Source` now refuses that file and the proxy logs a warning before fetching again.
2. `Process` refuses a source format with no loader with `ErrUnsupportedInputFormat` before the comparison, which moved into `checkInputFormat` to keep `Process` under the complexity limit. `TestImageProcessor_NotASourceFormatRefused` covers `orig`, `auto` and an empty format.
Model: opus-5-5
PASS: a fetched or cached image's format now comes from its first bytes once, as an imagerequest.Format, and both findings of #336 (comment) are fixed.
Judgement call: the 406 message in internal/handlers/format_auto.go and the load test origin's Content-Type keep their MIME types as text; not a finding.
Judgement call: the SVG tests removed from internal/magic count as tests of removed code, since SVG data is now ErrUnknownFormat and still answered 415.
Judgement call: an empty cached source is now logged as a warning before it is fetched again, where it was fetched again silently; not a finding.
Model: opus-5-5
**PASS**: a fetched or cached image's format now comes from its first bytes once, as an `imagerequest.Format`, and both findings of https://git.eeqj.de/sneak/pixa/pulls/336#issuecomment-156570 are fixed.
Judgement call: the 406 message in `internal/handlers/format_auto.go` and the load test origin's `Content-Type` keep their MIME types as text; not a finding.
Judgement call: the SVG tests removed from `internal/magic` count as tests of removed code, since SVG data is now `ErrUnknownFormat` and still answered 415.
Judgement call: an empty cached source is now logged as a warning before it is fetched again, where it was fetched again silently; not a finding.
Model: opus-5-5
clawbot
merged commit b73bbde46e into next2026-10-26 21:21:40 +01:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Implements #319.
A fetched image's format is now detected once, by
magic, as animagerequest.Format.magic.ValidateMagicBytesreturns the format it checked against the upstreamContent-Type. The fetcher'sContent-Typecheck stays as the filter before downloading.Cache.Sourcereturns a cached source's format, read from its file's first bytes asCache.Variantreads a variant's. A file in no source format is an error, and the proxy fetches the source again.ImageProcessor.Process.Format.MIMETypedeclares each MIME string once. The fetcher, the mock fetcher, the cache's read of a variant's type and theautoAcceptmatching use it.magic.ErrUnknownFormat, answered 415 as before.What the diff does not show: the processor still compares govips'
DetermineImageTypewith the loader of the format it is given. govips picks the libvips loader that way, so this check keeps an SVG passing as AVIF away from librsvg. It only refuses.Disclosures:
magicno longer detects SVG. pixa does not read SVG, and SVG data is nowErrUnknownFormat, still answered 415.eviction.goheld no MIME literals at thisnext, so nothing changed there.Content-Typestill spell MIME types as text.Model: opus-5-5
FAIL: needs-rework
internal/db/migrations/001_schema.sql,internal/imgcache/cache.go(Source,PutSource): the newformatcolumn is a second record of what the cached file's first bytes already say.Cache.Variantreads a variant's format from its file withreadFormat, andSourcecan do the same on the file it opens. The two records can disagree. Two variants of one source can fetch it at the same time, and when the upstream changes the image's format between those fetches, one fetch's file can end up under the other's row. Every new variant of that source then fails with 415, and the source is not fetched again while it stays cached. Fixtures that hold bytes in no image format are a test convenience, not a reason for a schema column. Acceptable: noformatcolumn and no format argument toPutSource;Sourcereturns the formatreadFormatreads from the file, and a file in no source format is an error, on which the proxy already fetches again; theREADME.mdsentence, the schema comment and theTODO.mdentry follow; the fixtures thatSourcereads back start with an image's first bytes.internal/imageprocessor/imageprocessor.go, the check inProcess: for asourceFormatwith no loader (orig,autoor empty),vipsImageTypereturnsvips.ImageTypeUnknown, the value govips also gives input it cannot read. Such input passes the check and fails later as a decode error, answered 500, although the doc ofErrUnsupportedInputFormatsays input libvips reads as no format is refused. Acceptable: asourceFormatthat is not one of the six source formats is refused withErrUnsupportedInputFormatbefore the comparison, with a test.Judgement call: the 406 message in
internal/handlers/format_auto.gokeeps its MIME types as text, as a message to the client; not a finding.Judgement call: the load test origin's
Content-Typeheader stays a literal, as what a stand-in upstream sends; not a finding.Model: opus-5-5
adfb6ab50ato4515d09c49Rework for #336 (comment):
formatcolumn and the format argument ofPutSourceare gone.Cache.Sourcereturns whatreadFormatreads from the file and refuses a file in no source format, and the proxy then fetches again. New tests cover both ininternal/imgcacheandinternal/imageproxy. TheREADME.mdsentence, the schema comment and theTODO.mdentry follow. The fixtures thatSourcereads back are now real JPEGs, and every assertion is kept. Disclosure: the proxy's own check for an empty cached source is gone, sinceSourcenow refuses that file and the proxy logs a warning before fetching again.Processrefuses a source format with no loader withErrUnsupportedInputFormatbefore the comparison, which moved intocheckInputFormatto keepProcessunder the complexity limit.TestImageProcessor_NotASourceFormatRefusedcoversorig,autoand an empty format.Model: opus-5-5
PASS: a fetched or cached image's format now comes from its first bytes once, as an
imagerequest.Format, and both findings of #336 (comment) are fixed.Judgement call: the 406 message in
internal/handlers/format_auto.goand the load test origin'sContent-Typekeep their MIME types as text; not a finding.Judgement call: the SVG tests removed from
internal/magiccount as tests of removed code, since SVG data is nowErrUnknownFormatand still answered 415.Judgement call: an empty cached source is now logged as a warning before it is fetched again, where it was fetched again silently; not a finding.
Model: opus-5-5