Skip to content

Commit

Permalink
Merge branch 'main' into feature/add-app-test-config
Browse files Browse the repository at this point in the history
  • Loading branch information
grivera64 authored Nov 12, 2024
2 parents 78e32a7 + 2c7bdb9 commit 20bac97
Show file tree
Hide file tree
Showing 49 changed files with 3,445 additions and 681 deletions.
6 changes: 4 additions & 2 deletions .github/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -561,8 +561,10 @@ import (

func main() {
app := fiber.New(fiber.Config{
EnableTrustedProxyCheck: true,
TrustedProxies: []string{"0.0.0.0", "1.1.1.1/30"}, // IP address or IP address range
TrustProxy: true,
TrustProxyConfig: fiber.TrustProxyConfig{
Proxies: []string{"0.0.0.0", "1.1.1.1/30"}, // IP address or IP address range
},
ProxyHeader: fiber.HeaderXForwardedFor,
})

Expand Down
Binary file added .github/testdata/fs/img/fiberpng
Binary file not shown.
Binary file added .github/testdata/fs/img/fiberpng.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/testdata/fs/img/fiberpng.notvalidext
Binary file not shown.
6 changes: 3 additions & 3 deletions .github/workflows/benchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ jobs:

# This will only run if we have Benchmark Results from main branch
- name: Compare PR Benchmark Results with main branch
uses: benchmark-action/[email protected].3
uses: benchmark-action/[email protected].4
if: steps.cache.outputs.cache-hit == 'true'
with:
tool: 'go'
Expand All @@ -72,7 +72,7 @@ jobs:
alert-threshold: "150%"

- name: Store Benchmark Results for main branch
uses: benchmark-action/[email protected].3
uses: benchmark-action/[email protected].4
if: ${{ github.ref_name == 'main' }}
with:
tool: 'go'
Expand All @@ -86,7 +86,7 @@ jobs:
alert-threshold: "150%"

- name: Publish Benchmark Results to GitHub Pages
uses: benchmark-action/[email protected].3
uses: benchmark-action/[email protected].4
if: ${{ github.ref_name == 'main' }}
with:
tool: 'go'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/linter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@ jobs:
uses: golangci/golangci-lint-action@v6
with:
# NOTE: Keep this in sync with the version from .golangci.yml
version: v1.61.0
version: v1.62.0
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ markdown:
## lint: 🚨 Run lint checks
.PHONY: lint
lint:
go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.61.0 run ./...
go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.0 run ./...

## test: 🚦 Execute all tests
.PHONY: test
Expand Down
75 changes: 56 additions & 19 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ type Config struct { //nolint:govet // Aligning the struct fields is not necessa
// Default: false
StrictRouting bool `json:"strict_routing"`

// When set to true, enables case sensitive routing.
// When set to true, enables case-sensitive routing.
// E.g. "/FoO" and "/foo" are treated as different routes.
// By default this is disabled and both "/FoO" and "/foo" will execute the same handler.
//
Expand Down Expand Up @@ -331,29 +331,31 @@ type Config struct { //nolint:govet // Aligning the struct fields is not necessa
// For example, the Host HTTP header is usually used to return the requested host.
// But when you’re behind a proxy, the actual host may be stored in an X-Forwarded-Host header.
//
// If you are behind a proxy, you should enable TrustedProxyCheck to prevent header spoofing.
// If you enable EnableTrustedProxyCheck and leave TrustedProxies empty Fiber will skip
// If you are behind a proxy, you should enable TrustProxy to prevent header spoofing.
// If you enable TrustProxy and do not provide a TrustProxyConfig, Fiber will skip
// all headers that could be spoofed.
// If request ip in TrustedProxies whitelist then:
// If the request IP is in the TrustProxyConfig.Proxies allowlist, then:
// 1. c.Scheme() get value from X-Forwarded-Proto, X-Forwarded-Protocol, X-Forwarded-Ssl or X-Url-Scheme header
// 2. c.IP() get value from ProxyHeader header.
// 3. c.Host() and c.Hostname() get value from X-Forwarded-Host header
// But if request ip NOT in Trusted Proxies whitelist then:
// 1. c.Scheme() WON't get value from X-Forwarded-Proto, X-Forwarded-Protocol, X-Forwarded-Ssl or X-Url-Scheme header,
// will return https in case when tls connection is handled by the app, of http otherwise
// But if the request IP is NOT in the TrustProxyConfig.Proxies allowlist, then:
// 1. c.Scheme() WON'T get value from X-Forwarded-Proto, X-Forwarded-Protocol, X-Forwarded-Ssl or X-Url-Scheme header,
// will return https when a TLS connection is handled by the app, or http otherwise.
// 2. c.IP() WON'T get value from ProxyHeader header, will return RemoteIP() from fasthttp context
// 3. c.Host() and c.Hostname() WON'T get value from X-Forwarded-Host header, fasthttp.Request.URI().Host()
// will be used to get the hostname.
//
// To automatically trust all loopback, link-local, or private IP addresses,
// without manually adding them to the TrustProxyConfig.Proxies allowlist,
// you can set TrustProxyConfig.Loopback, TrustProxyConfig.LinkLocal, or TrustProxyConfig.Private to true.
//
// Default: false
EnableTrustedProxyCheck bool `json:"enable_trusted_proxy_check"`
TrustProxy bool `json:"trust_proxy"`

// Read EnableTrustedProxyCheck doc.
// Read TrustProxy doc.
//
// Default: []string
TrustedProxies []string `json:"trusted_proxies"`
trustedProxiesMap map[string]struct{}
trustedProxyRanges []*net.IPNet
// Default: DefaultTrustProxyConfig
TrustProxyConfig TrustProxyConfig `json:"trust_proxy_config"`

// If set to true, c.IP() and c.IPs() will validate IP addresses before returning them.
// Also, c.IP() will return only the first valid IP rather than just the raw header
Expand All @@ -373,7 +375,7 @@ type Config struct { //nolint:govet // Aligning the struct fields is not necessa
// Default: nil
StructValidator StructValidator

// RequestMethods provides customizibility for HTTP methods. You can add/remove methods as you wish.
// RequestMethods provides customizability for HTTP methods. You can add/remove methods as you wish.
//
// Optional. Default: DefaultMethods
RequestMethods []string
Expand All @@ -386,6 +388,36 @@ type Config struct { //nolint:govet // Aligning the struct fields is not necessa
EnableSplittingOnParsers bool `json:"enable_splitting_on_parsers"`
}

// Default TrustProxyConfig
var DefaultTrustProxyConfig = TrustProxyConfig{}

// TrustProxyConfig is a struct for configuring trusted proxies if Config.TrustProxy is true.
type TrustProxyConfig struct {
ips map[string]struct{}

// Proxies is a list of trusted proxy IP addresses or CIDR ranges.
//
// Default: []string
Proxies []string `json:"proxies"`

ranges []*net.IPNet

// LinkLocal enables trusting all link-local IP ranges (e.g., 169.254.0.0/16, fe80::/10).
//
// Default: false
LinkLocal bool `json:"link_local"`

// Loopback enables trusting all loopback IP ranges (e.g., 127.0.0.0/8, ::1/128).
//
// Default: false
Loopback bool `json:"loopback"`

// Private enables trusting all private IP ranges (e.g., 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fc00::/7).
//
// Default: false
Private bool `json:"private"`
}

// RouteMessage is some message need to be print when server starts
type RouteMessage struct {
name string
Expand Down Expand Up @@ -511,8 +543,8 @@ func New(config ...Config) *App {
app.config.RequestMethods = DefaultMethods
}

app.config.trustedProxiesMap = make(map[string]struct{}, len(app.config.TrustedProxies))
for _, ipAddress := range app.config.TrustedProxies {
app.config.TrustProxyConfig.ips = make(map[string]struct{}, len(app.config.TrustProxyConfig.Proxies))
for _, ipAddress := range app.config.TrustProxyConfig.Proxies {
app.handleTrustedProxy(ipAddress)
}

Expand All @@ -530,17 +562,22 @@ func New(config ...Config) *App {
return app
}

// Adds an ip address to trustedProxyRanges or trustedProxiesMap based on whether it is an IP range or not
// Adds an ip address to TrustProxyConfig.ranges or TrustProxyConfig.ips based on whether it is an IP range or not
func (app *App) handleTrustedProxy(ipAddress string) {
if strings.Contains(ipAddress, "/") {
_, ipNet, err := net.ParseCIDR(ipAddress)
if err != nil {
log.Warnf("IP range %q could not be parsed: %v", ipAddress, err)
} else {
app.config.trustedProxyRanges = append(app.config.trustedProxyRanges, ipNet)
app.config.TrustProxyConfig.ranges = append(app.config.TrustProxyConfig.ranges, ipNet)
}
} else {
app.config.trustedProxiesMap[ipAddress] = struct{}{}
ip := net.ParseIP(ipAddress)
if ip == nil {
log.Warnf("IP address %q could not be parsed", ipAddress)
} else {
app.config.TrustProxyConfig.ips[ipAddress] = struct{}{}
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -451,9 +451,9 @@ func Test_App_Use_CaseSensitive(t *testing.T) {
require.NoError(t, err, "app.Test(req)")
require.Equal(t, StatusOK, resp.StatusCode, "Status code")

// check the detected path when the case insensitive recognition is activated
// check the detected path when the case-insensitive recognition is activated
app.config.CaseSensitive = false
// check the case sensitive feature
// check the case-sensitive feature
resp, err = app.Test(httptest.NewRequest(MethodGet, "/AbC", nil))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, StatusOK, resp.StatusCode, "Status code")
Expand Down
18 changes: 9 additions & 9 deletions client/hooks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ func Test_Parser_Request_Header(t *testing.T) {

err := parserRequestHeader(client, req)
require.NoError(t, err)
require.Equal(t, []byte(applicationJSON), req.RawRequest.Header.ContentType())
require.Equal(t, []byte(applicationJSON), req.RawRequest.Header.ContentType()) //nolint:testifylint // test
})

t.Run("auto set xml header", func(t *testing.T) {
Expand Down Expand Up @@ -297,8 +297,8 @@ func Test_Parser_Request_Header(t *testing.T) {

err := parserRequestHeader(client, req)
require.NoError(t, err)
require.True(t, strings.Contains(string(req.RawRequest.Header.MultipartFormBoundary()), "--FiberFormBoundary"))
require.True(t, strings.Contains(string(req.RawRequest.Header.ContentType()), multipartFormData))
require.Contains(t, string(req.RawRequest.Header.MultipartFormBoundary()), "--FiberFormBoundary")
require.Contains(t, string(req.RawRequest.Header.ContentType()), multipartFormData)
})

t.Run("ua should have default value", func(t *testing.T) {
Expand Down Expand Up @@ -436,7 +436,7 @@ func Test_Parser_Request_Body(t *testing.T) {

err := parserRequestBody(client, req)
require.NoError(t, err)
require.Equal(t, []byte("{\"name\":\"foo\"}"), req.RawRequest.Body())
require.Equal(t, []byte("{\"name\":\"foo\"}"), req.RawRequest.Body()) //nolint:testifylint // test
})

t.Run("xml body", func(t *testing.T) {
Expand Down Expand Up @@ -489,8 +489,8 @@ func Test_Parser_Request_Body(t *testing.T) {

err := parserRequestBody(client, req)
require.NoError(t, err)
require.True(t, strings.Contains(string(req.RawRequest.Body()), "----FiberFormBoundary"))
require.True(t, strings.Contains(string(req.RawRequest.Body()), "world"))
require.Contains(t, string(req.RawRequest.Body()), "----FiberFormBoundary")
require.Contains(t, string(req.RawRequest.Body()), "world")
})

t.Run("file and form data", func(t *testing.T) {
Expand All @@ -502,9 +502,9 @@ func Test_Parser_Request_Body(t *testing.T) {

err := parserRequestBody(client, req)
require.NoError(t, err)
require.True(t, strings.Contains(string(req.RawRequest.Body()), "----FiberFormBoundary"))
require.True(t, strings.Contains(string(req.RawRequest.Body()), "world"))
require.True(t, strings.Contains(string(req.RawRequest.Body()), "bar"))
require.Contains(t, string(req.RawRequest.Body()), "----FiberFormBoundary")
require.Contains(t, string(req.RawRequest.Body()), "world")
require.Contains(t, string(req.RawRequest.Body()), "bar")
})

t.Run("raw body", func(t *testing.T) {
Expand Down
4 changes: 2 additions & 2 deletions client/response_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ func Test_Response_Save(t *testing.T) {

data, err := io.ReadAll(file)
require.NoError(t, err)
require.Equal(t, "{\"status\":\"success\"}", string(data))
require.JSONEq(t, "{\"status\":\"success\"}", string(data))
})

t.Run("io.Writer", func(t *testing.T) {
Expand All @@ -396,7 +396,7 @@ func Test_Response_Save(t *testing.T) {

err = resp.Save(buf)
require.NoError(t, err)
require.Equal(t, "{\"status\":\"success\"}", buf.String())
require.JSONEq(t, "{\"status\":\"success\"}", buf.String())
})

t.Run("error type", func(t *testing.T) {
Expand Down
Loading

0 comments on commit 20bac97

Please sign in to comment.