Skip to content

roundtrip: support HTTP/2 fronting via ALPN-aware transport#9

Merged
myleshorton merged 8 commits into
mainfrom
fisk/h2-fronting
Jun 24, 2026
Merged

roundtrip: support HTTP/2 fronting via ALPN-aware transport#9
myleshorton merged 8 commits into
mainfrom
fisk/h2-fronting

Conversation

@myleshorton

@myleshorton myleshorton commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

What

Make the fronted round-trip ALPN-aware so it speaks whatever protocol the CDN edge selects — HTTP/1.1 or HTTP/2.

Why

dialFront offers a genuine browser ClientHello (Chrome 131), whose ALPN advertises h2, http/1.1. Many edges answer with h2 — including CloudFront (and Alibaba Cloud / Aliyun). But doRequest always wrapped the connection in an HTTP/1.1-only transport, so the HTTP/1.1 parser choked on the very first HTTP/2 SETTINGS frame:

net/http: HTTP/1.x transport connection broken:
malformed HTTP response "\x00\x00\x12\x04\x00\x00\x00\x00\x00..."

So fronting silently failed against any h2-negotiating edge. The "fix" of advertising only http/1.1 was rejected on purpose: ALPN travels in the cleartext ClientHello, so a Chrome fingerprint that offers only http/1.1 is itself a detectable anomaly. Better to keep the real ALPN and actually speak h2.

sequenceDiagram
    autonumber
    participant C as Client<br/>dialer.go
    participant E as Edge<br/>CloudFront / Aliyun
    participant R as doRequest<br/>roundtrip.go

    C->>E: ClientHello — ALPN offers h2, http/1.1<br/>dialer.go:78
    E-->>C: ServerHello — ALPN selects h2 ⚠️
    rect rgba(255, 200, 200, 0.3)
        Note over R: before: always the HTTP/1.1 transport 🐛<br/>h1 parser chokes on the h2 SETTINGS frame<br/>malformed HTTP response 0x00 0x00 0x12 0x04
    end
    Note over R: now: branch on negotiatedProtocol conn<br/>roundtrip.go:93
    alt negotiated h2
        R->>E: roundTripH2 — :authority = fronted host<br/>roundtrip.go:100
    else http/1.1 or none
        R->>E: newConnTransport — Host = fronted host<br/>roundtrip.go:106
    end
    E-->>C: response routed by the fronted host
Loading

How

  • negotiatedProtocol(conn) reads the settled ALPN off the utls connection.
  • On h2, roundTripH2 frames the request over the already-established conn via x/net/http2's Transport.NewClientConn. The library dials a fresh connection per request and never reuses it, so the ClientConn is single-shot: h2Body.Close() tears down the connection and its frame-reader goroutine when the response body closes.
  • Otherwise the existing HTTP/1.1 path is unchanged.

Routing is identical across protocols because rewriteRequest already points the request at the fronted host — that lands in the Host header (HTTP/1.1) or the :authority pseudo-header (HTTP/2), which is exactly what the CDN routes on.

Adds golang.org/x/net as a direct dependency. It was already pinned at v0.38.0 transitively (via utls), so no new version is introduced — go mod tidy only promotes it.

Test plan

  • New h2_test.go — offline, pipe-backed h2 mock server: asserts the fronted host lands in :authority, path + headers are preserved, and the response is HTTP/2. No real network.
  • Full suite green, including -race (validates the h2Body connection teardown).
  • Verified end-to-end against the live Aliyun CDN (in the companion provider PR): all edges front a cross-org Host over HTTP/2, 200 under the production Chrome_131 hello.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Enhanced HTTP/2 domain-fronting by using ALPN negotiation to ensure requests are framed and routed correctly.
  • Bug Fixes / Refactor

    • Improved protocol handling so HTTP/2 upgrade attempts are properly rejected and retried on mismatch.
    • Centralized HTTP/1.1 vs HTTP/2 request execution, including safer header handling for HTTP/2 and correct teardown behavior.
  • Tests

    • Added HTTP/2 coverage for request/response semantics, verify-with-POST behavior, header parsing/stripping, upgrade rejection, and close/error precedence.
  • Documentation / Chores

    • Updated README to clarify HTTP/1.1 + HTTP/2 support and refined dependency notes; refreshed module requirements.

