internal/pkg: expose response body
All checks were successful
Test / Create distribution (push) Successful in 49s
Test / Sandbox (push) Successful in 2m37s
Test / Hakurei (push) Successful in 3m52s
Test / ShareFS (push) Successful in 3m56s
Test / Hpkg (push) Successful in 4m26s
Test / Sandbox (race detector) (push) Successful in 4m56s
Test / Hakurei (race detector) (push) Successful in 5m52s
Test / Flake checks (push) Successful in 1m39s

This uses the new measured reader provided by Cache. This should make httpArtifact zero-copy.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
2026-01-25 16:07:07 +09:00
parent 334578fdde
commit 861801597d
5 changed files with 156 additions and 66 deletions

View File

@@ -1,14 +1,11 @@
package pkg
import (
"bytes"
"context"
"crypto/sha512"
"fmt"
"io"
"net/http"
"path"
"sync"
"unique"
)
// An httpArtifact is an [Artifact] backed by a [http] url string. The method is
@@ -18,18 +15,12 @@ type httpArtifact struct {
// Caller-supplied url string.
url string
// Caller-supplied checksum of the response body. This is validated during
// curing and the first call to Data.
checksum Checksum
// Caller-supplied checksum of the response body. This is validated when
// closing the [io.ReadCloser] returned by Cure.
checksum unique.Handle[Checksum]
// doFunc is the Do method of [http.Client] supplied by the caller.
doFunc func(req *http.Request) (*http.Response, error)
// Response body read to EOF.
data []byte
// Synchronises access to data.
mu sync.Mutex
}
var _ KnownChecksum = new(httpArtifact)
@@ -45,7 +36,7 @@ func NewHTTPGet(
if c == nil {
c = http.DefaultClient
}
return &httpArtifact{url: url, checksum: checksum, doFunc: c.Do}
return &httpArtifact{url: url, checksum: unique.Make(checksum), doFunc: c.Do}
}
// Kind returns the hardcoded [Kind] constant.
@@ -61,7 +52,7 @@ func (a *httpArtifact) Params(ctx *IContext) {
func (a *httpArtifact) Dependencies() []Artifact { return nil }
// Checksum returns the caller-supplied checksum.
func (a *httpArtifact) Checksum() Checksum { return a.checksum }
func (a *httpArtifact) Checksum() Checksum { return a.checksum.Value() }
// String returns [path.Base] over the backing url.
func (a *httpArtifact) String() string { return path.Base(a.url) }
@@ -74,11 +65,13 @@ func (e ResponseStatusError) Error() string {
return "the requested URL returned non-OK status: " + http.StatusText(int(e))
}
// do sends the caller-supplied request on the caller-supplied [http.Client]
// and reads its response body to EOF and returns the resulting bytes.
func (a *httpArtifact) do(ctx context.Context) (data []byte, err error) {
// Cure sends the http request and returns the resulting response body reader
// wrapped to perform checksum validation. It is valid but not encouraged to
// close the resulting [io.ReadCloser] before it is read to EOF, as that causes
// Close to block until all remaining data is consumed and validated.
func (a *httpArtifact) Cure(r *RContext) (rc io.ReadCloser, err error) {
var req *http.Request
req, err = http.NewRequestWithContext(ctx, http.MethodGet, a.url, nil)
req, err = http.NewRequestWithContext(r.Unwrap(), http.MethodGet, a.url, nil)
if err != nil {
return
}
@@ -93,37 +86,6 @@ func (a *httpArtifact) do(ctx context.Context) (data []byte, err error) {
return nil, ResponseStatusError(resp.StatusCode)
}
if data, err = io.ReadAll(resp.Body); err != nil {
_ = resp.Body.Close()
return
}
err = resp.Body.Close()
return
}
// Cure completes the http request and returns the resulting response body read
// to EOF. Data does not interact with the filesystem.
func (a *httpArtifact) Cure(ctx context.Context) (r io.ReadCloser, err error) {
a.mu.Lock()
defer a.mu.Unlock()
if a.data != nil {
// validated by cache or a previous call to Cure
return io.NopCloser(bytes.NewReader(a.data)), nil
}
var data []byte
if data, err = a.do(ctx); err != nil {
return
}
h := sha512.New384()
h.Write(data)
if got := (Checksum)(h.Sum(nil)); got != a.checksum {
return nil, &ChecksumMismatchError{got, a.checksum}
}
a.data = data
r = io.NopCloser(bytes.NewReader(data))
rc = r.NewMeasuredReader(resp.Body, a.checksum)
return
}