Skip to content

Measure the missing ECH call instead of describing it - #55

Merged
yschimke merged 1 commit into
mainfrom
claude/ech-conscrypt-build-oncco9
Aug 14, 2026
Merged

Measure the missing ECH call instead of describing it#55
yschimke merged 1 commit into
mainfrom
claude/ech-conscrypt-build-oncco9

Conversation

@yschimke

Copy link
Copy Markdown
Owner

Three changes that come at the same question from different sides.

A platform that makes the call

EchConscryptTest shows Conscrypt can encrypt a client hello — but from a socket factory, with a config list the suite kept for itself. It never says whether the config list OkHttp resolved would arrive.

EchConscryptPlatform is a Platform that makes the one call ConscryptPlatform omits. EchPlatformTest then runs EchTest's requests through ordinary public API with it installed: same client, same servers, same assertions. The difference between the two suites is one call to Conscrypt.setEchConfigList and nothing else, which makes the pair a measurement rather than an argument.

This can't be done upstream yet — OkHttp's master won't compile against a Conscrypt that has these methods, since no published Conscrypt does. That's why the platform lives here instead of waiting on lysine-dev/okhttp#9559. It is not a proposal for how OkHttp should do it: ConscryptPlatform is final with a private constructor, so this reimplements the parts it needs, and upstream uses the newer setEchParameters API rather than the setEchConfigList this google3-export build exposes.

On the public API rule. A Platform is declared nowhere but okhttp3.internal, so this file has to import it. Rather than delete the rule for everybody, a file can opt out by saying why, and checkPublicApiOnly prints every exemption on every run:

// USES-OKHTTP-INTERNALS: is a Platform, which OkHttp only declares internally.
public-api-only: EchConscryptPlatform.kt exempt — is a Platform, which OkHttp only declares internally.

Verified with no network involved:

default platform        = Jdk9Platform
client socket factory   = sun.security.ssl.SSLSocketFactoryImpl
installed platform      = EchConscryptPlatform
client socket factory   = org.conscrypt.OpenSSLSocketFactoryImpl   conscrypt=true
client trust manager    = okhttp.testbed.network.EchEnablingTrustManager
after uninstall         = Jdk9Platform

The handshake itself needs the servers, so the result arrives with the next network run.

Runs say what they ran on

Every workflow now records a platform, and collect_results.py carries it per suite rather than per version — a version card merges an Android artifact with a JVM one, so "which platform" is a property of the suite.

  • JVM: measured from the JVM's own properties, so a runner image or toolchain change shows up as a different platform instead of a stale label — Eclipse Adoptium JDK 21.0.8 · amd64.
  • Android: Android emulator API 37.0 · x86_64, from env vars the emulator step and the metadata both read, so the two can't drift.

This replaces the hardcoded "javaVersion": "21", which was neither measured nor true of the Android suite.

The ECH page leads with results

site/topics/ech.html now opens with a matrix built from latest.json: one row per server, one column per way of reaching it, platform read from the run. The gap between "OkHttp as shipped" and "OkHttp + the missing call" becomes a column comparison instead of a paragraph.

Rendered in a browser against synthesised results — including a suite being absent, and Android's [emulator-5554 - 17] case-name suffix — with no console errors.

Checked

  • :network:compileTestKotlin against 5.5.0-SNAPSHOT. Worth noting: the default pinned version excludes all ECH sources, so compiling without -PokhttpVersion silently checks nothing here.
  • collect_results.py end to end on synthetic artifacts: platform per suite, both platforms on the card, echPlatformTest ranked as a finding rather than a failure.
  • All three workflow YAMLs parse; echPlatformTest registers on the default version too.

Generated by Claude Code

Three changes that answer the same question from different sides.

**A platform that makes the call.** `EchConscryptTest` shows Conscrypt can encrypt
a client hello, but it shows it from a socket factory, with a config list the
suite kept for itself — so it never says whether the config list OkHttp resolved
would arrive. `EchConscryptPlatform` is a `Platform` that makes the one call
`ConscryptPlatform` omits, and `EchPlatformTest` runs `EchTest`'s requests
through ordinary public API with it installed. Same client, same servers, same
assertions; the difference between the two suites is one call to
`Conscrypt.setEchConfigList` and nothing else.

Upstream can't do this yet — OkHttp's master won't compile against a Conscrypt
with these methods, because no published Conscrypt has them — which is why the
platform is written here rather than waiting on #9559.

It has to import `okhttp3.internal`: a Platform is declared nowhere else. Rather
than delete the public-API rule for everybody, a file can now opt out by saying
why, and `checkPublicApiOnly` prints every exemption on every run:

    // USES-OKHTTP-INTERNALS: is a Platform, which OkHttp only declares internally.

Verified without a network: after `install()`, a plain `OkHttpClient()` gets
Conscrypt's socket factory and the ECH-enabling trust manager, and `uninstall()`
puts `Jdk9Platform` back.

**Runs say what they ran on.** Each workflow records a platform — measured from
the JVM's own properties, or from the emulator's declared API level and
architecture rather than a second copy of them — and `collect_results.py` carries
it per suite rather than per version, because a version card merges an Android
artifact with a JVM one. Replaces the hardcoded `"javaVersion": "21"`, which was
neither measured nor true of the Android suite.

**The ECH page leads with results.** A matrix of server against platform, read
from `latest.json`: one row per server, one column per way of reaching it, so the
gap between "OkHttp as shipped" and "OkHttp + the missing call" is a column
comparison rather than a paragraph. Checked in a browser against synthesised
results, including the case where a suite is absent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CqiK79k9uoWsn2AzgXpHMA
@yschimke
yschimke merged commit b4e4ec1 into main Aug 14, 2026
7 checks passed
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