Updates during review:

  • Addressed Copilot: centralized ALPN-aware framing in sendOverConn (vetting via verifyWithPost now speaks h2 too), h2Body.Close propagates the connection-close error, and hasConnectionUpgrade parses the comma-separated Connection token list.
  • Addressed CodeRabbit: test handshakes use HandshakeContext (cancelled at test end via t.Cleanup, not on helper return — the latter poisoned the conn on go 1.24).
  • Bumped utls v1.7.1 → v1.8.2 (uses Go 1.24 native ML-KEM, drops cloudflare/circl; HelloChrome_131 fingerprint unchanged; verified it still handshakes with real CloudFront/Aliyun edges over h2).

Frame the fronted request to match whichever protocol the edge selects
via ALPN. Under a genuine browser ClientHello (Chrome 131) both CloudFront
and Aliyun negotiate h2, which the HTTP/1.1-only transport could not parse
(it choked on the h2 SETTINGS frame with a "malformed HTTP response").

doRequest now branches on the negotiated protocol: h2 goes through
x/net/http2's single-shot NewClientConn (h2Body.Close tears down the conn
and its reader goroutine), http/1.1 keeps the existing path. Routing is
identical across both since the fronted host drives the Host header (h1)
or the :authority pseudo-header (h2).

Keeping the real Chrome ALPN (h2,http/1.1) avoids the fingerprint anomaly
of advertising only http/1.1. Adds golang.org/x/net as a direct dep
(already pinned v0.38.0 transitively via utls).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 23, 2026 03:53
@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

doRequest in roundtrip.go now inspects the ALPN-negotiated protocol on the uTLS connection and routes h2 traffic through a new sendOverConn dispatcher that creates a one-shot http2.ClientConn. Non-h2 paths use the existing transport logic. The front verification flow is updated to use the same sendOverConn routing. A new test file verifies HTTP/2 semantics and helper behavior, and go.mod and README.md are updated with required dependencies and feature documentation.

Changes

HTTP/2 ALPN routing

Layer / File(s) Summary
HTTP/2 core routing and helpers
roundtrip.go
Imports expanded with sync, utls, and golang.org/x/net/http2. Introduces errH2UpgradeUnsupported sentinel error, negotiatedProtocol to read ALPN from utls.UConn, hasConnectionUpgrade to detect upgrade tokens in Connection headers, stripConnHeaders to remove HTTP/2-forbidden headers, sendOverConn to dispatch requests based on ALPN with rewritten URL schemes, roundTripH2 creating a one-shot http2.ClientConn, and h2Body wrapping response bodies and closing the HTTP/2 client connection exactly once via sync.Once.
Front-pool error handling for HTTP/2 upgrade rejection
roundtrip.go
RoundTrip now treats errH2UpgradeUnsupported as a signal that the selected front is the wrong protocol, returning the front as healthy (triggering retry) instead of marking it as failed.
Request dispatch refactoring in doRequest
roundtrip.go
doRequest computes disableKeepAlives via hasConnectionUpgrade(req) and delegates all protocol-specific sending to sendOverConn, removing prior inline transport construction.
Front verification using HTTP/2 routing
domainfront.go
verifyWithPost replaces direct newConnTransport execution with sendOverConn(conn, req, true), ensuring the vetting POST request matches the negotiated ALPN protocol.
HTTP/2 integration and unit tests
h2_test.go
dialPipeH2 helper creates an in-memory TLS server with ALPN h2 and returns a handshaken utls.UConn client. TestDoRequest_HTTP2 validates correct HTTP/2 responses with :authority, path preservation, and header forwarding. TestVerifyWithPost_HTTP2 confirms POST over HTTP/2 and 202 acceptance. TestStripConnHeaders, TestSendOverConn_H2_RejectsUpgrade, and TestSendOverConn_H2_StripsForbiddenHeaders validate header and upgrade handling. TestH2Body_Close asserts exact-once closure and error precedence. TestHasConnectionUpgrade unit-tests the upgrade-token parser.
Dependency and documentation updates
go.mod, README.md
go.mod adds golang.org/x/net v0.38.0 and golang.org/x/text v0.23.0, updates utls to v1.8.2. README.md adds HTTP/1.1 and HTTP/2 feature bullet and updates the minimal-dependencies note to include x/net/http2.
Option helper formatting
domainfront.go
The With* option functions are reformatted for alignment consistency without functional changes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A rabbit runs the network trace,
"h2" announces ALPN's pace.
One-shot conn, frames aligned,
Authority and path combined.
x/net arrives in go.mod's keep—
HTTP/2 sleeps where protocols sleep! 🌐

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 54.55% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding HTTP/2 support via ALPN-aware transport. It is specific and directly reflects the primary objective of the PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fisk/h2-fronting

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR makes the domain-fronting round trip ALPN-aware so that requests are framed using the protocol actually negotiated during the TLS handshake (HTTP/1.1 or HTTP/2), preventing failures when CDN edges select h2.

