Skip to content

Commit

Permalink
chore: modular unpack handler
Browse files Browse the repository at this point in the history
Start moving the unpack handler towards something more modular and
configurable. Remove globals. Centralize the config into main.go.
  • Loading branch information
zimbatm committed Jul 28, 2024
1 parent a07415f commit 8f27921
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 29 deletions.
47 changes: 21 additions & 26 deletions api/unpack/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,39 @@ import (
"io"
"mime"
"net/http"
"os"
"path/filepath"
"strings"

"github.com/numtide/nar-serve/pkg/libstore"
"github.com/numtide/nar-serve/pkg/nar"
"github.com/numtide/nar-serve/pkg/narinfo"

"github.com/ulikunitz/xz"
"github.com/klauspost/compress/zstd"
"github.com/ulikunitz/xz"
)

// MountPath is where this handler is supposed to be mounted
const MountPath = "/nix/store/"

var nixCache = mustBinaryCacheReader()
type Handler struct {
cache libstore.BinaryCacheReader
mountPath string
}

func mustBinaryCacheReader() libstore.BinaryCacheReader {
r, err := libstore.NewBinaryCacheReader(context.Background(), getEnv("NAR_CACHE_URL", "https://cache.nixos.org"))
if err != nil {
panic(err)
func NewHandler(cache libstore.BinaryCacheReader, mountPath string) *Handler {
return &Handler{
cache: cache,
mountPath: mountPath,
}
return r
}

// MountPath is where this handler is supposed to be mounted
func (h *Handler) MountPath() string {
return h.mountPath
}

// Handler is the entry-point for @now/go as well as the stub main.go net/http
func Handler(w http.ResponseWriter, req *http.Request) {
func (h *Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
ctx := req.Context()
// remove the mount path from the path
path := strings.TrimPrefix(req.URL.Path, MountPath)
path := strings.TrimPrefix(req.URL.Path, h.mountPath)
// ignore trailing slashes
path = strings.TrimRight(path, "/")

Expand All @@ -52,7 +55,7 @@ func Handler(w http.ResponseWriter, req *http.Request) {
narName := strings.Split(narDir, "-")[0]

// Get the NAR info to find the NAR
narinfo, err := getNarInfo(ctx, narName)
narinfo, err := getNarInfo(ctx, h.cache, narName)
if err != nil {
http.Error(w, err.Error(), 500)
return
Expand All @@ -62,7 +65,7 @@ func Handler(w http.ResponseWriter, req *http.Request) {
// TODO: consider keeping a LRU cache
narPATH := narinfo.URL
fmt.Println("fetching the NAR:", narPATH)
file, err := nixCache.GetFile(ctx, narPATH)
file, err := h.cache.GetFile(ctx, narPATH)
if err != nil {
http.Error(w, err.Error(), 500)
return
Expand Down Expand Up @@ -157,7 +160,7 @@ func Handler(w http.ResponseWriter, req *http.Request) {

// Make sure the symlink is absolute

if !strings.HasPrefix(redirectPath, MountPath) {
if !strings.HasPrefix(redirectPath, h.mountPath) {
fmt.Fprintf(w, "found symlink out of store: %s\n", redirectPath)
} else {
http.Redirect(w, req, redirectPath, http.StatusMovedPermanently)
Expand All @@ -179,7 +182,7 @@ func Handler(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", ctype)
w.Header().Set("Content-Length", fmt.Sprintf("%d", hdr.Size))
if req.Method != "HEAD" {
io.CopyN(w, narReader, hdr.Size)
_, _ = io.CopyN(w, narReader, hdr.Size)
}
default:
http.Error(w, fmt.Sprintf("BUG: unknown NAR header type: %s", hdr.Type), 500)
Expand All @@ -192,16 +195,8 @@ func Handler(w http.ResponseWriter, req *http.Request) {
}
}

func getEnv(name, def string) string {
value := os.Getenv(name)
if value == "" {
return def
}
return value
}

// TODO: consider keeping a LRU cache
func getNarInfo(ctx context.Context, key string) (*narinfo.NarInfo, error) {
func getNarInfo(ctx context.Context, nixCache libstore.BinaryCacheReader, key string) (*narinfo.NarInfo, error) {
path := fmt.Sprintf("%s.narinfo", key)
fmt.Println("Fetching the narinfo:", path, "from:", nixCache.URL())
r, err := nixCache.GetFile(ctx, path)
Expand Down
17 changes: 14 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ package main

import (
"embed"
"context"
"io"
"log"
"net/http"
"os"

"github.com/numtide/nar-serve/pkg/libstore"
"github.com/numtide/nar-serve/api/unpack"

"github.com/go-chi/chi/v5"
Expand Down Expand Up @@ -35,14 +37,23 @@ func healthzHandler(w http.ResponseWriter, r *http.Request) {

func main() {
var (
port = getEnv("PORT", "8383")
addr = getEnv("HTTP_ADDR", "")
port = getEnv("PORT", "8383")
addr = getEnv("HTTP_ADDR", "")
nixCacheURL = getEnv("NAR_CACHE_URL", "https://cache.nixos.org")
)

if addr == "" {
addr = ":" + port
}

cache, err := libstore.NewBinaryCacheReader(context.Background(), nixCacheURL)
if err != nil {
panic(err)
}

// FIXME: get the mountPath from the binary cache /nix-cache-info file
h := unpack.NewHandler(cache, "/nix/store/")

r := chi.NewRouter()

r.Use(middleware.RequestID)
Expand All @@ -54,7 +65,7 @@ func main() {
r.Get("/", indexHandler)
r.Get("/healthz", healthzHandler)
r.Get("/robots.txt", robotsHandler)
r.Get(unpack.MountPath+"*", unpack.Handler)
r.Method("GET", h.MountPath()+"*", h)

log.Println("addr=", addr)
log.Fatal(http.ListenAndServe(addr, r))
Expand Down

0 comments on commit 8f27921

Please sign in to comment.