diff --git a/cmd/packserver/go.mod b/cmd/packserver/go.mod index 794e446..958080f 100644 --- a/cmd/packserver/go.mod +++ b/cmd/packserver/go.mod @@ -1,15 +1,15 @@ module src.lwithers.me.uk/go/htpack/cmd/packserver -go 1.22 +go 1.26.0 require ( - github.com/spf13/cobra v1.8.1 - src.lwithers.me.uk/go/htpack v1.3.3 + github.com/spf13/cobra v1.10.2 + src.lwithers.me.uk/go/htpack v1.5.0 ) require ( - github.com/gogo/protobuf v1.3.2 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/sys v0.22.0 // indirect + github.com/spf13/pflag v1.0.10 // indirect + golang.org/x/sys v0.47.0 // indirect + google.golang.org/protobuf v1.36.12 // indirect ) diff --git a/cmd/packserver/go.sum b/cmd/packserver/go.sum index cda383a..180bbaa 100644 --- a/cmd/packserver/go.sum +++ b/cmd/packserver/go.sum @@ -1,45 +1,19 @@ -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc= +google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -src.lwithers.me.uk/go/htpack v1.3.3 h1:Xvl6qR9HfSblmCgPyu+ACQ9o3aLQSIy3l8CrMbzj/jc= -src.lwithers.me.uk/go/htpack v1.3.3/go.mod h1:qKgCBgZ6iiiuYOxZkYOPVpXLBzp6gXEd4A0ksxgR6Nk= +src.lwithers.me.uk/go/htpack v1.5.0 h1:2UYM4AGIPc/XgktmO6AxUyqFxzmYkqg/jINcDCsZsCk= +src.lwithers.me.uk/go/htpack v1.5.0/go.mod h1:5dQlPdwV+ZeBAaFBdwiRI6/w5dpSgPsiotO4ZTYau88= diff --git a/cmd/packserver/main.go b/cmd/packserver/main.go index 641a100..a374a66 100644 --- a/cmd/packserver/main.go +++ b/cmd/packserver/main.go @@ -55,6 +55,10 @@ func main() { "Name of index file (index.html or similar)") rootCmd.Flags().Duration("expiry", 0, "Tell client how long it can cache data for; 0 means no caching") + rootCmd.Flags().Duration("expiry404", 0, + "Tell client how long it can cache 404 responses for; 0 means default (inherit from --expiry, max. 60s); -1 means no caching") + rootCmd.Flags().Bool("immutable", false, + "Tell client that cached assets are immutable (will not be revalidated on page reload)") rootCmd.Flags().String("fallback-404", "", "Name of file to return if response would be 404 (spa.html or similar)") rootCmd.Flags().String("frames", "sameorigin", @@ -135,15 +139,35 @@ func run(c *cobra.Command, args []string) error { // parse expiry time // NB: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control + cacheControl200, cacheControl404 := "no-cache", "no-cache" + immutable, err := c.Flags().GetBool("immutable") + if err != nil { + return err + } + var immutableDirective string + if immutable { + immutableDirective = ", immutable" + } + expiry, err := c.Flags().GetDuration("expiry") if err != nil { return err } - if expiry <= 0 { - extraHeaders.Set("Cache-Control", "no-cache") - } else { - extraHeaders.Set("Cache-Control", - fmt.Sprintf("public, max-age=%d", expiry/1e9)) + if expiry > 0 { + cacheControl200 = fmt.Sprintf("public, max-age=%d%s", expiry/1e9, immutableDirective) + } + + expiry404, err := c.Flags().GetDuration("expiry404") + if err != nil { + return err + } + switch { + case expiry404 == 0 && expiry == 0: + case expiry404 == 0 && expiry > 0: + expiry404 = min(60*time.Second, expiry) + fallthrough + case expiry404 > 0: + cacheControl404 = fmt.Sprintf("public, max-age=%d", expiry404/1e9) } // optional index file @@ -199,6 +223,7 @@ func run(c *cobra.Command, args []string) error { if err != nil { return err } + packHandler.SetCacheControl(cacheControl200, cacheControl404) if indexFile != "" { packHandler.SetIndex(indexFile) } diff --git a/go.mod b/go.mod index 9f23a57..075ad33 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,8 @@ module src.lwithers.me.uk/go/htpack -go 1.25.0 +go 1.26.0 require ( - github.com/google/go-cmp v0.7.0 - golang.org/x/sys v0.45.0 - google.golang.org/protobuf v1.36.11 + golang.org/x/sys v0.47.0 + google.golang.org/protobuf v1.36.12 ) diff --git a/go.sum b/go.sum index f246438..3bd003e 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,6 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= -golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= -google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc= +google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= diff --git a/handler.go b/handler.go index a429f19..9f13f42 100644 --- a/handler.go +++ b/handler.go @@ -4,6 +4,7 @@ import ( "fmt" "net/http" "path" + "path/filepath" "strconv" "strings" "time" @@ -14,7 +15,6 @@ import ( const ( encodingGzip = "gzip" encodingBrotli = "br" - encodingZstd = "zstd" ) // New returns a new handler. Standard security headers are set. @@ -23,21 +23,14 @@ func New(packfile string) (*Handler, error) { if err != nil { return nil, err } - return NewMapped("/", mapped), nil -} -// NewMapped returns a new handler, given an already-mapped datafile. Standard -// security headers are set. -// -// Files are served under the directory prefix given by path, which may be -// empty or "/" for the root path. Note the [Handler.ServeMapped] method may specify -// additional files to be served under different paths, not affected by this -// path. -func NewMapped(path string, mapped *packed.Mapped) *Handler { h := &Handler{ - files: make(map[string]handlerFile), - headers: make(map[string]string), - startTime: time.Now(), + mapped: mapped.Data, + dir: mapped.Directory.Files, + headers: make(map[string]string), + cacheControl200: "no-cache", + cacheControl404: "no-cache", + startTime: time.Now(), } // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options @@ -46,24 +39,18 @@ func NewMapped(path string, mapped *packed.Mapped) *Handler { // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options h.SetHeader("X-Content-Type-Options", "nosniff") - h.addFiles(path, mapped) - - return h + return h, nil } // Handler implements http.Handler and allows options to be set. type Handler struct { - files map[string]handlerFile - headers map[string]string - startTime time.Time - notFound *handlerFile -} - -// AddMapped serves up additional files from the given mapped packfile. The -// given path is taken as a serving prefix. If a file for a given path already -// exists, the existing file takes precedence. -func (h *Handler) AddMapped(path string, mapped *packed.Mapped) { - h.addFiles(path, mapped) + mapped []byte + dir map[string]*packed.File + headers map[string]string + cacheControl200 string + cacheControl404 string + startTime time.Time + notFound *packed.File } // SetHeader allows a custom header to be set on HTTP responses. These are @@ -92,11 +79,11 @@ func (h *Handler) SetHeader(key, value string) { // multiple times with different filenames (noting later calls would not // overwrite files matching earlier calls). func (h *Handler) SetIndex(filename string) { - for k, v := range h.files { - if path.Base(k) == filename { - routeToAdd := path.Dir(k) - if _, exists := h.files[routeToAdd]; !exists { - h.files[routeToAdd] = v + for k, v := range h.dir { + if filepath.Base(k) == filename { + routeToAdd := filepath.Dir(k) + if _, exists := h.dir[routeToAdd]; !exists { + h.dir[routeToAdd] = v } } } @@ -116,60 +103,25 @@ func (h *Handler) SetNotFound(notFound string) error { } notFound = path.Clean(notFound) - f, ok := h.files[path.Clean(notFound)] - if !ok { + dir := h.dir[path.Clean(notFound)] + if dir == nil { return fmt.Errorf("no such resource %q", notFound) } - h.notFound = &f + h.notFound = dir return nil } -type handlerFile struct { - contentType, etag string - uncompressed, gzip, brotli, zstd []byte -} - -func (h *Handler) addFiles(prefix string, mapped *packed.Mapped) { - // our directory contains files like "/index.html". We will add - // a prefix, which might be "" or "/foo". - prefix = path.Clean(prefix) - prefix = strings.TrimSuffix(prefix, "/") - switch { - case prefix == "": - case prefix == ".": - prefix = "" - case !strings.HasPrefix(prefix, "/"): - prefix = "/" + prefix - } - - for filePath, file := range mapped.Directory.Files { - handlerPath := prefix + filePath - if _, exists := h.files[handlerPath]; exists { - // don't overwrite existing entries - continue - } - h.files[handlerPath] = buildHandlerFile(mapped.Data, file) - } -} - -func buildHandlerFile(data []byte, file *packed.File) handlerFile { - byteSlice := func(fileData *packed.FileData) []byte { - if fileData == nil { - return nil - } - start := fileData.Offset - end := start + fileData.Length - return data[start:end] - } - hf := handlerFile{ - contentType: file.ContentType, - etag: file.Etag, - } - hf.uncompressed = byteSlice(file.Uncompressed) - hf.gzip = byteSlice(file.Gzip) - hf.brotli = byteSlice(file.Brotli) - hf.zstd = byteSlice(file.Zstd) - return hf +// SetCacheControl allows control over the cache-control header. If not called, +// "Cache-Control: no-cache" is the default. +// +// The first and second arguments are required, and specify the header to use +// for 200 responses and 404 responses respectively. +// +// TODO: could be expanded to take extra varargs for more specific control over +// particular assets, like different expiry per content-type. +func (h *Handler) SetCacheControl(hdr200, hdr404 string) { + h.cacheControl200 = hdr200 + h.cacheControl404 = hdr404 } // ServeHTTP handles requests for files. It supports GET and HEAD methods, with @@ -190,48 +142,46 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - info, ok := h.files[path.Clean(req.URL.Path)] - if !ok { + info := h.dir[path.Clean(req.URL.Path)] + if info == nil { + w.Header().Set("Cache-Control", h.cacheControl404) if h.notFound == nil { http.NotFound(w, req) return } - info = *h.notFound + info = h.notFound } // set standard headers + w.Header().Set("Cache-Control", h.cacheControl200) w.Header().Set("Vary", "Accept-Encoding") - w.Header().Set("Etag", info.etag) - w.Header().Set("Content-Type", info.contentType) + w.Header().Set("Etag", info.Etag) + w.Header().Set("Content-Type", info.ContentType) w.Header().Set("Accept-Ranges", "bytes") // process etag / modtime - if clientHasCachedVersion(info.etag, h.startTime, req) { + if clientHasCachedVersion(info.Etag, h.startTime, req) { w.WriteHeader(http.StatusNotModified) return } // select compression - data := info.uncompressed - gzip, brotli, zstd := acceptedEncodings(req) - switch { - case zstd && info.zstd != nil: - data = info.zstd - w.Header().Set("Content-Encoding", encodingZstd) - case brotli && info.brotli != nil: - data = info.brotli + data := info.Uncompressed + gzip, brotli := acceptedEncodings(req) + if brotli && info.Brotli != nil { + data = info.Brotli w.Header().Set("Content-Encoding", encodingBrotli) - case gzip && info.gzip != nil: - data = info.gzip + } else if gzip && info.Gzip != nil { + data = info.Gzip w.Header().Set("Content-Encoding", encodingGzip) } // range support (single-part ranges only) // https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests#Single_part_ranges - offset, length, isPartial := getFileRange(uint64(len(data)), req) + offset, length, isPartial := getFileRange(data, req) if isPartial { w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", - offset, offset+length-1, len(data))) + offset, offset+length-1, data.Length)) } // now we know exactly what we're writing, finalise HTTP header @@ -247,10 +197,11 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - w.Write(data[offset : offset+length]) + offset += data.Offset + w.Write(h.mapped[offset : offset+length]) } -func acceptedEncodings(req *http.Request) (gzip, brotli, zstd bool) { +func acceptedEncodings(req *http.Request) (gzip, brotli bool) { encodings := req.Header.Get("Accept-Encoding") for _, enc := range strings.Split(encodings, ",") { switch strings.TrimSpace(enc) { @@ -293,8 +244,8 @@ func clientHasCachedVersion(etag string, startTime time.Time, req *http.Request, // getFileRange returns the byte offset and length of the file to serve, along // with whether or not it's partial content. -func getFileRange(dataLength uint64, req *http.Request) (offset, length uint64, isPartial bool) { - length = dataLength +func getFileRange(data *packed.FileData, req *http.Request) (offset, length uint64, isPartial bool) { + length = data.Length // only accept "Range: bytes=…" r := req.Header.Get("Range") @@ -321,8 +272,8 @@ func getFileRange(dataLength uint64, req *http.Request) (offset, length uint64, // validate the interval lies within the file switch { case from > to, - from >= dataLength, - to >= dataLength: + from >= data.Length, + to >= data.Length: return } diff --git a/handler_test.go b/handler_test.go deleted file mode 100644 index c0b986f..0000000 --- a/handler_test.go +++ /dev/null @@ -1,49 +0,0 @@ -package htpack - -import ( - "bytes" - "io" - "net/http/httptest" - "os" - "testing" - - "github.com/google/go-cmp/cmp" - "src.lwithers.me.uk/go/htpack/packed" -) - -func TestHandler(t *testing.T) { - mapped, err := packed.MapAndLoad("packed/testdata/helloworld.pack") - if err != nil { - t.Fatal("loading test packfile:", err) - } - defer mapped.Close() - - orig, err := os.ReadFile("packed/testdata/helloworld.txt") - if err != nil { - t.Fatal("loading test textfile:", err) - } - - h := NewMapped("", mapped) - h.AddMapped("/foo", mapped) - - testsv := httptest.NewServer(h) - defer testsv.Close() - cl := testsv.Client() - url := testsv.URL + "/helloworld.txt" - - resp, err := cl.Get(url) - if err != nil { - t.Fatal("executing GET request:", err) - } - defer resp.Body.Close() - - body, err := io.ReadAll(resp.Body) - if err != nil { - t.Fatal("reading response body:", err) - } - - if !bytes.Equal(body, orig) { - t.Error("difference in response (- got, + expected):") - t.Error(cmp.Diff(string(body), string(orig))) - } -} diff --git a/packed/packed.pb.go b/packed/packed.pb.go index aa3429f..32fd75a 100644 --- a/packed/packed.pb.go +++ b/packed/packed.pb.go @@ -168,8 +168,6 @@ type File struct { Gzip *FileData `protobuf:"bytes,4,opt,name=gzip,proto3" json:"gzip,omitempty"` // Brotli compressed version of the file. Brotli *FileData `protobuf:"bytes,5,opt,name=brotli,proto3" json:"brotli,omitempty"` - // Zstd compressed version of the file. - Zstd *FileData `protobuf:"bytes,6,opt,name=zstd,proto3" json:"zstd,omitempty"` } func (x *File) Reset() { @@ -239,13 +237,6 @@ func (x *File) GetBrotli() *FileData { return nil } -func (x *File) GetZstd() *FileData { - if x != nil { - return x.Zstd - } - return nil -} - // FileData records the position of the file data within the pack. type FileData struct { state protoimpl.MessageState @@ -330,7 +321,7 @@ var file_packed_proto_rawDesc = []byte{ 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6c, 0x77, 0x69, 0x74, 0x68, 0x65, 0x72, 0x73, 0x2e, 0x68, 0x74, 0x70, 0x61, 0x63, 0x6b, 0x2e, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x22, 0xa9, 0x02, 0x0a, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, + 0x01, 0x22, 0xf3, 0x01, 0x0a, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x65, 0x74, 0x61, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x65, 0x74, 0x61, @@ -345,17 +336,13 @@ var file_packed_proto_rawDesc = []byte{ 0x06, 0x62, 0x72, 0x6f, 0x74, 0x6c, 0x69, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6c, 0x77, 0x69, 0x74, 0x68, 0x65, 0x72, 0x73, 0x2e, 0x68, 0x74, 0x70, 0x61, 0x63, 0x6b, 0x2e, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, - 0x06, 0x62, 0x72, 0x6f, 0x74, 0x6c, 0x69, 0x12, 0x34, 0x0a, 0x04, 0x7a, 0x73, 0x74, 0x64, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6c, 0x77, 0x69, 0x74, 0x68, 0x65, 0x72, 0x73, - 0x2e, 0x68, 0x74, 0x70, 0x61, 0x63, 0x6b, 0x2e, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x2e, 0x46, - 0x69, 0x6c, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x04, 0x7a, 0x73, 0x74, 0x64, 0x22, 0x3a, 0x0a, - 0x08, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, - 0x73, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x06, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, - 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x06, 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x42, 0x22, 0x5a, 0x20, 0x73, 0x72, 0x63, - 0x2e, 0x6c, 0x77, 0x69, 0x74, 0x68, 0x65, 0x72, 0x73, 0x2e, 0x6d, 0x65, 0x2e, 0x75, 0x6b, 0x2f, - 0x68, 0x74, 0x70, 0x61, 0x63, 0x6b, 0x2f, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x06, 0x62, 0x72, 0x6f, 0x74, 0x6c, 0x69, 0x22, 0x3a, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x44, + 0x61, 0x74, 0x61, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x06, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6c, + 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x06, 0x52, 0x06, 0x6c, 0x65, 0x6e, + 0x67, 0x74, 0x68, 0x42, 0x22, 0x5a, 0x20, 0x73, 0x72, 0x63, 0x2e, 0x6c, 0x77, 0x69, 0x74, 0x68, + 0x65, 0x72, 0x73, 0x2e, 0x6d, 0x65, 0x2e, 0x75, 0x6b, 0x2f, 0x68, 0x74, 0x70, 0x61, 0x63, 0x6b, + 0x2f, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -383,13 +370,12 @@ var file_packed_proto_depIdxs = []int32{ 3, // 1: lwithers.htpack.packed.File.uncompressed:type_name -> lwithers.htpack.packed.FileData 3, // 2: lwithers.htpack.packed.File.gzip:type_name -> lwithers.htpack.packed.FileData 3, // 3: lwithers.htpack.packed.File.brotli:type_name -> lwithers.htpack.packed.FileData - 3, // 4: lwithers.htpack.packed.File.zstd:type_name -> lwithers.htpack.packed.FileData - 2, // 5: lwithers.htpack.packed.Directory.FilesEntry.value:type_name -> lwithers.htpack.packed.File - 6, // [6:6] is the sub-list for method output_type - 6, // [6:6] is the sub-list for method input_type - 6, // [6:6] is the sub-list for extension type_name - 6, // [6:6] is the sub-list for extension extendee - 0, // [0:6] is the sub-list for field type_name + 2, // 4: lwithers.htpack.packed.Directory.FilesEntry.value:type_name -> lwithers.htpack.packed.File + 5, // [5:5] is the sub-list for method output_type + 5, // [5:5] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name } func init() { file_packed_proto_init() } diff --git a/packed/packed.proto b/packed/packed.proto index f83ed0e..771e1ec 100644 --- a/packed/packed.proto +++ b/packed/packed.proto @@ -47,9 +47,6 @@ message File { // Brotli compressed version of the file. FileData brotli = 5; - - // Zstd compressed version of the file. - FileData zstd = 6; } // FileData records the position of the file data within the pack.