Skip to content

Commit 61a3336

Browse files
add forward from domain (#2323)
* add forward from domain * add balancer forward * add unittest and readme * add short description new feature * add short description on signature * golangci-lint fix --------- Co-authored-by: René Werner <[email protected]>
1 parent 028d821 commit 61a3336

File tree

3 files changed

+198
-74
lines changed

3 files changed

+198
-74
lines changed

middleware/proxy/README.md

Lines changed: 91 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,16 @@ Proxy middleware for [Fiber](https://github.com/gofiber/fiber) that allows you t
1212
### Signatures
1313

1414
```go
15+
// Balancer create a load balancer among multiple upstrem servers.
1516
func Balancer(config Config) fiber.Handler
17+
// Forward performs the given http request and fills the given http response.
1618
func Forward(addr string, clients ...*fasthttp.Client) fiber.Handler
19+
// Do performs the given http request and fills the given http response.
1720
func Do(c *fiber.Ctx, addr string, clients ...*fasthttp.Client) error
21+
// DomainForward the given http request based on the given domain and fills the given http response
22+
func DomainForward(hostname string, addr string, clients ...*fasthttp.Client) fiber.Handler
23+
// BalancerForward performs the given http request based round robin balancer and fills the given http response
24+
func BalancerForward(servers []string, clients ...*fasthttp.Client) fiber.Handler
1825
```
1926

2027
### Examples
@@ -23,8 +30,8 @@ Import the middleware package that is part of the Fiber web framework
2330

2431
```go
2532
import (
26-
"github.com/gofiber/fiber/v2"
27-
"github.com/gofiber/fiber/v2/middleware/proxy"
33+
"github.com/gofiber/fiber/v2"
34+
"github.com/gofiber/fiber/v2/middleware/proxy"
2835
)
2936
```
3037

@@ -39,54 +46,64 @@ proxy.WithTlsConfig(&tls.Config{
3946

4047
// if you need to use global self-custom client, you should use proxy.WithClient.
4148
proxy.WithClient(&fasthttp.Client{
42-
NoDefaultUserAgentHeader: true,
43-
DisablePathNormalizing: true,
49+
NoDefaultUserAgentHeader: true,
50+
DisablePathNormalizing: true,
4451
})
4552

4653
// Forward to url
4754
app.Get("/gif", proxy.Forward("https://i.imgur.com/IWaBepg.gif"))
4855

56+
// If you want to forward with a specific domain. You have to use proxy.DomainForward.
57+
app.Get("/payments", proxy.DomainForward("docs.gofiber.io", "http://localhost:8000"))
58+
4959
// Forward to url with local custom client
5060
app.Get("/gif", proxy.Forward("https://i.imgur.com/IWaBepg.gif", &fasthttp.Client{
51-
NoDefaultUserAgentHeader: true,
52-
DisablePathNormalizing: true,
61+
NoDefaultUserAgentHeader: true,
62+
DisablePathNormalizing: true,
5363
}))
5464

5565
// Make request within handler
5666
app.Get("/:id", func(c *fiber.Ctx) error {
57-
url := "https://i.imgur.com/"+c.Params("id")+".gif"
58-
if err := proxy.Do(c, url); err != nil {
59-
return err
60-
}
61-
// Remove Server header from response
62-
c.Response().Header.Del(fiber.HeaderServer)
63-
return nil
67+
url := "https://i.imgur.com/"+c.Params("id")+".gif"
68+
if err := proxy.Do(c, url); err != nil {
69+
return err
70+
}
71+
// Remove Server header from response
72+
c.Response().Header.Del(fiber.HeaderServer)
73+
return nil
6474
})
6575

6676
// Minimal round robin balancer
6777
app.Use(proxy.Balancer(proxy.Config{
68-
Servers: []string{
69-
"http://localhost:3001",
70-
"http://localhost:3002",
71-
"http://localhost:3003",
72-
},
78+
Servers: []string{
79+
"http://localhost:3001",
80+
"http://localhost:3002",
81+
"http://localhost:3003",
82+
},
7383
}))
7484

7585
// Or extend your balancer for customization
7686
app.Use(proxy.Balancer(proxy.Config{
77-
Servers: []string{
78-
"http://localhost:3001",
79-
"http://localhost:3002",
80-
"http://localhost:3003",
81-
},
82-
ModifyRequest: func(c *fiber.Ctx) error {
83-
c.Request().Header.Add("X-Real-IP", c.IP())
84-
return nil
85-
},
86-
ModifyResponse: func(c *fiber.Ctx) error {
87-
c.Response().Header.Del(fiber.HeaderServer)
88-
return nil
89-
},
87+
Servers: []string{
88+
"http://localhost:3001",
89+
"http://localhost:3002",
90+
"http://localhost:3003",
91+
},
92+
ModifyRequest: func(c *fiber.Ctx) error {
93+
c.Request().Header.Add("X-Real-IP", c.IP())
94+
return nil
95+
},
96+
ModifyResponse: func(c *fiber.Ctx) error {
97+
c.Response().Header.Del(fiber.HeaderServer)
98+
return nil
99+
},
100+
}))
101+
102+
// Or this way if the balancer is using https and the destination server is only using http.
103+
app.Use(proxy.BalancerForward([]string{
104+
"http://localhost:3001",
105+
"http://localhost:3002",
106+
"http://localhost:3003",
90107
}))
91108
```
92109

@@ -95,50 +112,50 @@ app.Use(proxy.Balancer(proxy.Config{
95112
```go
96113
// Config defines the config for middleware.
97114
type Config struct {
98-
// Next defines a function to skip this middleware when returned true.
99-
//
100-
// Optional. Default: nil
101-
Next func(c *fiber.Ctx) bool
102-
103-
// Servers defines a list of <scheme>://<host> HTTP servers,
104-
//
105-
// which are used in a round-robin manner.
106-
// i.e.: "https://foobar.com, http://www.foobar.com"
107-
//
108-
// Required
109-
Servers []string
110-
111-
// ModifyRequest allows you to alter the request
112-
//
113-
// Optional. Default: nil
114-
ModifyRequest fiber.Handler
115-
116-
// ModifyResponse allows you to alter the response
117-
//
118-
// Optional. Default: nil
119-
ModifyResponse fiber.Handler
120-
121-
// Timeout is the request timeout used when calling the proxy client
122-
//
123-
// Optional. Default: 1 second
124-
Timeout time.Duration
125-
126-
// Per-connection buffer size for requests' reading.
127-
// This also limits the maximum header size.
128-
// Increase this buffer if your clients send multi-KB RequestURIs
129-
// and/or multi-KB headers (for example, BIG cookies).
130-
ReadBufferSize int
115+
// Next defines a function to skip this middleware when returned true.
116+
//
117+
// Optional. Default: nil
118+
Next func(c *fiber.Ctx) bool
119+
120+
// Servers defines a list of <scheme>://<host> HTTP servers,
121+
//
122+
// which are used in a round-robin manner.
123+
// i.e.: "https://foobar.com, http://www.foobar.com"
124+
//
125+
// Required
126+
Servers []string
127+
128+
// ModifyRequest allows you to alter the request
129+
//
130+
// Optional. Default: nil
131+
ModifyRequest fiber.Handler
132+
133+
// ModifyResponse allows you to alter the response
134+
//
135+
// Optional. Default: nil
136+
ModifyResponse fiber.Handler
137+
138+
// Timeout is the request timeout used when calling the proxy client
139+
//
140+
// Optional. Default: 1 second
141+
Timeout time.Duration
142+
143+
// Per-connection buffer size for requests' reading.
144+
// This also limits the maximum header size.
145+
// Increase this buffer if your clients send multi-KB RequestURIs
146+
// and/or multi-KB headers (for example, BIG cookies).
147+
ReadBufferSize int
131148

132-
// Per-connection buffer size for responses' writing.
133-
WriteBufferSize int
134-
135-
// tls config for the http client.
136-
TlsConfig *tls.Config
137-
138-
// Client is custom client when client config is complex.
139-
// Note that Servers, Timeout, WriteBufferSize, ReadBufferSize and TlsConfig
140-
// will not be used if the client are set.
141-
Client *fasthttp.LBClient
149+
// Per-connection buffer size for responses' writing.
150+
WriteBufferSize int
151+
152+
// tls config for the http client.
153+
TlsConfig *tls.Config
154+
155+
// Client is custom client when client config is complex.
156+
// Note that Servers, Timeout, WriteBufferSize, ReadBufferSize and TlsConfig
157+
// will not be used if the client are set.
158+
Client *fasthttp.LBClient
142159
}
143160
```
144161

middleware/proxy/proxy.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,3 +178,53 @@ func getScheme(uri []byte) []byte {
178178
}
179179
return uri[:i-1]
180180
}
181+
182+
// DomainForward performs an http request based on the given domain and populates the given http response.
183+
// This method will return an fiber.Handler
184+
func DomainForward(hostname, addr string, clients ...*fasthttp.Client) fiber.Handler {
185+
return func(c *fiber.Ctx) error {
186+
host := string(c.Request().Host())
187+
if host == hostname {
188+
return Do(c, addr+c.OriginalURL(), clients...)
189+
}
190+
return nil
191+
}
192+
}
193+
194+
type roundrobin struct {
195+
sync.Mutex
196+
197+
current int
198+
pool []string
199+
}
200+
201+
// this method will return a string of addr server from list server.
202+
func (r *roundrobin) get() string {
203+
r.Lock()
204+
defer r.Unlock()
205+
206+
if r.current >= len(r.pool) {
207+
r.current %= len(r.pool)
208+
}
209+
210+
result := r.pool[r.current]
211+
r.current++
212+
return result
213+
}
214+
215+
// BalancerForward Forward performs the given http request with round robin algorithm to server and fills the given http response.
216+
// This method will return an fiber.Handler
217+
func BalancerForward(servers []string, clients ...*fasthttp.Client) fiber.Handler {
218+
r := &roundrobin{
219+
current: 0,
220+
pool: servers,
221+
}
222+
return func(c *fiber.Ctx) error {
223+
server := r.get()
224+
if !strings.HasPrefix(server, "http") {
225+
server = "http://" + server
226+
}
227+
c.Request().Header.Add("X-Real-IP", c.IP())
228+
return Do(c, server+c.OriginalURL(), clients...)
229+
}
230+
}

middleware/proxy/proxy_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,3 +473,60 @@ func Test_ProxyBalancer_Custom_Client(t *testing.T) {
473473
utils.AssertEqual(t, nil, err)
474474
utils.AssertEqual(t, fiber.StatusTeapot, resp.StatusCode)
475475
}
476+
477+
// go test -run Test_Proxy_Domain_Forward_Local
478+
func Test_Proxy_Domain_Forward_Local(t *testing.T) {
479+
t.Parallel()
480+
ln, err := net.Listen(fiber.NetworkTCP4, "127.0.0.1:0")
481+
utils.AssertEqual(t, nil, err)
482+
app := fiber.New(fiber.Config{DisableStartupMessage: true})
483+
484+
// target server
485+
ln1, err := net.Listen(fiber.NetworkTCP4, "127.0.0.1:0")
486+
utils.AssertEqual(t, nil, err)
487+
app1 := fiber.New(fiber.Config{DisableStartupMessage: true})
488+
489+
app1.Get("/test", func(c *fiber.Ctx) error {
490+
return c.SendString("test_local_client:" + c.Query("query_test"))
491+
})
492+
493+
proxyAddr := ln.Addr().String()
494+
targetAddr := ln1.Addr().String()
495+
localDomain := strings.Replace(proxyAddr, "127.0.0.1", "localhost", 1)
496+
app.Use(DomainForward(localDomain, "http://"+targetAddr, &fasthttp.Client{
497+
NoDefaultUserAgentHeader: true,
498+
DisablePathNormalizing: true,
499+
500+
Dial: fasthttp.Dial,
501+
}))
502+
503+
go func() { utils.AssertEqual(t, nil, app.Listener(ln)) }()
504+
go func() { utils.AssertEqual(t, nil, app1.Listener(ln1)) }()
505+
506+
code, body, errs := fiber.Get("http://" + localDomain + "/test?query_test=true").String()
507+
utils.AssertEqual(t, 0, len(errs))
508+
utils.AssertEqual(t, fiber.StatusOK, code)
509+
utils.AssertEqual(t, "test_local_client:true", body)
510+
}
511+
512+
// go test -run Test_Proxy_Balancer_Forward_Local
513+
func Test_Proxy_Balancer_Forward_Local(t *testing.T) {
514+
t.Parallel()
515+
516+
app := fiber.New()
517+
518+
_, addr := createProxyTestServer(t, func(c *fiber.Ctx) error {
519+
return c.SendString("forwarded")
520+
})
521+
522+
app.Use(BalancerForward([]string{addr}))
523+
524+
resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil))
525+
utils.AssertEqual(t, nil, err)
526+
utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode)
527+
528+
b, err := io.ReadAll(resp.Body)
529+
utils.AssertEqual(t, nil, err)
530+
531+
utils.AssertEqual(t, string(b), "forwarded")
532+
}

0 commit comments

Comments
 (0)