Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions libp2p/host/basic_host.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,11 @@ async def _swarm_stream_handler(self, net_stream: INetStream) -> None:
protocol, handler = await self.multiselect.negotiate(
MultiselectCommunicator(net_stream), self.negotiate_timeout
)
if protocol is None:
await net_stream.reset()
raise StreamFailure(
"Failed to negotiate protocol: no protocol selected"
)
except MultiselectError as error:
peer_id = net_stream.muxed_conn.peer_id
logger.debug(
Expand Down
4 changes: 3 additions & 1 deletion libp2p/security/security_multistream.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ async def select_transport(
# Select protocol if non-initiator
protocol, _ = await self.multiselect.negotiate(communicator)
if protocol is None:
raise MultiselectError("fail to negotiate a security protocol")
raise MultiselectError(
"Failed to negotiate a security protocol: no protocol selected"
)
# Return transport from protocol
return self.transports[protocol]
4 changes: 3 additions & 1 deletion libp2p/stream_muxer/muxer_multistream.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,9 @@ async def select_transport(self, conn: IRawConnection) -> TMuxerClass:
else:
protocol, _ = await self.multiselect.negotiate(communicator)
if protocol is None:
raise MultiselectError("fail to negotiate a stream muxer protocol")
raise MultiselectError(
"Fail to negotiate a stream muxer protocol: no protocol selected"
)
return self.transports[protocol]

async def new_conn(self, conn: ISecureConn, peer_id: ID) -> IMuxedConn:
Expand Down
1 change: 1 addition & 0 deletions newsfragments/837.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added multiselect type consistency in negotiate method. Updates all the usages of the method.
37 changes: 37 additions & 0 deletions tests/core/host/test_basic_host.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
from unittest.mock import (
AsyncMock,
MagicMock,
)

import pytest

from libp2p import (
new_swarm,
)
Expand All @@ -10,6 +17,9 @@
from libp2p.host.defaults import (
get_default_protocols,
)
from libp2p.host.exceptions import (
StreamFailure,
)


def test_default_protocols():
Expand All @@ -22,3 +32,30 @@ def test_default_protocols():
# NOTE: comparing keys for equality as handlers may be closures that do not compare
# in the way this test is concerned with
assert handlers.keys() == get_default_protocols(host).keys()


@pytest.mark.trio
async def test_swarm_stream_handler_no_protocol_selected(monkeypatch):
key_pair = create_new_key_pair()
swarm = new_swarm(key_pair)
host = BasicHost(swarm)

# Create a mock net_stream
net_stream = MagicMock()
net_stream.reset = AsyncMock()
net_stream.muxed_conn.peer_id = "peer-test"

# Monkeypatch negotiate to simulate "no protocol selected"
async def fake_negotiate(comm, timeout):
return None, None

monkeypatch.setattr(host.multiselect, "negotiate", fake_negotiate)

# Now run the handler and expect StreamFailure
with pytest.raises(
StreamFailure, match="Failed to negotiate protocol: no protocol selected"
):
await host._swarm_stream_handler(net_stream)

# Ensure reset was called since negotiation failed
net_stream.reset.assert_awaited()
Loading