feat: run live tests against a local TrackMe Docker instance#43
Open
smeinecke wants to merge 9 commits intoDanny-Dasilva:mainfrom
Open
feat: run live tests against a local TrackMe Docker instance#43smeinecke wants to merge 9 commits intoDanny-Dasilva:mainfrom
smeinecke wants to merge 9 commits intoDanny-Dasilva:mainfrom
Conversation
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
…bility uTLS (used by TrackMe) fails with 'bad record MAC' when connections arrive via Docker's veth bridge NAT (172.18.0.1). Direct connections from localhost work fine. Using network_mode: host bypasses Docker's bridge entirely so the CycleTLS test runner connects directly to TrackMe without NAT.
curl (OpenSSL) triggers 'bad record MAC' in uTLS server for certain cipher suites. The container's own wget-based healthcheck (BusyBox TLS) works fine, as does CycleTLS itself. Poll docker inspect health status instead of using curl so the wait loop uses the same path that's known to work.
…ale connections TrackMe closes connections after each request; module-scoped CycleTLS clients reuse stale connections from the pool, causing "use of closed network connection" errors.
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.
…ve 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
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
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)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Changes proposed in this pull request:
As introduced in #42 the live and blocking test suites hit
tls.peet.ws(a publicfingerprint-inspection service) directly, making CI dependent on external
availability and preventing deterministic fingerprint assertions. This PR
introduces a self-hosted TrackMe
container that spins up inside CI, so all TLS/HTTP2 fingerprint tests run
against
https://localhost:8443with no external dependencies.Docker setup
docker/trackme/Dockerfile- builds TrackMe from source(
golang:1.24-alpine, no pcap/QUIC, listens on 8443/8080)docker/trackme/config.jsonandstatic/assetsdocker-compose.test.ymlwithnetwork_mode: host(required: uTLSrejects connections arriving via Docker's veth bridge NAT, which causes
bad record MACerrors; host networking bypasses the bridge entirely)scripts/setup-trackme-certs.sh- generates a self-signed cert andprints the
SSL_CERT_FILEhint for local runsCI workflows (
blocking-tests.yml,live-tests.yml)Go TLS stack trusts it without
insecure_skip_verifydocker composeand wait for the container's ownHEALTHCHECK(BusyBox wget) to report healthy - curl was avoided becauseOpenSSL triggers
bad record MACin the uTLS server for some cipher suitesTRACKME_URL=https://localhost:8443andSSL_CERT_FILEfor thetest run
live-tests.ymlto a Python 3.10 – 3.13 matrix (ubuntu-only,since TrackMe requires Docker), matching the unit-test matrix breadth
Test files (20+ files updated)
BASE_URL/ hardcodedtls.peet.wsreferences replaced withos.environ.get("TRACKME_URL", "https://tls.peet.ws")for backwardcompatibility when running locally without TrackMe
tls.peet.ws/scrapfly.ioin the unit-testworkflow now carry
pytestmark = pytest.mark.liveso they are excludedfrom unit tests (no external network) and included in live tests (TrackMe
available)
Stale connection fixes
CycleTLS loads the Go HTTP transport as a process-global shared library.
TrackMe closes the TLS connection after every response; the global Go
transport caches the closed connection in
roundTripper.cachedConnections,and the next test reuses it - causing
"use of closed network connection".Root fix: inject
enable_connection_reuse=False(which bypasses the pooland creates a fresh
roundTripperper request) via a_no_reusewrapperapplied to every fixture that makes requests to TrackMe:
conftest.pysession-scopedcycletls_client(coverstest_integration,test_http2_fingerprint,test_tls13)test_force_http1,test_http2,test_ja3_fingerprints,test_ja4_fingerprintstest_async_ja3(async tests create theirown client per test; the wrapper pattern can't be applied)
test_module_api: usescycletls.set_default(enable_connection_reuse=False)in
setup_methodtest_post_methodskips gracefully when TrackMerejects non-GET via HTTP/2 RST_STREAM;
test_multiple_clientspassesenable_connection_reuse=Falseexplicitly since those clients are createddirectly in the test body and bypass the fixture
Before submitting
CONTRIBUTINGdocs.CONTRIBUTINGdocs.After submitting