From 5412b9741182b9c45ebd92770c78c9ece1bc3889 Mon Sep 17 00:00:00 2001 From: Laurence Withers Date: Sat, 6 Jun 2026 10:16:03 +0100 Subject: [PATCH 1/7] Update Go version to 1.25, update golang.org/x/sys --- go.mod | 6 +++--- go.sum | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 85173b5..98c85d1 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,8 @@ module src.lwithers.me.uk/go/htpack +go 1.25.0 + require ( github.com/gogo/protobuf v1.3.2 - golang.org/x/sys v0.22.0 + golang.org/x/sys v0.45.0 ) - -go 1.22 diff --git a/go.sum b/go.sum index 5253528..c914128 100644 --- a/go.sum +++ b/go.sum @@ -19,8 +19,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ 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/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= 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= From cd9c8b600e0626dd51c9e7d682f3b32c51f5d18d Mon Sep 17 00:00:00 2001 From: Laurence Withers Date: Sat, 6 Jun 2026 10:16:40 +0100 Subject: [PATCH 2/7] packed: add ability to load from memory, add MapAndLoad The ability to load a packfile from memory means that it becomes easy to use the "embed" package to embed a small packfile. The refactor to allow loading from memory also makes it easy to add a MapAndLoad implementation, which means the file only needs to be opened / mmap()ed once. --- packed/load.go | 135 ++++++++++++++++++++++++-------- packed/load_test.go | 39 +++++++++ packed/testdata/helloworld.pack | Bin 0 -> 16597 bytes packed/testdata/helloworld.txt | 13 +++ 4 files changed, 154 insertions(+), 33 deletions(-) create mode 100644 packed/load_test.go create mode 100644 packed/testdata/helloworld.pack create mode 100644 packed/testdata/helloworld.txt diff --git a/packed/load.go b/packed/load.go index 10609e5..7ea1ade 100644 --- a/packed/load.go +++ b/packed/load.go @@ -2,9 +2,12 @@ package packed import ( fmt "fmt" + "io/fs" "os" "path" "strings" + + "golang.org/x/sys/unix" ) const ( @@ -17,14 +20,14 @@ const ( VersionInitial = 1 ) -// Load a ready-packed file. -func Load(f *os.File) (*Header, *Directory, error) { - hdr, err := loadHeader(f) +// Read a ready-packed file. +func Read(f []byte) (*Header, *Directory, error) { + hdr, err := readHeader(f) if err != nil { return nil, nil, err } - dir, err := loadDirectory(f, hdr) + dir, err := readDirectory(f, hdr) if le, ok := err.(*LoadError); ok { // augment error le.Magic = hdr.Magic @@ -33,20 +36,101 @@ func Load(f *os.File) (*Header, *Directory, error) { return hdr, dir, err // we may have a partial dir } -// loadHeader retrieves and decodes the header from the start of the file. It -// ensures the magic number and the version number match. Errors are returned -// as type LoadError. -func loadHeader(f *os.File) (*Header, error) { - raw := make([]byte, 36) - if _, err := f.ReadAt(raw, 0); err != nil { - return nil, &LoadError{ - Cause: IOError, - Underlying: err, +// Mapped holds an mmap()ed packfile. +type Mapped struct { + Data []byte + Header *Header + Directory *Directory +} + +func (m *Mapped) Close() error { + return unix.Munmap(m.Data) +} + +// MapAndLoad mmaps the named file and reads the pack data. +func MapAndLoad(fname string) (*Mapped, error) { + f, err := os.Open(fname) + if err != nil { + return nil, err + } + defer f.Close() + + st, err := f.Stat() + if err != nil { + return nil, &fs.PathError{ + Path: fname, + Op: "stat", + Err: err, } } + data, err := unix.Mmap(int(f.Fd()), 0, int(st.Size()), unix.PROT_READ, unix.MAP_SHARED) + if err != nil { + return nil, &fs.PathError{ + Path: fname, + Op: "mmap", + Err: err, + } + } + + hdr, dir, err := Read(data) + if err != nil { + unix.Munmap(data) + return nil, err + } + + return &Mapped{ + Data: data, + Header: hdr, + Directory: dir, + }, nil +} + +// Load a ready-packed file. +// +// Deprecated: use [Read] or [MapAndLoad]. +func Load(f *os.File) (*Header, *Directory, error) { + // we need to know the size of the file in order to mmap it + st, err := f.Stat() + if err != nil { + return nil, nil, &fs.PathError{ + Path: f.Name(), + Op: "stat", + Err: err, + } + } + + // compatibility: old code may have worked with non-zero offset + offset, err := f.Seek(0, os.SEEK_CUR) + if err != nil { + return nil, nil, &fs.PathError{ + Path: f.Name(), + Op: "seek", + Err: err, + } + } + + // mmap the file + data, err := unix.Mmap(int(f.Fd()), 0, int(st.Size()), unix.PROT_READ, unix.MAP_SHARED) + if err != nil { + return nil, nil, &fs.PathError{ + Path: f.Name(), + Op: "mmap", + Err: err, + } + } + defer unix.Munmap(data) + + // read as though it's from memory + return Read(data[offset:]) +} + +// readHeader retrieves and decodes the header from the start of the file. It +// ensures the magic number and the version number match. Errors are returned +// as type LoadError. +func readHeader(f []byte) (*Header, error) { hdr := new(Header) - if err := hdr.Unmarshal(raw); err != nil { + if err := hdr.Unmarshal(f[:36]); err != nil { return nil, &LoadError{ Cause: HeaderUnmarshalError, Underlying: err, @@ -77,18 +161,11 @@ func loadHeader(f *os.File) (*Header, error) { return hdr, nil } -// loadDirectory reads the directory from a file. The directory is checked +// readDirectory reads the directory from a file. The directory is checked // for consistency (offsets, filenames) but not integrity (file data is not // read/checksummed). -func loadDirectory(f *os.File, hdr *Header) (*Directory, error) { - fi, err := f.Stat() - if err != nil { - return nil, &LoadError{ - Cause: IOError, - Underlying: err, - } - } - fileSize := uint64(fi.Size()) +func readDirectory(f []byte, hdr *Header) (*Directory, error) { + fileSize := uint64(len(f)) if hdr.DirectoryOffset+hdr.DirectoryLength > fileSize { return nil, &LoadError{ @@ -96,16 +173,8 @@ func loadDirectory(f *os.File, hdr *Header) (*Directory, error) { } } - raw := make([]byte, hdr.DirectoryLength) - if _, err := f.ReadAt(raw, int64(hdr.DirectoryOffset)); err != nil { - return nil, &LoadError{ - Cause: IOError, - Underlying: err, - } - } - dir := new(Directory) - if err := dir.Unmarshal(raw); err != nil { + if err := dir.Unmarshal(f[hdr.DirectoryOffset : hdr.DirectoryOffset+hdr.DirectoryLength]); err != nil { return nil, &LoadError{ Cause: DirectoryUnmarshalError, Underlying: err, diff --git a/packed/load_test.go b/packed/load_test.go new file mode 100644 index 0000000..63be82e --- /dev/null +++ b/packed/load_test.go @@ -0,0 +1,39 @@ +package packed + +import ( + "bytes" + "os" + "testing" +) + +func TestMapAndLoad(t *testing.T) { + rawPack, err := os.ReadFile("testdata/helloworld.pack") + if err != nil { + t.Fatal(err) + } + rawText, err := os.ReadFile("testdata/helloworld.txt") + if err != nil { + t.Fatal(err) + } + + mapped, err := MapAndLoad("testdata/helloworld.pack") + if err != nil { + t.Fatal(err) + } + defer mapped.Close() + + if !bytes.Equal(mapped.Data, rawPack) { + t.Error("mapped data does not match raw file") + } + + file := mapped.Directory.Files["/helloworld.txt"] + if file == nil { + t.Fatal("directory does not contain /helloworld.txt") + } + + start := file.Uncompressed.Offset + end := start + file.Uncompressed.Length + if !bytes.Equal(rawText, mapped.Data[start:end]) { + t.Error("uncompressed file does not match testdata/helloworld.txt") + } +} diff --git a/packed/testdata/helloworld.pack b/packed/testdata/helloworld.pack new file mode 100644 index 0000000000000000000000000000000000000000..8d8541ec8ac97cd566f6031a9d93a0fe96692acb GIT binary patch literal 16597 zcmeI(O-K}B7zc1yDRi;SC8{-|w~fBc)!Eq(N6gSuf|Oc89@3hRcXmdm9hsSJ^&rHG z2uV?=GB1_YrHn!{LMW5kLnW40Y#&fwq5{!AC`$TRbv<_54#IyfGxI+@@ALkc)9^l7 zrDF{hReA6Fb8TsJmou$=?qS`tWP%$6AOHafKmY;|fB*y_009U<00IkJppt5uUO*zc zp-Fc8UPU#DtZI~qddT9{kV$wV(+JVj5H*P|lO`>Z5{oul1;n7drnQj9uxSw*(!)W; zZt27_)L@Vr#8N16iTa@?gPNwQhi2BWl!%h3?ji~e8>(rkVj&j&zq||k^F;R$fB*y_ z009U<00Izz00bbAB7xO!ZRN+F*6xgrTW0HqB_E!_B=@(M|j`sdod6q~2%|*LD{`#}oc|PVG^7^W7K8%bV__?^UZ6rEAKXj0S zouC8+AOHafKmY;|fB*y_009U<00RFKu#ej8IYr9M6u|5ZK%v!aId0qR&Y2_rMYE^< zONprPhDoimuqC@W2X%SeZibir9)=OUoFo);l1KJk@nbjq;zqCqBApigX literal 0 HcmV?d00001 diff --git a/packed/testdata/helloworld.txt b/packed/testdata/helloworld.txt new file mode 100644 index 0000000..3898fd6 --- /dev/null +++ b/packed/testdata/helloworld.txt @@ -0,0 +1,13 @@ +Hello, world + +This file contains a few lines of plain text, really just enough +to trigger the "compression worthwhile" heuristic. + +This file contains a few lines of plain text, really just enough +to trigger the "compression worthwhile" heuristic. + +This file contains a few lines of plain text, really just enough +to trigger the "compression worthwhile" heuristic. + +This file contains a few lines of plain text, really just enough +to trigger the "compression worthwhile" heuristic. From 203138bc0537eabfb62930ff00b2e587fadae840 Mon Sep 17 00:00:00 2001 From: Laurence Withers Date: Sat, 6 Jun 2026 10:19:50 +0100 Subject: [PATCH 3/7] Handler: use packed.MapAndLoad --- handler.go | 25 +++---------------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/handler.go b/handler.go index 0dd2dae..5edffef 100644 --- a/handler.go +++ b/handler.go @@ -3,14 +3,12 @@ package htpack import ( "fmt" "net/http" - "os" "path" "path/filepath" "strconv" "strings" "time" - "golang.org/x/sys/unix" "src.lwithers.me.uk/go/htpack/packed" ) @@ -21,31 +19,14 @@ const ( // New returns a new handler. Standard security headers are set. func New(packfile string) (*Handler, error) { - f, err := os.Open(packfile) + mapped, err := packed.MapAndLoad(packfile) if err != nil { return nil, err } - defer f.Close() - - fi, err := f.Stat() - if err != nil { - return nil, err - } - mapped, err := unix.Mmap(int(f.Fd()), 0, int(fi.Size()), - unix.PROT_READ, unix.MAP_SHARED) - if err != nil { - return nil, err - } - - _, dir, err := packed.Load(f) - if err != nil { - unix.Munmap(mapped) - return nil, err - } h := &Handler{ - mapped: mapped, - dir: dir.Files, + mapped: mapped.Data, + dir: mapped.Directory.Files, headers: make(map[string]string), startTime: time.Now(), } From 351d279a22bc3aa2a23c110ebd2df10ed374c756 Mon Sep 17 00:00:00 2001 From: Laurence Withers Date: Sat, 6 Jun 2026 11:00:04 +0100 Subject: [PATCH 4/7] =?UTF-8?q?packed:=20switch=20gogo/proto=20=E2=86=92?= =?UTF-8?q?=20google?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 2 +- go.sum | 35 +- packed/load.go | 9 +- packed/packed.pb.go | 1435 +++++++++++-------------------------------- packed/packed.proto | 5 +- 5 files changed, 360 insertions(+), 1126 deletions(-) diff --git a/go.mod b/go.mod index 98c85d1..00017b3 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,6 @@ module src.lwithers.me.uk/go/htpack go 1.25.0 require ( - github.com/gogo/protobuf v1.3.2 golang.org/x/sys v0.45.0 + google.golang.org/protobuf v1.36.11 ) diff --git a/go.sum b/go.sum index c914128..f246438 100644 --- a/go.sum +++ b/go.sum @@ -1,33 +1,6 @@ -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -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/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= +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= -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= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= diff --git a/packed/load.go b/packed/load.go index 7ea1ade..3dad308 100644 --- a/packed/load.go +++ b/packed/load.go @@ -8,6 +8,7 @@ import ( "strings" "golang.org/x/sys/unix" + "google.golang.org/protobuf/proto" ) const ( @@ -130,7 +131,7 @@ func Load(f *os.File) (*Header, *Directory, error) { // as type LoadError. func readHeader(f []byte) (*Header, error) { hdr := new(Header) - if err := hdr.Unmarshal(f[:36]); err != nil { + if err := proto.Unmarshal(f[:36], hdr); err != nil { return nil, &LoadError{ Cause: HeaderUnmarshalError, Underlying: err, @@ -166,15 +167,17 @@ func readHeader(f []byte) (*Header, error) { // read/checksummed). func readDirectory(f []byte, hdr *Header) (*Directory, error) { fileSize := uint64(len(f)) + start := hdr.DirectoryOffset + end := start + hdr.DirectoryLength - if hdr.DirectoryOffset+hdr.DirectoryLength > fileSize { + if end > fileSize { return nil, &LoadError{ Cause: BadOffsetError, } } dir := new(Directory) - if err := dir.Unmarshal(f[hdr.DirectoryOffset : hdr.DirectoryOffset+hdr.DirectoryLength]); err != nil { + if err := proto.Unmarshal(f[start:end], dir); err != nil { return nil, &LoadError{ Cause: DirectoryUnmarshalError, Underlying: err, diff --git a/packed/packed.pb.go b/packed/packed.pb.go index f14bd1a..32fd75a 100644 --- a/packed/packed.pb.go +++ b/packed/packed.pb.go @@ -1,43 +1,32 @@ -// Code generated by protoc-gen-gogo. DO NOT EDIT. +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.21.6 // source: packed.proto -/* - Package packed is a generated protocol buffer package. - - It is generated from these files: - packed.proto - - It has these top-level messages: - Header - Directory - File - FileData -*/ package packed import ( - fmt "fmt" - - proto "github.com/gogo/protobuf/proto" - - math "math" - - io "io" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" ) -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package - -// Header at start of file. This must be a fixed, known size. +// Header at start of file. This must be a fixed, known size. Fields cannot +// be zero. type Header struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // Magic number, used to quickly detect misconfigured systems or // corrupted files. Magic uint64 `protobuf:"fixed64,1,opt,name=magic,proto3" json:"magic,omitempty"` @@ -51,60 +40,122 @@ type Header struct { DirectoryLength uint64 `protobuf:"fixed64,4,opt,name=directory_length,json=directoryLength,proto3" json:"directory_length,omitempty"` } -func (m *Header) Reset() { *m = Header{} } -func (m *Header) String() string { return proto.CompactTextString(m) } -func (*Header) ProtoMessage() {} -func (*Header) Descriptor() ([]byte, []int) { return fileDescriptorPacked, []int{0} } +func (x *Header) Reset() { + *x = Header{} + if protoimpl.UnsafeEnabled { + mi := &file_packed_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} -func (m *Header) GetMagic() uint64 { - if m != nil { - return m.Magic +func (x *Header) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Header) ProtoMessage() {} + +func (x *Header) ProtoReflect() protoreflect.Message { + mi := &file_packed_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Header.ProtoReflect.Descriptor instead. +func (*Header) Descriptor() ([]byte, []int) { + return file_packed_proto_rawDescGZIP(), []int{0} +} + +func (x *Header) GetMagic() uint64 { + if x != nil { + return x.Magic } return 0 } -func (m *Header) GetVersion() uint64 { - if m != nil { - return m.Version +func (x *Header) GetVersion() uint64 { + if x != nil { + return x.Version } return 0 } -func (m *Header) GetDirectoryOffset() uint64 { - if m != nil { - return m.DirectoryOffset +func (x *Header) GetDirectoryOffset() uint64 { + if x != nil { + return x.DirectoryOffset } return 0 } -func (m *Header) GetDirectoryLength() uint64 { - if m != nil { - return m.DirectoryLength +func (x *Header) GetDirectoryLength() uint64 { + if x != nil { + return x.DirectoryLength } return 0 } // Directory of available files. type Directory struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // Files available within this pack. The key is the path of the URL to // serve, and the value describes the file associated with that path. - Files map[string]*File `protobuf:"bytes,1,rep,name=files" json:"files,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value"` + Files map[string]*File `protobuf:"bytes,1,rep,name=files,proto3" json:"files,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } -func (m *Directory) Reset() { *m = Directory{} } -func (m *Directory) String() string { return proto.CompactTextString(m) } -func (*Directory) ProtoMessage() {} -func (*Directory) Descriptor() ([]byte, []int) { return fileDescriptorPacked, []int{1} } +func (x *Directory) Reset() { + *x = Directory{} + if protoimpl.UnsafeEnabled { + mi := &file_packed_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} -func (m *Directory) GetFiles() map[string]*File { - if m != nil { - return m.Files +func (x *Directory) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Directory) ProtoMessage() {} + +func (x *Directory) ProtoReflect() protoreflect.Message { + mi := &file_packed_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Directory.ProtoReflect.Descriptor instead. +func (*Directory) Descriptor() ([]byte, []int) { + return file_packed_proto_rawDescGZIP(), []int{1} +} + +func (x *Directory) GetFiles() map[string]*File { + if x != nil { + return x.Files } return nil } // File that can be served. type File struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // ContentType of the file, copied directly into the "Content-Type" header. ContentType string `protobuf:"bytes,1,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"` // Etag of the file (includes double quotes). Remembered by the browser @@ -112,1086 +163,292 @@ type File struct { // requests. Etag string `protobuf:"bytes,2,opt,name=etag,proto3" json:"etag,omitempty"` // Uncompressed version of the file. - Uncompressed *FileData `protobuf:"bytes,3,opt,name=uncompressed" json:"uncompressed,omitempty"` + Uncompressed *FileData `protobuf:"bytes,3,opt,name=uncompressed,proto3" json:"uncompressed,omitempty"` // Gzip compressed version of the file. - Gzip *FileData `protobuf:"bytes,4,opt,name=gzip" json:"gzip,omitempty"` + 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" json:"brotli,omitempty"` + Brotli *FileData `protobuf:"bytes,5,opt,name=brotli,proto3" json:"brotli,omitempty"` } -func (m *File) Reset() { *m = File{} } -func (m *File) String() string { return proto.CompactTextString(m) } -func (*File) ProtoMessage() {} -func (*File) Descriptor() ([]byte, []int) { return fileDescriptorPacked, []int{2} } +func (x *File) Reset() { + *x = File{} + if protoimpl.UnsafeEnabled { + mi := &file_packed_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} -func (m *File) GetContentType() string { - if m != nil { - return m.ContentType +func (x *File) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*File) ProtoMessage() {} + +func (x *File) ProtoReflect() protoreflect.Message { + mi := &file_packed_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use File.ProtoReflect.Descriptor instead. +func (*File) Descriptor() ([]byte, []int) { + return file_packed_proto_rawDescGZIP(), []int{2} +} + +func (x *File) GetContentType() string { + if x != nil { + return x.ContentType } return "" } -func (m *File) GetEtag() string { - if m != nil { - return m.Etag +func (x *File) GetEtag() string { + if x != nil { + return x.Etag } return "" } -func (m *File) GetUncompressed() *FileData { - if m != nil { - return m.Uncompressed +func (x *File) GetUncompressed() *FileData { + if x != nil { + return x.Uncompressed } return nil } -func (m *File) GetGzip() *FileData { - if m != nil { - return m.Gzip +func (x *File) GetGzip() *FileData { + if x != nil { + return x.Gzip } return nil } -func (m *File) GetBrotli() *FileData { - if m != nil { - return m.Brotli +func (x *File) GetBrotli() *FileData { + if x != nil { + return x.Brotli } return nil } // FileData records the position of the file data within the pack. type FileData struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // Offset is the start of the file, in bytes relative to the start of // the pack. Offset uint64 `protobuf:"fixed64,1,opt,name=offset,proto3" json:"offset,omitempty"` - // Length is the + // Length of the (possibly compressed) file data, in bytes. Length uint64 `protobuf:"fixed64,2,opt,name=length,proto3" json:"length,omitempty"` } -func (m *FileData) Reset() { *m = FileData{} } -func (m *FileData) String() string { return proto.CompactTextString(m) } -func (*FileData) ProtoMessage() {} -func (*FileData) Descriptor() ([]byte, []int) { return fileDescriptorPacked, []int{3} } +func (x *FileData) Reset() { + *x = FileData{} + if protoimpl.UnsafeEnabled { + mi := &file_packed_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} -func (m *FileData) GetOffset() uint64 { - if m != nil { - return m.Offset +func (x *FileData) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FileData) ProtoMessage() {} + +func (x *FileData) ProtoReflect() protoreflect.Message { + mi := &file_packed_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FileData.ProtoReflect.Descriptor instead. +func (*FileData) Descriptor() ([]byte, []int) { + return file_packed_proto_rawDescGZIP(), []int{3} +} + +func (x *FileData) GetOffset() uint64 { + if x != nil { + return x.Offset } return 0 } -func (m *FileData) GetLength() uint64 { - if m != nil { - return m.Length +func (x *FileData) GetLength() uint64 { + if x != nil { + return x.Length } return 0 } -func init() { - proto.RegisterType((*Header)(nil), "packed.Header") - proto.RegisterType((*Directory)(nil), "packed.Directory") - proto.RegisterType((*File)(nil), "packed.File") - proto.RegisterType((*FileData)(nil), "packed.FileData") -} -func (m *Header) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} +var File_packed_proto protoreflect.FileDescriptor -func (m *Header) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Magic != 0 { - dAtA[i] = 0x9 - i++ - i = encodeFixed64Packed(dAtA, i, uint64(m.Magic)) - } - if m.Version != 0 { - dAtA[i] = 0x11 - i++ - i = encodeFixed64Packed(dAtA, i, uint64(m.Version)) - } - if m.DirectoryOffset != 0 { - dAtA[i] = 0x19 - i++ - i = encodeFixed64Packed(dAtA, i, uint64(m.DirectoryOffset)) - } - if m.DirectoryLength != 0 { - dAtA[i] = 0x21 - i++ - i = encodeFixed64Packed(dAtA, i, uint64(m.DirectoryLength)) - } - return i, nil -} - -func (m *Directory) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *Directory) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Files) > 0 { - for k, _ := range m.Files { - dAtA[i] = 0xa - i++ - v := m.Files[k] - msgSize := 0 - if v != nil { - msgSize = v.Size() - msgSize += 1 + sovPacked(uint64(msgSize)) - } - mapSize := 1 + len(k) + sovPacked(uint64(len(k))) + msgSize - i = encodeVarintPacked(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ - i = encodeVarintPacked(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) - if v != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintPacked(dAtA, i, uint64(v.Size())) - n1, err := v.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n1 - } - } - } - return i, nil -} - -func (m *File) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *File) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.ContentType) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintPacked(dAtA, i, uint64(len(m.ContentType))) - i += copy(dAtA[i:], m.ContentType) - } - if len(m.Etag) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintPacked(dAtA, i, uint64(len(m.Etag))) - i += copy(dAtA[i:], m.Etag) - } - if m.Uncompressed != nil { - dAtA[i] = 0x1a - i++ - i = encodeVarintPacked(dAtA, i, uint64(m.Uncompressed.Size())) - n2, err := m.Uncompressed.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n2 - } - if m.Gzip != nil { - dAtA[i] = 0x22 - i++ - i = encodeVarintPacked(dAtA, i, uint64(m.Gzip.Size())) - n3, err := m.Gzip.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n3 - } - if m.Brotli != nil { - dAtA[i] = 0x2a - i++ - i = encodeVarintPacked(dAtA, i, uint64(m.Brotli.Size())) - n4, err := m.Brotli.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n4 - } - return i, nil -} - -func (m *FileData) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *FileData) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Offset != 0 { - dAtA[i] = 0x9 - i++ - i = encodeFixed64Packed(dAtA, i, uint64(m.Offset)) - } - if m.Length != 0 { - dAtA[i] = 0x11 - i++ - i = encodeFixed64Packed(dAtA, i, uint64(m.Length)) - } - return i, nil -} - -func encodeFixed64Packed(dAtA []byte, offset int, v uint64) int { - dAtA[offset] = uint8(v) - dAtA[offset+1] = uint8(v >> 8) - dAtA[offset+2] = uint8(v >> 16) - dAtA[offset+3] = uint8(v >> 24) - dAtA[offset+4] = uint8(v >> 32) - dAtA[offset+5] = uint8(v >> 40) - dAtA[offset+6] = uint8(v >> 48) - dAtA[offset+7] = uint8(v >> 56) - return offset + 8 -} -func encodeFixed32Packed(dAtA []byte, offset int, v uint32) int { - dAtA[offset] = uint8(v) - dAtA[offset+1] = uint8(v >> 8) - dAtA[offset+2] = uint8(v >> 16) - dAtA[offset+3] = uint8(v >> 24) - return offset + 4 -} -func encodeVarintPacked(dAtA []byte, offset int, v uint64) int { - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ - } - dAtA[offset] = uint8(v) - return offset + 1 -} -func (m *Header) Size() (n int) { - var l int - _ = l - if m.Magic != 0 { - n += 9 - } - if m.Version != 0 { - n += 9 - } - if m.DirectoryOffset != 0 { - n += 9 - } - if m.DirectoryLength != 0 { - n += 9 - } - return n -} - -func (m *Directory) Size() (n int) { - var l int - _ = l - if len(m.Files) > 0 { - for k, v := range m.Files { - _ = k - _ = v - l = 0 - if v != nil { - l = v.Size() - l += 1 + sovPacked(uint64(l)) - } - mapEntrySize := 1 + len(k) + sovPacked(uint64(len(k))) + l - n += mapEntrySize + 1 + sovPacked(uint64(mapEntrySize)) - } - } - return n -} - -func (m *File) Size() (n int) { - var l int - _ = l - l = len(m.ContentType) - if l > 0 { - n += 1 + l + sovPacked(uint64(l)) - } - l = len(m.Etag) - if l > 0 { - n += 1 + l + sovPacked(uint64(l)) - } - if m.Uncompressed != nil { - l = m.Uncompressed.Size() - n += 1 + l + sovPacked(uint64(l)) - } - if m.Gzip != nil { - l = m.Gzip.Size() - n += 1 + l + sovPacked(uint64(l)) - } - if m.Brotli != nil { - l = m.Brotli.Size() - n += 1 + l + sovPacked(uint64(l)) - } - return n -} - -func (m *FileData) Size() (n int) { - var l int - _ = l - if m.Offset != 0 { - n += 9 - } - if m.Length != 0 { - n += 9 - } - return n -} - -func sovPacked(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n -} -func sozPacked(x uint64) (n int) { - return sovPacked(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} -func (m *Header) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowPacked - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: Header: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Header: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 1 { - return fmt.Errorf("proto: wrong wireType = %d for field Magic", wireType) - } - m.Magic = 0 - if (iNdEx + 8) > l { - return io.ErrUnexpectedEOF - } - iNdEx += 8 - m.Magic = uint64(dAtA[iNdEx-8]) - m.Magic |= uint64(dAtA[iNdEx-7]) << 8 - m.Magic |= uint64(dAtA[iNdEx-6]) << 16 - m.Magic |= uint64(dAtA[iNdEx-5]) << 24 - m.Magic |= uint64(dAtA[iNdEx-4]) << 32 - m.Magic |= uint64(dAtA[iNdEx-3]) << 40 - m.Magic |= uint64(dAtA[iNdEx-2]) << 48 - m.Magic |= uint64(dAtA[iNdEx-1]) << 56 - case 2: - if wireType != 1 { - return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) - } - m.Version = 0 - if (iNdEx + 8) > l { - return io.ErrUnexpectedEOF - } - iNdEx += 8 - m.Version = uint64(dAtA[iNdEx-8]) - m.Version |= uint64(dAtA[iNdEx-7]) << 8 - m.Version |= uint64(dAtA[iNdEx-6]) << 16 - m.Version |= uint64(dAtA[iNdEx-5]) << 24 - m.Version |= uint64(dAtA[iNdEx-4]) << 32 - m.Version |= uint64(dAtA[iNdEx-3]) << 40 - m.Version |= uint64(dAtA[iNdEx-2]) << 48 - m.Version |= uint64(dAtA[iNdEx-1]) << 56 - case 3: - if wireType != 1 { - return fmt.Errorf("proto: wrong wireType = %d for field DirectoryOffset", wireType) - } - m.DirectoryOffset = 0 - if (iNdEx + 8) > l { - return io.ErrUnexpectedEOF - } - iNdEx += 8 - m.DirectoryOffset = uint64(dAtA[iNdEx-8]) - m.DirectoryOffset |= uint64(dAtA[iNdEx-7]) << 8 - m.DirectoryOffset |= uint64(dAtA[iNdEx-6]) << 16 - m.DirectoryOffset |= uint64(dAtA[iNdEx-5]) << 24 - m.DirectoryOffset |= uint64(dAtA[iNdEx-4]) << 32 - m.DirectoryOffset |= uint64(dAtA[iNdEx-3]) << 40 - m.DirectoryOffset |= uint64(dAtA[iNdEx-2]) << 48 - m.DirectoryOffset |= uint64(dAtA[iNdEx-1]) << 56 - case 4: - if wireType != 1 { - return fmt.Errorf("proto: wrong wireType = %d for field DirectoryLength", wireType) - } - m.DirectoryLength = 0 - if (iNdEx + 8) > l { - return io.ErrUnexpectedEOF - } - iNdEx += 8 - m.DirectoryLength = uint64(dAtA[iNdEx-8]) - m.DirectoryLength |= uint64(dAtA[iNdEx-7]) << 8 - m.DirectoryLength |= uint64(dAtA[iNdEx-6]) << 16 - m.DirectoryLength |= uint64(dAtA[iNdEx-5]) << 24 - m.DirectoryLength |= uint64(dAtA[iNdEx-4]) << 32 - m.DirectoryLength |= uint64(dAtA[iNdEx-3]) << 40 - m.DirectoryLength |= uint64(dAtA[iNdEx-2]) << 48 - m.DirectoryLength |= uint64(dAtA[iNdEx-1]) << 56 - default: - iNdEx = preIndex - skippy, err := skipPacked(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthPacked - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *Directory) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowPacked - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: Directory: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Directory: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Files", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowPacked - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthPacked - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Files == nil { - m.Files = make(map[string]*File) - } - var mapkey string - var mapvalue *File - for iNdEx < postIndex { - entryPreIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowPacked - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - if fieldNum == 1 { - var stringLenmapkey uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowPacked - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - intStringLenmapkey := int(stringLenmapkey) - if intStringLenmapkey < 0 { - return ErrInvalidLengthPacked - } - postStringIndexmapkey := iNdEx + intStringLenmapkey - if postStringIndexmapkey > l { - return io.ErrUnexpectedEOF - } - mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) - iNdEx = postStringIndexmapkey - } else if fieldNum == 2 { - var mapmsglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowPacked - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - mapmsglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if mapmsglen < 0 { - return ErrInvalidLengthPacked - } - postmsgIndex := iNdEx + mapmsglen - if mapmsglen < 0 { - return ErrInvalidLengthPacked - } - if postmsgIndex > l { - return io.ErrUnexpectedEOF - } - mapvalue = &File{} - if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil { - return err - } - iNdEx = postmsgIndex - } else { - iNdEx = entryPreIndex - skippy, err := skipPacked(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthPacked - } - if (iNdEx + skippy) > postIndex { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - m.Files[mapkey] = mapvalue - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipPacked(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthPacked - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *File) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowPacked - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: File: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: File: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ContentType", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowPacked - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthPacked - } - postIndex := iNdEx + intStringLen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ContentType = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Etag", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowPacked - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthPacked - } - postIndex := iNdEx + intStringLen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Etag = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Uncompressed", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowPacked - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthPacked - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Uncompressed == nil { - m.Uncompressed = &FileData{} - } - if err := m.Uncompressed.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Gzip", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowPacked - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthPacked - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Gzip == nil { - m.Gzip = &FileData{} - } - if err := m.Gzip.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Brotli", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowPacked - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthPacked - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Brotli == nil { - m.Brotli = &FileData{} - } - if err := m.Brotli.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipPacked(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthPacked - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *FileData) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowPacked - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: FileData: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: FileData: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 1 { - return fmt.Errorf("proto: wrong wireType = %d for field Offset", wireType) - } - m.Offset = 0 - if (iNdEx + 8) > l { - return io.ErrUnexpectedEOF - } - iNdEx += 8 - m.Offset = uint64(dAtA[iNdEx-8]) - m.Offset |= uint64(dAtA[iNdEx-7]) << 8 - m.Offset |= uint64(dAtA[iNdEx-6]) << 16 - m.Offset |= uint64(dAtA[iNdEx-5]) << 24 - m.Offset |= uint64(dAtA[iNdEx-4]) << 32 - m.Offset |= uint64(dAtA[iNdEx-3]) << 40 - m.Offset |= uint64(dAtA[iNdEx-2]) << 48 - m.Offset |= uint64(dAtA[iNdEx-1]) << 56 - case 2: - if wireType != 1 { - return fmt.Errorf("proto: wrong wireType = %d for field Length", wireType) - } - m.Length = 0 - if (iNdEx + 8) > l { - return io.ErrUnexpectedEOF - } - iNdEx += 8 - m.Length = uint64(dAtA[iNdEx-8]) - m.Length |= uint64(dAtA[iNdEx-7]) << 8 - m.Length |= uint64(dAtA[iNdEx-6]) << 16 - m.Length |= uint64(dAtA[iNdEx-5]) << 24 - m.Length |= uint64(dAtA[iNdEx-4]) << 32 - m.Length |= uint64(dAtA[iNdEx-3]) << 40 - m.Length |= uint64(dAtA[iNdEx-2]) << 48 - m.Length |= uint64(dAtA[iNdEx-1]) << 56 - default: - iNdEx = preIndex - skippy, err := skipPacked(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthPacked - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func skipPacked(dAtA []byte) (n int, err error) { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowPacked - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - wireType := int(wire & 0x7) - switch wireType { - case 0: - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowPacked - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - iNdEx++ - if dAtA[iNdEx-1] < 0x80 { - break - } - } - return iNdEx, nil - case 1: - iNdEx += 8 - return iNdEx, nil - case 2: - var length int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowPacked - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - length |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - iNdEx += length - if length < 0 { - return 0, ErrInvalidLengthPacked - } - return iNdEx, nil - case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowPacked - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipPacked(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil - case 4: - return iNdEx, nil - case 5: - iNdEx += 4 - return iNdEx, nil - default: - return 0, fmt.Errorf("proto: illegal wireType %d", wireType) - } - } - panic("unreachable") +var file_packed_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x16, + 0x6c, 0x77, 0x69, 0x74, 0x68, 0x65, 0x72, 0x73, 0x2e, 0x68, 0x74, 0x70, 0x61, 0x63, 0x6b, 0x2e, + 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x22, 0x8e, 0x01, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x61, 0x67, 0x69, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x06, + 0x52, 0x05, 0x6d, 0x61, 0x67, 0x69, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x06, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x5f, 0x6f, + 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x06, 0x52, 0x0f, 0x64, 0x69, 0x72, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x29, 0x0a, 0x10, + 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x06, 0x52, 0x0f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x79, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, 0xa7, 0x01, 0x0a, 0x09, 0x44, 0x69, 0x72, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x42, 0x0a, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x6c, 0x77, 0x69, 0x74, 0x68, 0x65, 0x72, 0x73, 0x2e, + 0x68, 0x74, 0x70, 0x61, 0x63, 0x6b, 0x2e, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x2e, 0x44, 0x69, + 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x1a, 0x56, 0x0a, 0x0a, 0x46, 0x69, 0x6c, + 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x32, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 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, 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, + 0x67, 0x12, 0x44, 0x0a, 0x0c, 0x75, 0x6e, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, + 0x64, 0x18, 0x03, 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, 0x0c, 0x75, 0x6e, 0x63, 0x6f, 0x6d, + 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x12, 0x34, 0x0a, 0x04, 0x67, 0x7a, 0x69, 0x70, 0x18, + 0x04, 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, 0x67, 0x7a, 0x69, 0x70, 0x12, 0x38, 0x0a, + 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, 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 ( - ErrInvalidLengthPacked = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowPacked = fmt.Errorf("proto: integer overflow") + file_packed_proto_rawDescOnce sync.Once + file_packed_proto_rawDescData = file_packed_proto_rawDesc ) -func init() { proto.RegisterFile("packed.proto", fileDescriptorPacked) } - -var fileDescriptorPacked = []byte{ - // 359 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x74, 0x92, 0xc1, 0x4e, 0xab, 0x50, - 0x10, 0x86, 0x2f, 0x2d, 0x70, 0x6f, 0x07, 0x92, 0x4b, 0x26, 0xc6, 0x10, 0x63, 0x1a, 0x25, 0x2e, - 0xea, 0xa6, 0x0b, 0x74, 0x61, 0xba, 0x34, 0xb5, 0x71, 0x61, 0x62, 0x42, 0xdc, 0x37, 0x14, 0xa6, - 0x48, 0x4a, 0x81, 0x1c, 0x4e, 0x9b, 0xe0, 0x0b, 0xb8, 0x33, 0xbe, 0x93, 0x1b, 0x97, 0x3e, 0x82, - 0xd1, 0x17, 0x11, 0x0e, 0x87, 0x6a, 0x8d, 0x2e, 0x4e, 0x72, 0xfe, 0x6f, 0x7e, 0x06, 0x66, 0x7e, - 0xc0, 0xcc, 0xfd, 0x60, 0x41, 0xe1, 0x30, 0x67, 0x19, 0xcf, 0x50, 0x6f, 0x94, 0xf3, 0xa0, 0x80, - 0x7e, 0x49, 0x7e, 0x48, 0x0c, 0x77, 0x40, 0x5b, 0xfa, 0x51, 0x1c, 0xd8, 0xca, 0x81, 0x32, 0xd0, - 0xbd, 0x46, 0xa0, 0x0d, 0x7f, 0xd7, 0xc4, 0x8a, 0x38, 0x4b, 0xed, 0x8e, 0xe0, 0xad, 0xc4, 0x63, - 0xb0, 0xc2, 0x98, 0x51, 0xc0, 0x33, 0x56, 0x4e, 0xb3, 0xf9, 0xbc, 0x20, 0x6e, 0x77, 0x85, 0xe5, - 0xff, 0x86, 0x5f, 0x0b, 0xbc, 0x6d, 0x4d, 0x28, 0x8d, 0xf8, 0xad, 0xad, 0x7e, 0xb3, 0x5e, 0x09, - 0xec, 0xdc, 0x2b, 0xd0, 0x1b, 0xb7, 0x0c, 0x5d, 0xd0, 0xe6, 0x71, 0x42, 0x45, 0xf5, 0x4d, 0xdd, - 0x81, 0xe1, 0xee, 0x0f, 0xe5, 0x10, 0x1b, 0xc7, 0x70, 0x52, 0x97, 0x2f, 0x52, 0xce, 0x4a, 0xaf, - 0xb1, 0xee, 0x4d, 0x00, 0x3e, 0x21, 0x5a, 0xd0, 0x5d, 0x50, 0x29, 0x66, 0xea, 0x79, 0xf5, 0x15, - 0x1d, 0xd0, 0xd6, 0x7e, 0xb2, 0x22, 0x31, 0x8f, 0xe1, 0x9a, 0x6d, 0xcf, 0xfa, 0x21, 0xaf, 0x29, - 0x8d, 0x3a, 0x67, 0x8a, 0xf3, 0xa4, 0x80, 0x5a, 0x33, 0x3c, 0x04, 0x33, 0xc8, 0x52, 0x4e, 0x29, - 0x9f, 0xf2, 0x32, 0x27, 0xd9, 0xcb, 0x90, 0xec, 0xa6, 0x42, 0x88, 0xa0, 0x12, 0xf7, 0x23, 0xd1, - 0xb2, 0xe7, 0x89, 0x3b, 0x9e, 0x82, 0xb9, 0x4a, 0x83, 0x6c, 0x99, 0x33, 0x2a, 0x0a, 0x0a, 0xc5, - 0x6e, 0x0c, 0xd7, 0xfa, 0xfa, 0xba, 0xb1, 0xcf, 0x7d, 0x6f, 0xcb, 0x85, 0x47, 0xa0, 0x46, 0x77, - 0x71, 0x2e, 0xd6, 0xf3, 0x93, 0x5b, 0x54, 0x71, 0x00, 0xfa, 0xac, 0xca, 0x31, 0x89, 0x6d, 0xed, - 0x17, 0x9f, 0xac, 0x3b, 0x23, 0xf8, 0xd7, 0x32, 0xdc, 0x05, 0x5d, 0xe6, 0xd4, 0x44, 0x2c, 0x55, - 0xcd, 0x65, 0x28, 0x4d, 0xc4, 0x52, 0x9d, 0x5b, 0xcf, 0x6f, 0x7d, 0xe5, 0xa5, 0x3a, 0xaf, 0xd5, - 0x79, 0x7c, 0xef, 0xff, 0x99, 0xe9, 0xe2, 0xef, 0x39, 0xf9, 0x08, 0x00, 0x00, 0xff, 0xff, 0x07, - 0x4a, 0x55, 0x53, 0x4d, 0x02, 0x00, 0x00, +func file_packed_proto_rawDescGZIP() []byte { + file_packed_proto_rawDescOnce.Do(func() { + file_packed_proto_rawDescData = protoimpl.X.CompressGZIP(file_packed_proto_rawDescData) + }) + return file_packed_proto_rawDescData +} + +var file_packed_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_packed_proto_goTypes = []interface{}{ + (*Header)(nil), // 0: lwithers.htpack.packed.Header + (*Directory)(nil), // 1: lwithers.htpack.packed.Directory + (*File)(nil), // 2: lwithers.htpack.packed.File + (*FileData)(nil), // 3: lwithers.htpack.packed.FileData + nil, // 4: lwithers.htpack.packed.Directory.FilesEntry +} +var file_packed_proto_depIdxs = []int32{ + 4, // 0: lwithers.htpack.packed.Directory.files:type_name -> lwithers.htpack.packed.Directory.FilesEntry + 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 + 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() } +func file_packed_proto_init() { + if File_packed_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_packed_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Header); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_packed_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Directory); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_packed_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*File); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_packed_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FileData); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_packed_proto_rawDesc, + NumEnums: 0, + NumMessages: 5, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_packed_proto_goTypes, + DependencyIndexes: file_packed_proto_depIdxs, + MessageInfos: file_packed_proto_msgTypes, + }.Build() + File_packed_proto = out.File + file_packed_proto_rawDesc = nil + file_packed_proto_goTypes = nil + file_packed_proto_depIdxs = nil } diff --git a/packed/packed.proto b/packed/packed.proto index e797e59..771e1ec 100644 --- a/packed/packed.proto +++ b/packed/packed.proto @@ -1,6 +1,7 @@ syntax = "proto3"; -package packed; +package lwithers.htpack.packed; +option go_package = "src.lwithers.me.uk/htpack/packed"; // Header at start of file. This must be a fixed, known size. Fields cannot // be zero. @@ -54,6 +55,6 @@ message FileData { // the pack. fixed64 offset = 1; - // Length is the + // Length of the (possibly compressed) file data, in bytes. fixed64 length = 2; } From 0016a5e4fdc606344913516b518c5ffd6566d8ce Mon Sep 17 00:00:00 2001 From: Laurence Withers Date: Sat, 6 Jun 2026 11:02:02 +0100 Subject: [PATCH 5/7] packed: add zstd compressed field --- packed/packed.pb.go | 42 ++++++++++++++++++++++++++++-------------- packed/packed.proto | 3 +++ 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/packed/packed.pb.go b/packed/packed.pb.go index 32fd75a..aa3429f 100644 --- a/packed/packed.pb.go +++ b/packed/packed.pb.go @@ -168,6 +168,8 @@ 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() { @@ -237,6 +239,13 @@ 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 @@ -321,7 +330,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, 0xf3, 0x01, 0x0a, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, + 0x01, 0x22, 0xa9, 0x02, 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, @@ -336,13 +345,17 @@ 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, 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, 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, } var ( @@ -370,12 +383,13 @@ 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 - 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 + 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 } func init() { file_packed_proto_init() } diff --git a/packed/packed.proto b/packed/packed.proto index 771e1ec..f83ed0e 100644 --- a/packed/packed.proto +++ b/packed/packed.proto @@ -47,6 +47,9 @@ 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. From 5bece326fba7fb66dd3df72b571ca27c4ea5c586 Mon Sep 17 00:00:00 2001 From: Laurence Withers Date: Sat, 6 Jun 2026 11:10:44 +0100 Subject: [PATCH 6/7] Handler: support zstd encoding --- handler.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/handler.go b/handler.go index 5edffef..b61957e 100644 --- a/handler.go +++ b/handler.go @@ -15,6 +15,7 @@ import ( const ( encodingGzip = "gzip" encodingBrotli = "br" + encodingZstd = "zstd" ) // New returns a new handler. Standard security headers are set. @@ -148,11 +149,15 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { // select compression data := info.Uncompressed - gzip, brotli := acceptedEncodings(req) - if brotli && info.Brotli != nil { + 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 w.Header().Set("Content-Encoding", encodingBrotli) - } else if gzip && info.Gzip != nil { + case gzip && info.Gzip != nil: data = info.Gzip w.Header().Set("Content-Encoding", encodingGzip) } @@ -182,7 +187,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { w.Write(h.mapped[offset : offset+length]) } -func acceptedEncodings(req *http.Request) (gzip, brotli bool) { +func acceptedEncodings(req *http.Request) (gzip, brotli, zstd bool) { encodings := req.Header.Get("Accept-Encoding") for _, enc := range strings.Split(encodings, ",") { switch strings.TrimSpace(enc) { From 5b8873a4f595d5f3fb88f6bbc2e16b6e136f367b Mon Sep 17 00:00:00 2001 From: Laurence Withers Date: Sat, 6 Jun 2026 11:07:03 +0100 Subject: [PATCH 7/7] Handler: add NewMapped, AddMapped The Handler now supports serving multiple packfiles, possibly with different prefixes. Precedence is given by order in which packfiles are registered. --- go.mod | 1 + handler.go | 135 +++++++++++++++++++++++++++++++++++------------- handler_test.go | 49 ++++++++++++++++++ 3 files changed, 149 insertions(+), 36 deletions(-) create mode 100644 handler_test.go diff --git a/go.mod b/go.mod index 00017b3..9f23a57 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module src.lwithers.me.uk/go/htpack go 1.25.0 require ( + github.com/google/go-cmp v0.7.0 golang.org/x/sys v0.45.0 google.golang.org/protobuf v1.36.11 ) diff --git a/handler.go b/handler.go index b61957e..a429f19 100644 --- a/handler.go +++ b/handler.go @@ -4,7 +4,6 @@ import ( "fmt" "net/http" "path" - "path/filepath" "strconv" "strings" "time" @@ -24,10 +23,19 @@ 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{ - mapped: mapped.Data, - dir: mapped.Directory.Files, + files: make(map[string]handlerFile), headers: make(map[string]string), startTime: time.Now(), } @@ -38,16 +46,24 @@ func New(packfile string) (*Handler, error) { // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options h.SetHeader("X-Content-Type-Options", "nosniff") - return h, nil + h.addFiles(path, mapped) + + return h } // Handler implements http.Handler and allows options to be set. type Handler struct { - mapped []byte - dir map[string]*packed.File + files map[string]handlerFile headers map[string]string startTime time.Time - notFound *packed.File + 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) } // SetHeader allows a custom header to be set on HTTP responses. These are @@ -76,11 +92,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.dir { - if filepath.Base(k) == filename { - routeToAdd := filepath.Dir(k) - if _, exists := h.dir[routeToAdd]; !exists { - h.dir[routeToAdd] = v + 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 } } } @@ -100,14 +116,62 @@ func (h *Handler) SetNotFound(notFound string) error { } notFound = path.Clean(notFound) - dir := h.dir[path.Clean(notFound)] - if dir == nil { + f, ok := h.files[path.Clean(notFound)] + if !ok { return fmt.Errorf("no such resource %q", notFound) } - h.notFound = dir + h.notFound = &f 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 +} + // ServeHTTP handles requests for files. It supports GET and HEAD methods, with // anything else returning a 405. Exact path matches are required, else a 404 is // returned. @@ -126,48 +190,48 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - info := h.dir[path.Clean(req.URL.Path)] - if info == nil { + info, ok := h.files[path.Clean(req.URL.Path)] + if !ok { if h.notFound == nil { http.NotFound(w, req) return } - info = h.notFound + info = *h.notFound } // set standard headers 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 + data := info.uncompressed gzip, brotli, zstd := acceptedEncodings(req) switch { - case zstd && info.Zstd != nil: - data = info.Zstd + case zstd && info.zstd != nil: + data = info.zstd w.Header().Set("Content-Encoding", encodingZstd) - case brotli && info.Brotli != nil: - data = info.Brotli + case brotli && info.brotli != nil: + data = info.brotli w.Header().Set("Content-Encoding", encodingBrotli) - case gzip && info.Gzip != nil: - data = info.Gzip + case 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(data, req) + offset, length, isPartial := getFileRange(uint64(len(data)), req) if isPartial { w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", - offset, offset+length-1, data.Length)) + offset, offset+length-1, len(data))) } // now we know exactly what we're writing, finalise HTTP header @@ -183,8 +247,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - offset += data.Offset - w.Write(h.mapped[offset : offset+length]) + w.Write(data[offset : offset+length]) } func acceptedEncodings(req *http.Request) (gzip, brotli, zstd bool) { @@ -230,8 +293,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(data *packed.FileData, req *http.Request) (offset, length uint64, isPartial bool) { - length = data.Length +func getFileRange(dataLength uint64, req *http.Request) (offset, length uint64, isPartial bool) { + length = dataLength // only accept "Range: bytes=…" r := req.Header.Get("Range") @@ -258,8 +321,8 @@ func getFileRange(data *packed.FileData, req *http.Request) (offset, length uint // validate the interval lies within the file switch { case from > to, - from >= data.Length, - to >= data.Length: + from >= dataLength, + to >= dataLength: return } diff --git a/handler_test.go b/handler_test.go new file mode 100644 index 0000000..c0b986f --- /dev/null +++ b/handler_test.go @@ -0,0 +1,49 @@ +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))) + } +}