Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Path Replacement Rule #1374

Merged
merged 1 commit into from
Apr 27, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ Modifier rules only modify the request. They do not have any impact on routing d
Following is the list of existing modifier rules:

- `AddPrefix: /products`: Add path prefix to the existing request path prior to forwarding the request to the backend.
- `ReplacePath: /serverless-path`: Replaces the path and adds the old path to the `X-Replaced-Path` header. Useful for mapping to AWS Lambda or Google Cloud Functions.

### Matchers

Expand Down
20 changes: 20 additions & 0 deletions middlewares/replace_path.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package middlewares

import (
"net/http"
)

// ReplacePath is a middleware used to replace the path of a URL request
type ReplacePath struct {
Handler http.Handler
Path string
}

// ReplacedPathHeader is the default header to set the old path to
const ReplacedPathHeader = "X-Replaced-Path"

func (s *ReplacePath) ServeHTTP(w http.ResponseWriter, r *http.Request) {
r.Header.Add(ReplacedPathHeader, r.URL.Path)
r.URL.Path = s.Path
s.Handler.ServeHTTP(w, r)
}
44 changes: 44 additions & 0 deletions middlewares/replace_path_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package middlewares_test

import (
"net/http"
"testing"

"github.com/containous/traefik/middlewares"
)

func TestReplacePath(t *testing.T) {
const replacementPath = "/replacement-path"

paths := []string{
"/example",
"/some/really/long/path",
}

for _, path := range paths {
t.Run(path, func(t *testing.T) {
var newPath, oldPath string
handler := &middlewares.ReplacePath{
Path: replacementPath,
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
newPath = r.URL.Path
oldPath = r.Header.Get("X-Replaced-Path")
}),
}

req, err := http.NewRequest("GET", "http://localhost"+path, nil)
if err != nil {
t.Error(err)
}

handler.ServeHTTP(nil, req)
if newPath != replacementPath {
t.Fatalf("new path should be '%s'", replacementPath)
}

if oldPath != path {
t.Fatalf("old path should be '%s'", path)
}
})
}
}
8 changes: 8 additions & 0 deletions server/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ func (r *Rules) pathStrip(paths ...string) *mux.Route {
return r.route.route
}

func (r *Rules) replacePath(paths ...string) *mux.Route {
for _, path := range paths {
r.route.replacePath = path
}
return r.route.route
}

func (r *Rules) addPrefix(paths ...string) *mux.Route {
for _, path := range paths {
r.route.addPrefix = path
Expand Down Expand Up @@ -116,6 +123,7 @@ func (r *Rules) parseRules(expression string, onRule func(functionName string, f
"Headers": r.headers,
"HeadersRegexp": r.headersRegexp,
"AddPrefix": r.addPrefix,
"ReplacePath": r.replacePath,
}

if len(expression) == 0 {
Expand Down
9 changes: 9 additions & 0 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ type serverRoute struct {
route *mux.Route
stripPrefixes []string
addPrefix string
replacePath string
}

// NewServer returns an initialized Server.
Expand Down Expand Up @@ -806,6 +807,14 @@ func (server *Server) wireFrontendBackend(serverRoute *serverRoute, handler http
}
}

// path replace
if len(serverRoute.replacePath) > 0 {
handler = &middlewares.ReplacePath{
Path: serverRoute.replacePath,
Handler: handler,
}
}

serverRoute.route.Handler(handler)
}

Expand Down