New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
asyncio: add a new Protocol.connection_failed() method #67522
Comments
While working on the issue bpo-23243 ("asyncio: emit ResourceWarning warnings if transports/event loops are not explicitly closed"), I saw that SelectorEventLoop._accept_connection() currently ignores errors on the creation of a transport. When a server gets an incoming connection: it calls sock.accept(), creates a protocol, and then create the transport. It doesn't wait until the connection_made() of the protocol is called (until the transport was successfully created). For example, for a SSL server, the client may decide to close the connection because it doesn't trust the server certificate. In this case, the SSL handshake fails at server side. Currently, the user of the asyncio API cannot decide how to handle this failure. I propose to call the connection_lost() method of the protocol with the exception, even if the connection_made() method of the protocol was not called (and will never be called). Attached patch implements this idea. It's a change in the undocumented "state machine" of protocols. Before, it was not possible to switch directly to connection_lost(): there is even an assertion which ensures that it never occurs in some unit tests. A server may log the connection failure, blacklist temporarely the client IP, etc. Problem: Since the protocol doesn't know the transport yet, it doesn't have access to the socket, and so cannot retrieve the IP address of the client. Maybe a new method should be added to protocols to handle this case? How do other event loops (Twisted, eventlet, Tornado, etc.) handle failures on incoming connection? |
IMO, connection_lost() should never be called if connection_made() wasn't called. That's a breach of the API contract. (at one point, I suggested a connection_failed() for that purpose, but it was shut down - it was in relationship to the idea of a reconnecting client, but can still be more broadly useful) |
FYI I opened a thread about this issue on the Tulip mailing list. Antoine Pitrou added the comment:
Yes, I agree.
I like the "connection_failed" name. We may call |
Oh, accept_error.patch causes issues with the new SSL implementation. SSLProtocol.feed_data() is called before SSLProtocol.connection_made() is called. _SelectorSocketTransport constructor calls loop.add_reader() immediatly, but it only schedules a call to protocol.connection_made() with loop.call_soon(). The call to loop.add_reader() should maybe be scheduled after the call to connection_made()? To ensure that protocol methods (feed_data) are not called before connection_made() has been called. |
Fixed by: Only start reading when connection_made() has been called: Other fix related to this issue: Wake-up the waiter if it is not done yet. |
New patch which adds a new Protocol.connection_failed() method. The method is called when the creation of the transport failed, ie. when the connection failed, on SSL handshake failure for example. The patch also closes the transport on connection failure (avoid a ResourceWarning with patch of the issue bpo-23243). |
I splitted connection_failed.patch in two parts:
|
Oops. "... *before* connection_made() was called". |
New changeset c4fd6df9aea6 by Victor Stinner in branch '3.4': |
I commited a simplified version of accept_connection_failed.patch, without the call to connection_failed(). |
The consensus looks to be to reject this feature, so I close the issue. I already commits a compromise: log an error in debug mode. |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: