package ip import ( "net" "net/http" "strings" ) const ( xForwardedFor = "X-Forwarded-For" ) // Strategy a strategy for IP selection. type Strategy interface { GetIP(req *http.Request) string } // RemoteAddrStrategy a strategy that always return the remote address. type RemoteAddrStrategy struct{} // GetIP returns the selected IP. func (s *RemoteAddrStrategy) GetIP(req *http.Request) string { ip, _, err := net.SplitHostPort(req.RemoteAddr) if err != nil { return req.RemoteAddr } return ip } // DepthStrategy a strategy based on the depth inside the X-Forwarded-For from right to left. type DepthStrategy struct { Depth int } // GetIP return the selected IP. func (s *DepthStrategy) GetIP(req *http.Request) string { xff := req.Header.Get(xForwardedFor) xffs := strings.Split(xff, ",") if len(xffs) < s.Depth { return "" } return strings.TrimSpace(xffs[len(xffs)-s.Depth]) } // PoolStrategy is a strategy based on an IP Checker. // It allows to check whether addresses are in a given pool of IPs. type PoolStrategy struct { Checker *Checker } // GetIP checks the list of Forwarded IPs (most recent first) against the // Checker pool of IPs. It returns the first IP that is not in the pool, or the // empty string otherwise. func (s *PoolStrategy) GetIP(req *http.Request) string { if s.Checker == nil { return "" } xff := req.Header.Get(xForwardedFor) xffs := strings.Split(xff, ",") for i := len(xffs) - 1; i >= 0; i-- { xffTrimmed := strings.TrimSpace(xffs[i]) if len(xffTrimmed) == 0 { continue } if contain, _ := s.Checker.Contains(xffTrimmed); !contain { return xffTrimmed } } return "" }