ruyihttp 0.8.1__py3-none-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ruyihttp/__init__.py +98 -0
- ruyihttp/_connect.py +109 -0
- ruyihttp/_firefox.py +267 -0
- ruyihttp/_http2.py +548 -0
- ruyihttp/_http3.py +503 -0
- ruyihttp/_nss.py +378 -0
- ruyihttp/_retry.py +39 -0
- ruyihttp/_socks.py +57 -0
- ruyihttp/_transport.py +1234 -0
- ruyihttp/adapters.py +9 -0
- ruyihttp/api.py +40 -0
- ruyihttp/firefox-profile.json +49 -0
- ruyihttp/firefox-root-constraints.json +3 -0
- ruyihttp/firefox-roots.pem +2991 -0
- ruyihttp/firefox-suffixes.dat +16501 -0
- ruyihttp/freebl3.dll +0 -0
- ruyihttp/licenses/README.md +29 -0
- ruyihttp/licenses/firefox-license.html +6470 -0
- ruyihttp/licenses/firefox-notice.txt +9 -0
- ruyihttp/licenses/mozilla-runtime-data.txt +21 -0
- ruyihttp/licenses/rust-aho-corasick-1.1.0-COPYING.txt +3 -0
- ruyihttp/licenses/rust-aho-corasick-1.1.0-LICENSE-MIT.txt +21 -0
- ruyihttp/licenses/rust-aho-corasick-1.1.0-UNLICENSE.txt +24 -0
- ruyihttp/licenses/rust-base64-0.22.1-LICENSE-APACHE.txt +201 -0
- ruyihttp/licenses/rust-base64-0.22.1-LICENSE-MIT.txt +21 -0
- ruyihttp/licenses/rust-bindgen-0.73.1-LICENSE.txt +29 -0
- ruyihttp/licenses/rust-bitflags-2.10.0-LICENSE-MIT.txt +25 -0
- ruyihttp/licenses/rust-block-buffer-0.10.3-LICENSE-APACHE.txt +201 -0
- ruyihttp/licenses/rust-block-buffer-0.10.3-LICENSE-MIT.txt +25 -0
- ruyihttp/licenses/rust-bytes-1.11.1-LICENSE.txt +25 -0
- ruyihttp/licenses/rust-cexpr-0.6.0-LICENSE-MIT.txt +25 -0
- ruyihttp/licenses/rust-cfg-if-1.0.0-LICENSE-MIT.txt +25 -0
- ruyihttp/licenses/rust-cfg_aliases-0.2.2-LICENSE.txt +9 -0
- ruyihttp/licenses/rust-clang-sys-1.7.0-LICENSE.txt +202 -0
- ruyihttp/licenses/rust-cpufeatures-0.2.8-LICENSE-MIT.txt +25 -0
- ruyihttp/licenses/rust-crypto-common-0.1.6-LICENSE-MIT.txt +25 -0
- ruyihttp/licenses/rust-darling-0.20.10-LICENSE.txt +21 -0
- ruyihttp/licenses/rust-dependencies.json +2052 -0
- ruyihttp/licenses/rust-digest-0.10.7-LICENSE-MIT.txt +25 -0
- ruyihttp/licenses/rust-enum-map-2.7.3-LICENSES-Apache-2.0.txt +73 -0
- ruyihttp/licenses/rust-enum-map-2.7.3-LICENSES-CC0-1.0.txt +121 -0
- ruyihttp/licenses/rust-enum-map-2.7.3-LICENSES-MIT.txt +9 -0
- ruyihttp/licenses/rust-enumset-1.1.2-LICENSE-MIT.txt +26 -0
- ruyihttp/licenses/rust-env_logger-0.10.0-LICENSE-MIT.txt +23 -0
- ruyihttp/licenses/rust-equivalent-1.0.1-LICENSE-MIT.txt +25 -0
- ruyihttp/licenses/rust-fnv-1.0.7-LICENSE-MIT.txt +25 -0
- ruyihttp/licenses/rust-generic-array-0.14.6-LICENSE.txt +21 -0
- ruyihttp/licenses/rust-hashbrown-0.17.1-LICENSE-MIT.txt +25 -0
- ruyihttp/licenses/rust-heck-0.5.0-LICENSE-MIT.txt +25 -0
- ruyihttp/licenses/rust-hex-0.4.3-LICENSE-APACHE.txt +202 -0
- ruyihttp/licenses/rust-hex-0.4.3-LICENSE-MIT.txt +20 -0
- ruyihttp/licenses/rust-http-1.4.0-LICENSE-APACHE.txt +201 -0
- ruyihttp/licenses/rust-http-1.4.0-LICENSE-MIT.txt +25 -0
- ruyihttp/licenses/rust-ident_case-1.0.1-LICENSE.txt +19 -0
- ruyihttp/licenses/rust-indexmap-2.11.4-LICENSE-MIT.txt +25 -0
- ruyihttp/licenses/rust-itoa-1.0.15-LICENSE-APACHE.txt +176 -0
- ruyihttp/licenses/rust-libc-0.2.186-LICENSE-MIT.txt +25 -0
- ruyihttp/licenses/rust-libloading-0.8.6-LICENSE.txt +12 -0
- ruyihttp/licenses/rust-minimal-lexical-0.2.1-LICENSE-APACHE.txt +201 -0
- ruyihttp/licenses/rust-minimal-lexical-0.2.1-LICENSE.md.txt +37 -0
- ruyihttp/licenses/rust-mtu-0.4.1-LICENSE-MIT.txt +25 -0
- ruyihttp/licenses/rust-nom-7.1.3-LICENSE.txt +20 -0
- ruyihttp/licenses/rust-pkcs11-bindings-0.1.8-LICENSE.txt +21 -0
- ruyihttp/licenses/rust-qlog-0.16.0-COPYING.txt +23 -0
- ruyihttp/licenses/rust-quinn-udp-0.6.0-LICENSE-APACHE.txt +201 -0
- ruyihttp/licenses/rust-quinn-udp-0.6.0-LICENSE-MIT.txt +7 -0
- ruyihttp/licenses/rust-regex-syntax-0.8.5-src-unicode_tables-LICENSE-UNICODE.txt +57 -0
- ruyihttp/licenses/rust-rustc-hash-2.1.1-LICENSE-APACHE.txt +176 -0
- ruyihttp/licenses/rust-rustc-hash-2.1.1-LICENSE-MIT.txt +23 -0
- ruyihttp/licenses/rust-serde_spanned-1.0.3-LICENSE-MIT.txt +19 -0
- ruyihttp/licenses/rust-serde_with-3.12.0-LICENSE-MIT.txt +25 -0
- ruyihttp/licenses/rust-sfv-0.14.0-LICENSE-MIT.txt +27 -0
- ruyihttp/licenses/rust-sha1-0.10.5-LICENSE-MIT.txt +27 -0
- ruyihttp/licenses/rust-shlex-2.0.1-LICENSE-APACHE.txt +13 -0
- ruyihttp/licenses/rust-shlex-2.0.1-LICENSE-MIT.txt +21 -0
- ruyihttp/licenses/rust-smallvec-1.15.2-LICENSE-MIT.txt +25 -0
- ruyihttp/licenses/rust-static_assertions-1.1.0-LICENSE-MIT.txt +21 -0
- ruyihttp/licenses/rust-strsim-0.11.1-LICENSE.txt +23 -0
- ruyihttp/licenses/rust-strum-0.27.1-LICENSE.txt +21 -0
- ruyihttp/licenses/rust-typenum-1.16.0-LICENSE-APACHE.txt +201 -0
- ruyihttp/licenses/rust-typenum-1.16.0-LICENSE-MIT.txt +21 -0
- ruyihttp/licenses/rust-typenum-1.16.0-LICENSE.txt +1 -0
- ruyihttp/licenses/rust-unicode-ident-1.0.6-LICENSE-UNICODE.txt +46 -0
- ruyihttp/licenses/rust-version_check-0.9.4-LICENSE-MIT.txt +19 -0
- ruyihttp/licenses/rust-windows-0.62.2-license-apache-2.0.txt +201 -0
- ruyihttp/licenses/rust-windows-0.62.2-license-mit.txt +21 -0
- ruyihttp/licenses/rust-winnow-0.7.13-LICENSE-MIT.txt +18 -0
- ruyihttp/licenses/rust-zeroize-1.8.1-LICENSE-MIT.txt +21 -0
- ruyihttp/licenses/rust-zeroize_derive-1.4.2-LICENSE-MIT.txt +21 -0
- ruyihttp/mozglue.dll +0 -0
- ruyihttp/necko_core.dll +0 -0
- ruyihttp/necko_quic.dll +0 -0
- ruyihttp/nss3.dll +0 -0
- ruyihttp/runtime-manifest.json +16 -0
- ruyihttp/sessions.py +83 -0
- ruyihttp/softokn3.dll +0 -0
- ruyihttp-0.8.1.dist-info/METADATA +273 -0
- ruyihttp-0.8.1.dist-info/RECORD +175 -0
- ruyihttp-0.8.1.dist-info/WHEEL +5 -0
- ruyihttp-0.8.1.dist-info/licenses/LICENSE +373 -0
- ruyihttp-0.8.1.dist-info/licenses/THIRD_PARTY_NOTICES.md +36 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/README.md +29 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/firefox-license.html +6470 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/firefox-notice.txt +9 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/mozilla-runtime-data.txt +21 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-aho-corasick-1.1.0-COPYING.txt +3 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-aho-corasick-1.1.0-LICENSE-MIT.txt +21 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-aho-corasick-1.1.0-UNLICENSE.txt +24 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-base64-0.22.1-LICENSE-APACHE.txt +201 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-base64-0.22.1-LICENSE-MIT.txt +21 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-bindgen-0.73.1-LICENSE.txt +29 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-bitflags-2.10.0-LICENSE-MIT.txt +25 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-block-buffer-0.10.3-LICENSE-APACHE.txt +201 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-block-buffer-0.10.3-LICENSE-MIT.txt +25 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-bytes-1.11.1-LICENSE.txt +25 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-cexpr-0.6.0-LICENSE-MIT.txt +25 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-cfg-if-1.0.0-LICENSE-MIT.txt +25 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-cfg_aliases-0.2.2-LICENSE.txt +9 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-clang-sys-1.7.0-LICENSE.txt +202 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-cpufeatures-0.2.8-LICENSE-MIT.txt +25 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-crypto-common-0.1.6-LICENSE-MIT.txt +25 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-darling-0.20.10-LICENSE.txt +21 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-dependencies.json +2052 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-digest-0.10.7-LICENSE-MIT.txt +25 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-enum-map-2.7.3-LICENSES-Apache-2.0.txt +73 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-enum-map-2.7.3-LICENSES-CC0-1.0.txt +121 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-enum-map-2.7.3-LICENSES-MIT.txt +9 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-enumset-1.1.2-LICENSE-MIT.txt +26 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-env_logger-0.10.0-LICENSE-MIT.txt +23 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-equivalent-1.0.1-LICENSE-MIT.txt +25 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-fnv-1.0.7-LICENSE-MIT.txt +25 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-generic-array-0.14.6-LICENSE.txt +21 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-hashbrown-0.17.1-LICENSE-MIT.txt +25 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-heck-0.5.0-LICENSE-MIT.txt +25 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-hex-0.4.3-LICENSE-APACHE.txt +202 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-hex-0.4.3-LICENSE-MIT.txt +20 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-http-1.4.0-LICENSE-APACHE.txt +201 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-http-1.4.0-LICENSE-MIT.txt +25 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-ident_case-1.0.1-LICENSE.txt +19 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-indexmap-2.11.4-LICENSE-MIT.txt +25 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-itoa-1.0.15-LICENSE-APACHE.txt +176 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-libc-0.2.186-LICENSE-MIT.txt +25 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-libloading-0.8.6-LICENSE.txt +12 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-minimal-lexical-0.2.1-LICENSE-APACHE.txt +201 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-minimal-lexical-0.2.1-LICENSE.md.txt +37 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-mtu-0.4.1-LICENSE-MIT.txt +25 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-nom-7.1.3-LICENSE.txt +20 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-pkcs11-bindings-0.1.8-LICENSE.txt +21 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-qlog-0.16.0-COPYING.txt +23 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-quinn-udp-0.6.0-LICENSE-APACHE.txt +201 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-quinn-udp-0.6.0-LICENSE-MIT.txt +7 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-regex-syntax-0.8.5-src-unicode_tables-LICENSE-UNICODE.txt +57 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-rustc-hash-2.1.1-LICENSE-APACHE.txt +176 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-rustc-hash-2.1.1-LICENSE-MIT.txt +23 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-serde_spanned-1.0.3-LICENSE-MIT.txt +19 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-serde_with-3.12.0-LICENSE-MIT.txt +25 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-sfv-0.14.0-LICENSE-MIT.txt +27 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-sha1-0.10.5-LICENSE-MIT.txt +27 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-shlex-2.0.1-LICENSE-APACHE.txt +13 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-shlex-2.0.1-LICENSE-MIT.txt +21 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-smallvec-1.15.2-LICENSE-MIT.txt +25 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-static_assertions-1.1.0-LICENSE-MIT.txt +21 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-strsim-0.11.1-LICENSE.txt +23 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-strum-0.27.1-LICENSE.txt +21 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-typenum-1.16.0-LICENSE-APACHE.txt +201 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-typenum-1.16.0-LICENSE-MIT.txt +21 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-typenum-1.16.0-LICENSE.txt +1 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-unicode-ident-1.0.6-LICENSE-UNICODE.txt +46 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-version_check-0.9.4-LICENSE-MIT.txt +19 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-windows-0.62.2-license-apache-2.0.txt +201 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-windows-0.62.2-license-mit.txt +21 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-winnow-0.7.13-LICENSE-MIT.txt +18 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-zeroize-1.8.1-LICENSE-MIT.txt +21 -0
- ruyihttp-0.8.1.dist-info/licenses/ruyihttp/licenses/rust-zeroize_derive-1.4.2-LICENSE-MIT.txt +21 -0
- ruyihttp-0.8.1.dist-info/top_level.txt +1 -0
ruyihttp/__init__.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
4
|
+
|
|
5
|
+
"""Requests-style API with standalone NSS and Firefox transport defaults."""
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
from requests import (
|
|
10
|
+
ConnectionError,
|
|
11
|
+
ConnectTimeout,
|
|
12
|
+
FileModeWarning,
|
|
13
|
+
HTTPError,
|
|
14
|
+
JSONDecodeError,
|
|
15
|
+
PreparedRequest,
|
|
16
|
+
ReadTimeout,
|
|
17
|
+
Request,
|
|
18
|
+
RequestException,
|
|
19
|
+
Response,
|
|
20
|
+
Timeout,
|
|
21
|
+
TooManyRedirects,
|
|
22
|
+
URLRequired,
|
|
23
|
+
auth,
|
|
24
|
+
certs,
|
|
25
|
+
codes,
|
|
26
|
+
cookies,
|
|
27
|
+
exceptions,
|
|
28
|
+
hooks,
|
|
29
|
+
models,
|
|
30
|
+
packages,
|
|
31
|
+
status_codes,
|
|
32
|
+
structures,
|
|
33
|
+
utils,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
from . import adapters, api, sessions
|
|
37
|
+
from ._transport import HeaderMap
|
|
38
|
+
from .api import delete, get, head, options, patch, post, put, request
|
|
39
|
+
from .sessions import Session, session
|
|
40
|
+
|
|
41
|
+
__version__ = "0.8.1"
|
|
42
|
+
NeckoError = RequestException
|
|
43
|
+
|
|
44
|
+
for _module in (
|
|
45
|
+
auth,
|
|
46
|
+
certs,
|
|
47
|
+
cookies,
|
|
48
|
+
exceptions,
|
|
49
|
+
hooks,
|
|
50
|
+
models,
|
|
51
|
+
packages,
|
|
52
|
+
status_codes,
|
|
53
|
+
structures,
|
|
54
|
+
utils,
|
|
55
|
+
):
|
|
56
|
+
sys.modules[f"{__name__}.{_module.__name__.rsplit('.', 1)[-1]}"] = _module
|
|
57
|
+
|
|
58
|
+
__all__ = [
|
|
59
|
+
"Session",
|
|
60
|
+
"session",
|
|
61
|
+
"Request",
|
|
62
|
+
"PreparedRequest",
|
|
63
|
+
"Response",
|
|
64
|
+
"request",
|
|
65
|
+
"get",
|
|
66
|
+
"post",
|
|
67
|
+
"put",
|
|
68
|
+
"patch",
|
|
69
|
+
"delete",
|
|
70
|
+
"head",
|
|
71
|
+
"options",
|
|
72
|
+
"codes",
|
|
73
|
+
"RequestException",
|
|
74
|
+
"ConnectionError",
|
|
75
|
+
"ConnectTimeout",
|
|
76
|
+
"HTTPError",
|
|
77
|
+
"ReadTimeout",
|
|
78
|
+
"Timeout",
|
|
79
|
+
"TooManyRedirects",
|
|
80
|
+
"URLRequired",
|
|
81
|
+
"JSONDecodeError",
|
|
82
|
+
"FileModeWarning",
|
|
83
|
+
"NeckoError",
|
|
84
|
+
"HeaderMap",
|
|
85
|
+
"exceptions",
|
|
86
|
+
"auth",
|
|
87
|
+
"cookies",
|
|
88
|
+
"adapters",
|
|
89
|
+
"api",
|
|
90
|
+
"sessions",
|
|
91
|
+
"models",
|
|
92
|
+
"structures",
|
|
93
|
+
"utils",
|
|
94
|
+
"certs",
|
|
95
|
+
"hooks",
|
|
96
|
+
"packages",
|
|
97
|
+
"status_codes",
|
|
98
|
+
]
|
ruyihttp/_connect.py
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
4
|
+
|
|
5
|
+
import threading
|
|
6
|
+
import time
|
|
7
|
+
|
|
8
|
+
from requests.exceptions import ConnectTimeout
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Attempt:
|
|
12
|
+
def __init__(self, deadline):
|
|
13
|
+
self.deadline = deadline
|
|
14
|
+
self.cancelled = threading.Event()
|
|
15
|
+
self._lock = threading.Lock()
|
|
16
|
+
self._interrupt = None
|
|
17
|
+
|
|
18
|
+
def remaining(self):
|
|
19
|
+
if self.cancelled.is_set():
|
|
20
|
+
raise OSError("Connection attempt cancelled")
|
|
21
|
+
if self.deadline is None:
|
|
22
|
+
return None
|
|
23
|
+
remaining = self.deadline - time.monotonic()
|
|
24
|
+
if remaining <= 0:
|
|
25
|
+
raise ConnectTimeout("Connection deadline elapsed")
|
|
26
|
+
return remaining
|
|
27
|
+
|
|
28
|
+
def set_interrupt(self, interrupt):
|
|
29
|
+
with self._lock:
|
|
30
|
+
self._interrupt = interrupt
|
|
31
|
+
if self.cancelled.is_set():
|
|
32
|
+
interrupt()
|
|
33
|
+
|
|
34
|
+
def detach(self):
|
|
35
|
+
with self._lock:
|
|
36
|
+
self._interrupt = None
|
|
37
|
+
|
|
38
|
+
def cancel(self):
|
|
39
|
+
with self._lock:
|
|
40
|
+
self.cancelled.set()
|
|
41
|
+
if self._interrupt is not None:
|
|
42
|
+
self._interrupt()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def race(primary, backup, timeout, delay=0.1):
|
|
46
|
+
started = time.monotonic()
|
|
47
|
+
deadline = None if timeout is None else started + timeout
|
|
48
|
+
attempts = [Attempt(deadline), Attempt(deadline)]
|
|
49
|
+
condition = threading.Condition()
|
|
50
|
+
completed = {}
|
|
51
|
+
winner = []
|
|
52
|
+
stopped = False
|
|
53
|
+
|
|
54
|
+
def run(index, factory):
|
|
55
|
+
connection = None
|
|
56
|
+
error = None
|
|
57
|
+
try:
|
|
58
|
+
if index:
|
|
59
|
+
with condition:
|
|
60
|
+
while not stopped and not winner and 0 not in completed:
|
|
61
|
+
remaining = started + delay - time.monotonic()
|
|
62
|
+
if remaining <= 0:
|
|
63
|
+
break
|
|
64
|
+
condition.wait(remaining)
|
|
65
|
+
if stopped or winner:
|
|
66
|
+
return
|
|
67
|
+
attempts[index].remaining()
|
|
68
|
+
connection = factory(attempts[index])
|
|
69
|
+
except Exception as caught:
|
|
70
|
+
error = caught
|
|
71
|
+
finally:
|
|
72
|
+
attempts[index].detach()
|
|
73
|
+
with condition:
|
|
74
|
+
completed[index] = error
|
|
75
|
+
if connection is not None and not stopped and not winner:
|
|
76
|
+
winner.append(connection)
|
|
77
|
+
connection = None
|
|
78
|
+
condition.notify_all()
|
|
79
|
+
if connection is not None:
|
|
80
|
+
connection.close()
|
|
81
|
+
|
|
82
|
+
threads = [
|
|
83
|
+
threading.Thread(
|
|
84
|
+
target=run, args=(0, primary), name="necko-connect-h3", daemon=True
|
|
85
|
+
),
|
|
86
|
+
threading.Thread(
|
|
87
|
+
target=run, args=(1, backup), name="necko-connect-tcp", daemon=True
|
|
88
|
+
),
|
|
89
|
+
]
|
|
90
|
+
for thread in threads:
|
|
91
|
+
thread.start()
|
|
92
|
+
try:
|
|
93
|
+
with condition:
|
|
94
|
+
while not winner and len(completed) != 2:
|
|
95
|
+
remaining = None if deadline is None else deadline - time.monotonic()
|
|
96
|
+
if remaining is not None and remaining <= 0:
|
|
97
|
+
raise ConnectTimeout("Connection deadline elapsed")
|
|
98
|
+
condition.wait(remaining)
|
|
99
|
+
if winner:
|
|
100
|
+
return winner[0]
|
|
101
|
+
raise completed.get(1) or completed.get(0) or OSError("Connection failed")
|
|
102
|
+
finally:
|
|
103
|
+
with condition:
|
|
104
|
+
stopped = True
|
|
105
|
+
condition.notify_all()
|
|
106
|
+
for attempt in attempts:
|
|
107
|
+
attempt.cancel()
|
|
108
|
+
for thread in threads:
|
|
109
|
+
thread.join(timeout=0.1)
|
ruyihttp/_firefox.py
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
4
|
+
|
|
5
|
+
import copy
|
|
6
|
+
import ipaddress
|
|
7
|
+
import os
|
|
8
|
+
from http.cookiejar import DefaultCookiePolicy
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from urllib.parse import urlsplit, urlunsplit
|
|
11
|
+
|
|
12
|
+
from publicsuffixlist import PublicSuffixList
|
|
13
|
+
from requests.cookies import RequestsCookieJar, get_cookie_header
|
|
14
|
+
from requests.structures import CaseInsensitiveDict
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def data_path(name, directory=None):
|
|
18
|
+
runtime = os.environ.get("NECKO_CORE_LIBRARY")
|
|
19
|
+
if directory is not None or runtime:
|
|
20
|
+
base = (
|
|
21
|
+
Path(directory).expanduser()
|
|
22
|
+
if directory is not None
|
|
23
|
+
else Path(runtime).expanduser().resolve().parent
|
|
24
|
+
)
|
|
25
|
+
for candidate in (base / name, base / "ruyihttp" / name, base / "necko" / name):
|
|
26
|
+
if candidate.is_file():
|
|
27
|
+
return candidate
|
|
28
|
+
local = Path(__file__).with_name(name)
|
|
29
|
+
return local
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
_suffix_file = data_path("firefox-suffixes.dat")
|
|
33
|
+
if _suffix_file.is_file():
|
|
34
|
+
with _suffix_file.open(encoding="utf-8") as _suffix_data:
|
|
35
|
+
_SUFFIXES = PublicSuffixList(_suffix_data)
|
|
36
|
+
else:
|
|
37
|
+
_SUFFIXES = PublicSuffixList()
|
|
38
|
+
USER_AGENT = (
|
|
39
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:158.0) Gecko/20100101 Firefox/158.0"
|
|
40
|
+
)
|
|
41
|
+
ACCEPT = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def origin(url):
|
|
45
|
+
parsed = urlsplit(url)
|
|
46
|
+
host = parsed.hostname or ""
|
|
47
|
+
if ":" in host:
|
|
48
|
+
host = f"[{host}]"
|
|
49
|
+
port = parsed.port
|
|
50
|
+
if port and port != (443 if parsed.scheme == "https" else 80):
|
|
51
|
+
host += f":{port}"
|
|
52
|
+
return f"{parsed.scheme}://{host}"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def site(url):
|
|
56
|
+
parsed = urlsplit(url)
|
|
57
|
+
host = parsed.hostname or ""
|
|
58
|
+
return parsed.scheme, _SUFFIXES.privatesuffix(host) or host
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def relation(source, target):
|
|
62
|
+
if not source:
|
|
63
|
+
return "none"
|
|
64
|
+
if origin(source) == origin(target):
|
|
65
|
+
return "same-origin"
|
|
66
|
+
return "same-site" if site(source) == site(target) else "cross-site"
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def trustworthy(url):
|
|
70
|
+
parsed = urlsplit(url)
|
|
71
|
+
if parsed.scheme == "https" or parsed.hostname == "localhost":
|
|
72
|
+
return True
|
|
73
|
+
try:
|
|
74
|
+
return ipaddress.ip_address(parsed.hostname).is_loopback
|
|
75
|
+
except ValueError:
|
|
76
|
+
return False
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def default_headers(context):
|
|
80
|
+
return CaseInsensitiveDict([
|
|
81
|
+
("User-Agent", USER_AGENT),
|
|
82
|
+
("Accept", ACCEPT if context == "navigation" else "*/*"),
|
|
83
|
+
("Accept-Language", "en-US,en;q=0.9"),
|
|
84
|
+
("Accept-Encoding", "gzip, deflate, br, zstd"),
|
|
85
|
+
("Connection", "keep-alive"),
|
|
86
|
+
])
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def apply_headers(request, context, document_url, user_activation=True):
|
|
90
|
+
headers = request.headers
|
|
91
|
+
if not hasattr(request, "_firefox_explicit"):
|
|
92
|
+
request._firefox_explicit = set(headers)
|
|
93
|
+
explicit = request._firefox_explicit
|
|
94
|
+
if headers.get("Accept-Encoding") == "gzip, deflate, br, zstd" and not trustworthy(
|
|
95
|
+
request.url
|
|
96
|
+
):
|
|
97
|
+
headers["Accept-Encoding"] = "gzip, deflate"
|
|
98
|
+
reference = document_url or headers.get("Referer")
|
|
99
|
+
relationship = relation(reference, request.url)
|
|
100
|
+
computed = {}
|
|
101
|
+
if context == "navigation":
|
|
102
|
+
computed["Upgrade-Insecure-Requests"] = "1"
|
|
103
|
+
if trustworthy(request.url):
|
|
104
|
+
computed.update({
|
|
105
|
+
"Sec-Fetch-Dest": "document" if context == "navigation" else "empty",
|
|
106
|
+
"Sec-Fetch-Mode": "navigate" if context == "navigation" else "cors",
|
|
107
|
+
"Sec-Fetch-Site": relationship,
|
|
108
|
+
})
|
|
109
|
+
if context == "navigation" and user_activation:
|
|
110
|
+
computed["Sec-Fetch-User"] = "?1"
|
|
111
|
+
computed["Priority"] = "u=0, i" if context == "navigation" else "u=4"
|
|
112
|
+
if reference:
|
|
113
|
+
secure_downgrade = (
|
|
114
|
+
urlsplit(reference).scheme == "https"
|
|
115
|
+
and urlsplit(request.url).scheme != "https"
|
|
116
|
+
)
|
|
117
|
+
if not secure_downgrade:
|
|
118
|
+
parsed = urlsplit(reference)
|
|
119
|
+
computed["Referer"] = (
|
|
120
|
+
urlunsplit((
|
|
121
|
+
parsed.scheme,
|
|
122
|
+
urlsplit(origin(reference)).netloc,
|
|
123
|
+
parsed.path or "/",
|
|
124
|
+
parsed.query,
|
|
125
|
+
"",
|
|
126
|
+
))
|
|
127
|
+
if relationship == "same-origin"
|
|
128
|
+
else origin(reference) + "/"
|
|
129
|
+
)
|
|
130
|
+
if (
|
|
131
|
+
request.method not in ("GET", "HEAD")
|
|
132
|
+
or context == "fetch"
|
|
133
|
+
and relationship != "same-origin"
|
|
134
|
+
):
|
|
135
|
+
computed["Origin"] = origin(reference)
|
|
136
|
+
elif request.method not in ("GET", "HEAD"):
|
|
137
|
+
computed["Origin"] = "null"
|
|
138
|
+
for name in (
|
|
139
|
+
"Sec-Fetch-Dest",
|
|
140
|
+
"Sec-Fetch-Mode",
|
|
141
|
+
"Sec-Fetch-Site",
|
|
142
|
+
"Sec-Fetch-User",
|
|
143
|
+
"Upgrade-Insecure-Requests",
|
|
144
|
+
"Priority",
|
|
145
|
+
"Referer",
|
|
146
|
+
"Origin",
|
|
147
|
+
):
|
|
148
|
+
if name not in explicit:
|
|
149
|
+
headers.pop(name, None)
|
|
150
|
+
if name in computed:
|
|
151
|
+
headers[name] = computed[name]
|
|
152
|
+
request._firefox_context = context
|
|
153
|
+
request._firefox_document = reference
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def wire_headers(headers, http2):
|
|
157
|
+
ordered = CaseInsensitiveDict()
|
|
158
|
+
first = (
|
|
159
|
+
"Host",
|
|
160
|
+
"User-Agent",
|
|
161
|
+
"Accept",
|
|
162
|
+
"Accept-Language",
|
|
163
|
+
"Accept-Encoding",
|
|
164
|
+
"Referer",
|
|
165
|
+
"Origin",
|
|
166
|
+
"Content-Type",
|
|
167
|
+
"Content-Length",
|
|
168
|
+
"Connection",
|
|
169
|
+
"Cookie",
|
|
170
|
+
"Alt-Used",
|
|
171
|
+
"Upgrade-Insecure-Requests",
|
|
172
|
+
"Sec-Fetch-Dest",
|
|
173
|
+
"Sec-Fetch-Mode",
|
|
174
|
+
"Sec-Fetch-Site",
|
|
175
|
+
"Sec-Fetch-User",
|
|
176
|
+
"Priority",
|
|
177
|
+
)
|
|
178
|
+
for name in first:
|
|
179
|
+
if name in headers:
|
|
180
|
+
ordered[name] = headers[name]
|
|
181
|
+
for name, value in headers.items():
|
|
182
|
+
if name not in ordered:
|
|
183
|
+
ordered[name] = value
|
|
184
|
+
if http2:
|
|
185
|
+
ordered["TE"] = "trailers"
|
|
186
|
+
return ordered
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
class CookiePolicy(DefaultCookiePolicy):
|
|
190
|
+
def __init__(self, context="navigation", document_url=None):
|
|
191
|
+
super().__init__(strict_ns_domain=self.DomainStrictNonDomain)
|
|
192
|
+
self.context = context
|
|
193
|
+
self.document_url = document_url
|
|
194
|
+
|
|
195
|
+
def set_ok(self, cookie, request):
|
|
196
|
+
if not super().set_ok(cookie, request):
|
|
197
|
+
return False
|
|
198
|
+
domain = cookie.domain.lstrip(".")
|
|
199
|
+
if cookie.domain_specified and _SUFFIXES.publicsuffix(domain) == domain:
|
|
200
|
+
return False
|
|
201
|
+
if cookie.secure and not trustworthy(request.get_full_url()):
|
|
202
|
+
return False
|
|
203
|
+
if cookie.name.startswith("__Secure-") and not cookie.secure:
|
|
204
|
+
return False
|
|
205
|
+
if cookie.name.startswith("__Host-") and (
|
|
206
|
+
not cookie.secure or cookie.domain_specified or cookie.path != "/"
|
|
207
|
+
):
|
|
208
|
+
return False
|
|
209
|
+
same_site = self.same_site(cookie)
|
|
210
|
+
return same_site != "none" or cookie.secure
|
|
211
|
+
|
|
212
|
+
def return_ok(self, cookie, request):
|
|
213
|
+
if not super().return_ok(cookie, request):
|
|
214
|
+
return False
|
|
215
|
+
if not self.document_url or site(self.document_url) == site(
|
|
216
|
+
request.get_full_url()
|
|
217
|
+
):
|
|
218
|
+
return True
|
|
219
|
+
same_site = self.same_site(cookie)
|
|
220
|
+
if same_site == "none":
|
|
221
|
+
return cookie.secure
|
|
222
|
+
return (
|
|
223
|
+
same_site != "strict"
|
|
224
|
+
and self.context == "navigation"
|
|
225
|
+
and (
|
|
226
|
+
request.get_method()
|
|
227
|
+
if hasattr(request, "get_method")
|
|
228
|
+
else request._r.method
|
|
229
|
+
)
|
|
230
|
+
in ("GET", "HEAD", "OPTIONS", "TRACE")
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
@staticmethod
|
|
234
|
+
def same_site(cookie):
|
|
235
|
+
return str(
|
|
236
|
+
next(
|
|
237
|
+
(
|
|
238
|
+
value
|
|
239
|
+
for key, value in cookie._rest.items()
|
|
240
|
+
if key.lower() == "samesite"
|
|
241
|
+
),
|
|
242
|
+
"Lax",
|
|
243
|
+
)
|
|
244
|
+
).lower()
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def apply_cookies(request, context, document_url):
|
|
248
|
+
jar = getattr(request, "_cookies", None)
|
|
249
|
+
if jar is not None:
|
|
250
|
+
for cookie in list(jar):
|
|
251
|
+
if not cookie.domain:
|
|
252
|
+
bound = copy.copy(cookie)
|
|
253
|
+
bound.domain = urlsplit(request.url).hostname
|
|
254
|
+
if "." not in bound.domain and ":" not in bound.domain:
|
|
255
|
+
bound.domain += ".local"
|
|
256
|
+
jar.clear(cookie.domain, cookie.path, cookie.name)
|
|
257
|
+
jar.set_cookie(bound)
|
|
258
|
+
jar.set_policy(CookiePolicy(context, document_url))
|
|
259
|
+
if getattr(request, "_firefox_cookie_header", False):
|
|
260
|
+
request.headers.pop("Cookie", None)
|
|
261
|
+
value = get_cookie_header(jar, request)
|
|
262
|
+
if value:
|
|
263
|
+
request.headers["Cookie"] = value
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def cookie_jar(context, document_url):
|
|
267
|
+
return RequestsCookieJar(policy=CookiePolicy(context, document_url))
|