From fa1b4b3f0bdb11ca7427000c626627e74e56330a Mon Sep 17 00:00:00 2001 From: Ovv Date: Thu, 19 Apr 2018 15:23:34 +0200 Subject: [PATCH] Rework dialplan Current dialplan was not correct and complicated. This rework simplify it greatly and provide an easy way to do your own routing I think it will be a nice place to put helpers for authentifications. No idea if that will provide any advantages over a flat mapping of methods / middlewares issue #78 --- aiosip/application.py | 30 ++++++++++---------- aiosip/dialplan.py | 44 ++---------------------------- examples/back_to_back/registrar.py | 15 +++++++--- examples/back_to_back/server.py | 15 +++++++--- examples/call/server.py | 14 +++++++--- examples/subscribe/client.py | 5 ---- examples/subscribe/server.py | 14 +++++++--- 7 files changed, 59 insertions(+), 78 deletions(-) diff --git a/aiosip/application.py b/aiosip/application.py index 639531e..7594645 100644 --- a/aiosip/application.py +++ b/aiosip/application.py @@ -14,7 +14,7 @@ from . import __version__ from .dialog import Dialog -from .dialplan import Dialplan +from .dialplan import BaseDialplan from .protocol import UDP, TCP, WS from .peers import UDPConnector, TCPConnector, WSConnector from .message import Response @@ -39,7 +39,7 @@ def __init__(self, *, middleware=(), defaults=None, debug=False, - dialplan=None, + dialplan=BaseDialplan(), dns_resolver=aiodns.DNSResolver() ): @@ -62,7 +62,7 @@ def __init__(self, *, self._middleware = middleware self._tasks = list() - self.dialplan = dialplan or Dialplan() + self.dialplan = dialplan self.dialog_factory = dialog_factory self.loop = loop @@ -162,13 +162,6 @@ async def _run_dialplan(self, protocol, msg): via_addr = via['host'], int(via['port']) peer = await connector.get_peer(protocol, via_addr) - router = await self.dialplan.resolve( - username=msg.from_details['uri']['user'], - protocol=peer.protocol, - local_addr=peer.local_addr, - remote_addr=peer.peer_addr - ) - async def reply(*args, **kwargs): dialog = peer._create_dialog( method=msg.method, @@ -181,12 +174,19 @@ async def reply(*args, **kwargs): await dialog.reply(*args, **kwargs) await dialog.close(fast=True) - if not router: - await reply(msg, status_code=501) - return + try: + route = await self.dialplan.resolve( + username=msg.from_details['uri']['user'], + message=msg, + protocol=peer.protocol, + local_addr=peer.local_addr, + remote_addr=peer.peer_addr + ) + except Exception as e: + LOG.exception(e) + route = None - route = router.get(msg.method) - if not route: + if not route or not asyncio.iscoroutinefunction(route): await reply(msg, status_code=501) return diff --git a/aiosip/dialplan.py b/aiosip/dialplan.py index 48d4458..bb6aa19 100644 --- a/aiosip/dialplan.py +++ b/aiosip/dialplan.py @@ -6,47 +6,7 @@ LOG = logging.getLogger(__name__) -class Dialplan: - def __init__(self, default=None): - self._users = {} - self.default = default - - async def resolve(self, username, protocol, local_addr, remote_addr): +class BaseDialplan: + async def resolve(self, message, username, protocol, local_addr, remote_addr): LOG.debug('Resolving dialplan for %s connecting on %s from %s via %s', username, local_addr, remote_addr, protocol) - router = self._users.get(username) - if not router: - router = self._users.get('*', self.default) - return router - - def add_user(self, username, router): - self._users[username] = router - - -class Router(MutableMapping): - def __init__(self, default=None): - self._routes = {} - if default: - self._routes['*'] = default - - # MutableMapping API - def __eq__(self, other): - return self is other - - def __getitem__(self, key): - try: - return self._routes[key.lower()] - except KeyError: - return self._routes['*'] - - def __setitem__(self, key, value): - self._routes[key.lower()] = value - - def __delitem__(self, key): - del self._routes[key.lower()] - - def __len__(self): - return len(self._routes) - - def __iter__(self): - return iter(self._routes) diff --git a/examples/back_to_back/registrar.py b/examples/back_to_back/registrar.py index 723cb07..b5fa7bc 100644 --- a/examples/back_to_back/registrar.py +++ b/examples/back_to_back/registrar.py @@ -91,6 +91,17 @@ async def reader(peer): print("Subscription forwarding ended!") +class Dialplan(aiosip.BaseDialplan): + + async def resolve(self, *args, **kwargs): + await super().resolve(*args, **kwargs) + + if kwargs['message'].method == 'SUBSCRIBE': + return on_subscribe + elif kwargs['message'].method == 'REGISTER': + return on_register + + def start(app, protocol): app.loop.run_until_complete( app.run( @@ -116,10 +127,6 @@ def main(): loop = asyncio.get_event_loop() app = aiosip.Application(loop=loop) - app.dialplan.add_user('*', { - 'REGISTER': on_register, - 'SUBSCRIBE': on_subscribe, - }) if args.protocol == 'udp': start(app, aiosip.UDP) diff --git a/examples/back_to_back/server.py b/examples/back_to_back/server.py index 175cae2..b0cda61 100644 --- a/examples/back_to_back/server.py +++ b/examples/back_to_back/server.py @@ -46,6 +46,16 @@ async def on_subscribe(request, message): print('Subscription ended!') + +class Dialplan(aiosip.BaseDialplan): + + async def resolve(self, *args, **kwargs): + await super().resolve(*args, **kwargs) + + if kwargs['message'].method == 'SUBSCRIBE': + return on_subscribe() + + async def start(app, protocol): await app.run( protocol=protocol, @@ -89,10 +99,7 @@ def main(): sip_config['user'] = args.user loop = asyncio.get_event_loop() - app = aiosip.Application(loop=loop) - app.dialplan.add_user(args.user, { - 'SUBSCRIBE': on_subscribe - }) + app = aiosip.Application(loop=loop, dialplan=Dialplan()) if args.protocol == 'udp': server = start(app, aiosip.UDP) diff --git a/examples/call/server.py b/examples/call/server.py index dfc12ed..95cbd00 100644 --- a/examples/call/server.py +++ b/examples/call/server.py @@ -30,6 +30,15 @@ async def on_invite(request, message): break +class Dialplan(aiosip.BaseDialplan): + + async def resolve(self, *args, **kwargs): + await super().resolve(*args, **kwargs) + + if kwargs['message'].method == 'INVITE': + return on_invite + + def start(app, protocol): app.loop.run_until_complete( app.run( @@ -54,10 +63,7 @@ def main(): args = parser.parse_args() loop = asyncio.get_event_loop() - app = aiosip.Application(loop=loop) - app.dialplan.add_user('aiosip', { - 'INVITE': on_invite - }) + app = aiosip.Application(loop=loop, dialplan=Dialplan()) if args.protocol == 'udp': start(app, aiosip.UDP) diff --git a/examples/subscribe/client.py b/examples/subscribe/client.py index 376ec57..7a0af0a 100644 --- a/examples/subscribe/client.py +++ b/examples/subscribe/client.py @@ -17,10 +17,6 @@ } -async def option(dialog, request): - await dialog.reply(request, status_code=200) - - async def run_subscription(peer, duration): subscription = await peer.subscribe( from_details=aiosip.Contact.from_header('sip:{}@{}:{}'.format( @@ -65,7 +61,6 @@ def main(): loop = asyncio.get_event_loop() app = aiosip.Application(loop=loop) - app.dialplan.add_user('asterisk', option) if args.protocol == 'udp': loop.run_until_complete(start(app, aiosip.UDP, args.duration)) diff --git a/examples/subscribe/server.py b/examples/subscribe/server.py index e7bcd8a..e33b5cb 100644 --- a/examples/subscribe/server.py +++ b/examples/subscribe/server.py @@ -43,6 +43,15 @@ async def on_subscribe(request, message): print('Subscription ended!') +class Dialplan(aiosip.BaseDialplan): + + async def resolve(self, *args, **kwargs): + await super().resolve(*args, **kwargs) + + if kwargs['message'].method == 'SUBSCRIBE': + return on_subscribe + + def start(app, protocol): app.loop.run_until_complete( app.run( @@ -67,10 +76,7 @@ def main(): args = parser.parse_args() loop = asyncio.get_event_loop() - app = aiosip.Application(loop=loop) - app.dialplan.add_user('subscriber', { - 'SUBSCRIBE': on_subscribe - }) + app = aiosip.Application(loop=loop, dialplan=Dialplan()) if args.protocol == 'udp': start(app, aiosip.UDP)