1
0
mirror of https://github.com/taigrr/wasm-experiments synced 2025-01-18 04:03:21 -08:00
2018-05-13 15:59:39 +01:00

107 lines
2.8 KiB
Go

package gzipped
import (
"fmt"
"net/http"
"os"
"path"
"strings"
"github.com/golang/gddo/httputil/header"
)
type fileHandler struct {
root http.FileSystem
}
// FileServer is a drop-in replacement for Go's standard http.FileServer
// which adds support for static resources precompressed with gzip, at
// the cost of removing the support for directory browsing.
//
// If file filename.ext has a compressed version filename.ext.gz alongside
// it, if the client indicates that it accepts gzip-compressed data, and
// if the .gz file can be opened, then the compressed version of the file
// will be sent to the client. Otherwise the request is passed on to
// http.ServeContent, and the raw (uncompressed) version is used.
//
// It is up to you to ensure that the compressed and uncompressed versions
// of files match and have sensible timestamps.
//
// Compressed or not, requests are fulfilled using http.ServeContent, and
// details like accept ranges and content-type sniffing are handled by that
// method.
func FileServer(root http.FileSystem) http.Handler {
return &fileHandler{root}
}
func gzipAcceptable(r *http.Request) bool {
for _, aspec := range header.ParseAccept(r.Header, "Accept-Encoding") {
if aspec.Value == "gzip" && aspec.Q == 0.0 {
return false
}
if (aspec.Value == "gzip" || aspec.Value == "*") && aspec.Q > 0.0 {
return true
}
}
return false
}
func (f *fileHandler) openAndStat(path string) (http.File, os.FileInfo, error) {
file, err := f.root.Open(path)
var info os.FileInfo
// This slightly weird variable reuse is so we can get 100% test coverage
// without having to come up with a test file that can be opened, yet
// fails to stat.
if err == nil {
info, err = file.Stat()
}
if err != nil {
return file, nil, err
}
if info.IsDir() {
return file, nil, fmt.Errorf("%s is directory", path)
}
return file, info, nil
}
func (f *fileHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
upath := r.URL.Path
if !strings.HasPrefix(upath, "/") {
upath = "/" + upath
r.URL.Path = upath
}
fpath := path.Clean(upath)
if strings.HasSuffix(fpath, "/") {
// If you wanted to put back directory browsing support, this is
// where you'd do it.
http.NotFound(w, r)
return
}
// Try for a compressed version if appropriate
var file http.File
var err error
var info os.FileInfo
var gzip bool
if gzipAcceptable(r) {
gzpath := fpath + ".gz"
file, info, err = f.openAndStat(gzpath)
if err == nil {
gzip = true
}
}
// If we didn't manage to open a compressed version, try for uncompressed
if !gzip {
file, info, err = f.openAndStat(fpath)
}
if err != nil {
// Doesn't exist compressed or uncompressed
http.NotFound(w, r)
return
}
if gzip {
w.Header().Set("Content-Encoding", "gzip")
}
defer file.Close()
http.ServeContent(w, r, fpath, info.ModTime(), file)
}