Skip to content

Replace AndroidAsync WebSocket transport with OkHttp#254

Draft
mzorz wants to merge 1 commit into
trunkfrom
mzorz/okhttp-websocket-transport
Draft

Replace AndroidAsync WebSocket transport with OkHttp#254
mzorz wants to merge 1 commit into
trunkfrom
mzorz/okhttp-websocket-transport

Conversation

@mzorz

@mzorz mzorz commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

What

Replaces the realtime WebSocket transport from AndroidAsync (com.koushikdutta.async:androidasync:3.1.0) to OkHttp.

AndroidAsync is unmaintained (last release ~2022) and the source of intermittent transport crashes in the field — notably the java.lang.AssertionError: double connect callback during the SSL handshake (AsyncHttpClient$3.onConnectCompleted). OkHttp is actively maintained and already a dependency of most host apps (Simplenote ships OkHttp 5.x), so this removes a dead dependency without adding real weight.

Changes

  • OkHttpWebSocketProvider (new) — implements WebSocketManager.ConnectionProvider, mapping Connection{close,send} / ConnectionListener{onConnect,onMessage,onDisconnect,onError} onto OkHttp's WebSocketListener. OkHttp's send/close are thread-safe, so it drops the main-looper Handler hop the AndroidAsync provider needed.
  • onFailure routing — after a successful upgrade, OkHttp's onFailure is a terminal disconnect (no onClosed follows), so it's routed through listener.onDisconnect() (notifies channels, cancels the heartbeat, schedules reconnect), matching the AndroidAsync end/closed-callback behavior. A pre-open failure goes to onError.
  • User-Agent sanitization — Simperium passes the session id as the User-Agent header; OkHttp validates header values strictly (AndroidAsync did not), so non-printable/non-ASCII chars are stripped.
  • AndroidClient.buildChannelProvider switched to the new provider. AsyncWebSocketProvider is kept in the tree for now (AndroidAsync is still used for the auth HTTP client).

Validation

  • Compiles against the existing ConnectionProvider interface (:Simperium:compileDebugJavaWithJavac).
  • OkHttp delivers large inbound messages fully reassembled (16 KB → 2 MB verified via a MockWebServer probe), removing the AndroidAsync large-frame instability.
  • Exercised end-to-end in Simplenote Android (composite build): connects (HTTP 101), authenticates, indexes, downloads entity bodies, and recovers cleanly from transient Software caused connection abort disconnects via the new onFailure routing.

Before merge (open items)

  • Cert pinning. configureSSL() applies a custom SSLContext + PinningTrustManager to the AndroidAsync mHttpClient. The new OkHttpClient is a plain client and does not replicate it. Port the pinning to the OkHttp builder (sslSocketFactory + X509TrustManager, or CertificatePinner) — or make a deliberate decision to drop the (ancient AndroidPinning 1.0.0) pinning. Flagged with a TODO in AndroidClient.
  • Align the OkHttp version with the host app (this PR pins 4.12.0 for toolchain safety; Simplenote ships 5.1.0).
  • Optional: once the auth client is also migrated, AndroidAsync can be removed entirely.

Context

This came out of investigating large notes not syncing to Android (Simplenote SIMPL-58). That turned out to be a server-side object-size cap (the server returns 413 EXCEEDS_MAX_SIZE), not a transport bug — so this OkHttp swap is a maintenance/stability improvement, not the fix for that issue. It's worth doing on its own merits (kills the unmaintained, crash-prone transport).

AndroidAsync is unmaintained (last release ~2022) and the source of
intermittent transport crashes (e.g. the 'double connect callback'
AssertionError during the SSL handshake). Swap the realtime WebSocket
to OkHttp, which is actively maintained and already shipped by most
host apps.

- Add OkHttpWebSocketProvider implementing WebSocketManager.ConnectionProvider,
  mapping Connection/ConnectionListener onto OkHttp's WebSocketListener.
- Route a post-open onFailure through onDisconnect (OkHttp's terminal
  failure has no onClosed) so channels are notified, the heartbeat is
  cancelled, and a reconnect is scheduled — matching AndroidAsync.
- Sanitize the session id sent as User-Agent (OkHttp validates header
  values strictly; AndroidAsync did not).
- Wire AndroidClient.buildChannelProvider to the new provider; keep
  AsyncWebSocketProvider in the tree for now.

Note: cert pinning is not yet ported to the OkHttp client (see TODO);
must be addressed before merge.
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.

1 participant