Skip to content

Let writes wait when a renegotiation meets application data - #320

Merged
quinnj merged 1 commit into
masterfrom
jq/renegotiation-appdata
Oct 23, 2026
Merged

quinnj merged 1 commit into
masterfrom
jq/renegotiation-appdata

Conversation

@quinnj

@quinnj quinnj commented Oct 23, 2026

Copy link
Copy Markdown
Member

Follow-up to #317. During a peer-initiated renegotiation (opt-in via ssl_conf_renegotiation!), mbedtls_ssl_write returns MBEDTLS_ERR_SSL_WAITING_SERVER_HELLO_RENEGO when it reads application data while waiting for the ServerHello. mbedtls keeps that data for the next read; mbedtls_ssl_read treats the code as normal. The write path treated it as fatal: it abandoned the connection, so the reader lost the data and the server saw EOF (SSL - Unexpected message at ServerHello in renegotiation).

The writer now waits until a reader has taken the held data (ssl_check_pending turns false), then retries. Simply retrying is wrong: each ssl_write would read the held record again and count it against renego_max_records (16), failing within ~16 ms.

Trade-off: with no task reading, such a write now waits until something reads the data or the connection closes, instead of failing. HTTP.jl reads and writes concurrently, and doesn't enable renegotiation.

New testset: the server requests renegotiation and keeps sending; the client's reader takes the first byte and pauses while the client writes. Master: the write throws and the reader gets only "A". With this change: "ping" arrives, and the reader gets "ABC". It passed 3/3 locally.

Found by a fourth independent review pass; reproduced and checked here.

🤖 Generated with Claude Code

During a peer-initiated renegotiation, mbedtls_ssl_write returns
MBEDTLS_ERR_SSL_WAITING_SERVER_HELLO_RENEGO when it reads application data while waiting for
the ServerHello, and keeps that data for the next read. The write path treated it as fatal:
it abandoned the connection, so the reader lost data and the server saw EOF. The writer now
waits until a reader has taken the held data, then retries. (Just retrying would count the
held record against renego_max_records each time and fail.)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 88.99%. Comparing base (0fe84e1) to head (5a115b1).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #320      +/-   ##
==========================================
+ Coverage   88.93%   88.99%   +0.06%     
==========================================
  Files          12       12              
  Lines         904      909       +5     
==========================================
+ Hits          804      809       +5     
  Misses        100      100              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@quinnj
quinnj merged commit 06ecccb into master Oct 23, 2026
9 checks passed
@quinnj
quinnj deleted the jq/renegotiation-appdata branch October 23, 2026 20:46
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