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