Merge pull request #20 from tuxpowered/testing

Testing Branch for FastAGI support
master
Randall Degges 2015-11-19 17:01:10 -08:00
commit 2dab6612c8
3 changed files with 76 additions and 22 deletions

View File

@ -89,6 +89,17 @@ directly on the host where Asterisk is running. Since Asterisk doesn't
run on windows platforms (and probably never will) the agi part of the run on windows platforms (and probably never will) the agi part of the
package can only be run on Asterisk platforms. package can only be run on Asterisk platforms.
FastAGI
-------
FastAGI support is a python based raw SocketServer, To start the server
python fastagi.py should start it listening on localhost and the default
asterisk FastAGI port. This does require the newest version of pyst2.
The FastAGI server runs in as a Forked operation for each request, in
an attempt to prevent blocking by a single bad service. As a result the
FastAGI server may consume more memory then a single process. If you need
to use a single process simply uncomment the appropriate line. Future versions
of this will use a config file to set options.
Credits Credits
------- -------

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' : '',
@ -77,7 +77,7 @@ class AGIUsageError(AGIError):
class AGIInvalidCommand(AGIError): class AGIInvalidCommand(AGIError):
pass pass
class AGI: class AGI:
""" """
This class encapsulates communication between Asterisk an a python script. This class encapsulates communication between Asterisk an a python script.
@ -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,9 +111,9 @@ 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 """
@ -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()