// Package httpclient provides shared HTTP client helpers used across the // monorepo: client IP extraction, CORS middleware, and related utilities. package httpclient import ( "net" "net/http" "strings" ) // ClientIP extracts the best-effort client IP from a request by checking // common reverse-proxy and CDN headers in priority order, falling back to // the TCP remote address when no header is present. It does NOT respect // query-parameter overrides (those belong at the handler level — see // slask-finder/cmd/stores for the ?ip= debug pattern). // // Priority order: CF-Connecting-IP → X-Real-IP → X-Forwarded-For (first // entry) → RemoteAddr (with port stripped when present). func ClientIP(r *http.Request) string { for _, h := range []string{"CF-Connecting-IP", "X-Real-IP", "X-Forwarded-For"} { v := r.Header.Get(h) if v == "" { continue } // X-Forwarded-For may be a comma-separated list; take the first. if h == "X-Forwarded-For" { if i := strings.IndexByte(v, ','); i >= 0 { v = v[:i] } } return strings.TrimSpace(v) } // Fall back to the TCP remote address, stripping the port when present. if host, _, err := net.SplitHostPort(r.RemoteAddr); err == nil { return host } return r.RemoteAddr }