_url.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. """
  2. """
  3. """
  4. websocket - WebSocket client library for Python
  5. Copyright (C) 2010 Hiroki Ohtani(liris)
  6. This library is free software; you can redistribute it and/or
  7. modify it under the terms of the GNU Lesser General Public
  8. License as published by the Free Software Foundation; either
  9. version 2.1 of the License, or (at your option) any later version.
  10. This library is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. Lesser General Public License for more details.
  14. You should have received a copy of the GNU Lesser General Public
  15. License along with this library; if not, write to the Free Software
  16. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17. """
  18. import os
  19. import socket
  20. import struct
  21. from six.moves.urllib.parse import urlparse
  22. __all__ = ["parse_url", "get_proxy_info"]
  23. def parse_url(url):
  24. """
  25. parse url and the result is tuple of
  26. (hostname, port, resource path and the flag of secure mode)
  27. Parameters
  28. ----------
  29. url: str
  30. url string.
  31. """
  32. if ":" not in url:
  33. raise ValueError("url is invalid")
  34. scheme, url = url.split(":", 1)
  35. parsed = urlparse(url, scheme="http")
  36. if parsed.hostname:
  37. hostname = parsed.hostname
  38. else:
  39. raise ValueError("hostname is invalid")
  40. port = 0
  41. if parsed.port:
  42. port = parsed.port
  43. is_secure = False
  44. if scheme == "ws":
  45. if not port:
  46. port = 80
  47. elif scheme == "wss":
  48. is_secure = True
  49. if not port:
  50. port = 443
  51. else:
  52. raise ValueError("scheme %s is invalid" % scheme)
  53. if parsed.path:
  54. resource = parsed.path
  55. else:
  56. resource = "/"
  57. if parsed.query:
  58. resource += "?" + parsed.query
  59. return hostname, port, resource, is_secure
  60. DEFAULT_NO_PROXY_HOST = ["localhost", "127.0.0.1"]
  61. def _is_ip_address(addr):
  62. try:
  63. socket.inet_aton(addr)
  64. except socket.error:
  65. return False
  66. else:
  67. return True
  68. def _is_subnet_address(hostname):
  69. try:
  70. addr, netmask = hostname.split("/")
  71. return _is_ip_address(addr) and 0 <= int(netmask) < 32
  72. except ValueError:
  73. return False
  74. def _is_address_in_network(ip, net):
  75. ipaddr = struct.unpack('!I', socket.inet_aton(ip))[0]
  76. netaddr, netmask = net.split('/')
  77. netaddr = struct.unpack('!I', socket.inet_aton(netaddr))[0]
  78. netmask = (0xFFFFFFFF << (32 - int(netmask))) & 0xFFFFFFFF
  79. return ipaddr & netmask == netaddr
  80. def _is_no_proxy_host(hostname, no_proxy):
  81. if not no_proxy:
  82. v = os.environ.get("no_proxy", "").replace(" ", "")
  83. if v:
  84. no_proxy = v.split(",")
  85. if not no_proxy:
  86. no_proxy = DEFAULT_NO_PROXY_HOST
  87. if '*' in no_proxy:
  88. return True
  89. if hostname in no_proxy:
  90. return True
  91. if _is_ip_address(hostname):
  92. return any([_is_address_in_network(hostname, subnet) for subnet in no_proxy if _is_subnet_address(subnet)])
  93. for domain in [domain for domain in no_proxy if domain.startswith('.')]:
  94. if hostname.endswith(domain):
  95. return True
  96. return False
  97. def get_proxy_info(
  98. hostname, is_secure, proxy_host=None, proxy_port=0, proxy_auth=None,
  99. no_proxy=None, proxy_type='http'):
  100. """
  101. Try to retrieve proxy host and port from environment
  102. if not provided in options.
  103. Result is (proxy_host, proxy_port, proxy_auth).
  104. proxy_auth is tuple of username and password
  105. of proxy authentication information.
  106. Parameters
  107. ----------
  108. hostname: <type>
  109. websocket server name.
  110. is_secure: <type>
  111. is the connection secure? (wss) looks for "https_proxy" in env
  112. before falling back to "http_proxy"
  113. options: <type>
  114. - http_proxy_host: <type>
  115. http proxy host name.
  116. - http_proxy_port: <type>
  117. http proxy port.
  118. - http_no_proxy: <type>
  119. host names, which doesn't use proxy.
  120. - http_proxy_auth: <type>
  121. http proxy auth information. tuple of username and password. default is None
  122. - proxy_type: <type>
  123. if set to "socks5" PySocks wrapper will be used in place of a http proxy. default is "http"
  124. """
  125. if _is_no_proxy_host(hostname, no_proxy):
  126. return None, 0, None
  127. if proxy_host:
  128. port = proxy_port
  129. auth = proxy_auth
  130. return proxy_host, port, auth
  131. env_keys = ["http_proxy"]
  132. if is_secure:
  133. env_keys.insert(0, "https_proxy")
  134. for key in env_keys:
  135. value = os.environ.get(key, None)
  136. if value:
  137. proxy = urlparse(value)
  138. auth = (proxy.username, proxy.password) if proxy.username else None
  139. return proxy.hostname, proxy.port, auth
  140. return None, 0, None