Skip to content

Commit 608decb

Browse files
authored
[FEATURE] Enhance TLS/HTTP fingerprinting for Chrome 127-143 (#36)
* feat: Enhance TLS/HTTP fingerprinting for Chrome 127-143 - Added support for multiple Chrome profiles (127-143) with exact JA4 fingerprint matches. - Implemented Brotli and zlib decompression for the compress_certificate extension. - Updated SSL_CTX configuration to prevent redundant setups for browser profiles. - Improved session creation logic to initialize cookie jars and connection pools. - Enhanced tests to validate Chrome 143 fingerprint accuracy, including user-agent checks and TLS version support. - Updated browser profile aliases to default to Chrome 143. * updated pyproject.toml * fix: Add brotli CI dependencies and bump version to 0.2.8 - Add brotli dependencies to Linux CI (brotli-devel/libbrotli-dev) - Add brotli to macOS CI (brew install brotli) - Add brotli:x64-windows to Windows CI (vcpkg) - Update setup.py with Windows brotli path detection and linking - Fix flaky test_simple_get to use local httpbin_server fixture * fix: Build brotli from source on macOS for correct deployment target Homebrew brotli has MACOSX_DEPLOYMENT_TARGET=15.0 which causes delocate-wheel to fail when building wheels targeting macOS 11.0. - Add brotli build to scripts/darwin/setup_vendors.sh - Build with CMAKE_OSX_DEPLOYMENT_TARGET=11.0 for wheel compatibility - Update setup.py to prefer vendor brotli over Homebrew on macOS - Remove brotli from brew install in CI (built from source instead) * fix: Use correct brotli library names (without -static suffix) CMake builds brotli as libbrotlidec.a and libbrotlicommon.a, not libbrotlidec-static.a and libbrotlicommon-static.a. * feat: Enable HTTP/2 by default for Session class Chrome browsers use HTTP/2 by default, so httpmorph should too. This ensures correct JA4 fingerprint (h2 instead of h1) and Akamai HTTP/2 fingerprint matching out of the box. * feat: Perfect Chrome 143 fingerprint matching with documentation sync - Add Chrome-like HTTP/2 priority (weight=256, exclusive=1) in http2_logic.c - Remove zlib from certificate compression, use Brotli only (per Chrome behavior) - Add Chrome default headers to Session (sec-ch-ua, sec-fetch-*, priority) - Update all documentation to reflect Chrome 143 as default profile - Sync README and ReadTheDocs with actual code behavior: - Client and Session both default to http2=True - Certificate compression is Brotli only - Updated fingerprint characteristics (JA4, JA3N, Peetprint, Akamai) - Fix test names to match http2=True default behavior * fix: Restore zlib certificate decompression for server compatibility Chrome 143 advertises brotli in the compress_certificate extension, but some servers still send zlib-compressed certificates. Keep both decompression handlers registered for compatibility. Fixes TLS handshake failures with CERT_DECOMPRESSION_FAILED error on servers using zlib certificate compression. * fix: Match Chrome 143 TLS fingerprint exactly Changes to match Chrome 143's actual TLS fingerprint: 1. Certificate compression (compress_certificate extension 27): - Only advertise brotli (2), not zlib (1) - Chrome only supports brotli certificate compression - Servers compliant with RFC 8879 will only send brotli 2. Application settings (ALPS extension 17613): - Only advertise "h2" protocol, not "http/1.1" - Chrome only includes h2 in application_settings 3. Extension permutation: - Enable SSL_CTX_set_permute_extensions() to randomize extension order - Chrome randomizes extension order in each ClientHello - JA4 sorts extensions alphabetically so order doesn't affect JA4 Matching fingerprints: - Cipher hash (JA4 part 2): 8daaf6152771 ✓ - Akamai HTTP/2: 1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p ✓ - compress_certificate: brotli only ✓ - application_settings: h2 only ✓ Note: Chrome fingerprint shows 17 extensions vs httpmorph's 16 because Chrome capture was from a resumed session with pre_shared_key (41). Fresh connections don't include pre_shared_key, which is expected behavior. * fix: Prevent heap corruption in HTTP/2 response body buffer cleanup on Windows The HTTP/2 code was replacing the response body buffer (originally from the buffer pool) with a newly malloc'd buffer, but not properly handling the cleanup: 1. The original buffer from the pool was never returned to the pool 2. _buffer_pool was still pointing to the pool 3. _body_actual_size wasn't updated for the new buffer When httpmorph_response_destroy() was later called, it tried to return the malloc'd buffer to the buffer pool using the stale _body_actual_size, causing heap corruption (especially visible on Windows with error code 0xc0000374). Fix: Before assigning the new malloc'd buffer to response->body: - Return the original buffer to the pool (if from pool) or free it - Clear _buffer_pool since the new buffer is not from the pool - Set _body_actual_size to track the actual allocation size Fixes Windows CI heap corruption crash in Session.__del__
1 parent 4bf6a49 commit 608decb

File tree

22 files changed

+825
-340
lines changed

22 files changed

+825
-340
lines changed

.github/workflows/_test.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ jobs:
5757
if: runner.os == 'Linux'
5858
run: |
5959
sudo apt-get update
60-
sudo apt-get install -y cmake ninja-build libssl-dev pkg-config autoconf automake libtool libnghttp2-dev
60+
sudo apt-get install -y cmake ninja-build libssl-dev pkg-config autoconf automake libtool libnghttp2-dev libbrotli-dev
6161
6262
- name: Cache Homebrew packages (macOS)
6363
if: runner.os == 'macOS'
@@ -131,9 +131,9 @@ jobs:
131131
path: |
132132
C:/vcpkg/installed
133133
C:/vcpkg/packages
134-
key: vcpkg-nghttp2-zlib-${{ runner.os }}-v3
134+
key: vcpkg-nghttp2-zlib-brotli-${{ runner.os }}-v4
135135
restore-keys: |
136-
vcpkg-nghttp2-zlib-${{ runner.os }}-
136+
vcpkg-nghttp2-zlib-brotli-${{ runner.os }}-
137137
138138
- name: Setup vcpkg (Windows)
139139
if: runner.os == 'Windows' && steps.cache-vcpkg-restore.outputs.cache-hit != 'true'
@@ -158,7 +158,7 @@ jobs:
158158
run: |
159159
export VCPKG_ROOT="C:/vcpkg"
160160
export PATH="$VCPKG_ROOT:$PATH"
161-
vcpkg install nghttp2:x64-windows zlib:x64-windows --clean-after-build
161+
vcpkg install nghttp2:x64-windows zlib:x64-windows brotli:x64-windows --clean-after-build
162162
shell: bash
163163

164164
- name: Set vcpkg environment (Windows)
@@ -254,7 +254,7 @@ jobs:
254254
path: |
255255
C:/vcpkg/installed
256256
C:/vcpkg/packages
257-
key: vcpkg-nghttp2-zlib-${{ runner.os }}-v3
257+
key: vcpkg-nghttp2-zlib-brotli-${{ runner.os }}-v4
258258

259259
- name: Save vendor cache
260260
if: always() && steps.cache-vendor.outputs.cache-hit != 'true'

README.md

Lines changed: 33 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ A Python HTTP client focused on mimicking browser fingerprints.
1313
- **Requests-compatible API** - Drop-in replacement for most Python `requests` use cases
1414
- **High Performance** - Native C implementation with BoringSSL for HTTP/HTTPS
1515
- **HTTP/2 Support** - Full HTTP/2 with ALPN negotiation via nghttp2 (httpx-like API)
16-
- **Chrome 142 Fingerprint** - Perfect JA3N, JA4, and JA4_R matching
17-
- **Browser Fingerprinting** - Realistic Chrome 142 browser profile
18-
- **TLS Fingerprinting** - JA3/JA3N/JA4 fingerprint generation with post-quantum crypto
16+
- **Chrome 127-143 Fingerprints** - Perfect JA4 fingerprint matching
17+
- **Browser Fingerprinting** - Realistic Chrome browser profiles (127-143)
18+
- **TLS Fingerprinting** - JA3N/JA4/JA4_R fingerprint generation with post-quantum crypto
19+
- **HTTP/2 Fingerprinting** - Perfect Akamai HTTP/2 fingerprint matching
1920
- **Connection Pooling** - Automatic connection reuse for better performance
2021
- **Session Management** - Persistent cookies and headers across requests
2122

@@ -74,14 +75,14 @@ print(response.http_version) # '2.0'
7475
Mimic real browser behavior with pre-configured profiles:
7576

7677
```python
77-
# Use Chrome fingerprint (defaults to Chrome 142)
78+
# Use Chrome fingerprint (defaults to Chrome 143)
7879
response = httpmorph.get('https://example.com', browser='chrome')
7980

80-
# Use specific Chrome version
81-
session = httpmorph.Session(browser='chrome142')
81+
# Use specific Chrome version (127-143 supported)
82+
session = httpmorph.Session(browser='chrome143')
8283
response = session.get('https://example.com')
8384

84-
# Available browsers: chrome, chrome142
85+
# Available browsers: chrome, chrome127-chrome143
8586
```
8687

8788
### OS-Specific User Agents
@@ -111,35 +112,34 @@ session = httpmorph.Session(browser='chrome', os='linux')
111112

112113
The OS parameter only affects the User-Agent string, while all other fingerprinting characteristics (TLS, HTTP/2, JA3/JA4) remain consistent to match the specified browser profile.
113114

114-
### Chrome 142 Fingerprint Matching
115+
### Chrome Fingerprint Matching
115116

116-
httpmorph accurately mimics **Chrome 142** TLS fingerprints with:
117+
httpmorph accurately mimics **Chrome 127-143** TLS and HTTP/2 fingerprints with:
117118

118-
- **JA3N** ✅ Perfect match
119-
- **JA4** ✅ Perfect match
119+
- **JA4** ✅ Perfect match (`t13d1516h2_8daaf6152771_d8a2da3f94cd`)
120120
- **JA4_R** ✅ Perfect match
121-
- **User-Agent**: `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36`
121+
- **JA3N** ✅ Perfect match (normalized JA3: `dcefaf3f0e71d260d19dc1d0749c9278`)
122+
- **HTTP/2 Akamai** ✅ Perfect match (`1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p`)
123+
- **User-Agent**: Version-specific Chrome user agents
122124
- **TLS 1.3** with correct cipher suites and extensions
123-
- **HTTP/2** with Chrome-specific SETTINGS frame
125+
- **HTTP/2** with Chrome-specific SETTINGS frame and pseudo-header order
124126
- **Post-quantum cryptography** (X25519MLKEM768)
125-
- **Certificate compression** (Brotli, Zlib)
127+
- **Certificate compression** (Brotli)
126128

127129
**Verify your fingerprint:**
128130

129131
```python
130132
import httpmorph
131133

132134
# Make a request to fingerprint checker
133-
response = httpmorph.get('https://suip.biz/?act=ja4', browser='chrome142')
134-
print(response.text)
135+
response = httpmorph.get('https://tls.peet.ws/api/all', browser='chrome143')
136+
data = response.json()
137+
print(f"JA4: {data['tls']['ja4']}")
135138

136-
# You should see perfect matches for:
137-
# - JA4 fingerprint ✅
138-
# - JA3N fingerprint ✅
139-
# - User-Agent: Chrome/142.0.0.0 ✅
139+
# Expected: t13d1516h2_8daaf6152771_d8a2da3f94cd ✅
140140
```
141141

142-
httpmorph achieves **perfect matches** for all modern fingerprints including JA3N, JA4, and JA4_R when tested against real Chrome 142 browsers.
142+
All Chrome 127-143 profiles produce **exact JA4 matches** with real Chrome browsers.
143143

144144
## Advanced Usage
145145

@@ -148,19 +148,18 @@ httpmorph achieves **perfect matches** for all modern fingerprints including JA3
148148
httpmorph supports HTTP/2 with an httpx-like API:
149149

150150
```python
151-
# Enable HTTP/2 for a client (default is False)
152-
client = httpmorph.Client(http2=True)
151+
# Both Client and Session default to HTTP/2 (http2=True) like Chrome
152+
client = httpmorph.Client()
153153
response = client.get('https://www.google.com')
154154
print(response.http_version) # '2.0'
155155

156-
# Enable HTTP/2 for a session
157-
session = httpmorph.Session(browser='chrome', http2=True)
156+
session = httpmorph.Session(browser='chrome')
158157
response = session.get('https://www.google.com')
159158
print(response.http_version) # '2.0'
160159

161-
# Per-request HTTP/2 override
162-
client = httpmorph.Client(http2=False) # Default disabled
163-
response = client.get('https://www.google.com', http2=True) # Enable for this request
160+
# Per-request HTTP/2 override (disable for specific request)
161+
client = httpmorph.Client() # Defaults to HTTP/2
162+
response = client.get('https://example.com', http2=False) # Disable for this request
164163
```
165164

166165
### Custom Headers
@@ -417,7 +416,7 @@ Please open an issue or pull request on GitHub.
417416
httpmorph has a comprehensive test suite with 350+ tests covering:
418417

419418
- All HTTP methods and parameters
420-
- Chrome 142 fingerprint validation (JA3N, JA4, JA4_R)
419+
- Chrome 127-143 fingerprint validation (JA4, JA4_R)
421420
- TLS 1.2/1.3 with post-quantum cryptography
422421
- Certificate compression (Brotli, Zlib)
423422
- Redirect handling and history
@@ -439,16 +438,16 @@ pytest tests/ -v
439438
- Built on BoringSSL (Google) with post-quantum cryptography support
440439
- HTTP/2 support via nghttp2
441440
- Inspired by Python's requests and httpx libraries
442-
- Chrome 142 fingerprint matching with perfect JA3N, JA4, and JA4_R matches
443-
- Certificate compression support for Cloudflare-protected sites
441+
- Chrome 127-143 fingerprint matching with perfect JA4, JA3N, and HTTP/2 Akamai fingerprints
442+
- Certificate compression (Brotli) for Cloudflare-protected sites
444443

445444
## FAQ
446445

447446
**Q: Why another HTTP client?**
448447
A: httpmorph combines the performance of native C with browser fingerprinting capabilities, making it ideal for applications that need both speed and realistic browser behavior.
449448

450-
**Q: How accurate is the Chrome 142 fingerprint?**
451-
A: httpmorph achieves perfect matches for modern fingerprints including JA3N, JA4, and JA4_R. This is verified against real Chrome 142 browsers. Test your fingerprint at https://suip.biz/?act=ja4
449+
**Q: How accurate are the Chrome fingerprints?**
450+
A: httpmorph achieves perfect JA4 matches for Chrome 127-143. Test your fingerprint at https://tls.peet.ws/api/all
452451

453452
**Q: Is it production-ready?**
454453
A: No, httpmorph is still in active development and not yet recommended for production use.
@@ -457,7 +456,7 @@ A: No, httpmorph is still in active development and not yet recommended for prod
457456
A: For most common use cases, yes! We've implemented the most widely-used requests API. Some advanced features may have slight differences.
458457

459458
**Q: Does it work with Cloudflare-protected sites?**
460-
A: Yes! httpmorph supports certificate compression (Brotli, Zlib) which is required for many Cloudflare-protected sites. We successfully tested with icanhazip.com and postman-echo.com.
459+
A: Yes! httpmorph supports certificate compression (Brotli) which is required for many Cloudflare-protected sites. We successfully tested with icanhazip.com and postman-echo.com.
461460

462461
**Q: How do I report a bug?**
463462
A: Please open an issue on GitHub with a minimal reproduction example and your environment details (OS, Python version, httpmorph version).

docs/source/advanced.rst

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ TLS Fingerprinting
99
Browser-Specific Fingerprints
1010
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1111

12-
httpmorph generates accurate Chrome 142 TLS fingerprints with perfect JA3N, JA4, and JA4_R matching:
12+
httpmorph generates accurate Chrome 143 TLS fingerprints with perfect JA3N, JA4, JA4_R, and Akamai matching:
1313

1414
.. code-block:: python
1515
16-
# Chrome 142 profile (default)
16+
# Chrome 143 profile (default)
1717
session = httpmorph.Session(browser='chrome')
1818
response = session.get('https://example.com')
1919
@@ -23,7 +23,7 @@ httpmorph generates accurate Chrome 142 TLS fingerprints with perfect JA3N, JA4,
2323
print('HTTP:', response.http_version)
2424
2525
# Output example:
26-
# JA3: 8e19337e7524d2573be54efb2b0784c9 (Chrome 142 normalized)
26+
# JA3: dcefaf3f0e71d260d19dc1d0749c9278 (Chrome 143 normalized)
2727
# TLS: TLSv1.3
2828
# Cipher: TLS_AES_128_GCM_SHA256
2929
# HTTP: 2.0
@@ -53,7 +53,7 @@ Customize the User-Agent for different operating systems:
5353
GREASE Values
5454
~~~~~~~~~~~~~
5555

56-
Chrome 142 uses GREASE (Generate Random Extensions And Sustain Extensibility) values that are randomized per request to maintain TLS ecosystem extensibility:
56+
Chrome 143 uses GREASE (Generate Random Extensions And Sustain Extensibility) values that are randomized per request to maintain TLS ecosystem extensibility:
5757

5858
.. code-block:: python
5959

docs/source/api.rst

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -90,13 +90,13 @@ Client Class
9090

9191
.. code-block:: python
9292
93-
client = httpmorph.Client(http2=False)
93+
client = httpmorph.Client(http2=True)
9494
95-
HTTP client for making requests.
95+
HTTP client for making requests. Defaults to HTTP/2 to match Chrome behavior.
9696

9797
**Constructor Parameters:**
9898

99-
* ``http2`` (bool) - Enable HTTP/2. Default: ``False``
99+
* ``http2`` (bool) - Enable HTTP/2. Default: ``True``
100100

101101
**Methods:**
102102

@@ -127,15 +127,15 @@ Session Class
127127

128128
.. code-block:: python
129129
130-
session = httpmorph.Session(browser='chrome', os='macos', http2=False)
130+
session = httpmorph.Session(browser='chrome', os='macos', http2=True)
131131
132-
HTTP session with persistent cookies and headers.
132+
HTTP session with persistent cookies and headers. Sessions default to HTTP/2 to match Chrome browser behavior.
133133

134134
**Constructor Parameters:**
135135

136-
* ``browser`` (str) - Browser profile to mimic. Options: ``'chrome'``, ``'chrome142'``, ``'random'``. Default: ``'chrome'``
136+
* ``browser`` (str) - Browser profile to mimic. Options: ``'chrome'``, ``'chrome127'``-``'chrome143'``, ``'random'``. Default: ``'chrome'`` (Chrome 143)
137137
* ``os`` (str) - Operating system for User-Agent. Options: ``'macos'``, ``'windows'``, ``'linux'``. Default: ``'macos'``
138-
* ``http2`` (bool) - Enable HTTP/2. Default: ``False``
138+
* ``http2`` (bool) - Enable HTTP/2. Default: ``True`` (matches Chrome behavior)
139139

140140
**Attributes:**
141141

@@ -423,42 +423,55 @@ Browser Profiles
423423

424424
Available browser profiles for ``Session(browser=...)``:
425425

426-
Chrome 142
427-
~~~~~~~~~~
426+
Chrome 143 (Default)
427+
~~~~~~~~~~~~~~~~~~~~
428428

429-
The default and most accurate browser profile, mimicking Chrome 142:
429+
The default and most accurate browser profile, mimicking Chrome 143:
430430

431431
**Fingerprint Characteristics:**
432432

433-
* **JA3N**: ``8e19337e7524d2573be54efb2b0784c9`` (perfect match)
434-
* **JA4**: ``t13d1516h2_8daaf6152771_d8a2da3f94cd`` (perfect match)
435-
* **JA4_R**: ``t13d1516h2_002f,0035,009c,...`` (perfect match)
433+
* **JA4**: ``t13d1516h2_8daaf6152771_e5627efa2ab1`` (perfect match)
434+
* **JA3N**: ``dcefaf3f0e71d260d19dc1d0749c9278`` (perfect match)
435+
* **Peetprint**: ``1d4ffe9b0e34acac0bd883fa7f79d7b5`` (perfect match)
436+
* **Akamai HTTP/2**: ``1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p`` (perfect match)
436437
* **TLS 1.3** with 15 cipher suites
437438
* **Post-quantum cryptography**: X25519MLKEM768 (curve 4588)
438-
* **Certificate compression**: Brotli, Zlib
439+
* **Certificate compression**: Brotli (zlib fallback for compatibility)
439440
* **GREASE**: Randomized per request
440-
* **HTTP/2**: Chrome-specific SETTINGS frame
441+
* **HTTP/2**: Chrome-specific SETTINGS frame, priority (weight=256, exclusive=1)
442+
* **Default headers**: sec-ch-ua, sec-fetch-*, accept-language, priority
441443
442444
**User-Agent Variants:**
443445

444-
* **macOS**: ``Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36``
445-
* **Windows**: ``Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36``
446-
* **Linux**: ``Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36``
446+
* **macOS**: ``Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36``
447+
* **Windows**: ``Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36``
448+
* **Linux**: ``Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36``
447449

448450
**Usage:**
449451

450452
.. code-block:: python
451453
452-
# Use Chrome 142 profile (default)
454+
# Use Chrome 143 profile (default)
453455
session = httpmorph.Session(browser='chrome')
454456
455-
# Explicitly use Chrome 142
456-
session = httpmorph.Session(browser='chrome142')
457+
# Explicitly use Chrome 143
458+
session = httpmorph.Session(browser='chrome143')
457459
458460
# With specific OS
459461
session = httpmorph.Session(browser='chrome', os='windows')
460462
463+
Chrome 127-142
464+
~~~~~~~~~~~~~~
465+
466+
Older Chrome profiles are also available for compatibility testing:
467+
468+
.. code-block:: python
469+
470+
session = httpmorph.Session(browser='chrome127')
471+
session = httpmorph.Session(browser='chrome135')
472+
# etc.
473+
461474
Random
462475
~~~~~~
463476

464-
Randomly selects a browser profile for each session. Currently only Chrome 142 is available.
477+
Randomly selects a browser profile for each session from available Chrome profiles.

docs/source/quickstart.rst

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -102,12 +102,12 @@ Mimic Chrome browser with realistic fingerprints:
102102

103103
.. code-block:: python
104104
105-
# Chrome browser profile (defaults to Chrome 142)
105+
# Chrome browser profile (defaults to Chrome 143)
106106
session = httpmorph.Session(browser='chrome')
107107
response = session.get('https://example.com')
108108
109-
# Use specific Chrome version
110-
session = httpmorph.Session(browser='chrome142')
109+
# Use specific Chrome version (127-143 supported)
110+
session = httpmorph.Session(browser='chrome143')
111111
response = session.get('https://example.com')
112112
113113
# Random browser selection
@@ -118,9 +118,10 @@ The Chrome browser profile includes:
118118
* Chrome-specific User-Agent
119119
* Chrome-specific TLS cipher suites and extensions
120120
* Post-quantum cryptography (X25519MLKEM768)
121-
* Certificate compression (Brotli, Zlib)
122-
* Chrome-specific HTTP/2 settings
123-
* Perfect JA3N, JA4, and JA4_R fingerprint matching
121+
* Certificate compression (Brotli, with zlib fallback)
122+
* Chrome-specific HTTP/2 settings and priority
123+
* Perfect JA3N, JA4, JA4_R, and Akamai fingerprint matching
124+
* Chrome-like default headers (sec-ch-ua, sec-fetch-*, etc.)
124125
125126
OS-Specific User Agents
126127
~~~~~~~~~~~~~~~~~~~~~~~~
@@ -344,21 +345,22 @@ Upload files:
344345
HTTP/2
345346
------
346347

347-
Enable HTTP/2 support:
348+
Both Client and Session default to HTTP/2 to match Chrome behavior:
348349

349350
.. code-block:: python
350351
351-
# For all requests in a client
352-
client = httpmorph.Client(http2=True)
352+
# Both Client and Session default to HTTP/2 (http2=True)
353+
client = httpmorph.Client()
353354
response = client.get('https://www.google.com')
355+
print(response.http_version) # '2.0'
354356
355-
# For all requests in a session
356-
session = httpmorph.Session(browser='chrome', http2=True)
357+
session = httpmorph.Session(browser='chrome')
357358
response = session.get('https://www.google.com')
359+
print(response.http_version) # '2.0'
358360
359-
# Per-request override
360-
client = httpmorph.Client(http2=False)
361-
response = client.get('https://www.google.com', http2=True)
361+
# Per-request override (disable HTTP/2 for specific request)
362+
client = httpmorph.Client() # Defaults to HTTP/2
363+
response = client.get('https://example.com', http2=False)
362364
363365
Check HTTP version:
364366

0 commit comments

Comments
 (0)