python-tls-client 1.0.3__tar.gz → 1.0.4__tar.gz
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.
- python_tls_client-1.0.4/PKG-INFO +364 -0
- python_tls_client-1.0.4/README.md +333 -0
- python_tls_client-1.0.4/python_tls_client.egg-info/PKG-INFO +364 -0
- {python_tls_client-1.0.3 → python_tls_client-1.0.4}/python_tls_client.egg-info/SOURCES.txt +2 -0
- python_tls_client-1.0.4/tests/test_unit_cookies.py +65 -0
- python_tls_client-1.0.4/tests/test_unit_sessions.py +247 -0
- {python_tls_client-1.0.3 → python_tls_client-1.0.4}/tls_client/__init__.py +8 -1
- {python_tls_client-1.0.3 → python_tls_client-1.0.4}/tls_client/__version__.py +1 -1
- {python_tls_client-1.0.3 → python_tls_client-1.0.4}/tls_client/cffi.py +13 -1
- python_tls_client-1.0.4/tls_client/dependencies/tls-client-32.dll +0 -0
- python_tls_client-1.0.4/tls_client/dependencies/tls-client-64.dll +0 -0
- python_tls_client-1.0.3/tls_client/dependencies/tls-client-64.dll → python_tls_client-1.0.4/tls_client/dependencies/tls-client-amd64.so +0 -0
- python_tls_client-1.0.3/tls_client/dependencies/tls-client-x86.so → python_tls_client-1.0.4/tls_client/dependencies/tls-client-arm64.dylib +0 -0
- python_tls_client-1.0.3/tls_client/dependencies/tls-client-32.dll → python_tls_client-1.0.4/tls_client/dependencies/tls-client-arm64.so +0 -0
- python_tls_client-1.0.3/tls_client/dependencies/tls-client-amd64.so → python_tls_client-1.0.4/tls_client/dependencies/tls-client-x86.dylib +0 -0
- python_tls_client-1.0.4/tls_client/dependencies/tls-client-x86.so +0 -0
- python_tls_client-1.0.4/tls_client/exceptions.py +51 -0
- {python_tls_client-1.0.3 → python_tls_client-1.0.4}/tls_client/sessions.py +65 -10
- {python_tls_client-1.0.3 → python_tls_client-1.0.4}/tls_client/settings.py +42 -3
- python_tls_client-1.0.3/PKG-INFO +0 -195
- python_tls_client-1.0.3/README.md +0 -164
- python_tls_client-1.0.3/python_tls_client.egg-info/PKG-INFO +0 -195
- python_tls_client-1.0.3/tls_client/dependencies/tls-client-arm64.dylib +0 -0
- python_tls_client-1.0.3/tls_client/dependencies/tls-client-arm64.so +0 -0
- python_tls_client-1.0.3/tls_client/dependencies/tls-client-x86.dylib +0 -0
- python_tls_client-1.0.3/tls_client/exceptions.py +0 -3
- {python_tls_client-1.0.3 → python_tls_client-1.0.4}/LICENSE +0 -0
- {python_tls_client-1.0.3 → python_tls_client-1.0.4}/MANIFEST.in +0 -0
- {python_tls_client-1.0.3 → python_tls_client-1.0.4}/pyproject.toml +0 -0
- {python_tls_client-1.0.3 → python_tls_client-1.0.4}/python_tls_client.egg-info/dependency_links.txt +0 -0
- {python_tls_client-1.0.3 → python_tls_client-1.0.4}/python_tls_client.egg-info/requires.txt +0 -0
- {python_tls_client-1.0.3 → python_tls_client-1.0.4}/python_tls_client.egg-info/top_level.txt +0 -0
- {python_tls_client-1.0.3 → python_tls_client-1.0.4}/requirements.txt +0 -0
- {python_tls_client-1.0.3 → python_tls_client-1.0.4}/setup.cfg +0 -0
- {python_tls_client-1.0.3 → python_tls_client-1.0.4}/setup.py +0 -0
- {python_tls_client-1.0.3 → python_tls_client-1.0.4}/tests/test_binary_response.py +0 -0
- {python_tls_client-1.0.3 → python_tls_client-1.0.4}/tls_client/cookies.py +0 -0
- {python_tls_client-1.0.3 → python_tls_client-1.0.4}/tls_client/dependencies/__init__.py +0 -0
- {python_tls_client-1.0.3 → python_tls_client-1.0.4}/tls_client/response.py +0 -0
- {python_tls_client-1.0.3 → python_tls_client-1.0.4}/tls_client/structures.py +0 -0
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: python-tls-client
|
|
3
|
+
Version: 1.0.4
|
|
4
|
+
Summary: Advanced Python HTTP Client with TLS fingerprint spoofing (fork with binary-response fix and certificate pinning)
|
|
5
|
+
Author: Emanuele Scarlata
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Fenix46/Python-Tls-Client
|
|
8
|
+
Project-URL: Source, https://github.com/Fenix46/Python-Tls-Client
|
|
9
|
+
Project-URL: Original, https://github.com/FlorianREGAZ/Python-Tls-Client
|
|
10
|
+
Classifier: Environment :: Web Environment
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Natural Language :: English
|
|
13
|
+
Classifier: Operating System :: Unix
|
|
14
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
15
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
16
|
+
Classifier: Programming Language :: Python
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
24
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
25
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
26
|
+
Requires-Python: >=3.8
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Requires-Dist: typing-extensions
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
# Python-TLS-Client
|
|
33
|
+
|
|
34
|
+
Advanced Python HTTP client with TLS fingerprint spoofing, built on
|
|
35
|
+
[bogdanfinn/tls-client](https://github.com/bogdanfinn/tls-client) and inspired
|
|
36
|
+
by [requests](https://github.com/psf/requests).
|
|
37
|
+
|
|
38
|
+
> **Fork notice:** this is a fork of
|
|
39
|
+
> [Python-Tls-Client](https://github.com/FlorianREGAZ/Python-Tls-Client) by
|
|
40
|
+
> Florian Zager, published on PyPI under a different name
|
|
41
|
+
> (`python-tls-client`) because the original name `tls-client` is already
|
|
42
|
+
> taken by an unrelated project. The importable module is unchanged:
|
|
43
|
+
> `import tls_client`.
|
|
44
|
+
>
|
|
45
|
+
> Changes on top of the original:
|
|
46
|
+
> - Fixed corruption of binary response bodies (protobuf, images, ...) — see [Changelog](#changelog) 1.0.2 / 1.0.3
|
|
47
|
+
> - Certificate pinning — see [Changelog](#changelog) 1.0.1
|
|
48
|
+
> - Typed exceptions, safer session lifecycle, clearer native-library load errors — see [Changelog](#changelog) 1.0.4
|
|
49
|
+
> - Native binaries updated to `bogdanfinn/tls-client` v1.16.0, fixing a musl/glibc mismatch on Linux (Chrome 150/152, Firefox 147/148, Brave 146, HTTP/3 over SOCKS5, session ticket control, ...) — see [Changelog](#changelog) 1.0.4
|
|
50
|
+
|
|
51
|
+
## Installation
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pip install python-tls-client
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Quick start
|
|
58
|
+
|
|
59
|
+
The API is intentionally close to [requests](https://github.com/psf/requests),
|
|
60
|
+
so most of what you already know carries over directly.
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
import tls_client
|
|
64
|
+
|
|
65
|
+
session = tls_client.Session(
|
|
66
|
+
client_identifier="chrome_150",
|
|
67
|
+
random_tls_extension_order=True,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
response = session.get(
|
|
71
|
+
"https://www.example.com/",
|
|
72
|
+
headers={"key1": "value1"},
|
|
73
|
+
proxy="http://user:password@host:port",
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
print(response.status_code, response.text)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Available `client_identifier` presets (see `tls_client/settings.py` for the
|
|
80
|
+
full, up-to-date list — kept in sync with the
|
|
81
|
+
[`MappedTLSClients`](https://github.com/bogdanfinn/tls-client/blob/v1.16.0/profiles/profiles.go)
|
|
82
|
+
map of the native library version shipped in `tls_client/dependencies/`,
|
|
83
|
+
currently v1.16.0):
|
|
84
|
+
|
|
85
|
+
| Browser | Identifiers |
|
|
86
|
+
|----------|-------------|
|
|
87
|
+
| Chrome | `chrome_103`...`chrome_112`, `chrome_116_PSK`, `chrome_116_PSK_PQ`, `chrome_117`, `chrome_120`, `chrome_124`, `chrome_130_PSK`, `chrome_131(_PSK)`, `chrome_133(_PSK)`, `chrome_144(_PSK)`, `chrome_146(_PSK)`, `chrome_150(_PSK)`, `chrome_152(_PSK)` |
|
|
88
|
+
| Brave | `brave_146`, `brave_146_PSK` |
|
|
89
|
+
| Firefox | `firefox_102`...`firefox_110`, `firefox_117`, `firefox_120`, `firefox_123`, `firefox_132`, `firefox_133`, `firefox_135`, `firefox_146_PSK`, `firefox_147(_PSK)`, `firefox_148` |
|
|
90
|
+
| Opera | `opera_89`, `opera_90`, `opera_91` |
|
|
91
|
+
| Safari | `safari_15_6_1`, `safari_16_0` |
|
|
92
|
+
| iOS | `safari_ios_15_5`, `safari_ios_15_6`, `safari_ios_16_0`, `safari_ios_17_0`, `safari_ios_18_0`, `safari_ios_18_5`, `safari_ios_26_0` |
|
|
93
|
+
| iPadOS | `safari_ipad_15_6` |
|
|
94
|
+
| Android | `okhttp4_android_7` through `okhttp4_android_13` |
|
|
95
|
+
|
|
96
|
+
> Only `chrome_144`, `chrome_144_PSK`, `firefox_147`, `firefox_147_PSK` and
|
|
97
|
+
> `firefox_148` carry an HTTP/3 fingerprint matching the real browser; every
|
|
98
|
+
> other profile sends a minimal, non-representative SETTINGS frame if you
|
|
99
|
+
> end up negotiating HTTP/3. This doesn't affect the TLS or HTTP/2
|
|
100
|
+
> fingerprint. Use `disable_http3=True` (see below) if the HTTP/3 fingerprint
|
|
101
|
+
> matters and you're not using one of those five profiles.
|
|
102
|
+
|
|
103
|
+
## Releasing the session
|
|
104
|
+
|
|
105
|
+
Every `Session` holds native resources (a connection pool) in the underlying
|
|
106
|
+
Go library that must be released explicitly. Always use the context manager,
|
|
107
|
+
or call `.close()` yourself:
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
with tls_client.Session(client_identifier="chrome_150") as session:
|
|
111
|
+
response = session.get("https://www.example.com/")
|
|
112
|
+
# session is closed automatically here
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
If you forget, the session emits a `ResourceWarning` and does a best-effort
|
|
116
|
+
cleanup when it is garbage collected — but that isn't guaranteed to run
|
|
117
|
+
promptly (or at all), so don't rely on it in production code.
|
|
118
|
+
|
|
119
|
+
## Custom TLS fingerprint
|
|
120
|
+
|
|
121
|
+
Instead of a `client_identifier` preset, you can fully customize the TLS/HTTP2
|
|
122
|
+
fingerprint:
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
import tls_client
|
|
126
|
+
|
|
127
|
+
session = tls_client.Session(
|
|
128
|
+
ja3_string=(
|
|
129
|
+
"771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-"
|
|
130
|
+
"156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513,29-23-24,0"
|
|
131
|
+
),
|
|
132
|
+
h2_settings={
|
|
133
|
+
"HEADER_TABLE_SIZE": 65536,
|
|
134
|
+
"MAX_CONCURRENT_STREAMS": 1000,
|
|
135
|
+
"INITIAL_WINDOW_SIZE": 6291456,
|
|
136
|
+
"MAX_HEADER_LIST_SIZE": 262144,
|
|
137
|
+
},
|
|
138
|
+
h2_settings_order=[
|
|
139
|
+
"HEADER_TABLE_SIZE",
|
|
140
|
+
"MAX_CONCURRENT_STREAMS",
|
|
141
|
+
"INITIAL_WINDOW_SIZE",
|
|
142
|
+
"MAX_HEADER_LIST_SIZE",
|
|
143
|
+
],
|
|
144
|
+
supported_signature_algorithms=[
|
|
145
|
+
"ECDSAWithP256AndSHA256",
|
|
146
|
+
"PSSWithSHA256",
|
|
147
|
+
"PKCS1WithSHA256",
|
|
148
|
+
"ECDSAWithP384AndSHA384",
|
|
149
|
+
"PSSWithSHA384",
|
|
150
|
+
"PKCS1WithSHA384",
|
|
151
|
+
"PSSWithSHA512",
|
|
152
|
+
"PKCS1WithSHA512",
|
|
153
|
+
],
|
|
154
|
+
supported_versions=["GREASE", "1.3", "1.2"],
|
|
155
|
+
key_share_curves=["GREASE", "X25519"],
|
|
156
|
+
cert_compression_algo="brotli",
|
|
157
|
+
pseudo_header_order=[":method", ":authority", ":scheme", ":path"],
|
|
158
|
+
connection_flow=15663105,
|
|
159
|
+
header_order=["accept", "user-agent", "accept-encoding", "accept-language"],
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
response = session.post(
|
|
163
|
+
"https://www.example.com/",
|
|
164
|
+
headers={"key1": "value1"},
|
|
165
|
+
json={"key1": "key2"},
|
|
166
|
+
)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Additional fingerprint and connection controls (v1.16.0+)
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
import tls_client
|
|
173
|
+
|
|
174
|
+
session = tls_client.Session(
|
|
175
|
+
client_identifier="chrome_150",
|
|
176
|
+
# Disable TLS session ticket caching/resumption
|
|
177
|
+
disable_session_tickets=True,
|
|
178
|
+
# Force HTTP/2, even for a profile that would otherwise try HTTP/3
|
|
179
|
+
disable_http3=True,
|
|
180
|
+
)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
For a custom client (`client_identifier=None`), you can additionally set:
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
session = tls_client.Session(
|
|
187
|
+
client_identifier=None,
|
|
188
|
+
ja3_string="...",
|
|
189
|
+
alpn_protocols=["h2", "http/1.1"],
|
|
190
|
+
alps_protocols=["h2"],
|
|
191
|
+
# Required if ja3_string lists TLS extension 51764 (trust_anchors)
|
|
192
|
+
trust_anchors_payload="...",
|
|
193
|
+
)
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Certificate pinning
|
|
197
|
+
|
|
198
|
+
Restrict which server certificates are accepted for given hosts by passing
|
|
199
|
+
their pinned public key hashes (SPKI, base64-encoded SHA-256 — the same
|
|
200
|
+
format used by HPKP `pin-sha256` and most certificate-pinning tooling):
|
|
201
|
+
|
|
202
|
+
```python
|
|
203
|
+
import tls_client
|
|
204
|
+
|
|
205
|
+
session = tls_client.Session(
|
|
206
|
+
client_identifier="chrome_150",
|
|
207
|
+
certificate_pinning={
|
|
208
|
+
"example.com": [
|
|
209
|
+
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
|
|
210
|
+
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=", # backup pin
|
|
211
|
+
],
|
|
212
|
+
},
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
response = session.get("https://example.com/")
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Requests to a pinned host whose certificate doesn't match any of the given
|
|
219
|
+
pins raise `tls_client.TLSClientCertificateError` (see
|
|
220
|
+
[Error handling](#error-handling)). Including a backup pin is recommended so
|
|
221
|
+
rotating the leaf certificate doesn't lock you out.
|
|
222
|
+
|
|
223
|
+
## Error handling
|
|
224
|
+
|
|
225
|
+
Failures originating from the underlying Go client are raised as
|
|
226
|
+
`TLSClientExeption` (kept misspelled for backwards compatibility) or one of
|
|
227
|
+
its more specific subclasses, so you can catch broadly or narrowly:
|
|
228
|
+
|
|
229
|
+
```python
|
|
230
|
+
from tls_client import (
|
|
231
|
+
TLSClientExeption,
|
|
232
|
+
TLSClientTimeoutError,
|
|
233
|
+
TLSClientProxyError,
|
|
234
|
+
TLSClientConnectionError,
|
|
235
|
+
TLSClientCertificateError,
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
try:
|
|
239
|
+
response = session.get("https://example.com/", timeout_seconds=5)
|
|
240
|
+
except TLSClientTimeoutError:
|
|
241
|
+
... # request exceeded timeout_seconds
|
|
242
|
+
except TLSClientProxyError:
|
|
243
|
+
... # the configured proxy refused/rejected the connection
|
|
244
|
+
except TLSClientConnectionError:
|
|
245
|
+
... # DNS, TCP, or TLS handshake failure
|
|
246
|
+
except TLSClientCertificateError:
|
|
247
|
+
... # response failed certificate pinning validation
|
|
248
|
+
except TLSClientExeption:
|
|
249
|
+
... # any other backend error
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
All four subclasses inherit from `TLSClientExeption`, so existing code that
|
|
253
|
+
only catches the base class keeps working unchanged. Classification is
|
|
254
|
+
best-effort pattern matching on the backend's error message (it doesn't
|
|
255
|
+
expose a structured error code) and falls back to the base exception when
|
|
256
|
+
the message doesn't match a known pattern.
|
|
257
|
+
|
|
258
|
+
## Packaging with PyInstaller / PyArmor
|
|
259
|
+
|
|
260
|
+
The compiled native library ships inside `tls_client/dependencies/` and needs
|
|
261
|
+
to be included explicitly when bundling with PyInstaller or PyArmor.
|
|
262
|
+
|
|
263
|
+
| Platform | `--add-binary` argument |
|
|
264
|
+
|--------------------------|--------------------------|
|
|
265
|
+
| Linux (x86, 32-bit) | `'{path_to_library}/tls_client/dependencies/tls-client-x86.so:tls_client/dependencies'` |
|
|
266
|
+
| Linux (AMD64 / x86_64) | `'{path_to_library}/tls_client/dependencies/tls-client-amd64.so:tls_client/dependencies'` |
|
|
267
|
+
| macOS (Intel) | `'{path_to_library}/tls_client/dependencies/tls-client-x86.dylib:tls_client/dependencies'` |
|
|
268
|
+
| macOS (Apple Silicon) | `'{path_to_library}/tls_client/dependencies/tls-client-arm64.dylib:tls_client/dependencies'` |
|
|
269
|
+
| Windows (64-bit) | `'{path_to_library}/tls_client/dependencies/tls-client-64.dll;tls_client/dependencies'` |
|
|
270
|
+
|
|
271
|
+
## Development
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
pip install -r requirements.txt
|
|
275
|
+
python -m unittest discover -s tests -p "test_unit_*.py" -v
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
`tests/test_unit_*.py` are network-free and run in CI on every push and pull
|
|
279
|
+
request. `tests/test_binary_response.py` is a regression suite that hits
|
|
280
|
+
httpbin.org and is meant for manual local runs (`python -m pytest tests/ -v`),
|
|
281
|
+
not CI.
|
|
282
|
+
|
|
283
|
+
## Acknowledgements
|
|
284
|
+
|
|
285
|
+
Big shout out to [Bogdanfinn](https://github.com/bogdanfinn) for open
|
|
286
|
+
sourcing [tls-client](https://github.com/bogdanfinn/tls-client) in Go, and to
|
|
287
|
+
[FlorianREGAZ](https://github.com/FlorianREGAZ) for the original Python
|
|
288
|
+
wrapper this project is forked from. The syntax stays close to
|
|
289
|
+
[requests](https://github.com/psf/requests) since most people already know it.
|
|
290
|
+
|
|
291
|
+
## Changelog
|
|
292
|
+
|
|
293
|
+
### 1.0.4
|
|
294
|
+
- **Fixed**: the Linux `.so` binaries published in `1.0.3` on PyPI
|
|
295
|
+
(`tls-client-amd64.so`, and likely the other Linux binaries from that
|
|
296
|
+
build) were linked against musl libc instead of glibc, causing
|
|
297
|
+
`OSError: ... libc.musl-x86_64.so.1` on any glibc-based distro (Ubuntu,
|
|
298
|
+
Debian, most standard LXC containers — anything that isn't Alpine).
|
|
299
|
+
The v1.16.0 binaries in this update were downloaded from upstream's
|
|
300
|
+
`*-ubuntu-*` release assets and confirmed glibc-linked (checked for
|
|
301
|
+
`GLIBC_*` version symbols and the absence of a `libc.musl` dependency).
|
|
302
|
+
- **Breaking**: `Session`'s default `client_identifier` changed from
|
|
303
|
+
`chrome_120` to `chrome_150` (matching the v1.16.0 native library's own
|
|
304
|
+
default profile). `chrome_120` imitates a browser version no longer in
|
|
305
|
+
circulation, which made it a distinguishing fingerprint rather than a
|
|
306
|
+
neutral one; pin `client_identifier="chrome_120"` explicitly if you
|
|
307
|
+
relied on the old default.
|
|
308
|
+
- **Added**: typed exception hierarchy (`TLSClientTimeoutError`,
|
|
309
|
+
`TLSClientProxyError`, `TLSClientConnectionError`,
|
|
310
|
+
`TLSClientCertificateError`), all subclasses of the existing
|
|
311
|
+
`TLSClientExeption` so current `except` clauses keep working.
|
|
312
|
+
- **Added**: `Session` now warns (`ResourceWarning`) and best-effort
|
|
313
|
+
auto-closes if garbage collected without an explicit `close()` call.
|
|
314
|
+
- **Fixed**: native library load failures now raise a clear `OSError`
|
|
315
|
+
naming the platform, architecture, and resolved library path instead of
|
|
316
|
+
a raw, hard-to-diagnose `ctypes` error.
|
|
317
|
+
- **Fixed**: invalid/misleading type hints in `Session.__init__` (bare
|
|
318
|
+
`Optional` on `bool` parameters, `str` instead of `Optional[str]`).
|
|
319
|
+
- **Added**: network-free unit tests (`tests/test_unit_*.py`) and a CI
|
|
320
|
+
workflow that runs them on every push and pull request.
|
|
321
|
+
- **Updated**: precompiled native binaries bumped from
|
|
322
|
+
`bogdanfinn/tls-client` ~v1.7.x to v1.16.0. Newest previously available
|
|
323
|
+
profile was `chrome_120`/`firefox_120` (obsolete browser versions by
|
|
324
|
+
2026, which made the fingerprint itself a distinguishing signal). Now
|
|
325
|
+
includes Chrome 150/152, Firefox 147/148, Brave 146, and 20+ other new
|
|
326
|
+
profiles — see the [client identifier table](#quick-start) above. Verified
|
|
327
|
+
against a live handshake (`chrome_150` against tls.peet.ws) and against
|
|
328
|
+
the full existing test suite (unit tests + the httpbin-backed binary
|
|
329
|
+
response regression suite) on macOS arm64.
|
|
330
|
+
- **Added**: `Session` parameters `alpn_protocols`, `alps_protocols`,
|
|
331
|
+
`trust_anchors_payload` (custom client only), `disable_session_tickets`,
|
|
332
|
+
`disable_http3`, exposing v1.16.0 request fields. All default to the
|
|
333
|
+
previous behavior, so existing calls are unaffected.
|
|
334
|
+
- **Fixed**: `cert_compression_algo` now sends `certCompressionAlgos`
|
|
335
|
+
(the v1.16.0 field name, plural/list) internally instead of the old
|
|
336
|
+
singular `certCompressionAlgo`, which the new binary silently ignored.
|
|
337
|
+
The public parameter itself is unchanged.
|
|
338
|
+
- **Fixed**: `ClientIdentifiers` type list corrected — `safari_ipad_15_6`
|
|
339
|
+
was previously listed as a duplicate of `safari_ios_15_6`, making the
|
|
340
|
+
real iPadOS identifier inaccessible through the type; `confirmed_android_2`
|
|
341
|
+
removed as it was never a valid identifier in any checked upstream version.
|
|
342
|
+
|
|
343
|
+
### 1.0.3
|
|
344
|
+
- **Fixed**: on Linux, 64-bit x86 machines (`platform.machine() == "x86_64"`)
|
|
345
|
+
were incorrectly matched by the `"x86" in machine()` check in `cffi.py`
|
|
346
|
+
and loaded the 32-bit `tls-client-x86.so` binary instead of the 64-bit
|
|
347
|
+
`tls-client-amd64.so`, causing the native library to fail to load on the
|
|
348
|
+
vast majority of Linux hosts. `machine()` is now matched explicitly
|
|
349
|
+
against `x86_64`/`amd64`/`AMD64` before falling back to the 32-bit binary.
|
|
350
|
+
|
|
351
|
+
### 1.0.2
|
|
352
|
+
- **Fixed**: binary response bodies (protobuf, images, ...) were corrupted
|
|
353
|
+
by a UTF-8 `byteReplacer` (U+FFFD) inside the Go C library. Every byte
|
|
354
|
+
greater than 127 that did not form a valid UTF-8 sequence was replaced by
|
|
355
|
+
`EF BF BD`.
|
|
356
|
+
- `sessions.py` now sends `isByteResponse: True` in the request payload
|
|
357
|
+
so the backend returns the body as a base64 data-URI.
|
|
358
|
+
- `response.py`'s `build_response` decodes the data-URI back to raw
|
|
359
|
+
bytes for `Response.content`; `Response.text` is a UTF-8 view (with
|
|
360
|
+
replacement characters for invalid sequences).
|
|
361
|
+
- Added `tests/test_binary_response.py` (regression suite, needs network).
|
|
362
|
+
|
|
363
|
+
### 1.0.1
|
|
364
|
+
- Certificate pinning.
|