Skip to content

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

Open
xinyitohh wants to merge 1 commit into
freeswitch:masterfrom
xinyitohh:tport-ws-ssl-pending
Open

xinyitohh wants to merge 1 commit into
freeswitch:masterfrom
xinyitohh:tport-ws-ssl-pending

Conversation

@xinyitohh

@xinyitohh xinyitohh commented Nov 6, 2026 •

Copy link
Copy Markdown

tport_recv_stream_ws() reads one WebSocket frame per call and returns 1. tport_recv_event() only loops again when the receive function returns more than 1. Over TLS, a second frame in the same TLS record is already decrypted in OpenSSL's buffer, so the socket is not readable and nothing wakes the poll for it. The frame is processed only when more data arrives on the connection.

The plain TLS transport already handles this (tls_pending(...) ? 2 : 1). This change does the same for WSS: return 2 while SSL_pending() reports buffered data.

Related: #349 is the same two-line change, found through a different symptom (100 Trying and 180 Ringing arriving in one record). I hit it independently, so I am adding a second trigger and measurements. Either PR is fine to merge; if #349 goes first, this one can be closed.

Symptom

A browser SIP client (JsSIP) behind a TLS-terminating nginx sends ACK and the authenticated INVITE back to back after a 407. Both frames land in one TLS record. The INVITE is not processed until another message arrives, so the first outbound call after an idle period hangs; when nothing else arrives, Timer B ends it with 408 after 32 s.

Test

A client writes two WebSocket frames over TLS and checks whether the second one gets a reply (stuck = no reply).

scenario before after
two writes, no gap, loopback straight to the WSS listener 20/20 stuck 0/12 stuck
two writes, no gap, public internet via nginx about 21/40 stuck 0/40 stuck
5 / 10 / 20 / 50 ms gap, public internet not measured 0/40 stuck each
1 / 2 / 5 / 10 / 20 / 50 ms gap, loopback not measured 0/12 stuck each

Environment: FreeSWITCH 1.10.13-dev. The numbers above were measured with sofia-sip built from 100d357 plus this change, and then re-measured with a build of this exact commit (current master 3cc785e plus this change) installed under the same FreeSWITCH: loopback 0/12 stuck for every gap, public internet 0/40 stuck for every gap.

🤖 Generated with Claude Code

tport_recv_stream_ws() reads one WebSocket frame per call and returns 1,
and tport_recv_event() only loops again when the receive function returns
more than 1. Over TLS, a second frame in the same TLS record is already in
OpenSSL's buffer, so the socket is not readable and nothing wakes the poll
for it. The frame is processed only when more data arrives.

The plain TLS transport avoids this with "tls_pending() ? 2 : 1". Do the
same for WSS with SSL_pending().

Seen as: a client sending ACK and the authenticated INVITE back to back
after a 407. The INVITE sat unprocessed, so the first outbound call after
idle hung until Timer B (408).
Copilot AI balanced review requested due to automatic review settings November 6, 2026 01:24

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

@izibrat

izibrat commented Nov 6, 2026

Copy link
Copy Markdown

Thanks for looking into this — it is the same fix as #349 (opened 2026-10-27): tport_recv_stream_ws() returns 2 while tls_pending() holds data, mirroring tport_recv_stream_tls(). The code changes are identical apart from the comment, so it may be easiest to keep one of the two open. We have been running #349's change on media hosts behind nginx's TLS-to-TLS WebSocket route, where a 180 Ringing coalesced into the same TLS record as the preceding frame otherwise waited until the next message (~3 s, or never).

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