diff options
| author | Ophestra <cat@gensokyo.uk> | 2026-07-07 20:18:18 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2026-07-07 20:18:18 +0900 |
| commit | 76360979f9b7898c1e331914b6037b01eab79807 (patch) | |
| tree | 6955b05d183c72a774b00befc3f4be1c0be11933 /internal/rosa/mirror.go | |
| parent | 6d55ee536e902d059380f79e870d06547627cc7c (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.go | 260 |
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 } |
