We have very similar code in corp, moving it to util/precompress allows it to be reused. Updates #5133 Signed-off-by: Mihai Parparita <mihai@tailscale.com>main
parent
dfcef3382e
commit
4aa88bc2c0
@ -0,0 +1,131 @@ |
||||
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package precompress provides build- and serving-time support for
|
||||
// precompressed static resources, to avoid the cost of repeatedly compressing
|
||||
// unchanging resources.
|
||||
package precompress |
||||
|
||||
import ( |
||||
"bytes" |
||||
"compress/gzip" |
||||
"io" |
||||
"io/fs" |
||||
"net/http" |
||||
"os" |
||||
"path" |
||||
"path/filepath" |
||||
|
||||
"github.com/andybalholm/brotli" |
||||
"golang.org/x/sync/errgroup" |
||||
"tailscale.com/tsweb" |
||||
) |
||||
|
||||
// PrecompressDir compresses static assets in dirPath using Gzip and Brotli, so
|
||||
// that they can be later served with OpenPrecompressedFile.
|
||||
func PrecompressDir(dirPath string, options Options) error { |
||||
var eg errgroup.Group |
||||
err := fs.WalkDir(os.DirFS(dirPath), ".", func(p string, d fs.DirEntry, err error) error { |
||||
if err != nil { |
||||
return err |
||||
} |
||||
if d.IsDir() { |
||||
return nil |
||||
} |
||||
if !compressibleExtensions[filepath.Ext(p)] { |
||||
return nil |
||||
} |
||||
p = path.Join(dirPath, p) |
||||
if options.ProgressFn != nil { |
||||
options.ProgressFn(p) |
||||
} |
||||
|
||||
eg.Go(func() error { |
||||
return precompress(p, options) |
||||
}) |
||||
return nil |
||||
}) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
return eg.Wait() |
||||
} |
||||
|
||||
type Options struct { |
||||
// FastCompression controls whether compression should be optimized for
|
||||
// speed rather than size.
|
||||
FastCompression bool |
||||
// ProgressFn, if non-nil, is invoked when a file in the directory is about
|
||||
// to be compressed.
|
||||
ProgressFn func(path string) |
||||
} |
||||
|
||||
// OpenPrecompressedFile opens a file from fs, preferring compressed versions
|
||||
// generated by PrecompressDir if possible.
|
||||
func OpenPrecompressedFile(w http.ResponseWriter, r *http.Request, path string, fs fs.FS) (fs.File, error) { |
||||
if tsweb.AcceptsEncoding(r, "br") { |
||||
if f, err := fs.Open(path + ".br"); err == nil { |
||||
w.Header().Set("Content-Encoding", "br") |
||||
return f, nil |
||||
} |
||||
} |
||||
if tsweb.AcceptsEncoding(r, "gzip") { |
||||
if f, err := fs.Open(path + ".gz"); err == nil { |
||||
w.Header().Set("Content-Encoding", "gzip") |
||||
return f, nil |
||||
} |
||||
} |
||||
|
||||
return fs.Open(path) |
||||
} |
||||
|
||||
var compressibleExtensions = map[string]bool{ |
||||
".js": true, |
||||
".css": true, |
||||
".wasm": true, |
||||
} |
||||
|
||||
func precompress(path string, options Options) error { |
||||
contents, err := os.ReadFile(path) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
fi, err := os.Lstat(path) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
gzipLevel := gzip.BestCompression |
||||
if options.FastCompression { |
||||
gzipLevel = gzip.BestSpeed |
||||
} |
||||
err = writeCompressed(contents, func(w io.Writer) (io.WriteCloser, error) { |
||||
return gzip.NewWriterLevel(w, gzipLevel) |
||||
}, path+".gz", fi.Mode()) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
brotliLevel := brotli.BestCompression |
||||
if options.FastCompression { |
||||
brotliLevel = brotli.BestSpeed |
||||
} |
||||
return writeCompressed(contents, func(w io.Writer) (io.WriteCloser, error) { |
||||
return brotli.NewWriterLevel(w, brotliLevel), nil |
||||
}, path+".br", fi.Mode()) |
||||
} |
||||
|
||||
func writeCompressed(contents []byte, compressedWriterCreator func(io.Writer) (io.WriteCloser, error), outputPath string, outputMode fs.FileMode) error { |
||||
var buf bytes.Buffer |
||||
compressedWriter, err := compressedWriterCreator(&buf) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
if _, err := compressedWriter.Write(contents); err != nil { |
||||
return err |
||||
} |
||||
if err := compressedWriter.Close(); err != nil { |
||||
return err |
||||
} |
||||
return os.WriteFile(outputPath, buf.Bytes(), outputMode) |
||||
} |
||||
Loading…
Reference in new issue