Repository: labstack/echo
Pull request #1701 (2020-11-30 18:14:19)

Adds IgnoreBase parameter to static middleware
Adds IgnoreBase parameter to static middleware to support the use case of nested route groups.

I've run into a use case of having a route group that is used with static middleware, but when you access the group's base url the filesystem path is doubled. For example

```
group := root.Group("somepath")
group .Use(middleware.Static(filepath.Join("filesystempath")))
```
When an incoming request comes for `/somepath` the actual filesystem request goes to `filesystempath/somepath` instead of only `filesystempath`. This is what is being solved with this PR.

It includes tests for both branches, with bool value set to true, or default -> false.


Patch:
diff --git a/_fixture/_fixture/README.md b/_fixture/_fixture/README.md
new file mode 100644
index 0000000..21a7858
--- /dev/null
+++ b/_fixture/_fixture/README.md
@@ -0,0 +1,1 @@
+This directory is used for the static middleware test
\ No newline at end of file
diff --git a/middleware/static.go b/middleware/static.go
index bc2087a..58b7890 100644
--- a/middleware/static.go
+++ b/middleware/static.go
@@ -36,6 +36,12 @@ type (
 		// Enable directory browsing.
 		// Optional. Default value false.
 		Browse bool `yaml:"browse"`
+
+		// Enable ignoring of the base of the URL path.
+		// Example: when assigning a static middleware to a non root path group,
+		// the filesystem path is not doubled
+		// Optional. Default value false.
+		IgnoreBase bool `yaml:"ignoreBase"`
 	}
 )
 
@@ -163,6 +169,15 @@ func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc {
 			}
 			name := filepath.Join(config.Root, path.Clean("/"+p)) // "/"+ for security
 
+			if config.IgnoreBase {
+				routePath := path.Base(strings.TrimRight(c.Path(), "/*"))
+				baseURLPath := path.Base(p)
+				if baseURLPath == routePath {
+					i := strings.LastIndex(name, routePath)
+					name = name[:i] + strings.Replace(name[i:], routePath, "", 1)
+				}
+			}
+
 			fi, err := os.Stat(name)
 			if err != nil {
 				if os.IsNotExist(err) {


Test patch:
diff --git a/middleware/static_test.go b/middleware/static_test.go
index 0d695d3..407dd15 100644
--- a/middleware/static_test.go
+++ b/middleware/static_test.go
@@ -3,6 +3,7 @@ package middleware
 import (
 	"net/http"
 	"net/http/httptest"
+	"path/filepath"
 	"testing"
 
 	"github.com/labstack/echo/v4"
@@ -67,4 +68,27 @@ func TestStatic(t *testing.T) {
 		assert.Equal(http.StatusOK, rec.Code)
 		assert.Contains(rec.Body.String(), "cert.pem")
 	}
+
+	// IgnoreBase
+	req = httptest.NewRequest(http.MethodGet, "/_fixture", nil)
+	rec = httptest.NewRecorder()
+	config.Root = "../_fixture"
+	config.IgnoreBase = true
+	static = StaticWithConfig(config)
+	c.Echo().Group("_fixture", static)
+	e.ServeHTTP(rec, req)
+
+	assert.Equal(http.StatusOK, rec.Code)
+	assert.Equal(rec.Header().Get(echo.HeaderContentLength), "122")
+
+	req = httptest.NewRequest(http.MethodGet, "/_fixture", nil)
+	rec = httptest.NewRecorder()
+	config.Root = "../_fixture"
+	config.IgnoreBase = false
+	static = StaticWithConfig(config)
+	c.Echo().Group("_fixture", static)
+	e.ServeHTTP(rec, req)
+
+	assert.Equal(http.StatusOK, rec.Code)
+	assert.Contains(rec.Body.String(), filepath.Join("..", "_fixture", "_fixture"))
 }