diff --git a/readthedocs/analytics/tests.py b/readthedocs/analytics/tests.py index db49953e660..eb0adcf5fa0 100644 --- a/readthedocs/analytics/tests.py +++ b/readthedocs/analytics/tests.py @@ -34,13 +34,19 @@ def test_anonymize_ua(self): ) def test_get_client_ip_with_x_forwarded_for(self): - # only client's ip is present request = RequestFactory().get('/') request.META['HTTP_X_FORWARDED_FOR'] = '203.0.113.195' client_ip = get_client_ip(request) self.assertEqual(client_ip, '203.0.113.195') + # only client's ip is present + request = RequestFactory().get('/') + ip = '2001:abc:def:012:345:6789:abcd:ef12' + request.META['HTTP_X_FORWARDED_FOR'] = ip + client_ip = get_client_ip(request) + self.assertEqual(client_ip, ip) + # proxy1 and proxy2 are present along with client's ip request = RequestFactory().get('/') request.META['HTTP_X_FORWARDED_FOR'] = '203.0.113.195, 70.41.3.18, 150.172.238.178' @@ -53,6 +59,14 @@ def test_get_client_ip_with_x_forwarded_for(self): client_ip = get_client_ip(request) self.assertEqual(client_ip, '203.0.113.195') + # client ip (ipv6), other clients with port + request = RequestFactory().get('/') + ip = '2001:abc:def:012:345:6789:abcd:ef12' + x_forwarded_for = f'{ip}, 203.0.113.195:8080, 70.41.3.18' + request.META['HTTP_X_FORWARDED_FOR'] = x_forwarded_for + client_ip = get_client_ip(request) + self.assertEqual(client_ip, ip) + # client ip with port but not proxy1 and proxy2 request = RequestFactory().get('/') request.META['HTTP_X_FORWARDED_FOR'] = '203.0.113.195:8080' diff --git a/readthedocs/analytics/utils.py b/readthedocs/analytics/utils.py index ae46188fd7d..9a903637bce 100644 --- a/readthedocs/analytics/utils.py +++ b/readthedocs/analytics/utils.py @@ -32,7 +32,9 @@ def get_client_ip(request): client_ip = x_forwarded_for.split(',')[0].strip() # Removing the port number (if present) - client_ip = client_ip.rsplit(':')[0] + # But be careful about IPv6 addresses + if client_ip.count(':') == 1: + client_ip = client_ip.rsplit(':', maxsplit=1)[0] else: client_ip = request.META.get('REMOTE_ADDR', None)