Coverage for jutil/request.py: 51%

# Coverage for jutil/request.py : 51%

## 39 statements

Hot-keys on this page

r m x p toggle line displays

j k next/prev highlighted chunk

0 (zero) top of page

1 (one) first highlighted chunk

1 import logging

2 from typing import Tuple, Union

3 from django.conf import settings

4 import requests

5 import socket

6 from django.http.request import HttpRequest

7 from ipware import get_client_ip # type: ignore

8 from rest_framework.request import Request

9

10

11 logger = logging.getLogger(__name__)

12

13

14 def get_ip(request: Union[HttpRequest, Request]) -> str:

15 """

16 Returns best-guess IP for given request.

17 Uses ipware library get_client_ip.

18 If you need to know is IP routable or not, use ipware get_client_ip directly.

19 See ipware documentation for more info.

20

21 Note: Why such a simple function wrapper? I'm generally against wrappers like this,

22 but in this case made an exceptions: I used to use ipware get_real_ip() everywhere before

23 it was deprecated and had quite big update process to change all code to use ipware get_client_ip.

24 I want to avoid such process again so added this wrapper.

25

26 :param request: Djangos HttpRequest or DRF Request

27 :return: IP-address or None

28 """

29 return get_client_ip(request)[0]

30

31

32 def get_geo_ip(ip: str, exceptions: bool = False, timeout: int = 10) -> dict:

33 """

34 Returns geo IP info or empty dict if geoip query fails at http://ipstack.com.

35 requires settings.IPSTACK_TOKEN set as valid access token to the API.

36

37 Example replies:

38

39 {'country_name': 'United States', 'country_code': 'US', 'region_code': 'TX', 'region_name': 'Texas',

40 'ip': '76.184.236.184', 'latitude': 33.1507, 'time_zone': 'America/Chicago', 'metro_code': 623, 'city':

41 'Frisco', 'longitude': -96.8236, 'zip_code': '75033'}

42

43 {'latitude': 60.1641, 'country_name': 'Finland', 'zip_code': '02920', 'region_name': 'Uusimaa', 'city':

44 'Espoo', 'metro_code': 0, 'ip': '194.100.27.41', 'time_zone': 'Europe/Helsinki', 'country_code': 'FI',

45 'longitude': 24.7136, 'region_code': '18'}

46

47 :param ip: str

48 :param exceptions: if True raises Exception on failure

49 :param timeout: timeout in seconds

50 :return: dict

51 """

52 try:

53 res = requests.get('http://api.ipstack.com/{}?access_key={}&format=1'.format(ip, settings.IPSTACK_TOKEN), timeout=timeout)

54 if res.status_code != 200: 54 ↛ 55line 54 didn't jump to line 55, because the condition on line 54 was never true

55 if exceptions:

56 raise Exception('api.ipstack.com HTTP {}'.format(res.status_code))

57 return {}

58 return res.json()

59 except Exception as e:

60 msg = 'geoip({}) failed: {}'.format(ip, e)

61 logger.error(msg)

62 if exceptions:

63 raise

64 return {}

65

66

67 def get_ip_info(ip: str, exceptions: bool = False, timeout: int = 10) -> Tuple[str, str, str]:

68 """

69 Returns (ip, country_code, host) tuple of the IP address.

70 :param ip: IP address

71 :param exceptions: Raise Exception or not

72 :param timeout: Timeout in seconds. Note that timeout only affects geo IP part, not getting host name.

73 :return: (ip, country_code, host)

74 """

75 if not ip: # localhost 75 ↛ 76line 75 didn't jump to line 76, because the condition on line 75 was never true

76 return '', '', ''

77 host = ''

78 country_code = get_geo_ip(ip, exceptions=exceptions, timeout=timeout).get('country_code', '')

79 try:

80 res = socket.gethostbyaddr(ip)

81 host = res[0][:255] if ip else ''

82 except Exception as e:

83 msg = 'socket.gethostbyaddr({}) failed: {}'.format(ip, e)

84 logger.error(msg)

85 if exceptions:

86 raise e

87 return ip, country_code, host