Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/websocket/_url.py: 44%
84 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:48 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:48 +0000
1import os
2import socket
3import struct
5from urllib.parse import unquote, urlparse
7"""
8_url.py
9websocket - WebSocket client library for Python
11Copyright 2022 engn33r
13Licensed under the Apache License, Version 2.0 (the "License");
14you may not use this file except in compliance with the License.
15You may obtain a copy of the License at
17 http://www.apache.org/licenses/LICENSE-2.0
19Unless required by applicable law or agreed to in writing, software
20distributed under the License is distributed on an "AS IS" BASIS,
21WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22See the License for the specific language governing permissions and
23limitations under the License.
24"""
26__all__ = ["parse_url", "get_proxy_info"]
29def parse_url(url: str) -> tuple:
30 """
31 parse url and the result is tuple of
32 (hostname, port, resource path and the flag of secure mode)
34 Parameters
35 ----------
36 url: str
37 url string.
38 """
39 if ":" not in url:
40 raise ValueError("url is invalid")
42 scheme, url = url.split(":", 1)
44 parsed = urlparse(url, scheme="http")
45 if parsed.hostname:
46 hostname = parsed.hostname
47 else:
48 raise ValueError("hostname is invalid")
49 port = 0
50 if parsed.port:
51 port = parsed.port
53 is_secure = False
54 if scheme == "ws":
55 if not port:
56 port = 80
57 elif scheme == "wss":
58 is_secure = True
59 if not port:
60 port = 443
61 else:
62 raise ValueError("scheme %s is invalid" % scheme)
64 if parsed.path:
65 resource = parsed.path
66 else:
67 resource = "/"
69 if parsed.query:
70 resource += "?" + parsed.query
72 return hostname, port, resource, is_secure
75DEFAULT_NO_PROXY_HOST = ["localhost", "127.0.0.1"]
78def _is_ip_address(addr: str) -> bool:
79 try:
80 socket.inet_aton(addr)
81 except socket.error:
82 return False
83 else:
84 return True
87def _is_subnet_address(hostname: str) -> bool:
88 try:
89 addr, netmask = hostname.split("/")
90 return _is_ip_address(addr) and 0 <= int(netmask) < 32
91 except ValueError:
92 return False
95def _is_address_in_network(ip: str, net: str) -> bool:
96 ipaddr = struct.unpack('!I', socket.inet_aton(ip))[0]
97 netaddr, netmask = net.split('/')
98 netaddr = struct.unpack('!I', socket.inet_aton(netaddr))[0]
100 netmask = (0xFFFFFFFF << (32 - int(netmask))) & 0xFFFFFFFF
101 return ipaddr & netmask == netaddr
104def _is_no_proxy_host(hostname: str, no_proxy: list) -> bool:
105 if not no_proxy:
106 v = os.environ.get("no_proxy", os.environ.get("NO_PROXY", "")).replace(" ", "")
107 if v:
108 no_proxy = v.split(",")
109 if not no_proxy:
110 no_proxy = DEFAULT_NO_PROXY_HOST
112 if '*' in no_proxy:
113 return True
114 if hostname in no_proxy:
115 return True
116 if _is_ip_address(hostname):
117 return any([_is_address_in_network(hostname, subnet) for subnet in no_proxy if _is_subnet_address(subnet)])
118 for domain in [domain for domain in no_proxy if domain.startswith('.')]:
119 if hostname.endswith(domain):
120 return True
121 return False
124def get_proxy_info(
125 hostname: str, is_secure: bool, proxy_host: str = None, proxy_port: int = 0, proxy_auth: tuple = None,
126 no_proxy: list = None, proxy_type: str = 'http') -> tuple:
127 """
128 Try to retrieve proxy host and port from environment
129 if not provided in options.
130 Result is (proxy_host, proxy_port, proxy_auth).
131 proxy_auth is tuple of username and password
132 of proxy authentication information.
134 Parameters
135 ----------
136 hostname: str
137 Websocket server name.
138 is_secure: bool
139 Is the connection secure? (wss) looks for "https_proxy" in env
140 before falling back to "http_proxy"
141 proxy_host: str
142 http proxy host name.
143 http_proxy_port: str or int
144 http proxy port.
145 http_no_proxy: list
146 Whitelisted host names that don't use the proxy.
147 http_proxy_auth: tuple
148 HTTP proxy auth information. Tuple of username and password. Default is None.
149 proxy_type: str
150 Specify the proxy protocol (http, socks4, socks4a, socks5, socks5h). Default is "http".
151 Use socks4a or socks5h if you want to send DNS requests through the proxy.
152 """
153 if _is_no_proxy_host(hostname, no_proxy):
154 return None, 0, None
156 if proxy_host:
157 port = proxy_port
158 auth = proxy_auth
159 return proxy_host, port, auth
161 env_keys = ["http_proxy"]
162 if is_secure:
163 env_keys.insert(0, "https_proxy")
165 for key in env_keys:
166 value = os.environ.get(key, os.environ.get(key.upper(), "")).replace(" ", "")
167 if value:
168 proxy = urlparse(value)
169 auth = (unquote(proxy.username), unquote(proxy.password)) if proxy.username else None
170 return proxy.hostname, proxy.port, auth
172 return None, 0, None