feat(example): showcase how to manually compose NetworkBehaviour
Description
Showcase how to compose NetworkBehaviours manually without the derive macro or Either.
Notes & open questions
Although this is not the recommended approach(see #3902, huge shoutout to thomas by the way), it can show us how the protocols and substreams get negotiated. Suggestions welcome!
Change checklist
- [ ] I have performed a self-review of my own code
- [ ] I have made corresponding changes to the documentation
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] A changelog entry has been made in the appropriate crates
Thanks for this effort @drHuangMHT!
But looking through the code of this PR, I am unsure if this will really help newcomers. It's 1200 lines of code that are mostly just the expanded NetworkBehavior macro, right? I think it will be difficult for users to extract the key aspects out of this.
I agree that it would be great to have more resources on how to manually compose NetworkBehaviors, but I think the format of a Tutorial would be more suited. That way the key aspects of delegating calls, polling order and composed event types could be explained with some code snippets, but we don't need all of this boilerplate code. Wdyt?
It's 1200 lines of code that are mostly just the expanded
NetworkBehaviormacro, right?
Yes indeed.
I think it will be difficult for users to extract the key aspects out of this.
Maybe for some, yes. But I'm expecting advanced users to read this. Plus I myself am not very familiar with how rust-libp2p works so I am also seeking for explanation for some things.
I agree that it would be great to have more resources on how to manually compose
NetworkBehaviors, but I think the format of a Tutorial would be more suited.
Yes, but there is also timeliness problem with tutorials right?
That way the key aspects of delegating calls, polling order and composed event types could be explained with some code snippets, but we don't need all of this boilerplate code. Wdyt?
Some people are more comfortable with a working example. But I have to admit that there is too much boilerplate code.
What I write above are not very strong argument for this PR though. Do you think some simple macros will make it better?
Do you think some simple macros will make it better?
Well, we already have the NetworkBehavior macro that does everything :smile:. I don't think adding more macros in the example will make it easier to understand.
Yes, but there is also timeliness problem with tutorials right?
You mean with them staying up-to-date? Generally I agree, but I would argue that central concepts when composing network behaviors will stay the same.
Generel question: when would one want to manually compose such a "parent" NetworkBehavior in the first place?.
Generel question: when would one want to manually compose such a "parent"
NetworkBehaviorin the first place?.
I would like to see if it is possible to negotiate protocols dynamically(instead of Toggle) and it seems it is? That's not possible with the derive macro.
I would like to see if it is possible to negotiate protocols dynamically(instead of Toggle) and it seems it is?
What do you mean with negotiate protocols dynamically?
What do you mean with negotiate protocols dynamically?
So I have this concern: when connecting to an unknown peer, one should leak as little information as possible. I had this misconecption when I was new to rust-libp2p: after the transport has been negotiated, all locally supported protocols will be sent to remote at once to let the remote learn what protocols to speak. Then I wrote my own macros to generate a NetworkBehaviour in the hope that it would give me more flexibility on when to expose more information. But I ended up blindly following the derive macro, just without using Either because I have no idea how it works.
After expanding my own macro here in this PR, I learned that the protocol negotiation actually happens when a ConnectionHandler negotiates a substream, which does not necessarily have to happen immediately after transport negotiation. So what I mean by "negotiate protocols dynamically" is actually "can I delay the protocol negotiation(or more precisely, substream negotiation)". So it is not Swarm or NetworkBehaviour's job to handle this, but rather ConnectionHandler implementation.
So let's take ping as an example. Once the handler is "spawned" and poll() is called, it tries to negotiate an outbound stream. And if the negotiation failed, it set its state to Inactive because it means the remote doesn't support ping, or at least at the time. If the remote later initates a substream request, it will be accepted because it works independently from ping::Handler::poll(), but the handler is "inactive" so it won't respond to the pings from the remote, which can be a problem if we want to delay the negotiation.
So how to implement this? My idea is that I will selectively collect some of all supported protocols, and later make some changes. As for propagating the changes to the remote, we already have identify and ConnectionEvent::RemoteProtocolsChange.
My understanding can be wrong here so feel free to correct me.