Skip to content

THttpAsyncServer: fix HTTPS responses >64KB hanging on Windows IOCP+TLS#460

Merged
synopse merged 1 commit intosynopse:masterfrom
robertomr1969:fix-https-large-response-iocp-winiocp
Apr 28, 2026
Merged

THttpAsyncServer: fix HTTPS responses >64KB hanging on Windows IOCP+TLS#460
synopse merged 1 commit intosynopse:masterfrom
robertomr1969:fix-https-large-response-iocp-winiocp

Conversation

@robertomr1969
Copy link
Copy Markdown
Contributor

Three bugs compound to prevent large TLS responses from being delivered:

  • Add ifSeparateWLock to THttpAsyncServerConnection.AfterCreate and Recycle to use separate R/W locks and avoid deadlock between ProcessRead and Write()
  • Add ifWriteWait (USE_WINIOCP) to bypass WaitFor(0) check in ProcessWrite so pending fWr data is retried when wieSend IOCP event fires
  • Set SSL_MODE_ENABLE_PARTIAL_WRITE|SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER in TOpenSslNetTls.AfterAccept so SSL_write returns partial byte count instead of -1 on WANT_WRITE, allowing mORMot to correctly advance the buffer pointer for the next SSL_write call

Without these fixes, SSL_write sends 4 TLS records (~65KB) then returns
WANT_WRITE. mORMot retries with a different buffer pointer (fWr.Buffer),
violating OpenSSL contract. The remaining data is never delivered and
the connection hangs indefinitely.

Tested on Windows with a 103KB HTTPS JSON response. Not reproduced on
Linux (epoll path unaffected).

@synopse
Copy link
Copy Markdown
Owner

synopse commented Apr 12, 2026

Sounds like a clever investigation.

But I don't see SSL_MODE_ENABLE_PARTIAL_WRITE and the other flag in the PR.

@robertomr1969
Copy link
Copy Markdown
Contributor Author

robertomr1969 commented Apr 13, 2026 via email

@myonlylonely
Copy link
Copy Markdown

Will this PR be merged?

@synopse
Copy link
Copy Markdown
Owner

synopse commented Apr 18, 2026

I am on holidays, AFK for a few days.
I will first try to reproduce the issue, and validate the fix.

@synopse
Copy link
Copy Markdown
Owner

synopse commented Apr 28, 2026

About Add ifSeparateWLock to THttpAsyncServerConnection.AfterCreate and Recycle to use separate R/W locks and avoid deadlock between ProcessRead and Write()...

I am confused. It should not deadlock from the same thread, since TMultiLightLock takes the current thread ID into account, so it is reentrant.
Was this assumption made by an AI? Or do you have more precise material to share?

Copy link
Copy Markdown
Owner

@synopse synopse left a comment

Choose a reason for hiding this comment

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

See my comment about the deadlock assumption.

@robertomr1969
Copy link
Copy Markdown
Contributor Author

Thanks for the review, and sorry for the delayed clarification.

To answer your direct question: yes, this PR was drafted with significant AI assistance (Claude Code). I want to be upfront about that. However, the core technical claim — the OpenSSL contract violation — is independently verifiable and I believe it is real regardless of how it was found.

On the SSL flags: If they are not visible in the diff, they are missing from the commit and that is a mistake in the PR. SSL_MODE_ENABLE_PARTIAL_WRITE is the most important fix. Without it, after the OS send buffer fills (~65KB on Windows), SSL_write returns WANT_WRITE and requires a retry with the exact same pointer and length. mORMot retries with fWr.Buffer which has already advanced — this violates the OpenSSL contract and causes the hang. This behavior is documented in the OpenSSL SSL_write man page.

On ifSeparateWLock: I defer to your knowledge of TMultiLightLock. If it correctly handles the cross-thread read/write scenario between ProcessRead and Write(), then this change may be unnecessary. The SSL partial write fix alone may be sufficient to resolve the hang.

I can strip ifSeparateWLock and ifWriteWait from the PR and submit only the SSL_MODE_ENABLE_PARTIAL_WRITE change if you prefer a minimal, reviewable fix.

@synopse
Copy link
Copy Markdown
Owner

synopse commented Apr 28, 2026

Yes, only the TLS part, please.

Without SSL_MODE_ENABLE_PARTIAL_WRITE, SSL_write returns WANT_WRITE (not
a partial byte count) when the OS send buffer fills (~65KB on Windows).
mORMot then retries with fWr.Buffer which has already advanced, violating
the OpenSSL contract that requires the same pointer/length on retry.
The remaining data is never sent and the connection hangs indefinitely.

SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER is also set so mORMot can retry with
a different buffer pointer after WANT_WRITE without triggering an error.

Reproduces with HTTPS responses >64KB on Windows IOCP. Not reproduced on
Linux (epoll path unaffected).
@robertomr1969 robertomr1969 force-pushed the fix-https-large-response-iocp-winiocp branch from 51beec8 to d2943a4 Compare April 28, 2026 16:27
@robertomr1969
Copy link
Copy Markdown
Contributor Author

PR actualized.

@synopse synopse merged commit 58bf523 into synopse:master Apr 28, 2026
synopse pushed a commit that referenced this pull request May 1, 2026
- added the #459 #460 OpenSSL fix also for client sockets, because they expect the same behavior than plain recv/send socket API calls
- applied the options at the CTX level, which are inherited by every accepted socket.
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.

3 participants