Aleth waits for 2 seconds after sending disconnect packet#5707
Aleth waits for 2 seconds after sending disconnect packet#5707halfalicious merged 4 commits intoethereum:masterfrom
Conversation
Codecov Report
@@ Coverage Diff @@
## master #5707 +/- ##
==========================================
+ Coverage 63.06% 63.14% +0.07%
==========================================
Files 353 353
Lines 30196 30193 -3
Branches 3386 3386
==========================================
+ Hits 19044 19064 +20
+ Misses 9918 9896 -22
+ Partials 1234 1233 -1 |
|
@twinstar26 Is this ready for review? If so, can you please request a review from myself / gumb0 / chfast? |
CHANGELOG.md
Outdated
| - Added: [#5634](https://github.com/ethereum/aleth/pull/5634) Bootnodes for Rinkeby and Goerli. | ||
| - Added: [#5640](https://github.com/ethereum/aleth/pull/5640) Istanbul support: EIP-1702 Generalized Account Versioning Scheme. | ||
| - Added: [#5690](https://github.com/ethereum/aleth/issues/5690) Istanbul support: EIP-2028 transaction data gas cost reduction. | ||
| - Added: [#5650](https://github.com/ethereum/aleth/issues/5650) Aleth should wait for 2 seconds to close socket after sending disconnect to peer. |
There was a problem hiding this comment.
Changelog should describe the behavior with your fix applied (e.g. "Aleth now waits for 2 seconds after sending disconnect to peer before closing socket")
libp2p/Session.cpp
Outdated
| RLPStream s; | ||
| prep(s, DisconnectPacket, 1) << (int)_reason; | ||
| sealAndSend(s); | ||
| auto disconnectTimer = m_server->createTimer(); |
There was a problem hiding this comment.
Session::disconnect will still close the socket immediately (Session::drop closes the socket and you're calling it in both your timer handler and outside the handler if it hasn't already been called i.e. !m_dropped). Since what we want to do is delay the socket closure for 2 seconds, you should move the code which closes the socket out of Session::drop and into the timer handler and also not call drop inside of the timer handler.
There was a problem hiding this comment.
@halfalicious
Thanks for the reviews!
Okay so I have to copy required logic of drop into disconnect and never call drop from inside of disconnect and its handler. Am I right??
There was a problem hiding this comment.
You should move the socket closure code out of drop and into the handler. Don’t call drop inside of the handler but still call it in your else statement in disconnect (since we want to prevent further session operations after we have sent the disconnect, you can see that we check for m_drop in various places in session and return early)
There was a problem hiding this comment.
@halfalicious
So pushed changes according to your suggested changes. But was curious what will happen to code thats calling drop() (apart from disconnect) separately??
There was a problem hiding this comment.
This still doesn't look right:
-
Changing
drop()now breaks all the cases where it is called from other methods ofSession. I guess those are reasonable cases where we wanted to just disconnect immediately without sendingDisconnectpacket (with current changes we won't disconnect) -
Calling
disconnectnow doesn't ever setm_droppedtotrue. @halfalicious Maybe you meant calling current form ofdropfrom insideif (!m_dropped)clause?
There was a problem hiding this comment.
@gumb0 You're right we need to also call drop() inside of the !m_dropped clause.
I don't think this new form of drop breaks anything though...even if drop doesn't close the socket all shared_ptr Session instances will still go out of scope and therefore Session will be garbage collected by Host and the socket will be closed when the dtor is invoked. Note that functions in which we pass new shared_ptr instances (shared_from_this) to handlers check if (m_dropped) and then prematurely terminate before creating new handlers (e.g. doRead). In fact, after taking a closer look at the Session code, I don't think we need to explicitly close the socket at all in any place other than the dtor (unless there's some benefit to closing the socket sooner rather than later that I'm not aware of)? If we just call drop() without closing the socket the Session instance will be destroyed once all queued handlers which contain a shared_ptr instance are executed and the Host garbage collects the Session.
So, to summarize:
- I think we should call drop() in both the if (!m_dropped) and else cases...in fact, we can simplify this by removing the "else" case and always calling drop() after the
if (!m_dropped)code block sincedropreturns early ifm_droppedis true. - I think we can remove all cases where we close the socket except for in the dtor.
@gumb0 : Thoughts?
There was a problem hiding this comment.
I don't think we need to explicitly close the socket at all in any place other than the dtor (unless there's some benefit to closing the socket sooner rather than later that I'm not aware of)?
@halfalicious The benefit I see is that we immediately stop answering any further requests on this connection, not waiting for all handlers to terminate, so maybe it's more efficient...
Also, what if some malicious peer keeps DOSing us with some invalid requests on this connection - it will be kept in the handler queue (because the new data keeps coming) and never disconnected, it seems so.
There was a problem hiding this comment.
@halfalicious pointed out to me:
I think that we will still stop reading packets from a peer if we call drop without closing the socket since we check for m_dropped at the beginning of Session::doRead before proceeding with the read:
Lines 318 to 322 in d8f4498
I think I agree with this reasoning.
There was a problem hiding this comment.
One corner case to check: spec requires that we disconnet immediately upon receiving Disconnect packet ourselves. My experiment confirms that this looks to be working correctly with current changes in this PR:
TRACE 08-08 11:54:24 p2p p2pcap Disconnect from (##a8768590…@51.141.78.53:30303)
DEBUG 08-08 11:54:24 p2p p2pcap Disconnect (reason: Peer had too many connections.) from (##a8768590…@51.141.78.53:30303)
DEBUG 08-08 11:54:24 p2p net Closing peer session with (##a8768590…@51.141.78.53:30303)
There was a problem hiding this comment.
One corner case to check: spec requires that we disconnet immediately upon receiving Disconnect packet ourselves. My experiment confirms that this looks to be working correctly with current changes in this PR:
TRACE 08-08 11:54:24 p2p p2pcap Disconnect from (##a8768590…@51.141.78.53:30303) DEBUG 08-08 11:54:24 p2p p2pcap Disconnect (reason: Peer had too many connections.) from (##a8768590…@51.141.78.53:30303) DEBUG 08-08 11:54:24 p2p net Closing peer session with (##a8768590…@51.141.78.53:30303)
@gumb0 Confirmed by looking at the code - when we receive a disconnect packet we call drop immediately in Session::interpretP2pPacket:
Lines 142 to 154 in 78355a2
libp2p/Session.cpp
Outdated
| RLPStream s; | ||
| prep(s, DisconnectPacket, 1) << (int)_reason; | ||
| sealAndSend(s); | ||
| auto disconnectTimer = m_server->createTimer(); |
There was a problem hiding this comment.
I think this timer will be destroyed right after this function ends, and outstanding wait operation will be cancelled. (Probably should be made a member?)
There was a problem hiding this comment.
@gumb0 Agreed, in my prototype I had captured the timer in the handler but making it a member works as well (and also enables us to make it unique_ptr rather than shared_ptr as I see you've done) 😄
|
I moved the calls to timer's member inside |
|
Rebased. |
libp2p/Session.cpp
Outdated
| m_disconnectTimer = m_server->createTimer( | ||
| std::chrono::seconds(2), [self, this, _reason](boost::system::error_code) { | ||
| bi::tcp::socket& socket = m_socket->ref(); | ||
| if (socket.is_open()) |
There was a problem hiding this comment.
One thing I still don't like here is that we have the same code (closing the socket) here in the timer handler and then in destructor. And destructor is executed anyway, so perhaps we could avoid this duplication, maybe just posting empty handler? @halfalicious what do you think?
There was a problem hiding this comment.
@gumb0 Yes I agree with this, in fact I suggested this earlier in this PR 😄
|
Confirmed that we wait for 2 seconds now: |
|
@gumb0 I think this is ready to be merged, please give it a quick look when you get a chance |
libp2p/Host.h
Outdated
|
|
||
| std::shared_ptr<CapabilityHostFace> capabilityHost() const { return m_capabilityHost; } | ||
|
|
||
| /// Execute work on the network thread after an @_expiryDelay delay. |
There was a problem hiding this comment.
That was doxygen syntax to refer to the parameter inside description
(http://www.doxygen.nl/manual/commands.html#cmda)
| /// Execute work on the network thread after an @_expiryDelay delay. | |
| /// Execute work on the network thread after an @a _expiryDelay delay. |
Closing the socket in the handler is unnecessary since it will be closed when the dtor executes.
Fixes #5650
Old PR #5658