fix: stripPrefix and stripPrefixRegex.
This commit is contained in:
parent
770b3739e0
commit
f843f260ee
6 changed files with 55 additions and 52 deletions
|
@ -3,20 +3,16 @@
|
||||||
Removing Prefixes From the Path Before Forwarding the Request (Using a Regex)
|
Removing Prefixes From the Path Before Forwarding the Request (Using a Regex)
|
||||||
{: .subtitle }
|
{: .subtitle }
|
||||||
|
|
||||||
`TODO: add schema`
|
|
||||||
|
|
||||||
Remove the matching prefixes from the URL path.
|
Remove the matching prefixes from the URL path.
|
||||||
|
|
||||||
## Configuration Examples
|
## Configuration Examples
|
||||||
|
|
||||||
```yaml tab="Docker"
|
```yaml tab="Docker"
|
||||||
# Replace the path by /foo
|
|
||||||
labels:
|
labels:
|
||||||
- "traefik.http.middlewares.test-stripprefixregex.stripprefixregex.regex=^/foo/(.*)",
|
- "traefik.http.middlewares.test-stripprefixregex.stripprefixregex.regex=/foo/[a-z0-9]+/[0-9]+/",
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="Kubernetes"
|
```yaml tab="Kubernetes"
|
||||||
# Replace the path by /foo
|
|
||||||
apiVersion: traefik.containo.us/v1alpha1
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
kind: Middleware
|
kind: Middleware
|
||||||
metadata:
|
metadata:
|
||||||
|
@ -24,36 +20,33 @@ metadata:
|
||||||
spec:
|
spec:
|
||||||
stripPrefixRegex:
|
stripPrefixRegex:
|
||||||
regex:
|
regex:
|
||||||
- "^/foo/(.*)"
|
- "/foo/[a-z0-9]+/[0-9]+/"
|
||||||
```
|
```
|
||||||
|
|
||||||
```json tab="Marathon"
|
```json tab="Marathon"
|
||||||
"labels": {
|
"labels": {
|
||||||
"traefik.http.middlewares.test-stripprefixregex.stripprefixregex.regex": "^/foo/(.*)"
|
"traefik.http.middlewares.test-stripprefixregex.stripprefixregex.regex": "/foo/[a-z0-9]+/[0-9]+/"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="Rancher"
|
```yaml tab="Rancher"
|
||||||
# Replace the path by /foo
|
|
||||||
labels:
|
labels:
|
||||||
- "traefik.http.middlewares.test-stripprefixregex.stripprefixregex.regex=^/foo/(.*)",
|
- "traefik.http.middlewares.test-stripprefixregex.stripprefixregex.regex=/foo/[a-z0-9]+/[0-9]+/",
|
||||||
```
|
```
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
# Replace the path by /foo
|
|
||||||
[http.middlewares]
|
[http.middlewares]
|
||||||
[http.middlewares.test-stripprefixregex.stripPrefixRegex]
|
[http.middlewares.test-stripprefixregex.stripPrefixRegex]
|
||||||
regex = ["^/foo/(.*)"]
|
regex = ["/foo/[a-z0-9]+/[0-9]+/"]
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
# Replace the path by /foo
|
|
||||||
http:
|
http:
|
||||||
middlewares:
|
middlewares:
|
||||||
test-stripprefixregex:
|
test-stripprefixregex:
|
||||||
stripPrefixRegex:
|
stripPrefixRegex:
|
||||||
regex:
|
regex:
|
||||||
- "^/foo/(.*)"
|
- "/foo/[a-z0-9]+/[0-9]+/"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Configuration Options
|
## Configuration Options
|
||||||
|
|
|
@ -150,9 +150,9 @@
|
||||||
[http.middlewares.foo-slash-add-prefix.addPrefix]
|
[http.middlewares.foo-slash-add-prefix.addPrefix]
|
||||||
prefix = "/foo/"
|
prefix = "/foo/"
|
||||||
[http.middlewares.id-strip-regex-prefix.stripPrefixRegex]
|
[http.middlewares.id-strip-regex-prefix.stripPrefixRegex]
|
||||||
regex = ["/{id:[a-z]+}"]
|
regex = ["/[a-z]+"]
|
||||||
[http.middlewares.id-slash-strip-regex-prefix.stripPrefixRegex]
|
[http.middlewares.id-slash-strip-regex-prefix.stripPrefixRegex]
|
||||||
regex = ["/{id:[a-z]+}/"]
|
regex = ["/[a-z]+/"]
|
||||||
[http.middlewares.api-regex-replace.replacePathRegex]
|
[http.middlewares.api-regex-replace.replacePathRegex]
|
||||||
regex = "/api"
|
regex = "/api"
|
||||||
replacement = "/"
|
replacement = "/"
|
||||||
|
|
|
@ -49,7 +49,7 @@ func (s *stripPrefix) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
http.NotFound(rw, req)
|
s.next.ServeHTTP(rw, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stripPrefix) serveRequest(rw http.ResponseWriter, req *http.Request, prefix string) {
|
func (s *stripPrefix) serveRequest(rw http.ResponseWriter, req *http.Request, prefix string) {
|
||||||
|
|
|
@ -28,7 +28,8 @@ func TestStripPrefix(t *testing.T) {
|
||||||
Prefixes: []string{},
|
Prefixes: []string{},
|
||||||
},
|
},
|
||||||
path: "/noprefixes",
|
path: "/noprefixes",
|
||||||
expectedStatusCode: http.StatusNotFound,
|
expectedStatusCode: http.StatusOK,
|
||||||
|
expectedPath: "/noprefixes",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "wildcard (.*) requests",
|
desc: "wildcard (.*) requests",
|
||||||
|
@ -76,7 +77,8 @@ func TestStripPrefix(t *testing.T) {
|
||||||
Prefixes: []string{"/stat/"},
|
Prefixes: []string{"/stat/"},
|
||||||
},
|
},
|
||||||
path: "/status",
|
path: "/status",
|
||||||
expectedStatusCode: http.StatusNotFound,
|
expectedStatusCode: http.StatusOK,
|
||||||
|
expectedPath: "/status",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "general prefix on matching path",
|
desc: "general prefix on matching path",
|
||||||
|
@ -149,6 +151,8 @@ func TestStripPrefix(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost"+test.path, nil)
|
req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost"+test.path, nil)
|
||||||
|
req.RequestURI = req.URL.RequestURI()
|
||||||
|
|
||||||
resp := &httptest.ResponseRecorder{Code: http.StatusOK}
|
resp := &httptest.ResponseRecorder{Code: http.StatusOK}
|
||||||
|
|
||||||
handler.ServeHTTP(resp, req)
|
handler.ServeHTTP(resp, req)
|
||||||
|
|
|
@ -3,13 +3,13 @@ package stripprefixregex
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containous/traefik/v2/pkg/config/dynamic"
|
"github.com/containous/traefik/v2/pkg/config/dynamic"
|
||||||
"github.com/containous/traefik/v2/pkg/middlewares"
|
"github.com/containous/traefik/v2/pkg/middlewares"
|
||||||
"github.com/containous/traefik/v2/pkg/middlewares/stripprefix"
|
"github.com/containous/traefik/v2/pkg/middlewares/stripprefix"
|
||||||
"github.com/containous/traefik/v2/pkg/tracing"
|
"github.com/containous/traefik/v2/pkg/tracing"
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"github.com/opentracing/opentracing-go/ext"
|
"github.com/opentracing/opentracing-go/ext"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -19,9 +19,9 @@ const (
|
||||||
|
|
||||||
// StripPrefixRegex is a middleware used to strip prefix from an URL request.
|
// StripPrefixRegex is a middleware used to strip prefix from an URL request.
|
||||||
type stripPrefixRegex struct {
|
type stripPrefixRegex struct {
|
||||||
next http.Handler
|
next http.Handler
|
||||||
router *mux.Router
|
expressions []*regexp.Regexp
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
// New builds a new StripPrefixRegex middleware.
|
// New builds a new StripPrefixRegex middleware.
|
||||||
|
@ -29,13 +29,16 @@ func New(ctx context.Context, next http.Handler, config dynamic.StripPrefixRegex
|
||||||
middlewares.GetLogger(ctx, name, typeName).Debug("Creating middleware")
|
middlewares.GetLogger(ctx, name, typeName).Debug("Creating middleware")
|
||||||
|
|
||||||
stripPrefix := stripPrefixRegex{
|
stripPrefix := stripPrefixRegex{
|
||||||
next: next,
|
next: next,
|
||||||
router: mux.NewRouter(),
|
name: name,
|
||||||
name: name,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, prefix := range config.Regex {
|
for _, exp := range config.Regex {
|
||||||
stripPrefix.router.PathPrefix(prefix)
|
reg, err := regexp.Compile(strings.TrimSpace(exp))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
stripPrefix.expressions = append(stripPrefix.expressions, reg)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &stripPrefix, nil
|
return &stripPrefix, nil
|
||||||
|
@ -46,32 +49,28 @@ func (s *stripPrefixRegex) GetTracingInformation() (string, ext.SpanKindEnum) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stripPrefixRegex) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
func (s *stripPrefixRegex) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||||
var match mux.RouteMatch
|
for _, exp := range s.expressions {
|
||||||
if s.router.Match(req, &match) {
|
parts := exp.FindStringSubmatch(req.URL.Path)
|
||||||
params := make([]string, 0, len(match.Vars)*2)
|
if len(parts) > 0 && len(parts[0]) > 0 {
|
||||||
for key, val := range match.Vars {
|
prefix := parts[0]
|
||||||
params = append(params, key)
|
if !strings.HasPrefix(req.URL.Path, prefix) {
|
||||||
params = append(params, val)
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
prefix, err := match.Route.URL(params...)
|
req.Header.Add(stripprefix.ForwardedPrefixHeader, prefix)
|
||||||
if err != nil || len(prefix.Path) > len(req.URL.Path) {
|
|
||||||
logger := middlewares.GetLogger(req.Context(), s.name, typeName)
|
req.URL.Path = strings.Replace(req.URL.Path, prefix, "", 1)
|
||||||
logger.Error("Error in stripPrefix middleware", err)
|
if req.URL.RawPath != "" {
|
||||||
|
req.URL.RawPath = req.URL.RawPath[len(prefix):]
|
||||||
|
}
|
||||||
|
|
||||||
|
req.RequestURI = ensureLeadingSlash(req.URL.RequestURI())
|
||||||
|
s.next.ServeHTTP(rw, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
req.URL.Path = req.URL.Path[len(prefix.Path):]
|
|
||||||
if req.URL.RawPath != "" {
|
|
||||||
req.URL.RawPath = req.URL.RawPath[len(prefix.Path):]
|
|
||||||
}
|
|
||||||
req.Header.Add(stripprefix.ForwardedPrefixHeader, prefix.Path)
|
|
||||||
req.RequestURI = ensureLeadingSlash(req.URL.RequestURI())
|
|
||||||
|
|
||||||
s.next.ServeHTTP(rw, req)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
http.NotFound(rw, req)
|
|
||||||
|
s.next.ServeHTTP(rw, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ensureLeadingSlash(str string) string {
|
func ensureLeadingSlash(str string) string {
|
||||||
|
|
|
@ -15,7 +15,7 @@ import (
|
||||||
|
|
||||||
func TestStripPrefixRegex(t *testing.T) {
|
func TestStripPrefixRegex(t *testing.T) {
|
||||||
testPrefixRegex := dynamic.StripPrefixRegex{
|
testPrefixRegex := dynamic.StripPrefixRegex{
|
||||||
Regex: []string{"/a/api/", "/b/{regex}/", "/c/{category}/{id:[0-9]+}/"},
|
Regex: []string{"/a/api/", "/b/([a-z0-9]+)/", "/c/[a-z0-9]+/[0-9]+/"},
|
||||||
}
|
}
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
|
@ -27,7 +27,13 @@ func TestStripPrefixRegex(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
path: "/a/test",
|
path: "/a/test",
|
||||||
expectedStatusCode: http.StatusNotFound,
|
expectedStatusCode: http.StatusOK,
|
||||||
|
expectedPath: "/a/test",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/a/test",
|
||||||
|
expectedStatusCode: http.StatusOK,
|
||||||
|
expectedPath: "/a/test",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/a/api/test",
|
path: "/a/api/test",
|
||||||
|
@ -65,7 +71,8 @@ func TestStripPrefixRegex(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/c/api/abc/test4",
|
path: "/c/api/abc/test4",
|
||||||
expectedStatusCode: http.StatusNotFound,
|
expectedStatusCode: http.StatusOK,
|
||||||
|
expectedPath: "/c/api/abc/test4",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/a/api/a%2Fb",
|
path: "/a/api/a%2Fb",
|
||||||
|
|
Loading…
Reference in a new issue