Reenable gzipping, pull in statigz fix, fix proxy prefix (#2039)

Co-authored-by: peolic <66393006+peolic@users.noreply.github.com>
This commit is contained in:
kermieisinthehouse
2021-11-18 18:30:21 -08:00
committed by GitHub
parent 9a76b4cf11
commit 74fcaa9a3f
12 changed files with 29 additions and 465 deletions

View File

@@ -1,39 +0,0 @@
// Package brotli provides encoding for statigz.Server.
package brotli
import (
"bytes"
"io"
"github.com/andybalholm/brotli"
"github.com/vearutop/statigz"
)
// AddEncoding is an option that prepends brotli to encodings of statigz.Server.
//
// It is located in a separate package to allow better control of imports graph.
func AddEncoding(server *statigz.Server) {
enc := statigz.Encoding{
FileExt: ".br",
ContentEncoding: "br",
Decoder: func(r io.Reader) (io.Reader, error) {
return brotli.NewReader(r), nil
},
Encoder: func(r io.Reader) ([]byte, error) {
res := bytes.NewBuffer(nil)
w := brotli.NewWriterLevel(res, 8)
if _, err := io.Copy(w, r); err != nil {
return nil, err
}
if err := w.Close(); err != nil {
return nil, err
}
return res.Bytes(), nil
},
}
server.Encodings = append([]statigz.Encoding{enc}, server.Encodings...)
}

View File

@@ -1,27 +0,0 @@
package brotli_test
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/vearutop/statigz"
"github.com/vearutop/statigz/brotli"
)
func TestAddEncoding(t *testing.T) {
s := &statigz.Server{}
s.Encodings = append(s.Encodings, statigz.GzipEncoding())
brotli.AddEncoding(s)
assert.Equal(t, ".br", s.Encodings[0].FileExt)
assert.Equal(t, ".gz", s.Encodings[1].FileExt)
d, err := s.Encodings[0].Decoder(nil)
assert.NoError(t, err)
assert.NotNil(t, d)
e, err := s.Encodings[0].Encoder(strings.NewReader(strings.Repeat("A", 10000)))
assert.NoError(t, err)
assert.NotEmpty(t, e)
assert.Less(t, len(e), 100)
}

View File

@@ -1,9 +0,0 @@
module github.com/vearutop/statigz
go 1.16
require (
github.com/andybalholm/brotli v1.0.3
github.com/bool64/dev v0.1.41
github.com/stretchr/testify v1.4.0
)

View File

@@ -1,15 +0,0 @@
github.com/andybalholm/brotli v1.0.3 h1:fpcw+r1N1h0Poc1F/pHbW40cUm/lMEQslZtCkBQ0UnM=
github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/bool64/dev v0.1.41 h1:L554LCQZc3d7mtcdPUgDbSrCVbr48/30zgu0VuC/FTA=
github.com/bool64/dev v0.1.41/go.mod h1:cTHiTDNc8EewrQPy3p1obNilpMpdmlUesDkFTF2zRWU=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@@ -216,6 +216,11 @@ func (s *Server) serve(rw http.ResponseWriter, req *http.Request, fn, suf, enc s
ctype = "application/octet-stream" // Prevent unreliable Content-Type detection on compressed data.
}
// This is used to enforce application/javascript MIME on Windows (https://github.com/golang/go/issues/32350)
if strings.HasSuffix(req.URL.Path, ".js") {
ctype = "application/javascript"
}
rw.Header().Set("Content-Type", ctype)
rw.Header().Set("Etag", info.hash)

View File

