42 lines
1.4 KiB
Go
42 lines
1.4 KiB
Go
// Package clientip exposes the helper that resolves the originating client
|
|
// IP for an inbound HTTP request. Backend trusts the value because the
|
|
// network segment between gateway and backend is the trust boundary
|
|
// (`ARCHITECTURE.md` §15-16): gateway is responsible for sanitising and
|
|
// populating `X-Forwarded-For` before the request reaches backend.
|
|
//
|
|
// Both the public-auth handler chain (handlers_auth_helpers.go) and the
|
|
// user-surface geo-counter middleware reuse the same extraction so the two
|
|
// surfaces never disagree about the IP they record.
|
|
package clientip
|
|
|
|
import (
|
|
"net"
|
|
"strings"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// ExtractSourceIP returns the originating client IP for the request behind
|
|
// c. The leftmost entry of `X-Forwarded-For` is preferred; when the header
|
|
// is absent or empty, the connection RemoteAddr is used (with the port
|
|
// stripped). The empty string is returned when neither source yields a
|
|
// usable value, which lets callers treat the result as "no IP available"
|
|
// and skip dependent work.
|
|
func ExtractSourceIP(c *gin.Context) string {
|
|
if c == nil || c.Request == nil {
|
|
return ""
|
|
}
|
|
if xff := c.GetHeader("X-Forwarded-For"); xff != "" {
|
|
first := xff
|
|
if idx := strings.IndexByte(first, ','); idx >= 0 {
|
|
first = first[:idx]
|
|
}
|
|
return strings.TrimSpace(first)
|
|
}
|
|
addr := c.Request.RemoteAddr
|
|
if host, _, err := net.SplitHostPort(addr); err == nil {
|
|
return host
|
|
}
|
|
return addr
|
|
}
|