Skip to content

HTTPS WebSocket queued writes can stall after TLS WANT_READ

Summary

The HTTPS WebSocket send path treats MBEDTLS_ERR_SSL_WANT_READ and MBEDTLS_ERR_SSL_WANT_WRITE as the same condition, queues unsent data, and only drains queued data from the FD_WRITE branch. If mbedTLS needs read-side progress to finish a write, queued response data can remain stuck indefinitely.

This blocks Beta 0.7.3 because a slow or adversarial HTTPS Web UI client can wedge a request thread and interact badly with shutdown.

Evidence

  • srchybrid/WebSocket.cpp:51 maps both TLS WANT results through IsTlsWant.
  • srchybrid/WebSocket.cpp:310 attempts direct HTTPS writes and queues remaining bytes when IsTlsWant is returned.
  • srchybrid/WebSocket.cpp:473 handles FD_READ by reading only; it does not retry queued writes when the previous write wanted read-side progress.
  • srchybrid/WebSocket.cpp:497 drains queued sends only under FD_WRITE.
  • srchybrid/WebSocket.cpp:550 keeps the accepted thread alive while m_pHead remains queued.

Execution Plan

  1. Split TLS WANT handling so the connection remembers whether the pending TLS operation needs read readiness, write readiness, or either.
  2. Ensure queued writes are retried after the readiness condition requested by mbedTLS, including WANT_READ.
  3. Avoid busy loops by returning to WSAEventSelect/wait readiness between retries.
  4. Preserve legacy plain HTTP send behavior.
  5. Add a seam-level test for a TLS write returning WANT_READ with queued response data.
  6. Add a shutdown regression where a queued HTTPS response does not keep the WebSocket subsystem alive indefinitely.

Acceptance Criteria

  • Queued HTTPS sends drain after both TLS WANT_READ and WANT_WRITE paths.
  • The fix does not introduce a CPU spin under repeated TLS WANT responses.
  • Shutdown can drain or abandon a queued HTTPS response without leaking threads or handles.
  • Existing REST malformed/concurrent request coverage remains green.

Validation

  • 2026-05-08: Done in app commit dfcf1fe.
  • python -m emule_workspace validate --workspace-root . passed.
  • python -m emule_workspace build app --workspace-root . --config Release --platform x64 --variant main passed; log root workspaces\v0.72a\state\build-logs\20260508-094234.
  • Code validation: queued WebSocket sends now drain through one helper, and the HTTPS accepted-client loop retries queued writes after read readiness so a previous TLS WANT_READ cannot wait forever for FD_WRITE.