Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1446,7 +1446,8 @@ func Test_Client_Cookie_With_Server(t *testing.T) {

handler := func(c fiber.Ctx) error {
return c.SendString(
c.Cookies("k1") + c.Cookies("k2") + c.Cookies("k3") + c.Cookies("k4"))
c.Cookies("k1") + c.Cookies("k2") + c.Cookies("k3") + c.Cookies("k4"),
)
}

wrapAgent := func(c *Client) {
Expand All @@ -1464,7 +1465,8 @@ func Test_Client_Cookie_With_Server(t *testing.T) {
func Test_Client_CookieJar(t *testing.T) {
handler := func(c fiber.Ctx) error {
return c.SendString(
c.Cookies("k1") + c.Cookies("k2") + c.Cookies("k3"))
c.Cookies("k1") + c.Cookies("k2") + c.Cookies("k3"),
)
}

jar := AcquireCookieJar()
Expand All @@ -1491,7 +1493,8 @@ func Test_Client_CookieJar_Response(t *testing.T) {
Value: "v4",
})
return c.SendString(
c.Cookies("k1") + c.Cookies("k2") + c.Cookies("k3"))
c.Cookies("k1") + c.Cookies("k2") + c.Cookies("k3"),
)
}

jar := AcquireCookieJar()
Expand All @@ -1518,7 +1521,8 @@ func Test_Client_CookieJar_Response(t *testing.T) {
Expires: time.Now().Add(1 * time.Nanosecond),
})
return c.SendString(
c.Cookies("k1") + c.Cookies("k2") + c.Cookies("k3"))
c.Cookies("k1") + c.Cookies("k2") + c.Cookies("k3"),
)
}

jar := AcquireCookieJar()
Expand All @@ -1544,7 +1548,8 @@ func Test_Client_CookieJar_Response(t *testing.T) {
Value: "v2",
})
return c.SendString(
c.Cookies("k1") + c.Cookies("k2"))
c.Cookies("k1") + c.Cookies("k2"),
)
}

jar := AcquireCookieJar()
Expand Down
21 changes: 14 additions & 7 deletions client/request_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1120,7 +1120,8 @@ func Test_Request_Cookie_With_Server(t *testing.T) {
t.Parallel()
handler := func(c fiber.Ctx) error {
return c.SendString(
c.Cookies("k1") + c.Cookies("k2") + c.Cookies("k3") + c.Cookies("k4"))
c.Cookies("k1") + c.Cookies("k2") + c.Cookies("k3") + c.Cookies("k4"),
)
}

