mirror of
https://github.com/containous/traefik.git
synced 2024-12-22 13:34:03 +03:00
Merge pull request #1339 from seguins/928-fix-regex-pathstrip
Fix regex with PathStrip
This commit is contained in:
commit
441d5442a1
@ -104,9 +104,11 @@ Following is the list of existing matcher rules along with examples:
|
||||
- `HostRegexp: traefik.io, {subdomain:[a-z]+}.traefik.io`: Match request host. It accepts a sequence of literal and regular expression hosts.
|
||||
- `Method: GET, POST, PUT`: Match request HTTP method. It accepts a sequence of HTTP methods.
|
||||
- `Path: /products/, /articles/{category}/{id:[0-9]+}`: Match exact request path. It accepts a sequence of literal and regular expression paths.
|
||||
- `PathStrip: /products/, /articles/{category}/{id:[0-9]+}`: Match exact path and strip off the path prior to forwarding the request to the backend. It accepts a sequence of literal and regular expression paths.
|
||||
- `PathStrip: /products/`: Match exact path and strip off the path prior to forwarding the request to the backend. It accepts a sequence of literal paths.
|
||||
- `PathStripRegex: /articles/{category}/{id:[0-9]+}`: Match exact path and strip off the path prior to forwarding the request to the backend. It accepts a sequence of literal and regular expression paths.
|
||||
- `PathPrefix: /products/, /articles/{category}/{id:[0-9]+}`: Match request prefix path. It accepts a sequence of literal and regular expression prefix paths.
|
||||
- `PathPrefixStrip: /products/, /articles/{category}/{id:[0-9]+}`: Match request prefix path and strip off the path prefix prior to forwarding the request to the backend. It accepts a sequence of literal and regular expression prefix paths. Starting with Traefik 1.3, the stripped prefix path will be available in the `X-Forwarded-Prefix` header.
|
||||
- `PathPrefixStrip: /products/`: Match request prefix path and strip off the path prefix prior to forwarding the request to the backend. It accepts a sequence of literal prefix paths. Starting with Traefik 1.3, the stripped prefix path will be available in the `X-Forwarded-Prefix` header.
|
||||
- `PathPrefixStripRegex: /articles/{category}/{id:[0-9]+}`: Match request prefix path and strip off the path prefix prior to forwarding the request to the backend. It accepts a sequence of literal and regular expression prefix paths. Starting with Traefik 1.3, the stripped prefix path will be available in the `X-Forwarded-Prefix` header.
|
||||
|
||||
In order to use regular expressions with Host and Path matchers, you must declare an arbitrarily named variable followed by the colon-separated regular expression, all enclosed in curly braces. Any pattern supported by [Go's regexp package](https://golang.org/pkg/regexp/) may be used. Example: `/posts/{id:[0-9]+}`.
|
||||
|
||||
|
54
middlewares/stripPrefixRegex.go
Normal file
54
middlewares/stripPrefixRegex.go
Normal file
@ -0,0 +1,54 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/containous/mux"
|
||||
"github.com/containous/traefik/log"
|
||||
)
|
||||
|
||||
// StripPrefixRegex is a middleware used to strip prefix from an URL request
|
||||
type StripPrefixRegex struct {
|
||||
Handler http.Handler
|
||||
router *mux.Router
|
||||
}
|
||||
|
||||
// NewStripPrefixRegex builds a new StripPrefixRegex given a handler and prefixes
|
||||
func NewStripPrefixRegex(handler http.Handler, prefixes []string) *StripPrefixRegex {
|
||||
stripPrefix := StripPrefixRegex{Handler: handler, router: mux.NewRouter()}
|
||||
|
||||
for _, prefix := range prefixes {
|
||||
stripPrefix.router.PathPrefix(prefix)
|
||||
}
|
||||
|
||||
return &stripPrefix
|
||||
}
|
||||
|
||||
func (s *StripPrefixRegex) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
var match mux.RouteMatch
|
||||
if s.router.Match(r, &match) {
|
||||
params := make([]string, 0, len(match.Vars)*2)
|
||||
for key, val := range match.Vars {
|
||||
params = append(params, key)
|
||||
params = append(params, val)
|
||||
}
|
||||
|
||||
prefix, err := match.Route.URL(params...)
|
||||
if err != nil || len(prefix.Path) > len(r.URL.Path) {
|
||||
log.Error("Error in stripPrefix middleware", err)
|
||||
return
|
||||
}
|
||||
|
||||
r.URL.Path = r.URL.Path[len(prefix.Path):]
|
||||
r.Header[forwardedPrefixHeader] = []string{prefix.Path}
|
||||
r.RequestURI = r.URL.RequestURI()
|
||||
s.Handler.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
|
||||
// SetHandler sets handler
|
||||
func (s *StripPrefixRegex) SetHandler(Handler http.Handler) {
|
||||
s.Handler = Handler
|
||||
}
|
55
middlewares/stripPrefixRegex_test.go
Normal file
55
middlewares/stripPrefixRegex_test.go
Normal file
@ -0,0 +1,55 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStripPrefixRegex(t *testing.T) {
|
||||
|
||||
handlerPath := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprint(w, r.URL.Path)
|
||||
})
|
||||
|
||||
handler := NewStripPrefixRegex(handlerPath, []string{"/a/api/", "/b/{regex}/", "/c/{category}/{id:[0-9]+}/"})
|
||||
server := httptest.NewServer(handler)
|
||||
defer server.Close()
|
||||
|
||||
tests := []struct {
|
||||
expectedCode int
|
||||
expectedResponse string
|
||||
url string
|
||||
}{
|
||||
{url: "/a/test", expectedCode: 404, expectedResponse: "404 page not found\n"},
|
||||
{url: "/a/api/test", expectedCode: 200, expectedResponse: "test"},
|
||||
|
||||
{url: "/b/api/", expectedCode: 200, expectedResponse: ""},
|
||||
{url: "/b/api/test1", expectedCode: 200, expectedResponse: "test1"},
|
||||
{url: "/b/api2/test2", expectedCode: 200, expectedResponse: "test2"},
|
||||
|
||||
{url: "/c/api/123/", expectedCode: 200, expectedResponse: ""},
|
||||
{url: "/c/api/123/test3", expectedCode: 200, expectedResponse: "test3"},
|
||||
{url: "/c/api/abc/test4", expectedCode: 404, expectedResponse: "404 page not found\n"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
resp, err := http.Get(server.URL + test.url)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if resp.StatusCode != test.expectedCode {
|
||||
t.Fatalf("Received non-%d response: %d\n", test.expectedCode, resp.StatusCode)
|
||||
}
|
||||
response, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if test.expectedResponse != string(response) {
|
||||
t.Errorf("Expected '%s' : '%s'\n", test.expectedResponse, response)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -75,6 +75,16 @@ func (r *Rules) pathStrip(paths ...string) *mux.Route {
|
||||
return r.route.route
|
||||
}
|
||||
|
||||
func (r *Rules) pathStripRegex(paths ...string) *mux.Route {
|
||||
sort.Sort(bySize(paths))
|
||||
r.route.stripPrefixesRegex = paths
|
||||
router := r.route.route.Subrouter()
|
||||
for _, path := range paths {
|
||||
router.Path(strings.TrimSpace(path))
|
||||
}
|
||||
return r.route.route
|
||||
}
|
||||
|
||||
func (r *Rules) replacePath(paths ...string) *mux.Route {
|
||||
for _, path := range paths {
|
||||
r.route.replacePath = path
|
||||
@ -99,6 +109,16 @@ func (r *Rules) pathPrefixStrip(paths ...string) *mux.Route {
|
||||
return r.route.route
|
||||
}
|
||||
|
||||
func (r *Rules) pathPrefixStripRegex(paths ...string) *mux.Route {
|
||||
sort.Sort(bySize(paths))
|
||||
r.route.stripPrefixesRegex = paths
|
||||
router := r.route.route.Subrouter()
|
||||
for _, path := range paths {
|
||||
router.PathPrefix(strings.TrimSpace(path))
|
||||
}
|
||||
return r.route.route
|
||||
}
|
||||
|
||||
func (r *Rules) methods(methods ...string) *mux.Route {
|
||||
return r.route.route.Methods(methods...)
|
||||
}
|
||||
@ -113,17 +133,19 @@ func (r *Rules) headersRegexp(headers ...string) *mux.Route {
|
||||
|
||||
func (r *Rules) parseRules(expression string, onRule func(functionName string, function interface{}, arguments []string) error) error {
|
||||
functions := map[string]interface{}{
|
||||
"Host": r.host,
|
||||
"HostRegexp": r.hostRegexp,
|
||||
"Path": r.path,
|
||||
"PathStrip": r.pathStrip,
|
||||
"PathPrefix": r.pathPrefix,
|
||||
"PathPrefixStrip": r.pathPrefixStrip,
|
||||
"Method": r.methods,
|
||||
"Headers": r.headers,
|
||||
"HeadersRegexp": r.headersRegexp,
|
||||
"AddPrefix": r.addPrefix,
|
||||
"ReplacePath": r.replacePath,
|
||||
"Host": r.host,
|
||||
"HostRegexp": r.hostRegexp,
|
||||
"Path": r.path,
|
||||
"PathStrip": r.pathStrip,
|
||||
"PathStripRegex": r.pathStripRegex,
|
||||
"PathPrefix": r.pathPrefix,
|
||||
"PathPrefixStrip": r.pathPrefixStrip,
|
||||
"PathPrefixStripRegex": r.pathPrefixStripRegex,
|
||||
"Method": r.methods,
|
||||
"Headers": r.headers,
|
||||
"HeadersRegexp": r.headersRegexp,
|
||||
"AddPrefix": r.addPrefix,
|
||||
"ReplacePath": r.replacePath,
|
||||
}
|
||||
|
||||
if len(expression) == 0 {
|
||||
|
@ -62,10 +62,11 @@ type serverEntryPoint struct {
|
||||
}
|
||||
|
||||
type serverRoute struct {
|
||||
route *mux.Route
|
||||
stripPrefixes []string
|
||||
addPrefix string
|
||||
replacePath string
|
||||
route *mux.Route
|
||||
stripPrefixes []string
|
||||
stripPrefixesRegex []string
|
||||
addPrefix string
|
||||
replacePath string
|
||||
}
|
||||
|
||||
// NewServer returns an initialized Server.
|
||||
@ -807,6 +808,11 @@ func (server *Server) wireFrontendBackend(serverRoute *serverRoute, handler http
|
||||
}
|
||||
}
|
||||
|
||||
// strip prefix with regex
|
||||
if len(serverRoute.stripPrefixesRegex) > 0 {
|
||||
handler = middlewares.NewStripPrefixRegex(handler, serverRoute.stripPrefixesRegex)
|
||||
}
|
||||
|
||||
// path replace
|
||||
if len(serverRoute.replacePath) > 0 {
|
||||
handler = &middlewares.ReplacePath{
|
||||
|
Loading…
Reference in New Issue
Block a user