aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--cmd/mbf/cache.go2
-rw-r--r--cmd/mbf/main.go4
-rw-r--r--internal/rosa/mirror.go260
-rw-r--r--internal/rosa/mirror_test.go9
4 files changed, 157 insertions, 118 deletions
diff --git a/cmd/mbf/cache.go b/cmd/mbf/cache.go
index 20dbd855..aea54546 100644
--- a/cmd/mbf/cache.go
+++ b/cmd/mbf/cache.go
@@ -100,7 +100,7 @@ func (cache *cache) open() (err error) {
return
}
var r rosa.Remote
- if r, err = rosa.NewRemote(cache.mirror, pub, http.DefaultClient); err != nil {
+ if r, err = rosa.NewRemote(http.DefaultClient, cache.mirror, pub); err != nil {
cache.c.Close()
return err
}
diff --git a/cmd/mbf/main.go b/cmd/mbf/main.go
index b2f2c0d6..471b595a 100644
--- a/cmd/mbf/main.go
+++ b/cmd/mbf/main.go
@@ -544,7 +544,9 @@ func main() {
if base, err := os.OpenRoot(cm.base); err != nil {
return err
} else {
- h = rosa.NewMirror(msg, base.FS(), key)
+ var mux http.ServeMux
+ rosa.NewMirror(msg, base.FS(), key).Register(&mux)
+ h = &mux
}
server := http.Server{Addr: args[0], Handler: h}
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
}
diff --git a/internal/rosa/mirror_test.go b/internal/rosa/mirror_test.go
index a671cd31..acd45c39 100644
--- a/internal/rosa/mirror_test.go
+++ b/internal/rosa/mirror_test.go
@@ -4,6 +4,7 @@ import (
"crypto/ed25519"
"io/fs"
"log"
+ "net/http"
"net/http/httptest"
"os"
"testing"
@@ -52,14 +53,16 @@ func TestMirror(t *testing.T) {
}
wantChecksums := pkg.Encode(wantChecksum)
- server := httptest.NewServer(rosa.NewMirror(msg, fstest.MapFS{
+ var mux http.ServeMux
+ rosa.NewMirror(msg, fstest.MapFS{
"identifier/" + ids: {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantChecksums)},
"checksum/" + wantChecksums: {Mode: os.ModeDir | 0500},
- }, priv))
+ }, priv).Register(&mux)
+ server := httptest.NewServer(&mux)
t.Cleanup(server.Close)
var extern pkg.External
- extern, err = rosa.NewRemote("http://example.com:80", pub, server.Client())
+ extern, err = rosa.NewRemote(server.Client(), "http://example.com:80", pub)
if err != nil {
t.Fatal(err)
}