Structured Failure Reasons for Connection Callbacks #201
Closed
SeanTAllen
started this conversation in
Research
Replies: 1 comment
|
Implemented in PR #202 (merged). The implementation matched the design exactly — three type aliases ( |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Motivation
Courier's structured error type analysis identified that
_on_connection_failure(),_on_start_failure(), and_on_tls_failure()carry no information about why the failure occurred. Internally, lori knows the category of failure at each call site but discards it before invoking the callback. This blocks downstream libraries from reporting meaningful failure types.Current State
All three failure callbacks are parameterless:
A client connection can fail for three distinct reasons — DNS resolution, TCP connection, or SSL/TLS — but the application receives the same bare callback for all of them. The same applies to server start failures (currently only SSL) and TLS upgrade failures (auth vs general).
Proposed Types
Three new type aliases following the existing pattern (
SendError,StartTLSError):ConnectionFailureReason
StartFailureReason
Currently a single-variant union. The type alias exists for forward compatibility — adding a non-SSL failure mode later won't change the callback signature.
TLSFailureReason
Callback Changes
This is a breaking change. Code that overrides these callbacks must add the parameter. Code that relies on the default implementation is unaffected.
Semantic Mapping
_on_connection_failure(reason)pony_os_connect_tcpinitiates 0 connection attempts (name resolution failed)ConnectionFailedDNS_connecting_callbackdraining to 0)ConnectionFailedTCPclose()during connecting; inflight connections drain without successConnectionFailedTCP(DNS failure fires synchronously before user code runs, so only TCP is reachable here)hard_close()during connectingConnectionFailedTCP(same reasoning)ssl_ctx.client()throws)ConnectionFailedSSL_on_connectedConnectionFailedSSL_on_start_failure(reason)ssl_ctx.server()throws)StartFailedSSL_on_startedStartFailedSSL_on_tls_failure(reason)ssl.state()returnsSSLAuthFailduring upgradeTLSAuthFailedssl.state()returnsSSLErrorduring upgradeTLSGeneralErrorDesign Decisions
DNS vs TCP distinction:
pony_os_connect_tcpreturns the number of connection attempts initiated (not addresses resolved). If it returns 0, no TCP connections were attempted. If it returns >0 but all attempts fail, it's a TCP failure. A new internal flag tracks whether any connections were ever initiated.Known imprecision: If DNS succeeds but socket creation fails for every resolved address (e.g., the system is out of file descriptors),
pony_os_connect_tcpstill returns 0. This would be reported asConnectionFailedDNSeven though DNS succeeded. This is a rare edge case not worth distinguishing at this level.SSL creation vs handshake: Both map to the same reason (
ConnectionFailedSSL/StartFailedSSL). Session creation failure is a configuration problem (bad cert files); handshake failure is a runtime problem (cert validation, protocol mismatch). Both result in "the SSL layer prevented the connection," which is the actionable category.Application-initiated abort during connecting: When the application calls
close()orhard_close()during the connecting phase,_on_connection_failurestill fires withConnectionFailedTCP(DNS failure fires synchronously before user code can run, so only TCP is reachable). The application already knows it initiated the abort — the reason tells it what phase the connection was in, not that a spontaneous failure occurred.TLS auth vs general for initial SSL: The
SSLAuthFail/SSLErrordistinction fromssl.state()is available for initial SSL handshake failures too, but is collapsed intoConnectionFailedSSL/StartFailedSSL. The auth/general split is only exposed for_on_tls_failurewhere the connection was already established and the distinction is more actionable (the application may want to report "certificate invalid" vs "protocol error" to the user differently).What This Does NOT Include
sslpackage.pony_os_connect_tcpreturns a count, not an error code. Specific DNS errors (NXDOMAIN, SERVFAIL, timeout) would require ponyc runtime changes.These could be added in future iterations by changing the primitives to classes that carry data, though that would be another breaking change.
Implementation Sketch
Two new internal fields on
TCPConnection:_had_inflight: Bool— set whenpony_os_connect_tcpreturns > 0, used to distinguish DNS from TCP failure._ssl_auth_failed: Bool— set in_ssl_poll()whenSSLAuthFailis detected, before thehard_close()call. Only consulted in the_tls_upgradepath ofhard_close()where_on_tls_failurefires; for initial SSL connections, auth and general failures are both reported asConnectionFailedSSL/StartFailedSSL.Each callback call site reads the appropriate fields to determine the reason before invoking the callback.
All reactions