check / check (push) Canceled after 0s
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
98 lines
2.4 KiB
Go
98 lines
2.4 KiB
Go
package httpfetcher
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io/fs"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"sneak.berlin/go/pixa/internal/imagerequest"
|
|
)
|
|
|
|
// MockFetcher implements Fetcher using an embedded filesystem.
|
|
// Files are organized as: hostname/path/to/file.ext
|
|
// The source with host example.com and path /images/photo.jpg maps to
|
|
// example.com/images/photo.jpg; the source query is ignored.
|
|
type MockFetcher struct {
|
|
fs fs.FS
|
|
}
|
|
|
|
// NewMock creates a new mock fetcher backed by the given filesystem.
|
|
func NewMock(fsys fs.FS) *MockFetcher {
|
|
return &MockFetcher{fs: fsys}
|
|
}
|
|
|
|
// Fetch retrieves the source's content from the mock filesystem.
|
|
func (m *MockFetcher) Fetch(
|
|
ctx context.Context, source imagerequest.Source,
|
|
) (*FetchResult, error) {
|
|
// Check context cancellation
|
|
select {
|
|
case <-ctx.Done():
|
|
return nil, ctx.Err()
|
|
default:
|
|
}
|
|
|
|
path := source.Host + source.Path
|
|
|
|
// Open the file
|
|
f, err := m.fs.Open(path)
|
|
if err != nil {
|
|
if errors.Is(err, fs.ErrNotExist) {
|
|
return nil, fmt.Errorf("%w: status 404", ErrUpstreamError)
|
|
}
|
|
|
|
return nil, fmt.Errorf("failed to open mock file: %w", err)
|
|
}
|
|
|
|
// Get file info for content length
|
|
stat, err := f.Stat()
|
|
if err != nil {
|
|
_ = f.Close()
|
|
|
|
return nil, fmt.Errorf("failed to stat mock file: %w", err)
|
|
}
|
|
|
|
// Detect content type from extension
|
|
contentType := detectContentTypeFromPath(path)
|
|
|
|
return &FetchResult{
|
|
Content: f,
|
|
ContentLength: stat.Size(),
|
|
ContentType: contentType,
|
|
Headers: make(http.Header),
|
|
}, nil
|
|
}
|
|
|
|
// The content types the mock fetcher gives files in no format pixa reads.
|
|
const (
|
|
contentTypeSVG = "image/svg+xml"
|
|
contentTypeOctetStream = "application/octet-stream"
|
|
)
|
|
|
|
// detectContentTypeFromPath returns the MIME type based on file extension.
|
|
func detectContentTypeFromPath(path string) string {
|
|
path = strings.ToLower(path)
|
|
|
|
switch {
|
|
case strings.HasSuffix(path, ".jpg"), strings.HasSuffix(path, ".jpeg"):
|
|
return imagerequest.FormatJPEG.MIMEType()
|
|
case strings.HasSuffix(path, ".png"):
|
|
return imagerequest.FormatPNG.MIMEType()
|
|
case strings.HasSuffix(path, ".gif"):
|
|
return imagerequest.FormatGIF.MIMEType()
|
|
case strings.HasSuffix(path, ".webp"):
|
|
return imagerequest.FormatWebP.MIMEType()
|
|
case strings.HasSuffix(path, ".avif"):
|
|
return imagerequest.FormatAVIF.MIMEType()
|
|
case strings.HasSuffix(path, ".jxl"):
|
|
return imagerequest.FormatJXL.MIMEType()
|
|
case strings.HasSuffix(path, ".svg"):
|
|
return contentTypeSVG
|
|
default:
|
|
return contentTypeOctetStream
|
|
}
|
|
}
|