Modified agi.py to accept stdin/out/error to allow support for FastAGI access.

Included new fastagi.py to be used as basic FastAGI server.
master
Ben Davis 2015-11-18 09:48:52 -08:00
parent 750662bea5
commit 1a0b54bbc6
2 changed files with 66 additions and 23 deletions

47
asterisk/agi.py 100644 → 100755
View File

@ -8,9 +8,9 @@ pyvr
{'agi_callerid' : 'mars.putland.int', {'agi_callerid' : 'mars.putland.int',
'agi_channel' : 'IAX[kputland@kputland]/119', 'agi_channel' : 'IAX[kputland@kputland]/119',
'agi_context' : 'default', 'agi_context' : 'default',
'agi_dnid' : '666', 'agi_dnid' : '1000',
'agi_enhanced' : '0.0', 'agi_enhanced' : '0.0',
'agi_extension': '666', 'agi_extension': '1000',
'agi_language' : 'en', 'agi_language' : 'en',
'agi_priority' : '1', 'agi_priority' : '1',
'agi_rdnis' : '', 'agi_rdnis' : '',
@ -85,21 +85,24 @@ class AGI:
Asterisk. Asterisk.
""" """
def __init__(self): def __init__(self, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr):
self._got_sighup = False self._got_sighup = False
signal.signal(signal.SIGHUP, self._handle_sighup) # handle SIGHUP signal.signal(signal.SIGHUP, self._handle_sighup) # handle SIGHUP
sys.stderr.write('ARGS: ') self.stderr.write('ARGS: ')
sys.stderr.write(str(sys.argv)) self.stderr.write(str(sys.argv))
sys.stderr.write('\n') self.stderr.write('\n')
self.stdin = stdin
self.stdout = stdout
self.stderr = stderr
self.env = {} self.env = {}
self._get_agi_env() self._get_agi_env()
def _get_agi_env(self): def _get_agi_env(self):
while 1: while 1:
line = sys.stdin.readline().strip() line = self.stdin.readline().strip()
sys.stderr.write('ENV LINE: ') self.stderr.write('ENV LINE: ')
sys.stderr.write(line) self.stderr.write(line)
sys.stderr.write('\n') self.stderr.write('\n')
if line == '': if line == '':
#blank line signals end #blank line signals end
break break
@ -108,12 +111,12 @@ class AGI:
data = data.strip() data = data.strip()
if key != '': if key != '':
self.env[key] = data self.env[key] = data
sys.stderr.write('class AGI: self.env = ') self.stderr.write('class AGI: self.env = ')
sys.stderr.write(pprint.pformat(self.env)) self.stderr.write(pprint.pformat(self.env))
sys.stderr.write('\n') self.stderr.write('\n')
def _quote(self, string): def _quote(self, string):
""" provides double quotes to string, converts int/bool to string """ """ provides double quotes to string, converts int/bool to string """
if isinstance(string, int): if isinstance(string, int):
string = str(string) string = str(string)
if isinstance(string, float): if isinstance(string, float):
@ -149,16 +152,16 @@ class AGI:
command = command.strip() command = command.strip()
if command[-1] != '\n': if command[-1] != '\n':
command += '\n' command += '\n'
sys.stderr.write(' COMMAND: %s' % command) self.stderr.write(' COMMAND: %s' % command)
sys.stdout.write(command) self.stdout.write(command)
sys.stdout.flush() self.stdout.flush()
def get_result(self, stdin=sys.stdin): def get_result(self, stdin=sys.stdin):
"""Read the result of a command from Asterisk""" """Read the result of a command from Asterisk"""
code = 0 code = 0
result = {'result': ('', '')} result = {'result': ('', '')}
line = stdin.readline().strip() line = self.stdin.readline().strip()
sys.stderr.write(' RESULT_LINE: %s\n' % line) self.stderr.write(' RESULT_LINE: %s\n' % line)
m = re_code.search(line) m = re_code.search(line)
if m: if m:
code, response = m.groups() code, response = m.groups()
@ -175,16 +178,16 @@ class AGI:
if key == 'result' and value == '-1': if key == 'result' and value == '-1':
raise AGIAppError("Error executing application, or hangup") raise AGIAppError("Error executing application, or hangup")
sys.stderr.write(' RESULT_DICT: %s\n' % pprint.pformat(result)) self.stderr.write(' RESULT_DICT: %s\n' % pprint.pformat(result))
return result return result
elif code == 510: elif code == 510:
raise AGIInvalidCommand(response) raise AGIInvalidCommand(response)
elif code == 520: elif code == 520:
usage = [line] usage = [line]
line = stdin.readline().strip() line = self.stdin.readline().strip()
while line[:3] != '520': while line[:3] != '520':
usage.append(line) usage.append(line)
line = stdin.readline().strip() line = self.stdin.readline().strip()
usage.append(line) usage.append(line)
usage = '%s\n' % '\n'.join(usage) usage = '%s\n' % '\n'.join(usage)
raise AGIUsageError(usage) raise AGIUsageError(usage)

View File

@ -0,0 +1,40 @@
#!/usr/bin/env python
# FastAGI service for Asterisk
# Requires modified pyst2 to support reading stdin/out/err
#
# Copyright 2011 VOICE1, LLC
# By: Ben Davis <ben@voice1-dot-me>
import sys
import SocketServer
import asterisk.agi
# import pkg_resources
# PYST_VERSION = pkg_resources.get_distribution("pyst2").version
__verison__ = 0.1
#TODO: Read options from config file.
HOST, PORT = "127.0.0.1", 4573
class FastAGI(SocketServer.StreamRequestHandler):
# Close connections not finished in 5seconds.
timeout = 5
def handle(self):
try:
agi=asterisk.agi.AGI(stdin=self.rfile, stdout=self.wfile, stderr=sys.stderr)
agi.verbose("pyst2: FastAGI on: {}:{}".format(HOST, PORT))
except TypeError as e:
sys.stderr.write('Unable to connect to agi://{} {}\n'.format(self.client_address[0], str(e)))
except SocketServer.socket.timeout as e:
sys.stderr.write('Timeout receiving data from {}\n'.format(self.client_address))
except SocketServer.socket.error as e:
sys.stderr.write('Could not open the socket. Is someting else listening on this port?\n')
except Exception as e:
sys.stderr.write('An unknown error: {}\n'.format(str(e)))
if __name__ == "__main__":
# server = SocketServer.TCPServer((HOST, PORT), FastAGI)
server = SocketServer.ForkingTCPServer((HOST, PORT), FastAGI)
# Keep server running until CTRL-C is pressed.
server.serve_forever()