Skip to content

Commit

Permalink
Various optimization and API polishing (#51)
Browse files Browse the repository at this point in the history
* feat: optimize auto options and method not allowed memory allocation

* feat: make EnableFullDuplex part of the ResponseWrite api.

* feat: improve .gitignore

* feat: move to rand/v2

* feat: prevent denial of service on adversary Xff of Fwd header line.

* feat: prevent denial of service on adversary Xff of Fwd header line.

* feat: improve ip strategy performance

* feat: improve test coverage for clientip strategy

* feat: improve test coverage for clientip strategy

* feat: better naming for system options

* feat: rename ClientIPStrategy to ClientIPResolver

* feat: rename ClientIPStrategy to ClientIPResolver
  • Loading branch information
tigerwill90 authored Nov 28, 2024
1 parent 7034888 commit 4c60ec0
Show file tree
Hide file tree
Showing 20 changed files with 614 additions and 292 deletions.
10 changes: 7 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
.idea
.prof
.test
*.prof
*.test
*.out
go.work
go.work.sum
go.work.sum
qodana.yaml
new.txt
old.txt
21 changes: 11 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -524,23 +524,23 @@ f := fox.New(
)
````

## Client IP Derivation
The `WithClientIPStrategy` option allows you to set up strategies to resolve the client IP address based on your
## Resolving Client IP
The `WithClientIPResolver` option allows you to set up strategies to resolve the client IP address based on your
use case and network topology. Accurately determining the client IP is hard, particularly in environments with proxies or
load balancers. For example, the leftmost IP in the `X-Forwarded-For` header is commonly used and is often regarded as the
"closest to the client" and "most real," but it can be easily spoofed. Therefore, you should absolutely avoid using it
for any security-related purposes, such as request throttling.

The strategy used must be chosen and tuned for your network configuration. This should result in the strategy never returning
The resolver used must be chosen and tuned for your network configuration. This should result in a resolver never returning
an error and if it does, it should be treated as an application issue or a misconfiguration, rather than defaulting to an
untrustworthy IP.

The sub-package `github.com/tigerwill90/fox/clientip` provides a set of best practices strategies that should cover most use cases.
The sub-package `github.com/tigerwill90/fox/clientip` provides a set of best practices resolvers that should cover most use cases.

````go
f := fox.New(
fox.DefaultOptions(),
fox.WithClientIPStrategy(
fox.WithClientIPResolver(
// We are behind one or many trusted proxies that have all private-space IP addresses.
clientip.NewRightmostNonPrivate(clientip.XForwardedForKey),
),
Expand All @@ -549,7 +549,7 @@ f := fox.New(
f.MustHandle(http.MethodGet, "/foo/bar", func(c fox.Context) {
ipAddr, err := c.ClientIP()
if err != nil {
// If the current strategy is not able to derive the client IP, an error
// If the current resolver is not able to derive the client IP, an error
// will be returned rather than falling back on an untrustworthy IP. It
// should be treated as an application issue or a misconfiguration.
panic(err)
Expand All @@ -558,23 +558,24 @@ f.MustHandle(http.MethodGet, "/foo/bar", func(c fox.Context) {
})
````

It is also possible to create a chain with multiple strategies that attempt to derive the client IP, stopping when the first one succeeds.
It is also possible to create a chain with multiple resolvers that attempt to derive the client IP, stopping when the first one succeeds.

````go
f = fox.New(
fox.DefaultOptions(),
fox.WithClientIPStrategy(
fox.WithClientIPResolver(
// A common use for this is if a server is both directly connected to the
// internet and expecting a header to check.
clientip.NewChain(
clientip.NewLeftmostNonPrivate(clientip.ForwardedKey),
clientip.NewLeftmostNonPrivate(clientip.ForwardedKey, 10),
clientip.NewRemoteAddr(),
),
),
)
````

Note that there is no "sane" default strategy, so calling `Context.ClientIP` without a strategy configured will return an `ErrNoClientIPStrategy`.
Note that there is no "sane" default strategy, so calling `Context.ClientIP` without a resolver configured will return
an `ErrNoClientIPResolver`.

See this [blog post](https://adam-p.ca/blog/2022/03/x-forwarded-for/) for general guidance on choosing a strategy that fit your needs.
## Benchmark
Expand Down
283 changes: 149 additions & 134 deletions clientip/clientip.go

Large diffs are not rendered by default.

Loading

0 comments on commit 4c60ec0

Please sign in to comment.