aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/rosa/mirror.go
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-07-07 20:18:18 +0900
committerOphestra <cat@gensokyo.uk>2026-07-07 20:18:18 +0900
commit76360979f9b7898c1e331914b6037b01eab79807 (patch)
tree6955b05d183c72a774b00befc3f4be1c0be11933 /internal/rosa/mirror.go
parent6d55ee536e902d059380f79e870d06547627cc7c (diff)
internal/rosa: mirror response write methods
For the fs.FS implementation and static publishing. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/rosa/mirror.go')
-rw-r--r--internal/rosa/mirror.go260
1 files changed, 147 insertions, 113 deletions
diff --git a/internal/rosa/mirror.go b/internal/rosa/mirror.go
index 09419396..d4dfba1e 100644
--- a/internal/rosa/mirror.go
+++ b/internal/rosa/mirror.go
@@ -30,8 +30,8 @@ type Remote struct {
c *http.Client
}
-// NewRemote returns a populated [Remote]
-func NewRemote(base string, pub ed25519.PublicKey, c *http.Client) (Remote, error) {
+// NewRemote returns a populated [Remote].
+func NewRemote(c *http.Client, base string, pub ed25519.PublicKey) (Remote, error) {
u, err := url.Parse(base)
return Remote{u, pub, c}, err
}
@@ -201,14 +201,153 @@ func (r Remote) Status(
), nil
}
-// NewMirror returns an [http.Handler] for servicing mirror requests.
+// Mirror services requests made by [Remote].
+type Mirror struct {
+ // Cache on-filesystem format.
+ fsys fs.FS
+ // Signs payloads.
+ key ed25519.PrivateKey
+ // Verbose output.
+ msg message.Msg
+}
+
+// NewMirror returns a populated [Mirror] for servicing mirror requests.
func NewMirror(
msg message.Msg,
fsys fs.FS,
key ed25519.PrivateKey,
-) http.Handler {
+) Mirror {
+ return Mirror{fsys, key, msg}
+}
+
+// writeArtifact writes a signed dirArtifact response.
+func (m Mirror) writeArtifact(
+ buf *[2 * len(pkg.Checksum{})]byte,
+ w io.Writer,
+ header http.Header,
+) error {
+ ids := pkg.Encode((pkg.Checksum)(buf[:len(pkg.Checksum{})]))
+ if linkname, err := fs.ReadLink(m.fsys, filepath.Join(
+ "identifier",
+ ids,
+ )); err != nil {
+ return err
+ } else if err = pkg.Decode(
+ (*pkg.Checksum)(buf[len(pkg.Checksum{}):]),
+ filepath.Base(linkname),
+ ); err != nil {
+ return err
+ }
+ m.msg.Verbosef("serving artifact %s", ids)
+
+ if header != nil {
+ header.Set(
+ "Content-Length",
+ strconv.Itoa(ed25519.SignatureSize+len(buf)),
+ )
+ }
+ _, err := w.Write(append(ed25519.Sign(m.key, buf[:]), buf[:]...))
+ return err
+}
+
+// writeOutcome writes a dirOutcome response.
+func (m Mirror) writeOutcome(
+ buf *pkg.Checksum,
+ w io.Writer,
+ header http.Header,
+) error {
+ checksums := pkg.Encode(*buf)
+ rel := filepath.Join("checksum", checksums)
+ if _, err := fs.Lstat(m.fsys, rel); err != nil {
+ return err
+ }
+ m.msg.Verbosef("serving outcome %s", checksums)
+
+ _fsys, err := fs.Sub(m.fsys, rel)
+ if err != nil {
+ return err
+ }
+
+ var gw *gzip.Writer
+ if gw, err = gzip.NewWriterLevel(w, gzip.BestCompression); err != nil {
+ return err
+ }
+ if header != nil {
+ header.Set("Content-Encoding", "gzip")
+ }
+ if err = pkg.Write(_fsys, ".", gw); err != nil {
+ _ = gw.Close()
+ return err
+ }
+ return gw.Close()
+}
+
+// writeStatus writes a signed dirStatus response.
+func (m Mirror) writeStatus(
+ buf *[2 * len(pkg.Checksum{})]byte,
+ w io.Writer,
+ _ http.Header,
+) error {
+ ids := pkg.Encode((pkg.Checksum)(buf[:len(pkg.Checksum{})]))
+ f, err := m.fsys.Open(filepath.Join(
+ "status",
+ ids,
+ ))
+ if err != nil {
+ return err
+ }
+ s, ok := f.(io.Seeker)
+ if !ok {
+ return &fs.PathError{Op: "seek", Path: ids, Err: fs.ErrInvalid}
+ }
+ m.msg.Verbosef("serving status %s", ids)
+
+ h := sha512.New384()
+ if _, err = io.Copy(h, f); err != nil {
+ _ = f.Close()
+ return err
+ }
+ h.Sum(buf[len(pkg.Checksum{}):len(pkg.Checksum{})])
+ if _, err = w.Write(append(ed25519.Sign(m.key, buf[:]), buf[:]...)); err != nil {
+ _ = f.Close()
+ return err
+ } else if _, err = s.Seek(0, io.SeekStart); err != nil {
+ _ = f.Close()
+ return err
+ } else if _, err = io.Copy(w, f); err != nil {
+ _ = f.Close()
+ return err
+ }
+ return f.Close()
+}
+
+// writeF is a helper for writing a mirror response with error handling.
+func writeF[B any](
+ m Mirror,
+ w http.ResponseWriter,
+ buf B,
+ f func(
+ buf B,
+ w io.Writer,
+ header http.Header,
+ ) error,
+) {
+ err := f(buf, w, w.Header())
+ if err == nil {
+ return
+ }
+
+ if errors.Is(err, os.ErrNotExist) {
+ w.WriteHeader(http.StatusNotFound)
+ return
+ }
+ m.msg.GetLogger().Println(err)
+ w.WriteHeader(http.StatusInternalServerError)
+}
+
+// Register configures an [http.ServeMux] for servicing mirror requests.
+func (m Mirror) Register(mux *http.ServeMux) {
const identName = "ident"
- var mux http.ServeMux
mux.HandleFunc("/"+dirArtifact+"/{"+identName+"}", func(
w http.ResponseWriter,
@@ -222,38 +361,7 @@ func NewMirror(
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
-
- ids := pkg.Encode((pkg.Checksum)(buf[:len(pkg.Checksum{})]))
- if linkname, err := fs.ReadLink(fsys, filepath.Join(
- "identifier",
- ids,
- )); err != nil {
- if errors.Is(err, os.ErrNotExist) {
- w.WriteHeader(http.StatusNotFound)
- return
- }
- msg.GetLogger().Println(err)
- w.WriteHeader(http.StatusInternalServerError)
- return
- } else if err = pkg.Decode(
- (*pkg.Checksum)(buf[len(pkg.Checksum{}):]),
- filepath.Base(linkname),
- ); err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
- msg.Verbosef("serving artifact %s", ids)
-
- w.Header().Set(
- "Content-Length",
- strconv.Itoa(ed25519.SignatureSize+len(buf)),
- )
- if _, err := w.Write(append(
- ed25519.Sign(key, buf[:]),
- buf[:]...,
- )); err != nil {
- msg.Verbose(err)
- }
+ writeF(m, w, &buf, m.writeArtifact)
})
mux.HandleFunc("/"+dirOutcome+"/{"+identName+"}", func(
@@ -273,40 +381,7 @@ func NewMirror(
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
-
- checksums := pkg.Encode(buf)
- rel := filepath.Join("checksum", checksums)
- if _, err := fs.Lstat(fsys, rel); err != nil {
- if errors.Is(err, os.ErrNotExist) {
- w.WriteHeader(http.StatusNotFound)
- return
- }
- msg.GetLogger().Println(err)
- w.WriteHeader(http.StatusInternalServerError)
- return
- }
- msg.Verbosef("serving outcome %s", pkg.Encode(buf))
-
- _fsys, err := fs.Sub(fsys, rel)
- if err != nil {
- msg.GetLogger().Println(err)
- w.WriteHeader(http.StatusInternalServerError)
- return
- }
-
- var gw *gzip.Writer
- if gw, err = gzip.NewWriterLevel(w, gzip.BestCompression); err != nil {
- msg.GetLogger().Println(err)
- w.WriteHeader(http.StatusInternalServerError)
- return
- }
- w.Header().Set("Content-Encoding", "gzip")
- if err = pkg.Write(_fsys, ".", gw); err != nil {
- msg.Verbose(err)
- }
- if err = gw.Close(); err != nil {
- msg.GetLogger().Println(err)
- }
+ writeF(m, w, &buf, m.writeOutcome)
})
mux.HandleFunc("/"+dirStatus+"/{"+identName+"}", func(
@@ -321,47 +396,6 @@ func NewMirror(
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
-
- ids := pkg.Encode((pkg.Checksum)(buf[:len(pkg.Checksum{})]))
- f, err := fsys.Open(filepath.Join(
- "status",
- ids,
- ))
- if err != nil {
- if errors.Is(err, os.ErrNotExist) {
- w.WriteHeader(http.StatusNotFound)
- return
- }
- msg.GetLogger().Println(err)
- w.WriteHeader(http.StatusInternalServerError)
- return
- }
- s, ok := f.(io.Seeker)
- if !ok {
- msg.GetLogger().Println("backing filesystem does not support seek")
- w.WriteHeader(http.StatusInternalServerError)
- return
- }
- msg.Verbosef("serving status %s", ids)
-
- h := sha512.New384()
- if _, err = io.Copy(h, f); err != nil {
- _ = f.Close()
- msg.Verbose(err)
- w.WriteHeader(http.StatusInternalServerError)
- }
- h.Sum(buf[len(pkg.Checksum{}):len(pkg.Checksum{})])
- if _, err = w.Write(append(ed25519.Sign(key, buf[:]), buf[:]...)); err != nil {
- msg.Verbose(err)
- return
- } else if _, err = s.Seek(0, io.SeekStart); err != nil {
- msg.GetLogger().Println(err)
- return
- } else if _, err = io.Copy(w, f); err != nil {
- msg.Verbose(err)
- return
- }
+ writeF(m, w, &buf, m.writeStatus)
})
-
- return &mux
}