2026-07-26 20:07:46 +02:00
|
|
|
package handler
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"net/http"
|
|
|
|
|
"strings"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type filenameDownload struct {
|
2026-08-29 11:19:54 +01:00
|
|
|
ecosystem string
|
|
|
|
|
upstreamURL string
|
|
|
|
|
suffix string
|
|
|
|
|
parseErr string
|
|
|
|
|
fetchErr string
|
|
|
|
|
parse func(string) (name, version string)
|
2026-07-26 20:07:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *Proxy) handleFilenameDownload(w http.ResponseWriter, r *http.Request, d filenameDownload) {
|
|
|
|
|
filename := r.PathValue("filename")
|
|
|
|
|
if filename == "" || !strings.HasSuffix(filename, d.suffix) {
|
|
|
|
|
http.Error(w, "invalid filename", http.StatusBadRequest)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
name, version := d.parse(filename)
|
|
|
|
|
if name == "" || version == "" {
|
|
|
|
|
http.Error(w, d.parseErr, http.StatusBadRequest)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p.Logger.Info(d.ecosystem+" download request",
|
|
|
|
|
"name", name, "version", version, "filename", filename)
|
|
|
|
|
|
2026-08-29 11:19:54 +01:00
|
|
|
downloadURL := d.upstreamURL + r.URL.Path
|
|
|
|
|
result, err := p.GetOrFetchArtifactFromURL(
|
|
|
|
|
r.Context(), d.ecosystem, name, version, filename, downloadURL,
|
|
|
|
|
)
|
2026-07-26 20:07:46 +02:00
|
|
|
if err != nil {
|
|
|
|
|
p.serveArtifactError(w, err, d.fetchErr)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
Support HTTP byte-range requests for local file cached artifact downloads (#404)
* feat(storage): expose efficient seek for local files
Open file-backed artifacts with os.Open so local readers support
efficient seeking. Leave cloud storage readers unchanged.
Keep the legacy sidecar mapping test focused on fileblob's reader:
Blob.Open now reads local files directly and no longer consults the
sidecar. Continue checking sidecar cleanup and post-store reads.
* feat(handler): preserve efficient seek through integrity checks
Keep the cache result's single Reader field and preserve io.Seeker
when the underlying storage reader supports efficient seeking.
Sequential reads continue through whole-object integrity verification.
A successful seek switches subsequent reads to the source for range
responses, where full-object verification is not possible.
Test both modes and verify the source is closed once.
* feat(handler): add request-aware byte range responses
Add a request-aware artifact serving helper for bounded, open-ended,
and suffix byte ranges, including If-Range handling, 206 responses,
and 416 responses for valid unsatisfiable ranges.
Ignore malformed and multi-range requests by serving the full artifact.
Advertise byte-range support only for seekable readers, preserving HEAD,
redirect, and non-seekable behavior. Clarify that net/http recovers
ErrAbortHandler per request and keeps the server running.
* feat(handler): enable byte ranges across artifact downloads
Forward each artifact request to the shared range-aware response helper,
including cached generic release assets and OCI blobs. This lets the
handler honor ranges only when its reader supports efficient seeking,
without changing direct-storage redirect behavior.
Add warm-cache endpoint tests that assert partial response headers and
bytes without an upstream refetch. Document supported range behavior,
unsupported reader fallback, and the integrity limitation of partial reads.
2026-10-04 09:48:41 -07:00
|
|
|
ServeArtifactRequest(w, r, result)
|
2026-07-26 20:07:46 +02:00
|
|
|
}
|