wrapAgent := func(req *Request) {
Expand Down Expand Up @@ -1187,7 +1188,8 @@ func Test_Request_Body_With_Server(t *testing.T) {

t.Run("json body", func(t *testing.T) {
t.Parallel()
testRequest(t,
testRequest(
t,
func(c fiber.Ctx) error {
require.Equal(t, "application/json", string(c.Request().Header.ContentType()))
return c.SendString(string(c.Request().Body()))
Expand All @@ -1203,7 +1205,8 @@ func Test_Request_Body_With_Server(t *testing.T) {

t.Run("xml body", func(t *testing.T) {
t.Parallel()
testRequest(t,
testRequest(
t,
func(c fiber.Ctx) error {
require.Equal(t, "application/xml", string(c.Request().Header.ContentType()))
return c.SendString(string(c.Request().Body()))
Expand All @@ -1222,7 +1225,8 @@ func Test_Request_Body_With_Server(t *testing.T) {

t.Run("cbor body", func(t *testing.T) {
t.Parallel()
testRequest(t,
testRequest(
t,
func(c fiber.Ctx) error {
require.Equal(t, "application/cbor", string(c.Request().Header.ContentType()))
return c.SendString(string(c.Request().Body()))
Expand Down Expand Up @@ -1382,7 +1386,8 @@ func Test_Request_Body_With_Server(t *testing.T) {

t.Run("raw body", func(t *testing.T) {
t.Parallel()
testRequest(t,
testRequest(
t,
func(c fiber.Ctx) error {
return c.SendString(string(c.Request().Body()))
},
Expand Down Expand Up @@ -1458,7 +1463,8 @@ func Test_Request_Error_Body_With_Server(t *testing.T) {
t.Parallel()
t.Run("json error", func(t *testing.T) {
t.Parallel()
testRequestFail(t,
testRequestFail(
t,
func(c fiber.Ctx) error {
return c.SendString("")
},
Expand All @@ -1471,7 +1477,8 @@ func Test_Request_Error_Body_With_Server(t *testing.T) {

t.Run("xml error", func(t *testing.T) {
t.Parallel()
testRequestFail(t,
testRequestFail(
t,
func(c fiber.Ctx) error {
return c.SendString("")
},
Expand Down
18 changes: 12 additions & 6 deletions ctx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1593,7 +1593,8 @@ func Test_Ctx_Cookie_DefaultPath(t *testing.T) {
}

c.Res().Cookie(ck)
require.Equal(t,
require.Equal(
t,
"p=v; path=/; SameSite=Lax",
c.Res().Get(HeaderSetCookie),
)
Expand All @@ -1612,7 +1613,8 @@ func Test_Ctx_Cookie_MaxAgeOnly(t *testing.T) {
}
c.Res().Cookie(ck)

require.Equal(t,
require.Equal(
t,
"ttl=v; max-age=3600; path=/; SameSite=Lax",
c.Res().Get(HeaderSetCookie),
)
Expand All @@ -1633,7 +1635,8 @@ func Test_Ctx_Cookie_StrictPartitioned(t *testing.T) {
}
c.Res().Cookie(ck)

require.Equal(t,
require.Equal(
t,
"sp=v; path=/; secure; SameSite=Strict; Partitioned",
c.Res().Get(HeaderSetCookie),
)
Expand Down Expand Up @@ -2122,7 +2125,8 @@ func Test_Ctx_AutoFormat_Struct(t *testing.T) {
err := c.AutoFormat(data)
require.NoError(t, err)
require.Equal(t, MIMEApplicationJSONCharsetUTF8, c.GetRespHeader(HeaderContentType)) //nolint:testifylint // this is comparing content-type headers, not JSON content
require.JSONEq(t,
require.JSONEq(
t,
`{"Sender":"Carol","Recipients":["Alice","Bob"],"Urgency":3}`,
string(c.Response().Body()),
)
Expand Down Expand Up @@ -2153,7 +2157,8 @@ func Test_Ctx_AutoFormat_Struct(t *testing.T) {
err = c.AutoFormat(data)
require.NoError(t, err)
require.Equal(t, MIMEApplicationXMLCharsetUTF8, c.GetRespHeader(HeaderContentType))
require.Equal(t,
require.Equal(
t,
`<Message sender="Carol" urgency="3"><Recipients>Alice</Recipients><Recipients>Bob</Recipients></Message>`,
string(c.Response().Body()),
)
Expand Down Expand Up @@ -8725,7 +8730,8 @@ func Test_Ctx_OverrideParam(t *testing.T) {
t.Run("plus_wildcard_params", func(t *testing.T) {
t.Parallel()
app := New()
app.Get("/files+/+",
app.Get(
"/files+/+",
func(c Ctx) error {
c.OverrideParam("+", "changed")
c.OverrideParam("+2", "changed2")
Expand Down
12 changes: 12 additions & 0 deletions docs/middleware/proxy.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@ func DomainForward(hostname string, addr string, clients ...*fasthttp.Client) fi
func BalancerForward(servers []string, clients ...*fasthttp.Client) fiber.Handler
```

## Security

The `Forward`, `DomainForward`, and `BalancerForward` functions automatically set the `X-Real-IP` header to the actual client IP address obtained from `c.IP()` before forwarding the request upstream. This protects against IP spoofing attacks where a malicious client attempts to forge their IP address by sending a fake `X-Real-IP` header. Any existing `X-Real-IP` header on the incoming request is overwritten with the real client IP. Note that `DomainForward` only applies this overwrite when the request host matches the configured hostname; non-matching requests are not forwarded and are passed through unchanged.

If you're using the `Balancer` function with the `Config` struct, you can achieve the same protection by using the `ModifyRequest` callback as shown in the examples below.

When using `Do`, `DoRedirects`, `DoDeadline`, or `DoTimeout` directly, the `X-Real-IP` header is not automatically set. You should set it manually if your upstream server requires it:

```go
c.Request().Header.Set("X-Real-IP", c.IP())
```

## Examples

Import the middleware package:
Expand Down
30 changes: 20 additions & 10 deletions domain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,8 @@ func Test_Domain_MultipleHandlers(t *testing.T) {

app := New()

app.Domain("api.example.com").Get("/test",
app.Domain("api.example.com").Get(
"/test",
func(c Ctx) error {
c.Set("X-First", "true")
return c.Next()
Expand Down Expand Up @@ -1203,7 +1204,8 @@ func Test_Domain_HandlerTypes(t *testing.T) {
t.Run("express next-err returns-err", func(t *testing.T) {
t.Parallel()
app := New()
app.Domain("api.example.com").Get("/test",
app.Domain("api.example.com").Get(
"/test",
func(_ Req, res Res, next func() error) error {
res.Set("X-MW", "yes")
return next()
Expand All @@ -1226,7 +1228,8 @@ func Test_Domain_HandlerTypes(t *testing.T) {
t.Run("express with next error", func(t *testing.T) {
t.Parallel()
app := New()
app.Domain("api.example.com").Get("/test",
app.Domain("api.example.com").Get(
"/test",
func(_ Req, res Res, next func() error) {
res.Set("X-MW", "yes")
_ = next() //nolint:errcheck // test
Expand All @@ -1246,7 +1249,8 @@ func Test_Domain_HandlerTypes(t *testing.T) {
t.Run("express with noarg next error", func(t *testing.T) {
t.Parallel()
app := New()
app.Domain("api.example.com").Get("/test",
app.Domain("api.example.com").Get(
"/test",
func(_ Req, res Res, next func()) error {
res.Set("X-MW", "yes")
next()
Expand All @@ -1267,7 +1271,8 @@ func Test_Domain_HandlerTypes(t *testing.T) {
t.Run("express with noarg next", func(t *testing.T) {
t.Parallel()
app := New()
app.Domain("api.example.com").Get("/test",
app.Domain("api.example.com").Get(
"/test",
func(_ Req, res Res, next func()) {
res.Set("X-MW", "yes")
next()
Expand All @@ -1287,7 +1292,8 @@ func Test_Domain_HandlerTypes(t *testing.T) {
t.Run("express with error next", func(t *testing.T) {
t.Parallel()
app := New()
app.Domain("api.example.com").Get("/test",
app.Domain("api.example.com").Get(
"/test",
func(_ Req, res Res, next func(error)) {
res.Set("X-MW", "yes")
next(nil)
Expand All @@ -1307,7 +1313,8 @@ func Test_Domain_HandlerTypes(t *testing.T) {
t.Run("express with error next error", func(t *testing.T) {
t.Parallel()
app := New()
app.Domain("api.example.com").Get("/test",
app.Domain("api.example.com").Get(
"/test",
func(_ Req, res Res, next func(error)) error {
res.Set("X-MW", "yes")
next(nil)
Expand All @@ -1328,7 +1335,8 @@ func Test_Domain_HandlerTypes(t *testing.T) {
t.Run("express errnext-err returns-err", func(t *testing.T) {
t.Parallel()
app := New()
app.Domain("api.example.com").Get("/test",
app.Domain("api.example.com").Get(
"/test",
func(_ Req, res Res, next func(error) error) {
res.Set("X-MW", "yes")
_ = next(nil) //nolint:errcheck // test
Expand All @@ -1348,7 +1356,8 @@ func Test_Domain_HandlerTypes(t *testing.T) {
t.Run("express errnext-err returns-err err", func(t *testing.T) {
t.Parallel()
app := New()
app.Domain("api.example.com").Get("/test",
app.Domain("api.example.com").Get(
"/test",
func(_ Req, res Res, next func(error) error) error {
res.Set("X-MW", "yes")
return next(nil)
Expand Down Expand Up @@ -1424,7 +1433,8 @@ func Test_Domain_HandlerTypes(t *testing.T) {
t.Run("non-matching domain skips all handler types", func(t *testing.T) {
t.Parallel()
app := New()
app.Domain("api.example.com").Get("/test",
app.Domain("api.example.com").Get(
"/test",
func(c Ctx) error {
c.Set("X-Handler", "ran")
return c.SendString("should-not-run")
Expand Down
3 changes: 2 additions & 1 deletion helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,8 @@ func (app *App) quoteRawString(raw string) string {
bb.B = append(bb.B, '\\', 'r')
case c < 0x20 || c == 0x7f:
// percent-encode control and DEL
bb.B = append(bb.B,
bb.B = append(
bb.B,
'%',
hex[c>>4],
hex[c&0x0f],
Expand Down
3 changes: 2 additions & 1 deletion helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -640,7 +640,8 @@ func Test_Utils_UniqueRouteStack(t *testing.T) {
route1,
route2,
route3,
}))
}),
)
}

func Test_Utils_getGroupPath(t *testing.T) {
Expand Down
9 changes: 6 additions & 3 deletions middleware/compress/compress.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,19 +65,22 @@ func New(config ...Config) fiber.Handler {
switch cfg.Level {
case LevelDefault:
// LevelDefault
compressor = fasthttp.CompressHandlerBrotliLevel(fctx,
compressor = fasthttp.CompressHandlerBrotliLevel(
fctx,
fasthttp.CompressBrotliDefaultCompression,
fasthttp.CompressDefaultCompression,
)
case LevelBestSpeed:
// LevelBestSpeed
compressor = fasthttp.CompressHandlerBrotliLevel(fctx,
compressor = fasthttp.CompressHandlerBrotliLevel(
fctx,
fasthttp.CompressBrotliBestSpeed,
fasthttp.CompressBestSpeed,
)
case LevelBestCompression:
// LevelBestCompression
compressor = fasthttp.CompressHandlerBrotliLevel(fctx,
compressor = fasthttp.CompressHandlerBrotliLevel(
fctx,
fasthttp.CompressBrotliBestCompression,
fasthttp.CompressBestCompression,
)
Expand Down
3 changes: 2 additions & 1 deletion middleware/envvar/envvar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ func Test_EnvVarHandler(t *testing.T) {
Vars map[string]string `json:"vars"`
}{
Vars: map[string]string{"testKey": "testVal"},
})
},
)
require.NoError(t, err)

app := fiber.New()
Expand Down
3 changes: 2 additions & 1 deletion middleware/logger/default_logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ func defaultLoggerInstance(c fiber.Ctx, data *Data, cfg *Config) error {
if data.ChainErr != nil {
formatErr = colors.Red + " | " + data.ChainErr.Error() + colors.Reset
}
fmt.Fprintf(buf,
fmt.Fprintf(
buf,
"%s |%s %3d %s| %13v | %15s |%s %-7s %s| %-"+data.ErrPaddingStr+"s %s\n",
data.Timestamp.Load().(string), //nolint:forcetypeassert,errcheck // Timestamp is always a string
statusColor(c.Response().StatusCode(), &colors), c.Response().StatusCode(), colors.Reset,
Expand Down
2 changes: 2 additions & 0 deletions middleware/proxy/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ func WithClient(cli *fasthttp.Client) {
// This method will return a fiber.Handler
func Forward(addr string, clients ...*fasthttp.Client) fiber.Handler {
return func(c fiber.Ctx) error {
c.Request().Header.Set("X-Real-IP", c.IP())
return Do(c, addr, clients...)
}
}
Expand Down Expand Up @@ -240,6 +241,7 @@ func DomainForward(hostname, addr string, clients ...*fasthttp.Client) fiber.Han
return func(c fiber.Ctx) error {
host := utils.UnsafeString(c.Request().Host())
if host == hostname {
c.Request().Header.Set("X-Real-IP", c.IP())
return Do(c, addr+c.OriginalURL(), clients...)
}
return nil
Expand Down
Loading
Loading