39 lines
1.2 KiB
Go
39 lines
1.2 KiB
Go
// 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
|
|
}
|