Skip to content

Instantly share code, notes, and snippets.

@tigerwill90
Created February 3, 2025 12:59
Show Gist options
  • Select an option

  • Save tigerwill90/ebe0832814f5938d0fe852b5f94cd539 to your computer and use it in GitHub Desktop.

Select an option

Save tigerwill90/ebe0832814f5938d0fe852b5f94cd539 to your computer and use it in GitHub Desktop.
Client IP derivation with Fox
package main
import (
"context"
"errors"
"fmt"
"github.com/tigerwill90/fox"
"github.com/tigerwill90/fox/clientip"
"log"
"net"
"net/http"
)
type ipCtxKey struct{}
var ipKey ipCtxKey
func IpAddrFromContext(ctx context.Context) *net.IPAddr {
v := ctx.Value(ipKey)
if v != nil {
ipAddr, ok := v.(*net.IPAddr)
if ok {
return ipAddr
}
}
return nil
}
func IpAddrToContext(ctx context.Context, addr *net.IPAddr) context.Context {
return context.WithValue(ctx, ipKey, ctx)
}
func InjectIP() fox.MiddlewareFunc {
return func(next fox.HandlerFunc) fox.HandlerFunc {
return func(c fox.Context) {
ipAddr, err := c.ClientIP()
if err != nil {
log.Println(err)
next(c)
return
}
ctx := c.Request().Context()
req := c.Request()
defer func() {
// rollback to the original request
c.SetRequest(req)
}()
c.SetRequest(req.WithContext(IpAddrToContext(ctx, ipAddr)))
next(c)
}
}
}
func main() {
resolver, err := clientip.NewLeftmostNonPrivate(clientip.XForwardedForKey, 10)
if err != nil {
log.Fatal(err)
}
f, err := fox.New(
// Set up a global client ip resolver
fox.WithClientIPResolver(resolver),
// Set up a middleware that inject the client IP in the request context.
fox.WithMiddlewareFor(fox.RouteHandler, InjectIP()),
)
if err != nil {
log.Fatal(err)
}
f.MustHandle(http.MethodGet, "/hello/{name}", fox.WrapF(func(w http.ResponseWriter, r *http.Request) {
ipAddr := IpAddrFromContext(r.Context())
if ipAddr != nil {
fmt.Println(ipAddr)
}
}))
// Client IP resolver can be applied on a per-route basis or globally:
// - If applied globally, it affects all routes by default.
// - If applied to a specific route, it will override the global setting for that route.
// - Setting the resolver to nil is equivalent to no resolver configured.
cloudflareResolver, err := clientip.NewSingleIPHeader(fox.HeaderCFConnectionIP)
if err != nil {
log.Fatal(err)
}
f.MustHandle(http.MethodGet, "/foo/bar", fox.WrapF(func(w http.ResponseWriter, r *http.Request) {
ipAddr := IpAddrFromContext(r.Context())
if ipAddr != nil {
fmt.Println(ipAddr)
}
}), fox.WithClientIPResolver(cloudflareResolver))
if err := http.ListenAndServe(":8080", f); err != nil && !errors.Is(err, http.ErrServerClosed) {
log.Fatal(err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment