package http import ( "context" "net/http" "net/netip" "strings" "tug/internal/platform/origin" ) type Client = origin.Client func WithClient(ctx context.Context, c Client) context.Context { return origin.With(ctx, c) } func ClientOf(ctx context.Context) Client { return origin.Of(ctx) } func forwardedClient(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { next.ServeHTTP(w, r.WithContext(WithClient(r.Context(), Client{Addr: lastForwarded(r.Header), UserAgent: r.UserAgent()}))) }) } func lastForwarded(h http.Header) netip.Addr { lines := h.Values("X-Forwarded-For") if len(lines) == 0 { return netip.Addr{} } last := lines[len(lines)-1] return parseForwarded(strings.Trim(last[strings.LastIndexByte(last, ',')+1:], " \t")) } func parseForwarded(s string) netip.Addr { if a, err := netip.ParseAddr(s); err == nil { return a.WithZone("").Unmap() } if ap, err := netip.ParseAddrPort(s); err == nil { return ap.Addr().WithZone("").Unmap() } if inner, ok := strings.CutPrefix(s, "["); ok { if inner, ok = strings.CutSuffix(inner, "]"); ok { if a, err := netip.ParseAddr(inner); err == nil && a.Is6() { return a.WithZone("").Unmap() } } } return netip.Addr{} }