Skip to content

Commit

Permalink
Add FSPrefix to ignore parent dir of assets
Browse files Browse the repository at this point in the history
  • Loading branch information
vearutop committed Jul 13, 2023
1 parent d521178 commit e32e0d3
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 14 deletions.
12 changes: 2 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,15 +95,13 @@ Files with extensions ".gz", ".br", ".gif", ".jpg", ".png", ".webp" are excluded
### Mounting a subdirectory

It may be convenient to strip leading directory from an embedded file system, you can do that with `fs.Sub` and a type
assertion.
It may be convenient to strip leading directory from an embedded file system, you can do that with `statigz.FSPrefix`.

```go
package main

import (
"embed"
"io/fs"
"log"
"net/http"

Expand All @@ -117,14 +115,8 @@ import (
var st embed.FS

func main() {
// Retrieve sub directory.
sub, err := fs.Sub(st, "static")
if err != nil {
log.Fatal(err)
}

// Plug static assets handler to your server or router.
err = http.ListenAndServe(":80", statigz.FileServer(sub.(fs.ReadDirFS), brotli.AddEncoding))
err := http.ListenAndServe(":80", statigz.FileServer(st, brotli.AddEncoding, statigz.FSPrefix("static")))
if err != nil {
log.Fatal(err)
}
Expand Down
27 changes: 23 additions & 4 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,16 @@ type Server struct {
// of large embeddings, use with caution.
EncodeOnInit bool

info map[string]fileInfo
fs fs.ReadDirFS
// FSPrefix is a path prefix shat should be ignored.
// It is prepended to the incoming HTTP path.
// This can help to keep static assets in a subdirectory, e.g.
// //go:embed static/*
// But access files from HTTP without "/static/" prefix in the path.
FSPrefix string

info map[string]fileInfo
fs fs.ReadDirFS
fsPrefix string
}

const (
Expand Down Expand Up @@ -89,6 +97,10 @@ func FileServer(fs fs.ReadDirFS, options ...func(server *Server)) *Server {
o(&s)
}

if s.FSPrefix != "" {
s.fsPrefix = strings.Trim(s.FSPrefix, "/") + "/"
}

// Reading from "." is not expected to fail.
if err := s.hashDir("."); err != nil {
panic(err)
Expand Down Expand Up @@ -319,7 +331,7 @@ func (s *Server) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
return
}

fn := strings.TrimPrefix(req.URL.Path, "/")
fn := s.fsPrefix + strings.TrimPrefix(req.URL.Path, "/")
ae := req.Header.Get("Accept-Encoding")

if s.info[fn].isDir {
Expand Down Expand Up @@ -375,7 +387,7 @@ func (s *Server) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
//
// This can be useful for custom handling of requests to non-existent resources.
func (s *Server) Found(req *http.Request) bool {
fn := strings.TrimPrefix(req.URL.Path, "/")
fn := s.fsPrefix + strings.TrimPrefix(req.URL.Path, "/")
ae := req.Header.Get("Accept-Encoding")

if s.info[fn].isDir {
Expand Down Expand Up @@ -486,6 +498,13 @@ func EncodeOnInit(server *Server) {
server.EncodeOnInit = true
}

// FSPrefix declares file system path prefix that should be ignored.
func FSPrefix(prefix string) func(server *Server) {
return func(server *Server) {
server.FSPrefix = prefix
}
}

// localRedirect gives a Moved Permanently response.
// It does not convert relative paths to absolute paths like Redirect does.
//
Expand Down
30 changes: 30 additions & 0 deletions server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ func TestServer_ServeHTTP_found(t *testing.T) {
} else {
assert.Equal(t, http.StatusNotFound, rw.Code, u)
}

assert.Equal(t, found, s.Found(req))
}

for u, l := range map[string]string{
Expand Down Expand Up @@ -375,3 +377,31 @@ func ExampleServer_Found() {
log.Fatal(err)
}
}

func TestServer_ServeHTTP_FSPrefix(t *testing.T) {
s := statigz.FileServer(v, brotli.AddEncoding, statigz.EncodeOnInit, statigz.FSPrefix("testdata"))

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)
}

assert.Equal(t, found, s.Found(req))
}
}

0 comments on commit e32e0d3

Please sign in to comment.