Changes:

  • Branch request framing in doRequest based on negotiated ALPN and add an HTTP/2 code path using x/net/http2.
  • Add an offline, pipe-backed HTTP/2 unit test validating :authority, headers, path preservation, and HTTP/2 response.
  • Promote golang.org/x/net to a direct dependency and document HTTP/2 support in the README.

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
roundtrip.go Adds ALPN detection and an HTTP/2 RoundTrip implementation over the already-established TLS conn.
h2_test.go Introduces a pipe-backed HTTP/2 server test to validate correct authority/path/headers.
README.md Documents HTTP/2 support and updates the “minimal dependencies” bullet.
go.mod Promotes golang.org/x/net to a direct dependency.
go.sum Adds checksums for newly direct/indirect module entries.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread roundtrip.go Outdated
Comment thread roundtrip.go
Comment thread roundtrip.go Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@h2_test.go`:
- Line 41: Replace the direct call to srv.Handshake() with a context-aware
approach using HandshakeContext for explicit timeout control. Create a context
with a reasonable timeout using context.WithTimeout, then pass that context to
srv.HandshakeContext() instead of calling Handshake() without context. This
ensures the handshake operation has an explicit deadline and can be properly
cancelled if needed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 69a65a30-e137-4884-97d6-1a18c6bb5f79

📥 Commits

Reviewing files that changed from the base of the PR and between fdc839b and d1ab9e0.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (4)
  • README.md
  • go.mod
  • h2_test.go
  • roundtrip.go

Comment thread h2_test.go Outdated
myleshorton and others added 2 commits June 22, 2026 22:00
… Connection token list

- Centralize ALPN-aware framing in sendOverConn and route front vetting
  (verifyWithPost) through it too. Vetting gates whether a front becomes
  usable, and it ran unconditionally over HTTP/1.1 — so every h2 edge
  (CloudFront, Aliyun) would fail to vet and never enter the ready pool,
  defeating the h2 support. Adds TestVerifyWithPost_HTTP2.
- h2Body.Close now returns the cc.Close() error when the body close itself
  succeeded, instead of discarding connection-teardown failures.
- Detect "upgrade" as a token within the Connection header's comma-separated
  list (e.g. "keep-alive, Upgrade"), not just an exact match. Adds
  TestHasConnectionUpgrade.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Make h2Body.cc an io.Closer (a *http2.ClientConn in practice) so the
teardown precedence is unit-testable, and add TestH2Body_Close covering:
the cc error surfaces when body close succeeds, a body-close error takes
precedence, and the connection is closed exactly once.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 23, 2026 04:01
myleshorton added a commit that referenced this pull request Jun 23, 2026
Alibaba Cloud / Aliyun CDN edges route by the HTTP Host header (h1) /
:authority (h2) and ignore the TLS SNI for origin selection, so a censor
filtering on SNI sees only an innocent front domain (img.alicdn.com) while
the request reaches the real target. Verified 2026-06-23 cross-organization:
a TLS session bearing Alibaba's *.tbcdn.cn cert (SNI img.alicdn.com) reached
Bilibili (s1.hdslb.com) and Momo (img.momocdn.com) origins purely by Host.

This adds aliyun-provider.yaml: a complete, parseable Config with the
GlobalSign Root CA - R3 (anchors the Alibaba leaf chain), a verified seed
pool of edge IPs across three /24s, and frontingsnis driving img/gw/a
.alicdn.com SNIs. hostaliases + testurl are placeholders until Lantern
origins are onboarded as Aliyun CDN distributions (the edge silently drops
non-customer Host values).

aliyun_live_test.go (guarded by DOMAINFRONT_LIVE=1) drives the real
roundTripper: every edge completes TLS + GlobalSign verification under the
production Chrome_131 hello and fronts a cross-org Host over HTTP/2 (200).

Depends on #9 (HTTP/2 fronting) — Aliyun negotiates h2.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 6 changed files in this pull request and generated no new comments.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
h2_test.go (1)

35-35: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Use context-aware TLS handshake.

The past review comment flagging srv.Handshake() for lacking explicit timeout control remains valid. While net.Pipe() eliminates network stalls, adopting HandshakeContext aligns with Go best practices for goroutine lifecycle management.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@h2_test.go` at line 35, Replace the srv.Handshake() call with
srv.HandshakeContext() to provide explicit timeout control and align with Go
best practices for goroutine lifecycle management. Pass a context with an
appropriate timeout as the first argument to HandshakeContext, such as a context
derived from context.Background() with a timeout duration to prevent indefinite
blocking during the TLS handshake operation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@h2_test.go`:
- Line 35: Replace the srv.Handshake() call with srv.HandshakeContext() to
provide explicit timeout control and align with Go best practices for goroutine
lifecycle management. Pass a context with an appropriate timeout as the first
argument to HandshakeContext, such as a context derived from
context.Background() with a timeout duration to prevent indefinite blocking
during the TLS handshake operation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5dcbec52-1989-4ea4-9507-98358c52c141

📥 Commits

Reviewing files that changed from the base of the PR and between d1ab9e0 and 0580002.

📒 Files selected for processing (3)
  • domainfront.go
  • h2_test.go
  • roundtrip.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • roundtrip.go

myleshorton and others added 3 commits June 23, 2026 15:49
Use HandshakeContext with a 10s deadline for both the server and client
handshakes in dialPipeH2 so a broken pairing fails fast on the deadline
instead of hanging until the go test timeout. net.Pipe honors deadlines,
so the context can interrupt a stalled handshake.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
defer cancel() fired when dialPipeH2 returned, cancelling the handshake
context while the returned conn was still in use by the test. On go 1.24
that poisons the conn (past deadline) so the round-trip never reaches the
server and the test hangs to timeout (passed on 1.26 which resets the
deadline). t.Cleanup(cancel) defers cancellation to test end.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
v1.8.x uses Go 1.24's native ML-KEM (X25519MLKEM768) for Chrome's
post-quantum key share instead of vendoring cloudflare/circl, which is
why that transitive dep drops out of go.sum — it aligns with the existing
go 1.24 requirement. The HelloChrome_131 fingerprint we dial with is
unchanged.

Verified: full offline suite passes on go 1.24, and HelloChrome_131 still
handshakes with real CloudFront and Aliyun edges (negotiating h2 over TLS
1.3) under v1.8.2.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 23, 2026 21:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 6 changed files in this pull request and generated 1 comment.

Comment thread roundtrip.go
When ALPN negotiates HTTP/2, x/net/http2 rejects a request carrying
connection-specific headers (Transfer-Encoding, a Connection token other
than close/keep-alive, Upgrade) outright, so forwarding caller headers
as-is could fail on the h2 path while working on h1.

- sendOverConn now strips the connection-specific headers HTTP/2 forbids
  (RFC 7540 §8.1.2.2), including any header named in Connection, before
  framing over h2.
- A connection-upgrade request (e.g. WebSocket) can't be carried over h2,
  so it returns errH2UpgradeUnsupported. RoundTrip treats that as "wrong
  front for this request" — requeues the front as healthy and retries,
  ideally onto an http/1.1 front that can carry the upgrade — rather than
  marking the front bad or silently degrading the upgrade to a plain GET.

Tests: stripConnHeaders unit test, upgrade-rejected-over-h2 (server not
reached), and forbidden-headers-stripped request succeeds over h2.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 6 changed files in this pull request and generated 3 comments.

Comment thread roundtrip.go
Comment thread h2_test.go
Comment thread README.md Outdated
- stripConnHeaders doc: state it mutates req's headers in place and that
  both callers pass a request they own, instead of over-claiming it never
  touches the caller's request.
- dialPipeH2: close the conn via t.Cleanup so the server goroutine's
  ServeConn unblocks at test end — covers tests that never send a request
  (e.g. the rejected-upgrade case), avoiding a goroutine leak.
- README: x/net/http2 -> golang.org/x/net/http2 (full import path).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@myleshorton myleshorton merged commit 9359174 into main Jun 24, 2026
2 checks passed
@myleshorton myleshorton deleted the fisk/h2-fronting branch June 24, 2026 00:42
myleshorton added a commit to getlantern/kindling that referenced this pull request Jun 24, 2026
Picks up getlantern/domainfront#9, which makes the fronted round trip
ALPN-aware: it now frames requests as HTTP/2 when the CDN edge negotiates
h2 (CloudFront, Aliyun, ...) instead of speaking HTTP/1.1 over an h2
connection and failing with "malformed HTTP response". Also pulls the
utls v1.8.2 bump that ships with it (already the version kindling
resolves, so no utls change here).

domainfront's public API (New/Config/Provider/Masquerade/ParseConfig/
WithConfigURL) is unchanged, so this is a transparent dependency update.
Build, vet, and tests pass.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
myleshorton added a commit to getlantern/radiance that referenced this pull request Jun 24, 2026
- domainfront -> v0.0.0-20260624004218-93591749d736 (getlantern/domainfront#9):
  makes the fronted round trip ALPN-aware, framing requests as HTTP/2 when
  the CDN edge negotiates h2 (CloudFront, Aliyun, ...) instead of speaking
  HTTP/1.1 over the h2 connection and failing with a malformed response.
- kindling -> v0.0.0-20260624005117-737fcffe2860 (getlantern/kindling#40):
  the matching kindling bump, which also carries domainfront#9.

domainfront's public API is unchanged, so this is a transparent dependency
update. The kindling/* and config consumer packages build under both build
tag sets and the kindling tests pass.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
myleshorton added a commit to getlantern/lantern that referenced this pull request Jun 24, 2026
Propagates the HTTP/2 fronting fix down to the client:
- radiance    -> v0.0.0-20260624010426-955f5cbfe595 (getlantern/radiance#536)
- domainfront -> v0.0.0-20260624004218-93591749d736 (getlantern/domainfront#9, indirect)
- kindling    -> v0.0.0-20260624005117-737fcffe2860 (getlantern/kindling#40, indirect)
- lantern-box -> v0.0.95 (transitive; now required by radiance#536)

domainfront#9 makes the fronted round trip ALPN-aware — it frames requests
as HTTP/2 when the CDN edge negotiates h2 (CloudFront, Aliyun, ...) instead
of speaking HTTP/1.1 over the h2 connection and failing with a malformed
response. API-compatible. Ran go mod tidy and committed go.mod+go.sum
together so gomobile resolves lantern-box v0.0.95 (not a stale pin).

Verified: full Go build under the CI tag set (CGO_ENABLED=1, with_gvisor/
with_quic/with_wireguard/with_utls/with_grpc/with_conntrack), go vet, and
lantern-core tests all pass.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
myleshorton added a commit that referenced this pull request Jun 24, 2026
Alibaba Cloud / Aliyun CDN edges route by the HTTP Host header (h1) /
:authority (h2) and ignore the TLS SNI for origin selection, so a censor
filtering on SNI sees only an innocent front domain (img.alicdn.com) while
the request reaches the real target. Verified 2026-06-23 cross-organization:
a TLS session bearing Alibaba's *.tbcdn.cn cert (SNI img.alicdn.com) reached
Bilibili (s1.hdslb.com) and Momo (img.momocdn.com) origins purely by Host.

This adds aliyun-provider.yaml: a complete, parseable Config with the
GlobalSign Root CA - R3 (anchors the Alibaba leaf chain), a verified seed
pool of edge IPs across three /24s, and frontingsnis driving img/gw/a
.alicdn.com SNIs. hostaliases + testurl are placeholders until Lantern
origins are onboarded as Aliyun CDN distributions (the edge silently drops
non-customer Host values).

aliyun_live_test.go (guarded by DOMAINFRONT_LIVE=1) drives the real
roundTripper: every edge completes TLS + GlobalSign verification under the
production Chrome_131 hello and fronts a cross-org Host over HTTP/2 (200).

Depends on #9 (HTTP/2 fronting) — Aliyun negotiates h2.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

2 participants