Skip to content

tport_ws: read WSS frames that OpenSSL already decrypted - #349

Open
izibrat wants to merge 1 commit into
freeswitch:masterfrom
izibrat:tport-ws-read-tls-pending
Open

izibrat wants to merge 1 commit into
freeswitch:masterfrom
izibrat:tport-ws-read-tls-pending

Conversation

@izibrat

@izibrat izibrat commented Oct 27, 2026

Copy link
Copy Markdown

A TLS record can carry more than one WebSocket frame. tport_recv_stream_ws() reads one frame per call. A second
frame that arrived in the same record stays in OpenSSL's buffer, and nothing on the socket wakes the poll for it, so
it is processed only when the peer sends something else.

Symptom. A browser phone (SIP.js) answers an INVITE with 100 Trying and 180 Ringing back to back. Behind a
TLS-terminating WebSocket proxy (nginx re-encrypting towards FreeSWITCH), both frames land in one TLS record. The 180
is seen only when the next message arrives: seconds later, or never if the call is cancelled first. The B-leg is then
reported as never ringing.

Fix. Return 2 while SSL_pending() reports buffered data, so the transport reads again at once. This is what
tport_recv_stream_tls() already does with tls_pending().

Measured (FreeSWITCH 1.11.3, sofia-sip d636a9f, Debian 13): a WSS client wrote two SIP OPTIONS in one TLS record.

1st reply 2nd reply
unpatched ~40 ms ~3000 ms (only after a third message was sent)
patched 7–46 ms same moment as the 1st

Two messages written 50 ms apart (separate records) are answered at once either way.

🤖 Generated with Claude Code

A TLS record can carry more than one WebSocket frame. tport_recv_stream_ws()
reads one frame per call, so a second frame that arrived in the same record
stays in OpenSSL's buffer. Nothing on the socket wakes the poll for it, and it
is processed only when the peer sends something else: a "180 Ringing" sent
right behind a "100 Trying" waits until the next message, often seconds later
or never.

This is common behind a TLS-terminating WebSocket proxy (nginx re-encrypting
towards FreeSWITCH writes back-to-back frames into one record).

Return 2 while SSL_pending() reports buffered data, so the transport reads
again at once, as tport_recv_stream_tls() already does with tls_pending().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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