Skip to content

fix: synchronize WebSocket sends to prevent deflater crash#32

Merged
barrydeen merged 1 commit intomainfrom
fix/websocket-send-thread-safety
Feb 22, 2026
Merged

fix: synchronize WebSocket sends to prevent deflater crash#32
barrydeen merged 1 commit intomainfrom
fix/websocket-send-thread-safety

Conversation

@barrydeen
Copy link
Copy Markdown
Owner

Summary

Fix FATAL EXCEPTION crash in OkHttp's MessageDeflater.deflate().

Problem

OkHttp's MessageDeflater (used for permessage-deflate WebSocket compression) is not thread-safe. When RelayPool.sendToAll() iterates relays and calls relay.send() from multiple coroutines concurrently, the deflater's internal state gets corrupted:

FATAL EXCEPTION: OkHttp TaskRunner
java.lang.IllegalArgumentException: Failed requirement.
    at okhttp3.internal.ws.MessageDeflater.deflate(MessageDeflater.kt:39)
    at okhttp3.internal.ws.WebSocketWriter.writeMessageFrame(WebSocketWriter.kt:155)
    at okhttp3.internal.ws.RealWebSocket.writeOneFrame$okhttp(RealWebSocket.kt:527)

Fix

Add sendLock (synchronized) to serialize all ws.send() calls per Relay instance, including drainPendingMessages() on connect.

Testing

  • Load feed with many relays connected → verify no crash
  • Force reconnect → verify pending messages drain without crash
  • Rapid feed switches → verify concurrent sends don't crash

OkHttp's MessageDeflater (permessage-deflate) is not thread-safe.
Concurrent ws.send() calls from RelayPool's sendToAll/sendToReadRelays
crash with 'Failed requirement' in MessageDeflater.deflate().

Add sendLock to serialize all writes to each WebSocket connection,
including drainPendingMessages on connect.
@barrydeen barrydeen merged commit 867499d into main Feb 22, 2026
barrydeen added a commit that referenced this pull request Feb 22, 2026
OkHttp's MessageDeflater (permessage-deflate) has multiple threading
bugs that cause FATAL crashes:
1. Concurrent ws.send() → 'Failed requirement' in deflate()
2. Concurrent failWebSocket/close → NPE in Buffer.write() during
   DeflaterSink cleanup

The sendLock from PR #32 only fixes case 1. Case 2 happens in OkHttp's
internal cleanup path (failWebSocket → WebSocketWriter.close →
MessageDeflater.close) which we cannot synchronize.

Fix: strip Sec-WebSocket-Extensions header from server responses via a
network interceptor. This prevents OkHttp from activating the deflater
entirely. For Nostr's small JSON messages, compression savings are
negligible compared to crash-free operation.

The sendLock is retained as defense-in-depth.
@barrydeen barrydeen deleted the fix/websocket-send-thread-safety branch March 4, 2026 01:29
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