Skip to content

Merge upstream/main into main#28

Merged
smeinecke merged 30 commits into
mainfrom
merge/upstream-latest
May 14, 2026
Merged

Merge upstream/main into main#28
smeinecke merged 30 commits into
mainfrom
merge/upstream-latest

Conversation

@smeinecke

Copy link
Copy Markdown
Owner

This PR syncs the latest changes from Danny-Dasilva/CycleTLS (upstream/main) into our fork.

Key upstream changes included

Category Change
Go core fixes local_address binding now propagated through proxy dialers; Do() field drops fixed; TLS13AutoRetry JA3 corruption eliminated (Danny-Dasilva#65)
Transport fixes enable_connection_reuse=False now correctly sets DisableKeepAlives=true on the Go transport (Danny-Dasilva#52); idle conn timeout lowered 90s → 60s (Danny-Dasilva#51)
Extension types Exposes proper utls types for extension IDs 17, 17613, 30032 (Danny-Dasilva#66)
Live tests Replaces TrackMe self-hosted server with tlsfingerprint.com Docker instance for CI live tests (Danny-Dasilva#49)
Build Linux .so now targets glibc 2.17 via zig cc for broader compatibility (#41)
CI CodeQL analysis, Dependabot expansion, action version bumps, macOS runner optimization (Danny-Dasilva#47, Danny-Dasilva#53)
Dependencies Bumped fasthttp, gorilla/websocket, quic-go, utls, x/net, x/crypto, and others

Fork-specific features preservedh

  • Windows CI — retained in unit-tests.yml matrix
  • Python 3.9 & 3.14 — kept in test matrix alongside upstream versions
  • Connection-reuse logic — refined to only disable keep-alive for tlsfingerprint.com endpoints; httpbin.org and public endpoints still use normal HTTP/1.1 keep-alive to avoid EOF errors on redirect chains and cookie flows
  • All fingerprint test variantstest_*_tlsfingerprint.py modules retained

Merge conflict resolution notes

Conflicts were resolved across:

  • golang/client.go, golang/connect.go, golang/index.go — adopted upstream fixes while preserving local_address stamping
  • .github/workflows/ — merged upstream CI improvements with our Windows + Python 3.9/3.14 matrix entries
  • tests/conftest.py and test files — migrated TRACKME_URLTLSFP_URL naming, kept our smarter connection-reuse fixture behavior

Testing

  • Unit tests pass
  • Blocking tests pass against local tlsfingerprint.com
  • Live tests pass (external services)

smeinecke and others added 28 commits March 18, 2026 12:32
fix: make badssl.com tests resilient to transient network failures
Danny-Dasilva#53)

* ci(unit-tests): run macOS only for Python 3.13 to ease runner pressure

Free-tier macOS runners are scarce; running the full 4-Python matrix on macOS
queued 28+ jobs for 30+ minutes during a PR rebase storm. Linux covers the full
Python matrix; macOS divergence (if any) surfaces on latest Python just as well
as on older versions.

* test(async-concurrent): mark large_concurrent_batch as live

The 50-request burst against httpbin.org regularly trips the public service's
rate limit / connection ceiling, producing context-deadline timeouts that
aren't a cycletls bug. Move to the live-tests workflow so the blocking unit-
tests pipeline isn't held hostage to httpbin.org reliability.

* ci: re-trigger CI to clear stuck queue
…ny-Dasilva#47)

* add build hint for codeql.

* codeql did not detect the new workflow.

* Add CodeQL analysis workflow configuration

This file sets up a CodeQL workflow for analyzing code in multiple languages, including Go and Python, with scheduled runs and pull request checks.

* update codeql build to manual.

* add dependabot setting for golang.

* update codeql buil.d

* codeql fix v3

* build(deps): bump GitHub Actions to latest versions

Consolidates all 4 dependabot PRs (#1-#4):
- actions/setup-python: v5 → v6 (PR #1)
- actions/upload-artifact: v4 → v7 (PR #2)
- actions/setup-go: v5 → v6 (PR #3)
- actions/download-artifact: v4 → v8 (PR #4)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci(codeql): pin setup-go to v6 for consistency with other workflows

* ci: re-trigger after rebase on slim-matrix main

---------

Co-authored-by: Stefan Meinecke <meinecke@greensec.de>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…p-alives (Danny-Dasilva#51)

* fix(transport): lower IdleConnTimeout 90s -> 60s to undercut peer keep-alives

All four http.Transport literals in roundtripper.go declared
IdleConnTimeout: 90 * time.Second. Most servers behind nginx idle-close at
~65-75s (and badssl in particular at ~70s), so the 90s window guaranteed
that any second request after a brief pause checked out a peer-closed
connection from the idle pool. The transport then surfaced bare 'EOF' or
'http: server closed idle connection' to the caller.

Lower to 60s. This matches Go's net/http stdlib default and gives a
5-10s safety margin under typical peer keep-alive windows.

Tests: golang/roundtripper_test.go - source-level invariants pin the 60s
upper bound across all 4 transport sites and assert no IdleConnTimeout: 90
declarations regress.

* ci: re-trigger after rebase on slim-matrix main
…lva#54)

Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2 to 3.
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](softprops/action-gh-release@v2...v3)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: '3'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](actions/checkout@v4...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5 to 6.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](actions/setup-python@v5...v6)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5 to 6.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](actions/setup-go@v5...v6)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
The Linux wheel was compiled against glibc 2.32+ (ubuntu-latest),
making it incompatible with Debian Bullseye (glibc 2.31) and similar
older distros still in production.

Use zig cc with `-target x86_64-linux-gnu.2.17` as the CGO compiler
so the resulting libcycletls.so works on any Linux with glibc >= 2.17.

Co-authored-by: Danny Dasilva <yahchayildasilva@gmail.com>
…anny-Dasilva#48)

* fix(tests): mark badssl.com tests as live; fix CHANGELOG fetch-depth

* ci: re-trigger after rebase on slim-matrix main
…pAlives=true (Danny-Dasilva#52)

* ci: re-trigger after rebase on slim-matrix main

* fix(transport): propagate enable_connection_reuse=False as DisableKeepAlives=true

Previously enable_connection_reuse=False bypassed the global fhttp.Client
cache (client.go:249) but every http.Transport literal in roundtripper.go
hardcoded DisableKeepAlives: false. The inner transport therefore still
pooled idle connections across requests on the same client instance — so
the flag only avoided cross-test client reuse, not stale-conn races within
a single test.

Plumb the flag end-to-end:
- Add Browser.DisableKeepAlives (client.go) — Browser is the boundary the
  flag flows through into newRoundTripper.
- Add roundTripper.DisableKeepAlives, set in newRoundTripper from the
  Browser field.
- Bind all 4 http.Transport literals in roundtripper.go to
  rt.DisableKeepAlives instead of the hardcoded false.
- In getOrCreateClient: when enableConnectionReuse=false, set
  browser.DisableKeepAlives = true before constructing the client.

Default behaviour is preserved: when the flag is absent or true,
DisableKeepAlives ends up false on every transport, exactly as before.

Tests: golang/roundtripper_test.go pins propagation through Browser →
roundTripper, exercises the HTTP-scheme transport via getTransport, and
asserts the source-level invariant that no DisableKeepAlives: false hard
binding remains.

* ci: re-trigger after rebase on slim-matrix main

* ci: trigger after base change to main

* ci: re-trigger after rebase on action-bumps main
…lva#62)

Bumps [github.com/gorilla/websocket](https://github.com/gorilla/websocket) from 1.5.1 to 1.5.3.
- [Release notes](https://github.com/gorilla/websocket/releases)
- [Commits](gorilla/websocket@v1.5.1...v1.5.3)

---
updated-dependencies:
- dependency-name: github.com/gorilla/websocket
  dependency-version: 1.5.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…anny-Dasilva#59)

Bumps [github.com/refraction-networking/utls](https://github.com/refraction-networking/utls) from 1.8.0 to 1.8.2.
- [Release notes](https://github.com/refraction-networking/utls/releases)
- [Commits](refraction-networking/utls@v1.8.0...v1.8.2)

---
updated-dependencies:
- dependency-name: github.com/refraction-networking/utls
  dependency-version: 1.8.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…anny-Dasilva#66)

Audit of utls 1.8.x parrot map (genMap) revealed three pre-existing gaps
where dedicated utls extension types existed but genMap fell back to
GenericExtension. Replacing them with the proper types lets utls own the
wire encoding (including any future bugfixes) and removes hand-rolled
byte payloads:

  - ID 17 (status_request_v2): GenericExtension -> StatusRequestV2Extension
    (proper RFC 6961 encoding instead of zero-length GenericExtension)
  - ID 17613 (TLS ALPS new codepoint): GenericExtension -> ApplicationSettingsExtensionNew
    Hand-rolled 5-byte payload {0x00,0x03,0x02,0x68,0x32} replaced by
    SupportedProtocols-driven encoding identical to ID 17513.
  - ID 30032 (Channel ID): GenericExtension(Id:0x7550, Data:{0}) ->
    FakeChannelIDExtension{}. Wire format also corrected: real Chrome
    ChannelID is a 4-byte zero-payload extension, the previous
    GenericExtension emitted 5 bytes (length=1).

ID 51 (KeyShare), ID 43 (SupportedVersions), IDs 10/11 (curves/points)
remain populated by callers because they depend on per-call inputs
(version, GREASE flag, JA3 curves). ID 65037 (GREASE_ECH) is already
correctly using utls.BoringGREASEECH(). IDs 22 (encrypt_then_mac) and
49 (post_handshake_auth) intentionally stay as GenericExtension because
utls does not provide dedicated types for them.

go build ./... and go test ./... -short pass.
…anny-Dasilva#63)

Bumps [golang.org/x/net](https://github.com/golang/net) from 0.42.0 to 0.53.0.
- [Commits](golang/net@v0.42.0...v0.53.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.53.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…a#60)

Bumps [github.com/quic-go/quic-go](https://github.com/quic-go/quic-go) from 0.53.0 to 0.59.0.
- [Release notes](https://github.com/quic-go/quic-go/releases)
- [Commits](quic-go/quic-go@v0.53.0...v0.59.0)

---
updated-dependencies:
- dependency-name: github.com/quic-go/quic-go
  dependency-version: 0.59.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…asilva#64)

Bumps [github.com/valyala/fasthttp](https://github.com/valyala/fasthttp) from 1.69.0 to 1.70.0.
- [Release notes](https://github.com/valyala/fasthttp/releases)
- [Commits](valyala/fasthttp@v1.69.0...v1.70.0)

---
updated-dependencies:
- dependency-name: github.com/valyala/fasthttp
  dependency-version: 1.70.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…ilva#61)

Bumps [github.com/andybalholm/brotli](https://github.com/andybalholm/brotli) from 1.2.0 to 1.2.1.
- [Commits](andybalholm/brotli@v1.2.0...v1.2.1)

---
updated-dependencies:
- dependency-name: github.com/andybalholm/brotli
  dependency-version: 1.2.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](actions/checkout@v4...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…nstance (Danny-Dasilva#49)

* feat: use local TrackMe instance for TLS fingerprint tests

Replace hardcoded tls.peet.ws URLs with a TRACKME_URL environment variable
(defaulting to https://tls.peet.ws for backward compatibility) so CI and
local tests can run against a local TrackMe Docker instance.

- Add docker/trackme/Dockerfile + config.json: builds TrackMe from GitHub
  (golang:1.24-alpine, no pcap/QUIC, ports 8443/8080)
- Add docker-compose.test.yml: runs local TrackMe
- Add scripts/setup-trackme-certs.sh: generates self-signed certs; prints
  SSL_CERT_FILE hint for no-sudo local testing
- Update all test files (20 files) to read TRACKME_URL from env
- Update blocking-tests.yml and live-tests.yml:
  - Generate self-signed TLS certs; combine with system CA bundle
  - Start TrackMe via docker compose and wait for health
  - Set TRACKME_URL + SSL_CERT_FILE for the test run

Tested locally: 22/22 blocking tests pass against https://localhost:8443

* fix: use function-scoped fixtures in tlsfingerprint tests to avoid stale connections

TrackMe closes connections after each request; module-scoped CycleTLS clients
reuse stale connections from the pool, causing "use of closed network connection" errors.

* fix: disable connection reuse in live tests against local TrackMe

TrackMe closes TCP connections after each request. The Go transport
(loaded as a shared library) caches TLS connections in a global pool;
the next test reuses the already-closed connection, causing "use of
closed network connection". Setting enable_connection_reuse=False per
request forces a fresh roundTripper with empty cachedConnections,
matching how the blocking tests already work.

* fix: handle TrackMe HTTP/2 POST rejection and unpatched clients in live tests

- test_post_method: TrackMe rejects non-GET via HTTP/2 RST_STREAM causing timeout;
  skip gracefully instead of failing
- test_multiple_clients: explicitly pass enable_connection_reuse=False since these
  clients are created directly in the test body, bypassing the fixture wrapper

* test: mark external-resource tests as live; add Python matrix to live CI

Move test files that hit tls.peet.ws / scrapfly.io to the live test suite
by adding `pytestmark = pytest.mark.live`, so they no longer run in the
unit-test workflow (which has no network access to those endpoints).

Expand the live-tests workflow to a Python 3.10–3.13 matrix (ubuntu-only,
since TrackMe requires Docker) matching the breadth of the unit-test matrix.

Affected test files:
- test_async_ja3.py
- test_force_http1.py
- test_frame_headers.py
- test_http2.py
- test_http2_fingerprint.py
- test_integration.py
- test_ja3_fingerprints.py
- test_ja4_fingerprints.py

* fix: apply _no_reuse pattern to all live test fixtures hitting TrackMe

TrackMe closes the TLS connection after every response. The global Go
transport caches the closed connection; the next test gets
"use of closed network connection".

Fix by injecting enable_connection_reuse=False via setdefault in:
- conftest.py cycletls_client (covers test_integration, test_http2_fingerprint, test_tls13)
- test_force_http1.py client fixture
- test_http2.py cycle fixture
- test_ja3_fingerprints.py cycle_client fixture
- test_ja4_fingerprints.py cycle_client fixture
- test_async_ja3.py: add enable_connection_reuse=False to 5 individual requests
  that were missing it (async tests can't use the wrapper pattern)

* refactor(tests): rename TRACKME_URL env var to TLSFP_URL

Tests now read the local-server endpoint from TLSFP_URL instead of the
TrackMe-specific TRACKME_URL. Default remains https://tls.peet.ws so
local devs without the local server still hit production. The renamed
fixture (trackme_url -> tlsfp_url) and updated docstrings reflect that
the local target is Danny-Dasilva/tlsfingerprint.com (the source of
tls.peet.ws), not the third-party TrackMe image.

* ci(tests): launch Danny-Dasilva/tlsfingerprint.com Docker container for live tests

Replaces the third-party TrackMe Docker setup that this PR originally added
with the user's own tlsfingerprint.com server (the open-source code that
powers tls.peet.ws). Both blocking-tests and live-tests workflows now:

  1. Check out Danny-Dasilva/tlsfingerprint.com@master into .tlsfingerprint-server/
  2. Generate a self-signed cert via openssl
  3. Patch config.example.json to disable mongo/log_to_db
  4. Build + start the container via the upstream docker-compose.yml
     (NET_ADMIN/NET_RAW caps, ports 80/443/443-udp)
  5. Wait up to 90s for /api/clean to respond
  6. Inject TLSFP_URL=https://localhost and SSL_CERT_FILE so tests trust
     the self-signed cert and target the local server
  7. Tear the container down at the end of the run

Tests still fall back to https://tls.peet.ws when TLSFP_URL is unset, so
local developers without Docker keep working against production. Adds
tests/README.md documenting the local-dev path and test markers.

* ci: re-trigger after rebase on slim-matrix main

* ci: re-trigger

* ci: re-trigger after rebase on action-bumps main

* test(ja4): match JA4_r structurally to handle production vs local server padding

* test(blocking): apply JA4_r structural matcher to test_tlsfingerprint_blocking too

The previous commit (7ad1723) only patched tests/test_ja4_fingerprints.py.
The blocking test module (tests/test_tlsfingerprint_blocking.py) still
contained 5 exact-equality JA4_r assertions, including
test_ja4r_tls12_fingerprint_exact_match which was the actual CI failure
(production tls.peet.ws emits 't12d128h2' for 12 ciphers + 8 extensions
unpadded; the local tlsfingerprint.com Docker emits the spec-padded
't12d1208h2'; equality fails).

Promote parse_ja4r and assert_ja4r_equivalent helpers from
test_ja4_fingerprints.py into tests/conftest.py as public names so they
can be reused. Both test files now import from conftest. Replace the
five JA4_r equality assertions in test_tlsfingerprint_blocking.py with
structural matcher calls.

* test: scope _no_reuse to TLSFP_URL so httpbin tests keep keep-alive

The session-wide `enable_connection_reuse=False` wrapper was forcing
fresh TLS handshakes for *every* request, including ones against
httpbin.org. httpbin closes idle HTTP/1.1 connections aggressively, so
disabling reuse there causes "server closed idle connection" / EOF
errors on multi-request flows like:

  - test_http1_with_cookies (set cookie -> get cookie)
  - test_http1_with_redirects (redirect chain)

These two tests fail consistently across all 4 Python versions in the
Live Tests workflow.

Fix: only force `enable_connection_reuse=False` when the URL is the
local tlsfingerprint.com server (TLSFP_URL). Public endpoints
(httpbin.org) keep the default keep-alive behaviour.

---------

Co-authored-by: Stefan Meinecke <meinecke@greensec.de>
… JA3 corruption (replaces #40) (Danny-Dasilva#65)

* feat: add local_address parameter to bind outgoing TCP connections to a specific local IP

- Add default_local_address to config mapping and validation
- Add local_address field to Request schema with serialization
- Thread localAddress through Go client pooling, key generation, and dialer creation
- Apply local IP binding to both direct connections and proxy connections
- Validate local_address is a valid IP address before use

* fix: Do() drops ServerName/TLS13AutoRetry/DisableGrease; dispatchSSEAsync infinite loop on cancel/EOF

1. Do() silently dropped ServerName, TLS13AutoRetry, and DisableGrease when building the Browser struct. These fields were added to processRequest() in upstream commits 36f5171 (Draft Release #400, adds ServerName+TLS13AutoRetry) and a483b68 (Release 2.0.4 #390, adds DisableGrease) but were never propagated to Do().

2. dispatchSSEAsync used bare `break` inside `for { select { ... } }`, which only breaks out of the select, not the outer loop — causing an infinite spin on context cancellation, EOF, or read errors. Introduced in upstream 5990fef (Major Release 2.0.0 #387). Fixed by adding a `sseLoop:` label and using `break sseLoop`.

* fix(go): preserve JA3 supported_groups in TLS13AutoRetry proactive upgrade

convertJA3ForTLS13 was hard-coding tokens[3] = "29-23", silently dropping
P-384 (24), P-521 (25), FFDHE2048 (256), FFDHE3072 (257) and any other
groups from the JA3 string before the first handshake attempt. This made
TLS13AutoRetry (enabled by default) corrupt the fingerprint being applied.

Fix: filter the groups field using isTLS13CompatibleCurve() instead of
replacing it, preserving all RFC 8446 §4.2.7 compatible NamedGroups in
their original order. Also extend isTLS13CompatibleCurve() to include
FFDHE groups (256-260) which are valid TLS 1.3 NamedGroups per RFC 7919
and are advertised by Firefox/Safari in the supported_groups extension.

* docs+test: document local_address in api.py kwargs; add schema, validation and live tests

* docs(changelog): note Go core fixes from Danny-Dasilva#65

* fix(go-core): apply localAddress to SOCKS/HTTPS-proxy/HTTP3 dial paths

The PR #40 / Danny-Dasilva#65 localAddress implementation silently dropped the local-IP
bind on three proxy/transport paths. This change closes those gaps so the
LocalAddr the user provided actually controls the kernel-level bind on
the client->proxy socket on every supported scheme.

Gaps addressed (identified in the code-trace and network-research reports
at .claude/cache/agents/{code-trace,network-research}-localaddress-proxy):

1. SOCKS proxies (connect.go)
   The localAddress -> baseDialer block ran AFTER the scheme switch's
   socks5/socks5h/socks4 early returns, so the SOCKS dialers never saw
   LocalAddr. baseDialer is now built before the switch and:
   - socks5/socks5h: passed as the forward Dialer to proxy.SOCKS5, so
     the TCP-to-proxy connect honors LocalAddr.
   - socks4: when localAddress is set, h12.io/socks (which always uses
     net.DialTimeout internally with no LocalAddr hook) is bypassed via
     a small in-package socks4ContextDialer that performs the CONNECT
     handshake over a baseDialer.DialContext'd conn. With localAddress
     unset, the existing h12.io/socks path is preserved verbatim.

2. HTTPS proxies (connect.go)
   The default TLS-to-proxy step used tls.Dial(network, addr, &tlsConf),
   which bypasses any custom net.Dialer and silently dropped LocalAddr.
   Replaced with c.tlsDialer.DialContext (LocalAddr-aware) followed by
   tls.Client + HandshakeContext so the bind takes effect on the
   client->HTTPS-proxy TCP socket.

3. HTTP/3 / QUIC (http3.go)
   net.ListenPacket("udp", "") ignored localAddress entirely. Threaded
   LocalAddress through Browser -> roundTripper -> http3Dial; when set,
   the UDP socket is bound to <ip>:0. Empty localAddress preserves the
   prior kernel-chooses behavior.

Also expands the cycleTLSRequestOptions.LocalAddress field comment to
document that, when a Proxy is in the path, the bind applies to the
client->proxy hop only — the proxy host always sees this IP, but the
destination server never does. This is routing/interface control, not
an anonymizer against the proxy itself.

Verified: go build ./..., go vet ./... (only pre-existing index.go cancel
warnings remain, line numbers shifted by docstring), go test ./... -short
all pass on Go 1.26.0.

---------

Co-authored-by: Stefan Meinecke <meinecke@greensec.de>
…asilva#68)

Bumps [github.com/valyala/fasthttp](https://github.com/valyala/fasthttp) from 1.70.0 to 1.71.0.
- [Release notes](https://github.com/valyala/fasthttp/releases)
- [Commits](valyala/fasthttp@v1.70.0...v1.71.0)

---
updated-dependencies:
- dependency-name: github.com/valyala/fasthttp
  dependency-version: 1.71.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Upstream changes included:
- fix(go-core): local_address binding, Do() field drops, TLS13AutoRetry JA3 corruption (Danny-Dasilva#65)
- fix(transport): propagate enable_connection_reuse=False as DisableKeepAlives=true (Danny-Dasilva#52)
- fix(transport): lower IdleConnTimeout 90s to 60s (Danny-Dasilva#51)
- feat(extensions): expose proper utls types for IDs 17, 17613, 30032 (Danny-Dasilva#66)
- feat(tests): run live tests against local tlsfingerprint.com Docker instance (Danny-Dasilva#49)
- fix(release): build Linux .so targeting glibc 2.17 via zig cc (#41)
- ci: add CodeQL analysis, expand dependabot, bump action versions (Danny-Dasilva#47)
- ci: run macOS only for Python 3.13 to ease runner pressure (Danny-Dasilva#53)
- Various dependency bumps (utls, quic-go, x/net, gorilla/websocket, etc.)

Preserved fork features:
- Windows CI support, Python 3.9 and 3.14 test matrix
- Fingerprint registry and capture pipeline
- TLS fingerprint test variants
Comment thread tests/conftest.py Fixed
@smeinecke
smeinecke merged commit ab6435e into main May 14, 2026
38 checks passed
@smeinecke
smeinecke deleted the merge/upstream-latest branch May 14, 2026 21:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants