From 90ee9f39bc9685969e475b98e23a6d200404d4e8 Mon Sep 17 00:00:00 2001 From: Nigel Sim Date: Tue, 15 Oct 2019 23:02:26 +1000 Subject: [PATCH] feat: recursive fallback resolution --- README.md | 13 +++++++++++-- fallback.go | 24 ++++++++++++++++++++---- main.go | 6 +++--- 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 1ef8d12..ad9a112 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ Usage of /goStatic: -enable-basic-auth Enable basic auth. By default, password are randomly generated. Use --set-basic-auth to set it. -fallback string - Default relative to be used when no file requested found. E.g. /index.html + Default fallback file. Either absolute for a specific asset (/index.html), or relative to recursively resolve (index.html). -password-length int Size of the randomized password (default 16) -path string @@ -58,4 +58,13 @@ Usage of /goStatic: The listening port (default 8043) -set-basic-auth string Define the basic auth. Form must be user:password -``` \ No newline at end of file +``` + +#### Fallback + +The fallback option is principally useful for single page applications (SPAs) where the browser may request a file, but where part of the path is in fact an internal route in the application, not a file on disk. goStatic supports two possible usages of this option: + +1. Using an absolute path so that all not found requests resolve to the same file +2. Using a relative file, which searches up the tree for the specified file + +The second case is useful if you have multiple SPAs within the one filesystem. e.g., */* and */admin*. diff --git a/fallback.go b/fallback.go index 20f4159..94ec419 100644 --- a/fallback.go +++ b/fallback.go @@ -3,6 +3,7 @@ package main import ( "net/http" "os" + "path" ) // fallback opens defaultPath when the underlying fs returns os.ErrNotExist @@ -11,10 +12,25 @@ type fallback struct { fs http.FileSystem } -func (fb fallback) Open(path string) (http.File, error) { - f, err := fb.fs.Open(path) - if os.IsNotExist(err) { - return fb.fs.Open(fb.defaultPath) +func OpenDefault(fb fallback, requestPath string) (http.File, error) { + requestPath = path.Dir(requestPath) + defaultFile := requestPath + "/" + fb.defaultPath; + + f, err := fb.fs.Open(defaultFile) + if os.IsNotExist(err) && requestPath != "" { + parentPath, _ := path.Split(requestPath) + return OpenDefault(fb, parentPath) + } + return f, err +} + +func (fb fallback) Open(requestPath string) (http.File, error) { + f, err := fb.fs.Open(requestPath) + if os.IsNotExist(err) { + if len(fb.defaultPath) == 0 || fb.defaultPath[0] == '/' { + return fb.fs.Open(fb.defaultPath) + } + return OpenDefault(fb, requestPath) } return f, err } diff --git a/main.go b/main.go index f1ae3c2..2c4698c 100644 --- a/main.go +++ b/main.go @@ -15,8 +15,8 @@ var ( // Def of flags portPtr = flag.Int("port", 8043, "The listening port") context = flag.String("context", "", "The 'context' path on which files are served, e.g. 'doc' will serve the files at 'http://localhost:/doc/'") - path = flag.String("path", "/srv/http", "The path for the static files") - fallbackPath = flag.String("fallback", "", "Default relative to be used when no file requested found. E.g. /index.html") + basePath = flag.String("path", "/srv/http", "The path for the static files") + fallbackPath = flag.String("fallback", "", "Default fallback file. Either absolute for a specific asset (/index.html), or relative to recursively resolve (index.html)") headerFlag = flag.String("append-header", "", "HTTP response header, specified as `HeaderName:Value` that should be added to all responses.") basicAuth = flag.Bool("enable-basic-auth", false, "Enable basic auth. By default, password are randomly generated. Use --set-basic-auth to set it.") setBasicAuth = flag.String("set-basic-auth", "", "Define the basic auth. Form must be user:password") @@ -49,7 +49,7 @@ func main() { port := ":" + strconv.FormatInt(int64(*portPtr), 10) - var fileSystem http.FileSystem = http.Dir(*path) + var fileSystem http.FileSystem = http.Dir(*basePath) if *fallbackPath != "" { fileSystem = fallback{