@@ -1,352 +0,0 @@
package statigz_test
import (
"compress/gzip"
"embed"
"io"
"io/fs"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
brotli2 "github.com/andybalholm/brotli"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/vearutop/statigz"
"github.com/vearutop/statigz/brotli"
)
//go:embed _testdata/*
var v embed.FS
func TestServer_ServeHTTP_std(t *testing.T) {
s := http.FileServer(http.FS(v))
for u, found := range map[string]bool{
"/_testdata/favicon.png": true,
"/_testdata/nonexistent": false,
"/_testdata/swagger.json": true,
"/_testdata/deeper/swagger.json": false,
"/_testdata/deeper/openapi.json": false,
"/_testdata/": true,
"/_testdata/?abc": true,
} {
req, err := http.NewRequest(http.MethodGet, u, nil)
require.NoError(t, err)
rw := httptest.NewRecorder()
s.ServeHTTP(rw, req)
if found {
assert.Equal(t, "", rw.Header().Get("Content-Encoding"))
assert.Equal(t, http.StatusOK, rw.Code, u)
} else {
assert.Equal(t, http.StatusNotFound, rw.Code, u)
}
}
for u, l := range map[string]string{
"/_testdata/index.html": "./",
"/_testdata": "_testdata/",
"/_testdata?abc": "_testdata/?abc",
} {
req, err := http.NewRequest(http.MethodGet, u, nil)
require.NoError(t, err)
rw := httptest.NewRecorder()
s.ServeHTTP(rw, req)
assert.Equal(t, http.StatusMovedPermanently, rw.Code, u)
assert.Equal(t, l, rw.Header().Get("Location"))
}
}
func TestServer_ServeHTTP_found(t *testing.T) {
s := statigz.FileServer(v, brotli.AddEncoding, statigz.EncodeOnInit)
for u, found := range map[string]bool{
"/_testdata/favicon.png": true,
"/_testdata/nonexistent": false,
"/_testdata/swagger.json": true,
"/_testdata/deeper/swagger.json": true,
"/_testdata/deeper/openapi.json": true,
"/_testdata/": true,
} {
req, err := http.NewRequest(http.MethodGet, u, nil)
require.NoError(t, err)
rw := httptest.NewRecorder()
s.ServeHTTP(rw, req)
if found {
assert.Equal(t, "", rw.Header().Get("Content-Encoding"))
assert.Equal(t, http.StatusOK, rw.Code, u)
} else {
assert.Equal(t, http.StatusNotFound, rw.Code, u)
}
}
for u, l := range map[string]string{
"/_testdata/index.html": "./",
"/_testdata": "_testdata/",
"/_testdata?abc": "_testdata/?abc",
} {
req, err := http.NewRequest(http.MethodGet, u, nil)
require.NoError(t, err)
rw := httptest.NewRecorder()
s.ServeHTTP(rw, req)
assert.Equal(t, http.StatusMovedPermanently, rw.Code, u)
assert.Equal(t, l, rw.Header().Get("Location"))
}
}
func TestServer_ServeHTTP_error(t *testing.T) {
s := statigz.FileServer(v, brotli.AddEncoding)
req, err := http.NewRequest(http.MethodDelete, "/", nil)
require.NoError(t, err)
rw := httptest.NewRecorder()
s.ServeHTTP(rw, req)
assert.Equal(t, http.StatusMethodNotAllowed, rw.Code)
assert.Equal(t, "Method Not Allowed\n\nmethod should be GET or HEAD\n", rw.Body.String())
}
func TestServer_ServeHTTP_acceptEncoding(t *testing.T) {
s := statigz.FileServer(v, brotli.AddEncoding, statigz.EncodeOnInit)
req, err := http.NewRequest(http.MethodGet, "/_testdata/deeper/swagger.json", nil)
require.NoError(t, err)
req.Header.Set("Accept-Encoding", "gzip, br")
rw := httptest.NewRecorder()
s.ServeHTTP(rw, req)
assert.Equal(t, http.StatusOK, rw.Code)
assert.Equal(t, "br", rw.Header().Get("Content-Encoding"))
assert.Equal(t, "3b88egjdndqox", rw.Header().Get("Etag"))
assert.Len(t, rw.Body.Bytes(), 2548)
req.Header.Set("Accept-Encoding", "gzip")
rw = httptest.NewRecorder()
s.ServeHTTP(rw, req)
assert.Equal(t, http.StatusOK, rw.Code)
assert.Equal(t, "", rw.Header().Get("Content-Encoding"))
assert.Equal(t, "3b88egjdndqoxU", rw.Header().Get("Etag"))
assert.Len(t, rw.Body.Bytes(), 24919)
req.Header.Set("Accept-Encoding", "gzip, br")
req.Header.Set("If-None-Match", "3b88egjdndqox")
rw = httptest.NewRecorder()
s.ServeHTTP(rw, req)
assert.Equal(t, http.StatusNotModified, rw.Code)
assert.Equal(t, "", rw.Header().Get("Content-Encoding"))
assert.Equal(t, "", rw.Header().Get("Etag"))
assert.Len(t, rw.Body.Bytes(), 0)
}
func TestServer_ServeHTTP_badFile(t *testing.T) {
s := statigz.FileServer(v, brotli.AddEncoding,
statigz.OnError(func(rw http.ResponseWriter, r *http.Request, err error) {
assert.EqualError(t, err, "gzip: invalid header")
_, err = rw.Write([]byte("failed"))
assert.NoError(t, err)
}))
req, err := http.NewRequest(http.MethodGet, "/_testdata/bad.png", nil)
require.NoError(t, err)
rw := httptest.NewRecorder()
s.ServeHTTP(rw, req)
assert.Equal(t, http.StatusOK, rw.Code)
assert.Equal(t, "", rw.Header().Get("Content-Encoding"))
assert.Equal(t, "", rw.Header().Get("Etag"))
assert.Equal(t, "failed", rw.Body.String())
}
func TestServer_ServeHTTP_head(t *testing.T) {
s := statigz.FileServer(v, brotli.AddEncoding, statigz.EncodeOnInit)
req, err := http.NewRequest(http.MethodHead, "/_testdata/swagger.json", nil)
require.NoError(t, err)
req.Header.Set("Accept-Encoding", "gzip, br")
rw := httptest.NewRecorder()
s.ServeHTTP(rw, req)
assert.Equal(t, http.StatusOK, rw.Code)
assert.Equal(t, "br", rw.Header().Get("Content-Encoding"))
assert.Equal(t, "1bp69hxb9nd93.br", rw.Header().Get("Etag"))
assert.Len(t, rw.Body.String(), 0)
}
func TestServer_ServeHTTP_head_gz(t *testing.T) {
s := statigz.FileServer(v, statigz.EncodeOnInit)
req, err := http.NewRequest(http.MethodHead, "/_testdata/swagger.json", nil)
require.NoError(t, err)
req.Header.Set("Accept-Encoding", "gzip, br")
rw := httptest.NewRecorder()
s.ServeHTTP(rw, req)
assert.Equal(t, http.StatusOK, rw.Code)
assert.Equal(t, "gzip", rw.Header().Get("Content-Encoding"))
assert.Equal(t, "1bp69hxb9nd93.gz", rw.Header().Get("Etag"))
assert.Len(t, rw.Body.String(), 0)
}
func BenchmarkServer_ServeHTTP(b *testing.B) {
s := statigz.FileServer(v, statigz.EncodeOnInit)
req, err := http.NewRequest(http.MethodGet, "/_testdata/swagger.json", nil)
require.NoError(b, err)
req.Header.Set("Accept-Encoding", "gzip, br")
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
rw := httptest.NewRecorder()
s.ServeHTTP(rw, req)
}
}
func TestServer_ServeHTTP_get_gz(t *testing.T) {
s := statigz.FileServer(v, statigz.EncodeOnInit)
req, err := http.NewRequest(http.MethodGet, "/_testdata/swagger.json", nil)
require.NoError(t, err)
req.Header.Set("Accept-Encoding", "gzip, br")
rw := httptest.NewRecorder()
s.ServeHTTP(rw, req)
assert.Equal(t, http.StatusOK, rw.Code)
assert.Equal(t, "gzip", rw.Header().Get("Content-Encoding"))
assert.Equal(t, "1bp69hxb9nd93.gz", rw.Header().Get("Etag"))
assert.Equal(t, "Accept-Encoding", rw.Header().Get("Vary"))
assert.NotEmpty(t, rw.Body.String())
r, err := gzip.NewReader(rw.Body)
assert.NoError(t, err)
decoded, err := io.ReadAll(r)
assert.NoError(t, err)
raw, err := ioutil.ReadFile("_testdata/swagger.json")
assert.NoError(t, err)
assert.Equal(t, raw, decoded)
}
func TestServer_ServeHTTP_get_br(t *testing.T) {
s := statigz.FileServer(v, statigz.EncodeOnInit, brotli.AddEncoding)
req, err := http.NewRequest(http.MethodGet, "/_testdata/swagger.json", nil)
require.NoError(t, err)
req.Header.Set("Accept-Encoding", "gzip, br")
rw := httptest.NewRecorder()
s.ServeHTTP(rw, req)
assert.Equal(t, http.StatusOK, rw.Code)
assert.Equal(t, "br", rw.Header().Get("Content-Encoding"))
assert.Equal(t, "1bp69hxb9nd93.br", rw.Header().Get("Etag"))
assert.NotEmpty(t, rw.Body.String())
r := brotli2.NewReader(rw.Body)
decoded, err := io.ReadAll(r)
assert.NoError(t, err)
raw, err := ioutil.ReadFile("_testdata/swagger.json")
assert.NoError(t, err)
assert.Equal(t, raw, decoded)
}
func TestServer_ServeHTTP_indexCompressed(t *testing.T) {
s := statigz.FileServer(v)
req, err := http.NewRequest(http.MethodGet, "/_testdata/", nil)
require.NoError(t, err)
req.Header.Set("Accept-Encoding", "gzip, br")
rw := httptest.NewRecorder()
s.ServeHTTP(rw, req)
assert.Equal(t, http.StatusOK, rw.Code)
assert.Equal(t, "gzip", rw.Header().Get("Content-Encoding"))
assert.Equal(t, "45pls0g4wm91", rw.Header().Get("Etag"))
assert.NotEmpty(t, rw.Body.String())
r, err := gzip.NewReader(rw.Body)
assert.NoError(t, err)
decoded, err := io.ReadAll(r)
assert.NoError(t, err)
assert.Equal(t, "Hello!", string(decoded))
}
func TestServer_ServeHTTP_sub(t *testing.T) {
vs, err := fs.Sub(v, "_testdata")
require.NoError(t, err)
s := statigz.FileServer(vs.(fs.ReadDirFS), brotli.AddEncoding, statigz.EncodeOnInit)
for u, found := range map[string]bool{
"/favicon.png": true,
"/nonexistent": false,
"/swagger.json": true,
"/deeper/swagger.json": true,
"/deeper/openapi.json": true,
"/": true,
} {
req, err := http.NewRequest(http.MethodGet, u, nil)
require.NoError(t, err)
rw := httptest.NewRecorder()
s.ServeHTTP(rw, req)
if found {
assert.Equal(t, "", rw.Header().Get("Content-Encoding"))
assert.Equal(t, http.StatusOK, rw.Code, u)
} else {
assert.Equal(t, http.StatusNotFound, rw.Code, u)
}
}
for u, l := range map[string]string{
"/index.html": "./",
"/deeper": "deeper/",
"/deeper?abc": "deeper/?abc",
} {
req, err := http.NewRequest(http.MethodGet, u, nil)
require.NoError(t, err)
rw := httptest.NewRecorder()
s.ServeHTTP(rw, req)
assert.Equal(t, http.StatusMovedPermanently, rw.Code, u)
assert.Equal(t, l, rw.Header().Get("Location"))
}
}