2 from typing
import AnyStr
, Dict
, List
, TypeVar
4 from .logger
import get_logger
10 def isomorphic_decode(s
: AnyStr
) -> str:
11 """Decodes a binary string into a text string using iso-8859-1.
13 Returns `str`. The function is a no-op if the argument already has a text
14 type. iso-8859-1 is chosen because it is an 8-bit encoding whose code
15 points range from 0x0 to 0xFF and the values are the same as the binary
16 representations, so any binary string can be decoded into and encoded from
17 iso-8859-1 without any errors or data loss. Python 3 also uses iso-8859-1
18 (or latin-1) extensively in http:
19 https://github.com/python/cpython/blob/273fc220b25933e443c82af6888eb1871d032fb8/Lib/http/client.py#L213
21 if isinstance(s
, str):
24 if isinstance(s
, bytes
):
25 return s
.decode("iso-8859-1")
27 raise TypeError("Unexpected value (expecting string-like): %r" % s
)
30 def isomorphic_encode(s
: AnyStr
) -> bytes
:
31 """Encodes a text-type string into binary data using iso-8859-1.
33 Returns `bytes`. The function is a no-op if the argument already has a
34 binary type. This is the counterpart of isomorphic_decode.
36 if isinstance(s
, bytes
):
39 if isinstance(s
, str):
40 return s
.encode("iso-8859-1")
42 raise TypeError("Unexpected value (expecting string-like): %r" % s
)
45 def invert_dict(dict: Dict
[KT
, List
[VT
]]) -> Dict
[VT
, KT
]:
47 for key
, values
in dict.items():
55 class HTTPException(Exception):
56 def __init__(self
, code
: int, message
: str = ""):
58 self
.message
= message
61 def _open_socket(host
: str, port
: int) -> socket
.socket
:
62 sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
64 sock
.setsockopt(socket
.SOL_SOCKET
, socket
.SO_REUSEADDR
, 1)
65 sock
.bind((host
, port
))
70 def is_bad_port(port
: int) -> bool:
72 Bad port as per https://fetch.spec.whatwg.org/#port-blocking
109 135, # loc-srv / epmap
116 427, # afp (alternate)
117 465, # smtp (alternate)
131 587, # smtp (outgoing)
148 6665, # irc (alternate)
149 6666, # irc (alternate)
150 6667, # irc (default)
151 6668, # irc (alternate)
152 6669, # irc (alternate)
158 def get_port(host
: str = '') -> int:
159 host
= host
or '127.0.0.1'
162 free_socket
= _open_socket(host
, 0)
163 port
= free_socket
.getsockname()[1]
165 if not is_bad_port(port
):
169 def http2_compatible() -> bool:
170 # The HTTP/2.0 server requires OpenSSL 1.0.2+.
172 # For systems using other SSL libraries (e.g. LibreSSL), we assume they
173 # have the necessary support.
175 if not ssl
.OPENSSL_VERSION
.startswith("OpenSSL"):
176 logger
= get_logger()
178 'Skipping HTTP/2.0 compatibility check as system is not using '
179 'OpenSSL (found: %s)' % ssl
.OPENSSL_VERSION
)
182 # Note that OpenSSL's versioning scheme differs between 1.1.1 and
183 # earlier and 3.0.0. ssl.OPENSSL_VERSION_INFO returns a
184 # (major, minor, 0, patch, 0)
185 # tuple with OpenSSL 3.0.0 and later, and a
186 # (major, minor, fix, patch, status)
187 # tuple for older releases.
188 # Semantically, "patch" in 3.0.0+ is similar to "fix" in previous versions.
190 # What we do in the check below is allow OpenSSL 3.x.y+, 1.1.x+ and 1.0.2+.
191 ssl_v
= ssl
.OPENSSL_VERSION_INFO
192 return (ssl_v
[0] > 1 or
195 (ssl_v
[1] == 0 and ssl_v
[2] >= 2))))