client/web: add support for zst precomppressed assets

This will enable us to reduce the size of these embedded assets.

Updates tailscale/corp#20099

Signed-off-by: James Tucker <james@tailscale.com>
This commit is contained in:
James Tucker
2024-05-17 13:47:27 -07:00
parent adb7a86559
commit f39ac7d49c
10 changed files with 174 additions and 25 deletions

View File

@@ -4,6 +4,7 @@
package web
import (
"fmt"
"io"
"io/fs"
"log"
@@ -16,7 +17,9 @@ import (
"strings"
"time"
"github.com/klauspost/compress/zstd"
prebuilt "github.com/tailscale/web-client-prebuilt"
"tailscale.com/tsweb/tswebutil"
)
var start = time.Now()
@@ -63,7 +66,64 @@ func assetsHandler(devMode bool) (_ http.Handler, cleanup func()) {
}), nil
}
func openPrecompressedFile(w http.ResponseWriter, r *http.Request, path string, fs fs.FS) (fs.File, error) {
type zstFile struct {
f fs.File
*zstd.Decoder
}
func newZSTFile(f fs.File) (*zstFile, error) {
zr, err := zstd.NewReader(f)
if err != nil {
return nil, err
}
return &zstFile{f: f, Decoder: zr}, nil
}
func (z *zstFile) Seek(offset int64, whence int) (int64, error) {
reset := func() error {
if seeker, ok := z.f.(io.Seeker); ok {
seeker.Seek(0, io.SeekStart)
} else {
return fmt.Errorf("not seekable: %w", os.ErrInvalid)
}
return z.Decoder.Reset(z.f)
}
switch whence {
case io.SeekStart:
if err := reset(); err != nil {
return 0, err
}
return io.CopyN(io.Discard, z, offset)
case io.SeekCurrent:
if offset >= 0 {
io.CopyN(io.Discard, z, offset)
} else {
return 0, fmt.Errorf("unsupported negative seek: %w", os.ErrInvalid)
}
case io.SeekEnd:
if offset != 0 {
return 0, fmt.Errorf("unsupported non-zero offset for SeekEnd: %w", os.ErrInvalid)
}
return io.Copy(io.Discard, z)
}
return 0, os.ErrInvalid
}
func (z *zstFile) Close() error {
z.Decoder.Close()
return z.f.Close()
}
func openPrecompressedFile(w http.ResponseWriter, r *http.Request, path string, fs fs.FS) (io.ReadCloser, error) {
if f, err := fs.Open(path + ".zst"); err == nil {
if tswebutil.AcceptsEncoding(r, "zstd") {
w.Header().Set("Content-Encoding", "zstd")
return f, nil
}
return newZSTFile(f)
}
// TODO(raggi): remove this code path when no longer used
if f, err := fs.Open(path + ".gz"); err == nil {
w.Header().Set("Content-Encoding", "gzip")
return f, nil