Correct handling of multiple packfiles

During the 1.3.1 update, a change was made to stop using http.Handle (and ServeMux
underneath), in order to have manual control over the http.Server object.

Unfortunately, it was overlooked that nothing was doing routing / multiplexing, so
when using multiple packfiles only one handler (picked arbitrarily due to map)
would actually be active on any given invocation.

Correct this by adding an explicit handler. We don't use ServeMux so as to avoid
bringing in any of its more complex behaviours like path cleaning etc.
This commit is contained in:
Laurence Withers 2022-12-12 10:26:55 +00:00
parent 5398dddb02
commit 2b280de481
1 changed files with 55 additions and 3 deletions

View File

@ -12,6 +12,7 @@ import (
"os"
"os/signal"
"path/filepath"
"sort"
"strings"
"sync/atomic"
"syscall"
@ -192,7 +193,7 @@ func run(c *cobra.Command, args []string) error {
}
// load packfiles, registering handlers as we go
var handler http.Handler
router := &routerHandler{}
for prefix, packfile := range packPaths {
packHandler, err := htpack.New(packfile)
if err != nil {
@ -207,7 +208,7 @@ func run(c *cobra.Command, args []string) error {
}
packHandler.SetHeader("X-Frame-Options", framesHeader)
handler = &addHeaders{
var handler http.Handler = &addHeaders{
extraHeaders: extraHeaders,
handler: packHandler,
}
@ -215,12 +216,13 @@ func run(c *cobra.Command, args []string) error {
if prefix != "/" {
handler = http.StripPrefix(prefix, handler)
}
router.AddRoute(prefix, handler)
}
// HTTP server object setup
sv := &http.Server{
Addr: bindAddr,
Handler: handler,
Handler: router,
}
// register SIGINT, SIGTERM handler
@ -309,3 +311,53 @@ func (ah *addHeaders) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
ah.handler.ServeHTTP(w, r)
}
// routeEntry is used within routerHandler to map a specific prefix to a
// specific handler.
type routeEntry struct {
// prefix is a path prefix with trailing "/" such as "/foo/".
prefix string
// handler for the request if prefix matches.
handler http.Handler
}
// routerHandler holds a list of routes sorted by longest-prefix-first.
type routerHandler struct {
// entries are the list of prefixes, with longest prefix strings first.
// The sorting ensures we can iterate through from the start and match
// "/dir/subdir/" in preference to just "/dir/".
entries []routeEntry
}
// AddRoute adds a new entry into the handler. It is not concurrency safe; the
// handler should not be in use.
func (rh *routerHandler) AddRoute(prefix string, handler http.Handler) {
if !strings.HasSuffix(prefix, "/") {
prefix += "/"
}
rh.entries = append(rh.entries, routeEntry{
prefix: prefix,
handler: handler,
})
sort.Slice(rh.entries, func(i, j int) bool {
l1, l2 := len(rh.entries[i].prefix), len(rh.entries[j].prefix)
if l1 > l2 {
return true
}
if l1 == l2 {
return rh.entries[i].prefix < rh.entries[j].prefix
}
return false
})
}
func (rh *routerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
for _, entry := range rh.entries {
if strings.HasPrefix(r.URL.Path, entry.prefix) {
entry.handler.ServeHTTP(w, r)
return
}
}
http.NotFound(w, r)
}