python-ipware 2.0.4__py3-none-any.whl → 2.0.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- python_ipware/__version__.py +1 -1
- python_ipware/python_ipware.py +169 -180
- {python_ipware-2.0.4.dist-info → python_ipware-2.0.5.dist-info}/METADATA +1 -1
- python_ipware-2.0.5.dist-info/RECORD +9 -0
- python_ipware-2.0.4.dist-info/RECORD +0 -9
- {python_ipware-2.0.4.dist-info → python_ipware-2.0.5.dist-info}/LICENSE +0 -0
- {python_ipware-2.0.4.dist-info → python_ipware-2.0.5.dist-info}/WHEEL +0 -0
- {python_ipware-2.0.4.dist-info → python_ipware-2.0.5.dist-info}/top_level.txt +0 -0
python_ipware/__version__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "2.0.
|
|
1
|
+
__version__ = "2.0.5"
|
python_ipware/python_ipware.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import ipaddress
|
|
2
2
|
import logging
|
|
3
3
|
|
|
4
|
-
from typing import
|
|
4
|
+
from typing import Dict, List, Optional, Tuple, Union
|
|
5
5
|
|
|
6
6
|
IpAddressType = Union[ipaddress.IPv4Address, ipaddress.IPv6Address]
|
|
7
7
|
OptionalIpAddressType = Optional[IpAddressType]
|
|
@@ -11,125 +11,130 @@ logger = logging.getLogger(__name__)
|
|
|
11
11
|
|
|
12
12
|
class IpWareMeta:
|
|
13
13
|
"""
|
|
14
|
-
A class
|
|
14
|
+
A class to manage metadata extracted from HTTP request headers, primarily for identifying
|
|
15
|
+
the client's IP address behind load balancers and proxies.
|
|
15
16
|
"""
|
|
16
17
|
|
|
17
18
|
def __init__(
|
|
18
|
-
self,
|
|
19
|
-
precedence: Optional[Tuple[str, ...]] = None,
|
|
20
|
-
leftmost: bool = True,
|
|
19
|
+
self, precedence: Optional[Tuple[str, ...]] = None, leftmost: bool = True
|
|
21
20
|
) -> None:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
21
|
+
"""
|
|
22
|
+
Initializes the metadata handler with a precedence list of HTTP headers and the position of the client IP.
|
|
23
|
+
|
|
24
|
+
:param precedence: A tuple of header names that define the order to check for a valid IP.
|
|
25
|
+
If not provided, a default list of commonly used headers is applied.
|
|
26
|
+
:param leftmost: A boolean that indicates whether to use the left-most IP in the header
|
|
27
|
+
when multiple IPs are listed (typically the case with proxies).
|
|
28
|
+
"""
|
|
29
|
+
# Default precedence list of headers if none is provided
|
|
30
|
+
if precedence is None:
|
|
31
|
+
precedence = (
|
|
32
|
+
"X_FORWARDED_FOR", # Common header for proxies, load balancers, like AWS ELB. Default to the left-most IP.
|
|
33
|
+
"HTTP_X_FORWARDED_FOR", # Alternative header similar to `X_FORWARDED_FOR`.
|
|
34
|
+
"HTTP_CLIENT_IP", # Header used by some providers like Amazon EC2, Heroku.
|
|
35
|
+
"HTTP_X_REAL_IP", # Header used by some providers like Amazon EC2, Heroku.
|
|
36
|
+
"HTTP_X_FORWARDED", # Used by Squid and similar software.
|
|
37
|
+
"HTTP_X_CLUSTER_CLIENT_IP", # Used by Rackspace LB, Riverbed Stingray.
|
|
38
|
+
"HTTP_FORWARDED_FOR", # Standard header defined by RFC 7239.
|
|
39
|
+
"HTTP_FORWARDED", # Standard header defined by RFC 7239.
|
|
40
|
+
"HTTP_CF_CONNECTING_IP", # Used by CloudFlare.
|
|
41
|
+
"X-CLIENT-IP", # Used by Microsoft Azure.
|
|
42
|
+
"X-REAL-IP", # Commonly used by NGINX.
|
|
43
|
+
"X-CLUSTER-CLIENT-IP", # Used by Rackspace Cloud Load Balancers.
|
|
44
|
+
"X_FORWARDED", # Used by Squid.
|
|
45
|
+
"FORWARDED_FOR", # Standard header defined by RFC 7239.
|
|
46
|
+
"CF-CONNECTING-IP", # Used by CloudFlare.
|
|
47
|
+
"TRUE-CLIENT-IP", # Header for CloudFlare Enterprise.
|
|
48
|
+
"FASTLY-CLIENT-IP", # Used by Fastly, Firebase.
|
|
49
|
+
"FORWARDED", # Standard header defined by RFC 7239.
|
|
50
|
+
"CLIENT-IP", # Used by Akamai, Cloudflare's True-Client-IP, and Fastly's Fastly-Client-IP.
|
|
51
|
+
"REMOTE_ADDR", # The default IP address header (direct connection).
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
self.precedence = precedence
|
|
44
55
|
self.leftmost = leftmost
|
|
45
56
|
|
|
46
57
|
|
|
47
58
|
class IpWareIpAddress:
|
|
48
59
|
"""
|
|
49
|
-
A class
|
|
60
|
+
A class for handling and parsing IP address data from HTTP requests.
|
|
50
61
|
"""
|
|
51
62
|
|
|
52
|
-
def
|
|
53
|
-
"""
|
|
54
|
-
Given an IPv4 address or address:port, it extracts the IP address
|
|
55
|
-
@param ip_str: IP address or address:port
|
|
56
|
-
@return: IP address
|
|
63
|
+
def extract_ipv4(self, ip_address: str) -> str:
|
|
57
64
|
"""
|
|
65
|
+
Extracts the IPv4 address from a given string that may include a port number.
|
|
58
66
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
if ":" in ip_address:
|
|
62
|
-
ip_address = ip_address.split(":")[0]
|
|
63
|
-
return ip_address.strip()
|
|
64
|
-
|
|
65
|
-
return ip_address.strip()
|
|
67
|
+
Args:
|
|
68
|
+
ip_address (str): An IPv4 address possibly including a port (e.g., "192.168.1.1:8080").
|
|
66
69
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
def extract_ipv6_only(self, ip_address: str) -> str:
|
|
70
|
+
Returns:
|
|
71
|
+
str: The IPv4 address without the port. Returns an empty string if input is None.
|
|
70
72
|
"""
|
|
71
|
-
Given an IPv6 address or address:port, it extracts the IP address
|
|
72
|
-
@param ip_str: IP address or address:port
|
|
73
|
-
@return: IP address
|
|
74
|
-
"""
|
|
75
|
-
|
|
76
73
|
if ip_address:
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
ip_address = ip_address.split("]:")[0]
|
|
80
|
-
ip_address = ip_address.replace("[", "")
|
|
81
|
-
return ip_address.strip()
|
|
74
|
+
return ip_address.split(":")[0].strip()
|
|
75
|
+
return ""
|
|
82
76
|
|
|
83
|
-
|
|
77
|
+
def extract_ipv6(self, ip_address: str) -> str:
|
|
78
|
+
"""
|
|
79
|
+
Extracts the IPv6 address from a given string that may include a port number.
|
|
84
80
|
|
|
85
|
-
|
|
81
|
+
Args:
|
|
82
|
+
ip_address (str): An IPv6 address possibly including a port (e.g., "[2001:db8::1]:8080").
|
|
86
83
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
ip_str: str,
|
|
90
|
-
) -> OptionalIpAddressType:
|
|
84
|
+
Returns:
|
|
85
|
+
str: The IPv6 address without brackets or port. Returns an empty string if input is None.
|
|
91
86
|
"""
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
87
|
+
if ip_address and "]:" in ip_address:
|
|
88
|
+
return ip_address.split("]:")[0].replace("[", "").strip()
|
|
89
|
+
return ip_address.strip() if ip_address else ""
|
|
90
|
+
|
|
91
|
+
def parse_ip_address(self, ip_str: str):
|
|
95
92
|
"""
|
|
93
|
+
Parses the given IP address string to an IPv4Address or IPv6Address object.
|
|
96
94
|
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
Args:
|
|
96
|
+
ip_str (str): An IP address possibly including a port.
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
ipaddress.IPv4Address|ipaddress.IPv6Address|None: The parsed IP object or None if the address is invalid.
|
|
100
|
+
"""
|
|
101
|
+
try:
|
|
102
|
+
# First, try to parse as IPv6.
|
|
103
|
+
ipv6 = self.extract_ipv6(ip_str)
|
|
104
|
+
ip = ipaddress.IPv6Address(ipv6)
|
|
105
|
+
return ip.ipv4_mapped or ip
|
|
106
|
+
except ipaddress.AddressValueError:
|
|
99
107
|
try:
|
|
100
|
-
# try to parse as
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
ip = ip.ipv4_mapped or ip
|
|
108
|
+
# If IPv6 parsing fails, try to parse as IPv4.
|
|
109
|
+
ipv4 = self.extract_ipv4(ip_str)
|
|
110
|
+
return ipaddress.IPv4Address(ipv4)
|
|
104
111
|
except ipaddress.AddressValueError:
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
ip = ipaddress.IPv4Address(ipv4)
|
|
109
|
-
except ipaddress.AddressValueError:
|
|
110
|
-
# not a valid IP address, return None
|
|
111
|
-
logger.info("Invalid ip address. {0}".format(ip_str))
|
|
112
|
-
ip = None
|
|
113
|
-
return ip
|
|
112
|
+
# Log error if IP is invalid
|
|
113
|
+
# print(f"Invalid IP address: {ip_str}")
|
|
114
|
+
return None
|
|
114
115
|
|
|
115
116
|
def get_ips_from_string(
|
|
116
117
|
self,
|
|
117
118
|
ip_str: str,
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
Given a comma separated list of IP addresses or address:port, it parses the IP addresses
|
|
121
|
-
@param ip_str: comma separated list of IP addresses or address:port
|
|
122
|
-
@return: list of IP addresses of type IPv4Address or IPv6Address
|
|
119
|
+
strict: bool = False,
|
|
120
|
+
) -> Optional[List[Union[ipaddress.IPv4Address, ipaddress.IPv6Address]]]:
|
|
123
121
|
"""
|
|
124
|
-
|
|
122
|
+
Parses a comma-separated list of IP addresses, each possibly including a port.
|
|
125
123
|
|
|
124
|
+
Args:
|
|
125
|
+
ip_str (str): A comma-separated list of IP addresses or address:port entries.
|
|
126
|
+
strict (bool): True to bail out on first invalid ip, False to skip invalid ip
|
|
127
|
+
Returns:
|
|
128
|
+
List[ipaddress.IPv4Address|ipaddress.IPv6Address]|None: A list of IP address objects or None if any IP is invalid.
|
|
129
|
+
"""
|
|
130
|
+
ip_list = []
|
|
126
131
|
for ip_address in ip_str.split(","):
|
|
127
|
-
ip = self.
|
|
132
|
+
ip = self.parse_ip_address(ip_address.strip())
|
|
128
133
|
if ip:
|
|
129
134
|
ip_list.append(ip)
|
|
130
135
|
else:
|
|
131
|
-
|
|
132
|
-
|
|
136
|
+
if strict:
|
|
137
|
+
return None
|
|
133
138
|
|
|
134
139
|
if not self.leftmost:
|
|
135
140
|
ip_list.reverse()
|
|
@@ -139,90 +144,101 @@ class IpWareIpAddress:
|
|
|
139
144
|
|
|
140
145
|
class IpWareProxy:
|
|
141
146
|
"""
|
|
142
|
-
A class
|
|
147
|
+
A class to handle proxy data from an HTTP request, including validating
|
|
148
|
+
proxy counts and trusted proxy lists.
|
|
143
149
|
"""
|
|
144
150
|
|
|
145
151
|
def __init__(
|
|
146
|
-
self,
|
|
147
|
-
proxy_count: Optional[int] = None,
|
|
148
|
-
proxy_list: Optional[List[str]] = None,
|
|
152
|
+
self, proxy_count: Optional[int] = None, proxy_list: Optional[List[str]] = None
|
|
149
153
|
) -> None:
|
|
150
|
-
|
|
151
|
-
|
|
154
|
+
"""
|
|
155
|
+
Initialize the IpWareProxy class with optional proxy count and proxy list.
|
|
152
156
|
|
|
153
|
-
|
|
157
|
+
Args:
|
|
158
|
+
proxy_count: The expected number of proxies, can be None if no specific count is enforced.
|
|
159
|
+
proxy_list: A list of partial IP addresses as trusted proxies, can be None.
|
|
154
160
|
"""
|
|
155
|
-
|
|
156
|
-
|
|
161
|
+
if proxy_count is not None and proxy_count < 0:
|
|
162
|
+
raise ValueError("proxy_count must be non-negative")
|
|
163
|
+
self.proxy_count = proxy_count
|
|
164
|
+
self.proxy_list = self._validate_proxy_list(proxy_list or [])
|
|
165
|
+
|
|
166
|
+
def _validate_proxy_list(self, proxy_list: List[str]) -> List[str]:
|
|
157
167
|
"""
|
|
168
|
+
Validates that the proxy list contains only strings.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
proxy_list: A list of proxies.
|
|
158
172
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
if not all(isinstance(x, str) for x in proxy_list):
|
|
162
|
-
raise ValueError("All elements in list must be strings")
|
|
173
|
+
Returns:
|
|
174
|
+
The proxy list if all items are valid strings.
|
|
163
175
|
|
|
176
|
+
Raises:
|
|
177
|
+
ValueError: If the proxy list is not a list of strings.
|
|
178
|
+
"""
|
|
179
|
+
if not all(isinstance(ip, str) for ip in proxy_list):
|
|
180
|
+
raise ValueError("All elements in the proxy list must be strings.")
|
|
164
181
|
return proxy_list
|
|
165
182
|
|
|
166
|
-
def is_proxy_count_valid(
|
|
167
|
-
self, ip_list: List[IpAddressType], strict: bool = False
|
|
168
|
-
) -> bool:
|
|
183
|
+
def is_proxy_count_valid(self, ip_list: List[str], strict: bool = False) -> bool:
|
|
169
184
|
"""
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
185
|
+
Validates the proxy count against a list of IP addresses.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
ip_list: A list of IP addresses from the request headers.
|
|
189
|
+
strict: If True, the number of proxies must exactly match the proxy_count.
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
True if the proxy count is valid, False otherwise.
|
|
174
193
|
"""
|
|
175
|
-
# No proxy count check is required
|
|
176
194
|
if self.proxy_count is None:
|
|
177
|
-
return True
|
|
178
|
-
|
|
179
|
-
ip_count: int = len(ip_list)
|
|
195
|
+
return True # No proxy count specified, so always valid.
|
|
180
196
|
|
|
197
|
+
ip_count = len(ip_list)
|
|
181
198
|
if strict:
|
|
182
|
-
|
|
183
|
-
|
|
199
|
+
return ip_count - 1 == self.proxy_count
|
|
200
|
+
# Exact match required, excluding client's own IP.
|
|
184
201
|
|
|
185
|
-
|
|
186
|
-
#
|
|
187
|
-
return ip_count - 1 > self.proxy_count
|
|
202
|
+
return ip_count - 1 >= self.proxy_count
|
|
203
|
+
# Allow more proxies than the count, excluding client's IP.
|
|
188
204
|
|
|
189
205
|
def is_proxy_trusted_list_valid(
|
|
190
|
-
self,
|
|
191
|
-
ip_list: List[IpAddressType],
|
|
192
|
-
strict: bool = False,
|
|
206
|
+
self, ip_list: List[str], strict: bool = False
|
|
193
207
|
) -> bool:
|
|
194
208
|
"""
|
|
195
|
-
Checks if the
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
209
|
+
Checks if all proxies in the incoming list are trusted based on the proxy_list.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
ip_list: A list of IP addresses from the request headers.
|
|
213
|
+
strict: If True, the number of proxies must exactly match the length of proxy_list.
|
|
214
|
+
|
|
215
|
+
Returns:
|
|
216
|
+
True if all proxies are trusted, False otherwise.
|
|
199
217
|
"""
|
|
200
218
|
if not self.proxy_list:
|
|
201
|
-
return True
|
|
219
|
+
return True # No specific proxies to trust, so always valid.
|
|
202
220
|
|
|
203
221
|
ip_count = len(ip_list)
|
|
204
222
|
proxy_list_count = len(self.proxy_list)
|
|
205
223
|
|
|
206
|
-
# in strict mode, total ip count must be 1 more than proxy count
|
|
207
224
|
if strict and ip_count - 1 != proxy_list_count:
|
|
208
|
-
return False
|
|
225
|
+
return False # Strict mode: Exact count match required.
|
|
209
226
|
|
|
210
|
-
# total ip count (client + proxies) must be more than proxy count
|
|
211
227
|
if ip_count - 1 < proxy_list_count:
|
|
212
|
-
return False
|
|
228
|
+
return False # Not enough IPs to match the trusted proxies.
|
|
213
229
|
|
|
214
|
-
#
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
230
|
+
# Verify each proxy against the trusted list by comparing each corresponding element.
|
|
231
|
+
return all(
|
|
232
|
+
str(ip).startswith(trusted_proxy_pattern)
|
|
233
|
+
for ip, trusted_proxy_pattern in zip(
|
|
234
|
+
ip_list[-proxy_list_count:], self.proxy_list
|
|
235
|
+
)
|
|
236
|
+
)
|
|
221
237
|
|
|
222
238
|
|
|
223
239
|
class IpWare(IpWareMeta, IpWareProxy, IpWareIpAddress):
|
|
224
240
|
"""
|
|
225
|
-
A class that makes best effort to determine the client's IP address.
|
|
241
|
+
A class that makes a best effort to determine the client's IP address.
|
|
226
242
|
"""
|
|
227
243
|
|
|
228
244
|
def __init__(
|
|
@@ -237,24 +253,23 @@ class IpWare(IpWareMeta, IpWareProxy, IpWareIpAddress):
|
|
|
237
253
|
|
|
238
254
|
def get_meta_value(self, meta: Dict[str, str], key: str) -> str:
|
|
239
255
|
"""
|
|
240
|
-
|
|
241
|
-
@param key:
|
|
242
|
-
@return:
|
|
256
|
+
Returns a cleaned up version of the value for a given key.
|
|
257
|
+
@param key: The key to look up.
|
|
258
|
+
@return: The value of the key or an empty string if the key is not found.
|
|
243
259
|
"""
|
|
244
260
|
meta = meta or {}
|
|
245
261
|
return meta.get(key, meta.get(key.replace("_", "-"), "")).strip()
|
|
246
262
|
|
|
247
263
|
def get_meta_values(self, meta: Dict[str, str]) -> List[str]:
|
|
248
264
|
"""
|
|
249
|
-
|
|
250
|
-
@return:
|
|
265
|
+
Returns a list of cleaned up values for the keys defined in 'precedence'.
|
|
266
|
+
@return: A list of values.
|
|
251
267
|
"""
|
|
252
268
|
meta_list: List[str] = []
|
|
253
269
|
for key in self.precedence:
|
|
254
270
|
value = self.get_meta_value(meta, key).strip()
|
|
255
271
|
if value:
|
|
256
272
|
meta_list.append(value)
|
|
257
|
-
|
|
258
273
|
return meta_list
|
|
259
274
|
|
|
260
275
|
def get_client_ip(
|
|
@@ -265,13 +280,11 @@ class IpWare(IpWareMeta, IpWareProxy, IpWareIpAddress):
|
|
|
265
280
|
"""
|
|
266
281
|
Returns the client's IP address.
|
|
267
282
|
"""
|
|
268
|
-
|
|
269
283
|
loopback_list: List[IpAddressType] = []
|
|
270
284
|
private_list: List[OptionalIpAddressType] = []
|
|
271
285
|
|
|
272
286
|
for ip_str in self.get_meta_values(meta):
|
|
273
|
-
|
|
274
|
-
ip_list = self.get_ips_from_string(ip_str)
|
|
287
|
+
ip_list = self.get_ips_from_string(ip_str, strict)
|
|
275
288
|
if not ip_list:
|
|
276
289
|
continue
|
|
277
290
|
|
|
@@ -283,64 +296,40 @@ class IpWare(IpWareMeta, IpWareProxy, IpWareIpAddress):
|
|
|
283
296
|
if not proxy_list_validated:
|
|
284
297
|
continue
|
|
285
298
|
|
|
286
|
-
client_ip, trusted_route = self.get_best_ip(
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
# we found a private ip, save it
|
|
295
|
-
if client_ip is not None and client_ip.is_loopback:
|
|
296
|
-
loopback_list.append(client_ip)
|
|
297
|
-
else:
|
|
298
|
-
# if not global (public) or loopback (local), we treat it asd private
|
|
299
|
-
private_list.append(client_ip)
|
|
299
|
+
client_ip, trusted_route = self.get_best_ip(ip_list)
|
|
300
|
+
if client_ip is not None:
|
|
301
|
+
if client_ip.is_global:
|
|
302
|
+
return client_ip, trusted_route
|
|
303
|
+
if client_ip.is_loopback:
|
|
304
|
+
loopback_list.append(client_ip)
|
|
305
|
+
else:
|
|
306
|
+
private_list.append(client_ip)
|
|
300
307
|
|
|
301
|
-
# we have not been able to locate a global ip
|
|
302
|
-
# it could be the server is running on the intranet
|
|
303
|
-
# we will return the first private ip we found
|
|
304
308
|
if private_list:
|
|
305
309
|
return private_list[0], False
|
|
306
|
-
|
|
307
|
-
# we have not been able to locate a global ip, nor a private ip
|
|
308
|
-
# it could be the server is running on a loopback address serving local requests
|
|
309
310
|
if loopback_list:
|
|
310
311
|
return loopback_list[0], False
|
|
311
|
-
|
|
312
|
-
# we were unable to find any ip address
|
|
313
312
|
return None, False
|
|
314
313
|
|
|
315
314
|
def get_best_ip(
|
|
316
315
|
self,
|
|
317
316
|
ip_list: List[IpAddressType],
|
|
318
|
-
proxy_count_validated: bool = True,
|
|
319
|
-
proxy_list_validated: bool = True,
|
|
320
317
|
) -> Tuple[OptionalIpAddressType, bool]:
|
|
321
318
|
"""
|
|
322
|
-
|
|
319
|
+
Determines the best possible IP address for the client from a list of IP addresses.
|
|
323
320
|
"""
|
|
324
|
-
|
|
325
321
|
if not ip_list:
|
|
326
|
-
logger.warning("Invalid
|
|
322
|
+
logger.warning("Invalid IP list provided.")
|
|
327
323
|
return None, False
|
|
328
324
|
|
|
329
|
-
|
|
330
|
-
if len(self.proxy_list) > 0 and proxy_list_validated:
|
|
325
|
+
if self.proxy_list and len(self.proxy_list) > 0:
|
|
331
326
|
best_client_ip_index = len(self.proxy_list) + 1
|
|
332
327
|
best_client_ip = ip_list[-best_client_ip_index]
|
|
333
328
|
return best_client_ip, True
|
|
334
329
|
|
|
335
|
-
|
|
336
|
-
if (
|
|
337
|
-
self.proxy_count is not None
|
|
338
|
-
and self.proxy_count > 0
|
|
339
|
-
and proxy_count_validated
|
|
340
|
-
):
|
|
330
|
+
if self.proxy_count is not None:
|
|
341
331
|
best_client_ip_index = self.proxy_count + 1
|
|
342
332
|
best_client_ip = ip_list[-best_client_ip_index]
|
|
343
333
|
return best_client_ip, True
|
|
344
334
|
|
|
345
|
-
# we don't track proxy related info, so we just return the first ip
|
|
346
335
|
return ip_list[0], False
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
python_ipware/__init__.py,sha256=TCZx4mt5Gxr2D_zNU6j0ynopdsAN_v4xeiTWz1OXSpY,87
|
|
2
|
+
python_ipware/__version__.py,sha256=xEb7Z4b8xalXXExBg42XPAhbJKniHzcsEPjp-6S3ppg,22
|
|
3
|
+
python_ipware/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
python_ipware/python_ipware.py,sha256=IotMZQIGSOu2XCbmdYH4WuhBvXm-lJVJHe3FXylSMvc,12725
|
|
5
|
+
python_ipware-2.0.5.dist-info/LICENSE,sha256=MLpNxpqfTc4TLdcDk3x6k7Vz4lJGBNLV-SxQZlFMDU8,1103
|
|
6
|
+
python_ipware-2.0.5.dist-info/METADATA,sha256=3Lsxn8HubcN0KnuwggZLcDu5h67rZc5YOn5ad-YNNdY,15420
|
|
7
|
+
python_ipware-2.0.5.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
8
|
+
python_ipware-2.0.5.dist-info/top_level.txt,sha256=URE4dvjpReJtRHyQpJqoBow5EWt_RqVXFn85B9Tq5Xw,14
|
|
9
|
+
python_ipware-2.0.5.dist-info/RECORD,,
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
python_ipware/__init__.py,sha256=TCZx4mt5Gxr2D_zNU6j0ynopdsAN_v4xeiTWz1OXSpY,87
|
|
2
|
-
python_ipware/__version__.py,sha256=ZOg41aCgKEuObK5WhegikXektFWPJaeZ-TMDh0Ke95M,22
|
|
3
|
-
python_ipware/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
-
python_ipware/python_ipware.py,sha256=sN3NeWWs96beGzI9x3YZ9ucYnMIZh1Q4fWOOIm2_P-s,11987
|
|
5
|
-
python_ipware-2.0.4.dist-info/LICENSE,sha256=MLpNxpqfTc4TLdcDk3x6k7Vz4lJGBNLV-SxQZlFMDU8,1103
|
|
6
|
-
python_ipware-2.0.4.dist-info/METADATA,sha256=-KqYlSwY52kwnnAMA7ym2A31L1yX8BffjMdZ3I32Wl0,15420
|
|
7
|
-
python_ipware-2.0.4.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
8
|
-
python_ipware-2.0.4.dist-info/top_level.txt,sha256=URE4dvjpReJtRHyQpJqoBow5EWt_RqVXFn85B9Tq5Xw,14
|
|
9
|
-
python_ipware-2.0.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|