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.
Files changed (40) hide show
  1. python_tls_client-1.0.4/PKG-INFO +364 -0
  2. python_tls_client-1.0.4/README.md +333 -0
  3. python_tls_client-1.0.4/python_tls_client.egg-info/PKG-INFO +364 -0
  4. {python_tls_client-1.0.3 → python_tls_client-1.0.4}/python_tls_client.egg-info/SOURCES.txt +2 -0
  5. python_tls_client-1.0.4/tests/test_unit_cookies.py +65 -0
  6. python_tls_client-1.0.4/tests/test_unit_sessions.py +247 -0
  7. {python_tls_client-1.0.3 → python_tls_client-1.0.4}/tls_client/__init__.py +8 -1
  8. {python_tls_client-1.0.3 → python_tls_client-1.0.4}/tls_client/__version__.py +1 -1
  9. {python_tls_client-1.0.3 → python_tls_client-1.0.4}/tls_client/cffi.py +13 -1
  10. python_tls_client-1.0.4/tls_client/dependencies/tls-client-32.dll +0 -0
  11. python_tls_client-1.0.4/tls_client/dependencies/tls-client-64.dll +0 -0
  12. 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
  13. 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
  14. 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
  15. 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
  16. python_tls_client-1.0.4/tls_client/dependencies/tls-client-x86.so +0 -0
  17. python_tls_client-1.0.4/tls_client/exceptions.py +51 -0
  18. {python_tls_client-1.0.3 → python_tls_client-1.0.4}/tls_client/sessions.py +65 -10
  19. {python_tls_client-1.0.3 → python_tls_client-1.0.4}/tls_client/settings.py +42 -3
  20. python_tls_client-1.0.3/PKG-INFO +0 -195
  21. python_tls_client-1.0.3/README.md +0 -164
  22. python_tls_client-1.0.3/python_tls_client.egg-info/PKG-INFO +0 -195
  23. python_tls_client-1.0.3/tls_client/dependencies/tls-client-arm64.dylib +0 -0
  24. python_tls_client-1.0.3/tls_client/dependencies/tls-client-arm64.so +0 -0
  25. python_tls_client-1.0.3/tls_client/dependencies/tls-client-x86.dylib +0 -0
  26. python_tls_client-1.0.3/tls_client/exceptions.py +0 -3
  27. {python_tls_client-1.0.3 → python_tls_client-1.0.4}/LICENSE +0 -0
  28. {python_tls_client-1.0.3 → python_tls_client-1.0.4}/MANIFEST.in +0 -0
  29. {python_tls_client-1.0.3 → python_tls_client-1.0.4}/pyproject.toml +0 -0
  30. {python_tls_client-1.0.3 → python_tls_client-1.0.4}/python_tls_client.egg-info/dependency_links.txt +0 -0
  31. {python_tls_client-1.0.3 → python_tls_client-1.0.4}/python_tls_client.egg-info/requires.txt +0 -0
  32. {python_tls_client-1.0.3 → python_tls_client-1.0.4}/python_tls_client.egg-info/top_level.txt +0 -0
  33. {python_tls_client-1.0.3 → python_tls_client-1.0.4}/requirements.txt +0 -0
  34. {python_tls_client-1.0.3 → python_tls_client-1.0.4}/setup.cfg +0 -0
  35. {python_tls_client-1.0.3 → python_tls_client-1.0.4}/setup.py +0 -0
  36. {python_tls_client-1.0.3 → python_tls_client-1.0.4}/tests/test_binary_response.py +0 -0
  37. {python_tls_client-1.0.3 → python_tls_client-1.0.4}/tls_client/cookies.py +0 -0
  38. {python_tls_client-1.0.3 → python_tls_client-1.0.4}/tls_client/dependencies/__init__.py +0 -0
  39. {python_tls_client-1.0.3 → python_tls_client-1.0.4}/tls_client/response.py +0 -0
  40. {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.