Skip to content

Commit 9ae7047

Browse files
committed
Rewrite rules for proxy middleware
Signed-off-by: Vishal Rana <vr@labstack.com>
1 parent 62f95ac commit 9ae7047

File tree

3 files changed

+74
-1
lines changed

3 files changed

+74
-1
lines changed

middleware/middleware.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,34 @@
11
package middleware
22

3-
import "github.com/labstack/echo"
3+
import (
4+
"regexp"
5+
"strconv"
6+
"strings"
7+
8+
"github.com/labstack/echo"
9+
)
410

511
type (
612
// Skipper defines a function to skip middleware. Returning true skips processing
713
// the middleware.
814
Skipper func(c echo.Context) bool
915
)
1016

17+
func captureTokens(pattern *regexp.Regexp, input string) *strings.Replacer {
18+
groups := pattern.FindAllStringSubmatch(input, -1)
19+
if groups == nil {
20+
return nil
21+
}
22+
values := groups[0][1:]
23+
replace := make([]string, 2*len(values))
24+
for i, v := range values {
25+
j := 2 * i
26+
replace[j] = "$" + strconv.Itoa(i+1)
27+
replace[j+1] = v
28+
}
29+
return strings.NewReplacer(replace...)
30+
}
31+
1132
// DefaultSkipper returns false which processes the middleware.
1233
func DefaultSkipper(echo.Context) bool {
1334
return false

middleware/proxy.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"net/http"
99
"net/http/httputil"
1010
"net/url"
11+
"regexp"
12+
"strings"
1113
"sync"
1214
"sync/atomic"
1315
"time"
@@ -26,6 +28,17 @@ type (
2628
// Balancer defines a load balancing technique.
2729
// Required.
2830
Balancer ProxyBalancer
31+
32+
// Rewrite defines URL path rewrite rules. The values captured in asterisk can be
33+
// retrieved by index e.g. $1, $2 and so on.
34+
// Examples:
35+
// "/old": "/new",
36+
// "/api/*": "/$1",
37+
// "/js/*": "/public/javascripts/$1",
38+
// "/users/*/orders/*": "/user/$1/order/$2",
39+
Rewrite map[string]string
40+
41+
rewriteRegex map[*regexp.Regexp]string
2942
}
3043

3144
// ProxyTarget defines the upstream target.
@@ -187,6 +200,13 @@ func ProxyWithConfig(config ProxyConfig) echo.MiddlewareFunc {
187200
if config.Balancer == nil {
188201
panic("echo: proxy middleware requires balancer")
189202
}
203+
config.rewriteRegex = map[*regexp.Regexp]string{}
204+
205+
// Initialize
206+
for k, v := range config.Rewrite {
207+
k = strings.Replace(k, "*", "(\\S*)", -1)
208+
config.rewriteRegex[regexp.MustCompile(k)] = v
209+
}
190210

191211
return func(next echo.HandlerFunc) echo.HandlerFunc {
192212
return func(c echo.Context) (err error) {
@@ -198,6 +218,14 @@ func ProxyWithConfig(config ProxyConfig) echo.MiddlewareFunc {
198218
res := c.Response()
199219
tgt := config.Balancer.Next()
200220

221+
// Rewrite
222+
for k, v := range config.rewriteRegex {
223+
replacer := captureTokens(k, req.URL.Path)
224+
if replacer != nil {
225+
req.URL.Path = replacer.Replace(v)
226+
}
227+
}
228+
201229
// Fix header
202230
if req.Header.Get(echo.HeaderXRealIP) == "" {
203231
req.Header.Set(echo.HeaderXRealIP, c.RealIP())

middleware/proxy_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,28 @@ func TestProxy(t *testing.T) {
8484
e.ServeHTTP(rec, req)
8585
body = rec.Body.String()
8686
assert.Equal(t, "target 2", body)
87+
88+
// Rewrite
89+
e = echo.New()
90+
e.Pre(ProxyWithConfig(ProxyConfig{
91+
Balancer: rrb,
92+
Rewrite: map[string]string{
93+
"/old": "/new",
94+
"/api/*": "/$1",
95+
"/js/*": "/public/javascripts/$1",
96+
"/users/*/orders/*": "/user/$1/order/$2",
97+
},
98+
}))
99+
req.URL.Path = "/api/users"
100+
e.ServeHTTP(rec, req)
101+
assert.Equal(t, "/users", req.URL.Path)
102+
req.URL.Path = "/js/main.js"
103+
e.ServeHTTP(rec, req)
104+
assert.Equal(t, "/public/javascripts/main.js", req.URL.Path)
105+
req.URL.Path = "/old"
106+
e.ServeHTTP(rec, req)
107+
assert.Equal(t, "/new", req.URL.Path)
108+
req.URL.Path = "/users/jack/orders/1"
109+
e.ServeHTTP(rec, req)
110+
assert.Equal(t, "/user/jack/order/1", req.URL.Path)
87111
}

0 commit comments

Comments
 (0)