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
parent
750662bea5
commit
1a0b54bbc6
|
@ -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,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)
|
||||||
|
|
|
@ -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()
|
Loading…
Reference in New Issue