Skip to content

Commit

Permalink
Dialplan custom (#106)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
ovv authored Apr 20, 2018
1 parent 76ea3c3 commit 92eea1e
Show file tree
Hide file tree
Showing 11 changed files with 285 additions and 175 deletions.
35 changes: 15 additions & 20 deletions aiosip/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -39,7 +39,7 @@ def __init__(self, *,
middleware=(),
defaults=None,
debug=False,
dialplan=None,
dialplan=BaseDialplan(),
dns_resolver=aiodns.DNSResolver()
):

Expand All @@ -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

Expand Down Expand Up @@ -162,35 +162,30 @@ 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,
from_details=Contact.from_header(msg.headers['To']),
to_details=Contact.from_header(msg.headers['From']),
call_id=call_id,
inbound=True
call_id=call_id
)

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'],
method=msg.method,
protocol=peer.protocol,
local_addr=peer.local_addr,
remote_addr=peer.peer_addr
)

route = router.get(msg.method)
if not route:
await reply(msg, status_code=501)
return
if not route or not asyncio.iscoroutinefunction(route):
await reply(msg, status_code=501)
return

try:
t = asyncio.ensure_future(self._call_route(peer, route, msg))
self._tasks.append(t)
await t
Expand Down
51 changes: 4 additions & 47 deletions aiosip/dialplan.py
Original file line number Diff line number Diff line change
@@ -1,52 +1,9 @@
import logging

from collections import MutableMapping


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):
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)
class BaseDialplan:
async def resolve(self, method, username, protocol, local_addr, remote_addr):
LOG.debug('Resolving dialplan for %s %s connecting on %s from %s via %s',
method, username, local_addr, remote_addr, protocol)
80 changes: 80 additions & 0 deletions client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import argparse
import asyncio
import contextlib
import logging
import random

import aiosip

sip_config = {
'srv_host': 'qd.allocloud.com',
'srv_port': 10065,
'realm': 'XXXXXX',
'user': 'subscriber',
'pwd': 'hunter2',
'local_host': '0.0.0.0',
'local_port': random.randint(6001, 6100)
}


async def run_subscription(peer, duration):
subscription = await peer.subscribe(
from_details=aiosip.Contact.from_header('sip:{}@{}:{}'.format(
sip_config['user'], sip_config['local_host'],
sip_config['local_port'])),
to_details=aiosip.Contact.from_header('sip:666@{}:{}'.format(
sip_config['srv_host'], sip_config['srv_port'])),
password=sip_config['pwd'])

async def reader():
async for request in subscription:
print('NOTIFY:', request.payload)
await subscription.reply(request, status_code=200)

with contextlib.suppress(asyncio.TimeoutError):
await asyncio.wait_for(reader(), timeout=duration)

if subscription.status_code == 200:
await subscription.close()


async def start(app, protocol, duration):
if protocol is aiosip.WS:
peer = await app.connect(
'ws://{}:{}'.format(sip_config['srv_host'], sip_config['srv_port']),
protocol=protocol,
local_addr=(sip_config['local_host'], sip_config['local_port']))
else:
peer = await app.connect(
(sip_config['srv_host'], sip_config['srv_port']),
protocol=protocol,
local_addr=(sip_config['local_host'], sip_config['local_port']))

await run_subscription(peer, duration)
await app.close()


def main():
parser = argparse.ArgumentParser()
parser.add_argument('-p', '--protocol', default='udp')
parser.add_argument('-d', '--duration', type=int, default=5)
args = parser.parse_args()

loop = asyncio.get_event_loop()
app = aiosip.Application(loop=loop)

if args.protocol == 'udp':
loop.run_until_complete(start(app, aiosip.UDP, args.duration))
elif args.protocol == 'tcp':
loop.run_until_complete(start(app, aiosip.TCP, args.duration))
elif args.protocol == 'ws':
loop.run_until_complete(start(app, aiosip.WS, args.duration))
else:
raise RuntimeError("Unsupported protocol: {}".format(args.protocol))

loop.close()


if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG)
main()
15 changes: 11 additions & 4 deletions examples/back_to_back/registrar.py
Original file line number Diff line number Diff line change
Expand Up @@ -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['method'] == 'SUBSCRIBE':
return on_subscribe
elif kwargs['method'] == 'REGISTER':
return on_register


def start(app, protocol):
app.loop.run_until_complete(
app.run(
Expand All @@ -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)
Expand Down
14 changes: 10 additions & 4 deletions examples/back_to_back/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,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['method'] == 'SUBSCRIBE':
return on_subscribe()


async def start(app, protocol):
await app.run(
protocol=protocol,
Expand Down Expand Up @@ -89,10 +98,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)
Expand Down
14 changes: 10 additions & 4 deletions examples/call/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -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['method'] == 'INVITE':
return on_invite


def start(app, protocol):
app.loop.run_until_complete(
app.run(
Expand All @@ -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)
Expand Down
5 changes: 0 additions & 5 deletions examples/subscribe/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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))
Expand Down
14 changes: 10 additions & 4 deletions examples/subscribe/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -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['method'] == 'SUBSCRIBE':
return on_subscribe


def start(app, protocol):
app.loop.run_until_complete(
app.run(
Expand All @@ -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)
Expand Down
Loading

0 comments on commit 92eea1e

Please sign in to comment.