Repository navigation
fix(peer): send fatal-error answers before the connection closes - #149
Conversation
For a request with a fatal message error, such as an unsupported version or an invalid message length, Manager queued the required error answer and returned to the Server reader, which then stops reading and closes the transport. The session writer raced that close, so the answer was usually lost and observers often saw a second reply_failure event: in one run of 800 established TCP connections, 552 received no answer. StateMachine and Client write the answer before they return. For a fatal error, Manager.HandleMessageError now returns only after the answer its decision queued has been written, skipped or has failed, after the session has ended, or once Timers.Closing has elapsed since the decision. The Server's close therefore follows the answer, and a peer that stops reading cannot hold the reader past that bound. Non-fatal errors and decisions without an answer do not wait, and valid messages allocate nothing more. Behaviour changes, for accepted and dialed Manager connections over TCP, TLS and SCTP: - A request in Open with an unsupported version gets its 5011 answer, and one whose Message Length is shorter than the header or not a multiple of 4 gets its 5015 answer, before the connection closes (RFC 6733 sections 3 and 7.1.5, verified erratum 3805). - On an accepted connection, a CER with such a header error gets its error answer before the connection closes. - Observers see one event with Reply set to ProtocolReplyQueued for an answer that is written, and a ProtocolReplyFailure event only when the write itself fails. - A direct HandleMessageError call with a fatal error for a message with a valid header, such as an invalid AVP length (5014), also returns after its answer is written. The MessageErrorHandler contract now says that after a fatal error the Server reads no further and closes the connection when the call returns, so a handler must finish any answer it wants delivered before returning. TestAcceptedRestartFreshHandshake checks the replacement's admission after its DWR/DWA exchange instead of right after the handshake notification, which can arrive before admission is released. Closes #147.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configuration
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary
When a request arrived with a fatal message error (an unsupported version, or a Message Length shorter than the header or not a multiple of 4), Manager queued the error answer and returned to the Server reader. The reader then closed the transport, usually before the session writer had sent the answer. For a fatal error, Manager now returns only once that answer has been written or has failed, the session has ended, or
Timers.Closinghas elapsed since the decision. The answer therefore reaches the peer before the connection closes. TheMessageErrorHandlercontract now states this rule.Closes #147.
Behaviour
Answers delivered before EOF, Manager accepted and dialed:
Unchanged:
Testing
testing/synctesttests: every way the wait ends (written, failed, short write, skipped, terminal disposal, session end, the Closing bound), plus a peer that stops reading.BenchmarkInboundMessagesin peer and sm shows identical B/op and allocs/op to main in all 14 cases.TestAcceptedRestartFreshHandshakenow checks the replacement's admission after its DWR/DWA exchange. The handshake notification it waited on can arrive before admission is released, which made it fail intermittently under the race detector.Summary by cubic
Fixes fatal-error answers being lost when the connection closes. Manager now waits for the error answer before the Server closes the transport, so peers get the required 5011/5015/5014 answers instead of a dropped connection.
Previously Manager queued the answer and returned, letting the reader close the connection first — on 800 established TCP connections, 552 received no answer. Now
HandleMessageErrorreturns only after the answer is written, fails, the session ends, orTimers.Closingelapses since the decision, so a peer that stops reading can't hold the reader past that bound. Non-fatal errors and decisions without an answer don't wait.Contract
MessageErrorHandlerhandlers must finish any fatal-error answer before returning; documented indiam/server.go.Also fixed
TestAcceptedRestartFreshHandshakechecks the replacement's admission after its DWR/DWA exchange, since the handshake notification can arrive before admission is released.Closes #147.
Written for commit 63ee7e3. Summary will update on new commits.