From d8f309f8fea2c2b8a3d63991c6356ee0f2b55538 Mon Sep 17 00:00:00 2001 From: Jack Robison Date: Mon, 21 Oct 2019 19:02:59 -0400 Subject: [PATCH] fast discovery with multi_m_search -remove m_search_args from Gateway --- aioupnp/gateway.py | 12 ++++-------- aioupnp/protocols/ssdp.py | 15 +++++++++++++++ tests/test_gateway.py | 9 +++------ tests/test_upnp.py | 16 ++++++++-------- 4 files changed, 30 insertions(+), 22 deletions(-) diff --git a/aioupnp/gateway.py b/aioupnp/gateway.py index 95dd81c..a19fa00 100644 --- a/aioupnp/gateway.py +++ b/aioupnp/gateway.py @@ -8,7 +8,7 @@ from aioupnp.util import get_dict_val_case_insensitive from aioupnp.constants import SPEC_VERSION, SERVICE from aioupnp.commands import SOAPCommands, SCPDRequestDebuggingInfo from aioupnp.device import Device, Service -from aioupnp.protocols.ssdp import fuzzy_m_search, m_search +from aioupnp.protocols.ssdp import fuzzy_m_search, m_search, multi_m_search from aioupnp.protocols.scpd import scpd_get from aioupnp.serialization.ssdp import SSDPDatagram from aioupnp.util import flatten_keys @@ -69,12 +69,10 @@ def parse_location(location: bytes) -> typing.Tuple[bytes, int]: class Gateway: - def __init__(self, ok_packet: SSDPDatagram, m_search_args: typing.Dict[str, typing.Union[int, str]], - lan_address: str, gateway_address: str, + def __init__(self, ok_packet: SSDPDatagram, lan_address: str, gateway_address: str, loop: typing.Optional[asyncio.AbstractEventLoop] = None) -> None: self._loop = loop or asyncio.get_event_loop() self._ok_packet = ok_packet - self._m_search_args = m_search_args self._lan_address = lan_address self.usn: bytes = (ok_packet.usn or '').encode() self.ext: bytes = (ok_packet.ext or '').encode() @@ -149,7 +147,6 @@ class Gateway: 'gateway_xml': self._xml_response.decode(), 'services_xml': self._service_descriptors, 'services': {service.SCPDURL: service.as_dict() for service in self._services}, - 'm_search_args': OrderedDict(self._m_search_args), 'reply': self._ok_packet.as_dict(), 'soap_port': self.port, 'registered_soap_commands': self._registered_commands, @@ -170,14 +167,13 @@ class Gateway: ] while True: if not igd_args: - m_search_args, datagram = await fuzzy_m_search( + datagram = await multi_m_search( lan_address, gateway_address, timeout, loop, ignored, unicast ) else: - m_search_args = OrderedDict(igd_args) datagram = await m_search(lan_address, gateway_address, igd_args, timeout, loop, ignored, unicast) try: - gateway = cls(datagram, m_search_args, lan_address, gateway_address, loop=loop) + gateway = cls(datagram, lan_address, gateway_address, loop=loop) log.debug('get gateway descriptor %s', datagram.location) await gateway.discover_commands() requirements_met = all([gateway.commands.is_registered(required) for required in required_commands]) diff --git a/aioupnp/protocols/ssdp.py b/aioupnp/protocols/ssdp.py index ce9f8b2..f3794f9 100644 --- a/aioupnp/protocols/ssdp.py +++ b/aioupnp/protocols/ssdp.py @@ -152,6 +152,21 @@ async def m_search(lan_address: str, gateway_address: str, datagram_args: Dict[s protocol.disconnect() +async def multi_m_search(lan_address: str, gateway_address: str, timeout: int = 3, + loop: Optional[asyncio.AbstractEventLoop] = None, + ignored: Set[str] = None, unicast: bool = False) -> SSDPDatagram: + protocol, gateway_address, lan_address = await listen_ssdp( + lan_address, gateway_address, loop, ignored, unicast + ) + datagram_args = list(packet_generator()) + try: + return await protocol.m_search(address=gateway_address, timeout=timeout, datagrams=datagram_args) + except asyncio.TimeoutError: + raise UPnPError("M-SEARCH for {}:{} timed out".format(gateway_address, SSDP_PORT)) + finally: + protocol.disconnect() + + async def _fuzzy_m_search(lan_address: str, gateway_address: str, timeout: int = 30, loop: Optional[asyncio.AbstractEventLoop] = None, ignored: Set[str] = None, diff --git a/tests/test_gateway.py b/tests/test_gateway.py index 1512947..734335e 100644 --- a/tests/test_gateway.py +++ b/tests/test_gateway.py @@ -176,8 +176,7 @@ class TestDiscoverDLinkDIR890L(AsyncioTestCase): [('serviceType', 'urn:schemas-upnp-org:service:WANIPConnection:1'), ('serviceId', 'urn:upnp-org:serviceId:WANIPConn1'), ('controlURL', '/soap.cgi?service=WANIPConn1'), ('eventSubURL', '/gena.cgi?service=WANIPConn1'), ('SCPDURL', '/WANIPConnection.xml')])}, - 'm_search_args': OrderedDict([('HOST', '239.255.255.250:1900'), ('MAN', 'ssdp:discover'), ('MX', 1), - ('ST', 'urn:schemas-upnp-org:device:WANDevice:1')]), 'reply': OrderedDict( + 'reply': OrderedDict( [('CACHE_CONTROL', 'max-age=1800'), ('LOCATION', 'http://10.0.0.1:49152/InternetGatewayDevice.xml'), ('SERVER', 'Linux, UPnP/1.0, DIR-890L Ver 1.20'), ('ST', 'urn:schemas-upnp-org:device:WANDevice:1'), ('USN', 'uuid:11111111-2222-3333-4444-555555555555::urn:schemas-upnp-org:device:WANDevice:1')]), @@ -239,7 +238,7 @@ class TestDiscoverDLinkDIR890L(AsyncioTestCase): async def test_discover_commands(self): with mock_tcp_and_udp(self.loop, tcp_replies=self.replies): gateway = Gateway( - SSDPDatagram("OK", self.gateway_info['reply']), self.gateway_info['m_search_args'], + SSDPDatagram("OK", self.gateway_info['reply']), self.client_address, self.gateway_info['gateway_address'], loop=self.loop ) await gateway.discover_commands() @@ -274,9 +273,7 @@ class TestDiscoverNetgearNighthawkAC2350(TestDiscoverDLinkDIR890L): [('serviceType', 'urn:schemas-upnp-org:service:WANIPConnection:1'), ('serviceId', 'urn:upnp-org:serviceId:WANIPConn1'), ('controlURL', '/ctl/IPConn'), ('eventSubURL', '/evt/IPConn'), ('SCPDURL', '/WANIPCn.xml')])}, - 'm_search_args': OrderedDict( - [('HOST', '239.255.255.250:1900'), ('MAN', '"ssdp:discover"'), ('MX', 1), - ('ST', 'upnp:rootdevice')]), 'reply': OrderedDict( + 'reply': OrderedDict( [('CACHE_CONTROL', 'max-age=1800'), ('ST', 'upnp:rootdevice'), ('USN', 'uuid:11111111-2222-3333-4444-555555555555::upnp:rootdevice'), ('Server', 'R7500v2 UPnP/1.0 miniupnpd/1.0'), ('Location', 'http://192.168.0.1:5555/rootDesc.xml')]), diff --git a/tests/test_upnp.py b/tests/test_upnp.py index fcc9388..096a0d4 100644 --- a/tests/test_upnp.py +++ b/tests/test_upnp.py @@ -45,7 +45,7 @@ class TestGetExternalIPAddress(UPnPCommandTestCase): self.replies.update({request: b"HTTP/1.1 200 OK\r\nServer: WebServer\r\nDate: Wed, 22 May 2019 03:25:57 GMT\r\nConnection: close\r\nCONTENT-TYPE: text/xml; charset=\"utf-8\"\r\nCONTENT-LENGTH: 365 \r\nEXT:\r\n\r\n\n\n\t\n\t\t\n11.222.3.44\n\n\t\n\n"}) self.addCleanup(self.replies.pop, request) with mock_tcp_and_udp(self.loop, tcp_replies=self.replies): - gateway = Gateway(self.reply, self.m_search_args, self.client_address, self.gateway_address, loop=self.loop) + gateway = Gateway(self.reply, self.client_address, self.gateway_address, loop=self.loop) await gateway.discover_commands() upnp = UPnP(self.client_address, self.gateway_address, gateway) external_ip = await upnp.get_external_ip() @@ -57,7 +57,7 @@ class TestGetExternalIPAddress(UPnPCommandTestCase): request: b"HTTP/1.1 200 OK\r\nServer: WebServer\r\nDate: Wed, 22 May 2019 03:25:57 GMT\r\nConnection: close\r\nCONTENT-TYPE: text/xml; charset=\"utf-8\"\r\nCONTENT-LENGTH: 354 \r\nEXT:\r\n\r\n\n\n\t\n\t\t\n\n\n\t\n\n"}) self.addCleanup(self.replies.pop, request) with mock_tcp_and_udp(self.loop, tcp_replies=self.replies): - gateway = Gateway(self.reply, self.m_search_args, self.client_address, self.gateway_address, loop=self.loop) + gateway = Gateway(self.reply, self.client_address, self.gateway_address, loop=self.loop) await gateway.discover_commands() upnp = UPnP(self.client_address, self.gateway_address, gateway) with self.assertRaises(UPnPError): @@ -73,7 +73,7 @@ class TestMalformedGetExternalIPAddressResponse(UPnPCommandTestCase): b"11.222.3.44\n\n\t\n\n"}) self.addCleanup(self.replies.pop, self.get_ip_request) with mock_tcp_and_udp(self.loop, tcp_replies=self.replies): - gateway = Gateway(self.reply, self.m_search_args, self.client_address, self.gateway_address, loop=self.loop) + gateway = Gateway(self.reply, self.client_address, self.gateway_address, loop=self.loop) await gateway.discover_commands() upnp = UPnP(self.client_address, self.gateway_address, gateway) with self.assertRaises(UPnPError): @@ -84,7 +84,7 @@ class TestMalformedGetExternalIPAddressResponse(UPnPCommandTestCase): b"11.222.3.44\n\n\t\n\n"}) self.addCleanup(self.replies.pop, self.get_ip_request) with mock_tcp_and_udp(self.loop, tcp_replies=self.replies): - gateway = Gateway(self.reply, self.m_search_args, self.client_address, self.gateway_address, loop=self.loop) + gateway = Gateway(self.reply, self.client_address, self.gateway_address, loop=self.loop) await gateway.discover_commands() upnp = UPnP(self.client_address, self.gateway_address, gateway) external_ip = await upnp.get_external_ip() @@ -95,7 +95,7 @@ class TestMalformedGetExternalIPAddressResponse(UPnPCommandTestCase): b"11.222.3.44\n\n\t\n\n"}) self.addCleanup(self.replies.pop, self.get_ip_request) with mock_tcp_and_udp(self.loop, tcp_replies=self.replies): - gateway = Gateway(self.reply, self.m_search_args, self.client_address, self.gateway_address, loop=self.loop) + gateway = Gateway(self.reply, self.client_address, self.gateway_address, loop=self.loop) await gateway.discover_commands() upnp = UPnP(self.client_address, self.gateway_address, gateway) external_ip = await upnp.get_external_ip() @@ -113,7 +113,7 @@ class TestGetGenericPortMappingEntry(UPnPCommandTestCase): async def test_get_port_mapping_by_index(self): with mock_tcp_and_udp(self.loop, tcp_replies=self.replies): - gateway = Gateway(self.reply, self.m_search_args, self.client_address, self.gateway_address, loop=self.loop) + gateway = Gateway(self.reply, self.client_address, self.gateway_address, loop=self.loop) await gateway.discover_commands() upnp = UPnP(self.client_address, self.gateway_address, gateway) result = await upnp.get_port_mapping_by_index(0) @@ -135,7 +135,7 @@ class TestGetNextPortMapping(UPnPCommandTestCase): async def test_get_next_mapping(self): with mock_tcp_and_udp(self.loop, tcp_replies=self.replies): - gateway = Gateway(self.reply, self.m_search_args, self.client_address, self.gateway_address, loop=self.loop) + gateway = Gateway(self.reply, self.client_address, self.gateway_address, loop=self.loop) await gateway.discover_commands() upnp = UPnP(self.client_address, self.gateway_address, gateway) ext_port = await upnp.get_next_mapping(4567, "UDP", "aioupnp test mapping") @@ -155,7 +155,7 @@ class TestGetSpecificPortMapping(UPnPCommandTestCase): async def test_get_specific_port_mapping(self): with mock_tcp_and_udp(self.loop, tcp_replies=self.replies): - gateway = Gateway(self.reply, self.m_search_args, self.client_address, self.gateway_address, loop=self.loop) + gateway = Gateway(self.reply, self.client_address, self.gateway_address, loop=self.loop) await gateway.discover_commands() upnp = UPnP(self.client_address, self.gateway_address, gateway) try: