Skip to content

Commit 472bd21

Browse files
committed
🔥 Add DialDualStack option to proxy middleware for upstream IPv6 support
1 parent 370cc8b commit 472bd21

File tree

4 files changed

+130
-23
lines changed

4 files changed

+130
-23
lines changed

Diff for: docs/api/middleware/proxy.md

+15-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Proxy middleware for [Fiber](https://github.com/gofiber/fiber) that allows you t
99
## Signatures
1010

1111
```go
12-
// Balancer create a load balancer among multiple upstrem servers.
12+
// Balancer create a load balancer among multiple upstream servers.
1313
func Balancer(config Config) fiber.Handler
1414
// Forward performs the given http request and fills the given http response.
1515
func Forward(addr string, clients ...*fasthttp.Client) fiber.Handler
@@ -21,9 +21,9 @@ func DoRedirects(c fiber.Ctx, addr string, maxRedirectsCount int, clients ...*fa
2121
func DoDeadline(c fiber.Ctx, addr string, deadline time.Time, clients ...*fasthttp.Client) error
2222
// DoTimeout performs the given request and waits for response during the given timeout duration.
2323
func DoTimeout(c fiber.Ctx, addr string, timeout time.Duration, clients ...*fasthttp.Client) error
24-
// DomainForward the given http request based on the given domain and fills the given http response
24+
// DomainForward the given http request based on the given domain and fills the given http response.
2525
func DomainForward(hostname string, addr string, clients ...*fasthttp.Client) fiber.Handler
26-
// BalancerForward performs the given http request based round robin balancer and fills the given http response
26+
// BalancerForward performs the given http request based round robin balancer and fills the given http response.
2727
func BalancerForward(servers []string, clients ...*fasthttp.Client) fiber.Handler
2828
```
2929

@@ -137,6 +137,17 @@ app.Use(proxy.BalancerForward([]string{
137137
"http://localhost:3002",
138138
"http://localhost:3003",
139139
}))
140+
141+
142+
// Make round robin balancer with IPv6 support.
143+
app.Use(proxy.Balancer(proxy.Config{
144+
Servers: []string{
145+
"http://[::1]:3001",
146+
"http://127.0.0.1:3002",
147+
"http://localhost:3003",
148+
},
149+
DialDualStack: true,
150+
}))
140151
```
141152

142153
## Config
@@ -151,6 +162,7 @@ app.Use(proxy.BalancerForward([]string{
151162
| ReadBufferSize | `int` | Per-connection buffer size for requests' reading. This also limits the maximum header size. Increase this buffer if your clients send multi-KB RequestURIs and/or multi-KB headers (for example, BIG cookies). | (Not specified) |
152163
| WriteBufferSize | `int` | Per-connection buffer size for responses' writing. | (Not specified) |
153164
| TlsConfig | `*tls.Config` (or `*fasthttp.TLSConfig` in v3) | TLS config for the HTTP client. | `nil` |
165+
| DialDualStack | `bool` | Client will attempt to connect to both IPv4 and IPv6 host addresses if set to true. | `false` |
154166
| Client | `*fasthttp.LBClient` | Client is a custom client when client config is complex. | `nil` |
155167

156168
## Default Config

Diff for: middleware/proxy/config.go

+10-2
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,17 @@ type Config struct {
5151
TlsConfig *tls.Config //nolint:stylecheck,revive // TODO: Rename to "TLSConfig" in v3
5252

5353
// Client is custom client when client config is complex.
54-
// Note that Servers, Timeout, WriteBufferSize, ReadBufferSize and TlsConfig
55-
// will not be used if the client are set.
54+
// Note that Servers, Timeout, WriteBufferSize, ReadBufferSize, TlsConfig
55+
// and DialDualStack will not be used if the client are set.
5656
Client *fasthttp.LBClient
57+
58+
// Attempt to connect to both ipv4 and ipv6 host addresses if set to true.
59+
//
60+
// By default client connects only to ipv4 addresses, since unfortunately ipv6
61+
// remains broken in many networks worldwide :)
62+
//
63+
// Optional. Default: false
64+
DialDualStack bool
5765
}
5866

5967
// ConfigDefault is the default config

Diff for: middleware/proxy/proxy.go

+2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ func Balancer(config Config) fiber.Handler {
4545
WriteBufferSize: config.WriteBufferSize,
4646

4747
TLSConfig: config.TlsConfig,
48+
49+
DialDualStack: config.DialDualStack,
4850
}
4951

5052
lbc.Clients = append(lbc.Clients, client)

Diff for: middleware/proxy/proxy_test.go

+103-18
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ import (
1818
"github.com/valyala/fasthttp"
1919
)
2020

21-
func createProxyTestServer(t *testing.T, handler fiber.Handler) (*fiber.App, string) {
21+
func createProxyTestServer(t *testing.T, handler fiber.Handler, network, address string) (*fiber.App, string) {
2222
t.Helper()
2323

2424
target := fiber.New()
2525
target.Get("/", handler)
2626

27-
ln, err := net.Listen(fiber.NetworkTCP4, "127.0.0.1:0")
27+
ln, err := net.Listen(network, address)
2828
require.NoError(t, err)
2929

3030
go func() {
@@ -39,6 +39,16 @@ func createProxyTestServer(t *testing.T, handler fiber.Handler) (*fiber.App, str
3939
return target, addr
4040
}
4141

42+
func createProxyTestServerIPv4(t *testing.T, handler fiber.Handler) (*fiber.App, string) {
43+
t.Helper()
44+
return createProxyTestServer(t, handler, fiber.NetworkTCP4, "127.0.0.1:0")
45+
}
46+
47+
func createProxyTestServerIPv6(t *testing.T, handler fiber.Handler) (*fiber.App, string) {
48+
t.Helper()
49+
return createProxyTestServer(t, handler, fiber.NetworkTCP6, "[::1]:0")
50+
}
51+
4252
// go test -run Test_Proxy_Empty_Host
4353
func Test_Proxy_Empty_Upstream_Servers(t *testing.T) {
4454
t.Parallel()
@@ -86,7 +96,7 @@ func Test_Proxy_Next(t *testing.T) {
8696
func Test_Proxy(t *testing.T) {
8797
t.Parallel()
8898

89-
target, addr := createProxyTestServer(t, func(c fiber.Ctx) error {
99+
target, addr := createProxyTestServerIPv4(t, func(c fiber.Ctx) error {
90100
return c.SendStatus(fiber.StatusTeapot)
91101
})
92102

@@ -148,11 +158,86 @@ func Test_Proxy_Balancer_WithTlsConfig(t *testing.T) {
148158
resp.Close()
149159
}
150160

161+
// go test -run Test_Proxy_Balancer_IPv6_Upstream
162+
func Test_Proxy_Balancer_IPv6_Upstream(t *testing.T) {
163+
t.Parallel()
164+
165+
target, addr := createProxyTestServerIPv6(t, func(c fiber.Ctx) error {
166+
return c.SendStatus(fiber.StatusTeapot)
167+
})
168+
169+
resp, err := target.Test(httptest.NewRequest(fiber.MethodGet, "/", nil), 2*time.Second)
170+
require.NoError(t, err)
171+
require.Equal(t, fiber.StatusTeapot, resp.StatusCode)
172+
173+
app := fiber.New()
174+
175+
app.Use(Balancer(Config{Servers: []string{addr}}))
176+
177+
req := httptest.NewRequest(fiber.MethodGet, "/", nil)
178+
req.Host = addr
179+
resp, err = app.Test(req)
180+
require.NoError(t, err)
181+
require.Equal(t, fiber.StatusInternalServerError, resp.StatusCode)
182+
}
183+
184+
// go test -run Test_Proxy_Balancer_IPv6_Upstream
185+
func Test_Proxy_Balancer_IPv6_Upstream_With_DialDualStack(t *testing.T) {
186+
t.Parallel()
187+
188+
target, addr := createProxyTestServerIPv6(t, func(c fiber.Ctx) error {
189+
return c.SendStatus(fiber.StatusTeapot)
190+
})
191+
192+
resp, err := target.Test(httptest.NewRequest(fiber.MethodGet, "/", nil), 2*time.Second)
193+
require.NoError(t, err)
194+
require.Equal(t, fiber.StatusTeapot, resp.StatusCode)
195+
196+
app := fiber.New()
197+
198+
app.Use(Balancer(Config{
199+
Servers: []string{addr},
200+
DialDualStack: true,
201+
}))
202+
203+
req := httptest.NewRequest(fiber.MethodGet, "/", nil)
204+
req.Host = addr
205+
resp, err = app.Test(req)
206+
require.NoError(t, err)
207+
require.Equal(t, fiber.StatusTeapot, resp.StatusCode)
208+
}
209+
210+
// go test -run Test_Proxy_Balancer_IPv6_Upstream
211+
func Test_Proxy_Balancer_IPv4_Upstream_With_DialDualStack(t *testing.T) {
212+
t.Parallel()
213+
214+
target, addr := createProxyTestServerIPv4(t, func(c fiber.Ctx) error {
215+
return c.SendStatus(fiber.StatusTeapot)
216+
})
217+
218+
resp, err := target.Test(httptest.NewRequest(fiber.MethodGet, "/", nil), 2*time.Second)
219+
require.NoError(t, err)
220+
require.Equal(t, fiber.StatusTeapot, resp.StatusCode)
221+
222+
app := fiber.New()
223+
224+
app.Use(Balancer(Config{
225+
Servers: []string{addr},
226+
DialDualStack: true,
227+
}))
228+
229+
req := httptest.NewRequest(fiber.MethodGet, "/", nil)
230+
req.Host = addr
231+
resp, err = app.Test(req)
232+
require.NoError(t, err)
233+
require.Equal(t, fiber.StatusTeapot, resp.StatusCode)
234+
}
235+
151236
// go test -run Test_Proxy_Forward_WithTlsConfig_To_Http
152237
func Test_Proxy_Forward_WithTlsConfig_To_Http(t *testing.T) {
153238
t.Parallel()
154239

155-
_, targetAddr := createProxyTestServer(t, func(c fiber.Ctx) error {
240+
_, targetAddr := createProxyTestServerIPv4(t, func(c fiber.Ctx) error {
156241
return c.SendString("hello from target")
157242
})
158243

@@ -193,7 +278,7 @@ func Test_Proxy_Forward(t *testing.T) {
193278

194279
app := fiber.New()
195280

196-
_, addr := createProxyTestServer(t, func(c fiber.Ctx) error {
281+
_, addr := createProxyTestServerIPv4(t, func(c fiber.Ctx) error {
197282
return c.SendString("forwarded")
198283
})
199284

@@ -255,7 +340,7 @@ func Test_Proxy_Forward_WithClient_TLSConfig(t *testing.T) {
255340
func Test_Proxy_Modify_Response(t *testing.T) {
256341
t.Parallel()
257342

258-
_, addr := createProxyTestServer(t, func(c fiber.Ctx) error {
343+
_, addr := createProxyTestServerIPv4(t, func(c fiber.Ctx) error {
259344
return c.Status(500).SendString("not modified")
260345
})
261346

@@ -281,7 +366,7 @@ func Test_Proxy_Modify_Response(t *testing.T) {
281366
func Test_Proxy_Modify_Request(t *testing.T) {
282367
t.Parallel()
283368

284-
_, addr := createProxyTestServer(t, func(c fiber.Ctx) error {
369+
_, addr := createProxyTestServerIPv4(t, func(c fiber.Ctx) error {
285370
b := c.Request().Body()
286371
return c.SendString(string(b))
287372
})
@@ -308,7 +393,7 @@ func Test_Proxy_Modify_Request(t *testing.T) {
308393
func Test_Proxy_Timeout_Slow_Server(t *testing.T) {
309394
t.Parallel()
310395

311-
_, addr := createProxyTestServer(t, func(c fiber.Ctx) error {
396+
_, addr := createProxyTestServerIPv4(t, func(c fiber.Ctx) error {
312397
time.Sleep(300 * time.Millisecond)
313398
return c.SendString("fiber is awesome")
314399
})
@@ -332,7 +417,7 @@ func Test_Proxy_Timeout_Slow_Server(t *testing.T) {
332417
func Test_Proxy_With_Timeout(t *testing.T) {
333418
t.Parallel()
334419

335-
_, addr := createProxyTestServer(t, func(c fiber.Ctx) error {
420+
_, addr := createProxyTestServerIPv4(t, func(c fiber.Ctx) error {
336421
time.Sleep(1 * time.Second)
337422
return c.SendString("fiber is awesome")
338423
})
@@ -356,7 +441,7 @@ func Test_Proxy_With_Timeout(t *testing.T) {
356441
func Test_Proxy_Buffer_Size_Response(t *testing.T) {
357442
t.Parallel()
358443

359-
_, addr := createProxyTestServer(t, func(c fiber.Ctx) error {
444+
_, addr := createProxyTestServerIPv4(t, func(c fiber.Ctx) error {
360445
long := strings.Join(make([]string, 5000), "-")
361446
c.Set("Very-Long-Header", long)
362447
return c.SendString("ok")
@@ -383,7 +468,7 @@ func Test_Proxy_Buffer_Size_Response(t *testing.T) {
383468
// go test -race -run Test_Proxy_Do_RestoreOriginalURL
384469
func Test_Proxy_Do_RestoreOriginalURL(t *testing.T) {
385470
t.Parallel()
386-
_, addr := createProxyTestServer(t, func(c fiber.Ctx) error {
471+
_, addr := createProxyTestServerIPv4(t, func(c fiber.Ctx) error {
387472
return c.SendString("proxied")
388473
})
389474

@@ -470,7 +555,7 @@ func Test_Proxy_DoRedirects_TooManyRedirects(t *testing.T) {
470555
func Test_Proxy_DoTimeout_RestoreOriginalURL(t *testing.T) {
471556
t.Parallel()
472557

473-
_, addr := createProxyTestServer(t, func(c fiber.Ctx) error {
558+
_, addr := createProxyTestServerIPv4(t, func(c fiber.Ctx) error {
474559
return c.SendString("proxied")
475560
})
476561

@@ -492,7 +577,7 @@ func Test_Proxy_DoTimeout_RestoreOriginalURL(t *testing.T) {
492577
func Test_Proxy_DoTimeout_Timeout(t *testing.T) {
493578
t.Parallel()
494579

495-
_, addr := createProxyTestServer(t, func(c fiber.Ctx) error {
580+
_, addr := createProxyTestServerIPv4(t, func(c fiber.Ctx) error {
496581
time.Sleep(time.Second * 5)
497582
return c.SendString("proxied")
498583
})
@@ -510,7 +595,7 @@ func Test_Proxy_DoTimeout_Timeout(t *testing.T) {
510595
func Test_Proxy_DoDeadline_RestoreOriginalURL(t *testing.T) {
511596
t.Parallel()
512597

513-
_, addr := createProxyTestServer(t, func(c fiber.Ctx) error {
598+
_, addr := createProxyTestServerIPv4(t, func(c fiber.Ctx) error {
514599
return c.SendString("proxied")
515600
})
516601

@@ -532,7 +617,7 @@ func Test_Proxy_DoDeadline_RestoreOriginalURL(t *testing.T) {
532617
func Test_Proxy_DoDeadline_PastDeadline(t *testing.T) {
533618
t.Parallel()
534619

535-
_, addr := createProxyTestServer(t, func(c fiber.Ctx) error {
620+
_, addr := createProxyTestServerIPv4(t, func(c fiber.Ctx) error {
536621
time.Sleep(time.Second * 5)
537622
return c.SendString("proxied")
538623
})
@@ -550,7 +635,7 @@ func Test_Proxy_DoDeadline_PastDeadline(t *testing.T) {
550635
func Test_Proxy_Do_HTTP_Prefix_URL(t *testing.T) {
551636
t.Parallel()
552637

553-
_, addr := createProxyTestServer(t, func(c fiber.Ctx) error {
638+
_, addr := createProxyTestServerIPv4(t, func(c fiber.Ctx) error {
554639
return c.SendString("hello world")
555640
})
556641

@@ -641,7 +726,7 @@ func Test_Proxy_Forward_Local_Client(t *testing.T) {
641726
func Test_ProxyBalancer_Custom_Client(t *testing.T) {
642727
t.Parallel()
643728

644-
target, addr := createProxyTestServer(t, func(c fiber.Ctx) error {
729+
target, addr := createProxyTestServerIPv4(t, func(c fiber.Ctx) error {
645730
return c.SendStatus(fiber.StatusTeapot)
646731
})
647732

@@ -721,7 +806,7 @@ func Test_Proxy_Balancer_Forward_Local(t *testing.T) {
721806

722807
app := fiber.New()
723808

724-
_, addr := createProxyTestServer(t, func(c fiber.Ctx) error {
809+
_, addr := createProxyTestServerIPv4(t, func(c fiber.Ctx) error {
725810
return c.SendString("forwarded")
726811
})
727812

0 commit comments

Comments
 (0)