Raw
 1package http
 2
 3import (
 4	"context"
 5	"net/http"
 6	"net/netip"
 7	"strings"
 8
 9	"tug/internal/platform/origin"
10)
11
12type Client = origin.Client
13
14func WithClient(ctx context.Context, c Client) context.Context {
15	return origin.With(ctx, c)
16}
17
18func ClientOf(ctx context.Context) Client {
19	return origin.Of(ctx)
20}
21
22func forwardedClient(next http.Handler) http.Handler {
23	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
24		next.ServeHTTP(w, r.WithContext(WithClient(r.Context(), Client{Addr: lastForwarded(r.Header), UserAgent: r.UserAgent()})))
25	})
26}
27
28func lastForwarded(h http.Header) netip.Addr {
29	lines := h.Values("X-Forwarded-For")
30	if len(lines) == 0 {
31		return netip.Addr{}
32	}
33	last := lines[len(lines)-1]
34	return parseForwarded(strings.Trim(last[strings.LastIndexByte(last, ',')+1:], " \t"))
35}
36
37func parseForwarded(s string) netip.Addr {
38	if a, err := netip.ParseAddr(s); err == nil {
39		return a.WithZone("").Unmap()
40	}
41	if ap, err := netip.ParseAddrPort(s); err == nil {
42		return ap.Addr().WithZone("").Unmap()
43	}
44	if inner, ok := strings.CutPrefix(s, "["); ok {
45		if inner, ok = strings.CutSuffix(inner, "]"); ok {
46			if a, err := netip.ParseAddr(inner); err == nil && a.Is6() {
47				return a.WithZone("").Unmap()
48			}
49		}
50	}
51	return netip.Addr{}
52}