Successfully parsing HTTP requests from freeswitch
[ipypbx] / src / ipypbx / http.py
1 # Copyright (c) Stas Shtin, 2010
2
3 # This file is part of IPyPBX.
4
5 # IPyPBX is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9
10 # IPyPBX is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14
15 # You should have received a copy of the GNU General Public License
16 # along with IPyPBX.  If not, see <http://www.gnu.org/licenses/>.
17
18 from PyQt4 import QtCore, QtNetwork
19
20
21 class FreeswitchConfigServer(QtNetwork.QTcpServer):
22     """
23     TCP server that receives config requests from freeswitch.
24     """
25     def __init__(self, parent=None):
26         super(FreeswitchConfigServer, self).__init__(parent)
27
28         self.host = None
29         self.port = None
30         self.is_running = False
31         
32         self.httpRequestParser = HttpRequestParser()
33         
34     def setSocketData(self, host, port):
35         """
36         Set host and port for socket to listen on.
37         """
38         self.host = host
39         self.port = port
40
41     def startServer(self):
42         """
43         Start listening on our socket.
44         """
45         if not self.is_running:
46             if self.host and self.port:
47                 self.newConnection.connect(self.clientConnecting)
48                 self.listen(QtNetwork.QHostAddress(self.host), self.port)
49                 self.is_running = True
50
51     def stopServer(self):
52         """
53         Stop listening on our socket.
54         """
55         if self.is_running:
56             self.close()
57             self.is_running = False
58
59     def restartServer(self):
60         """
61         Restart server.
62         """
63         self.stopServer()
64         self.startServer()
65
66     def clientConnecting(self):
67         """
68         Handle client connection.
69         """
70         if self.hasPendingConnections():
71             self.socket = self.nextPendingConnection()
72             self.socket.readyRead.connect(self.receiveData)
73
74     def receiveData(self):
75         # TODO: read in chunks.
76         for line in str(self.socket.readAll()).split('\r\n'):
77             self.httpRequestParser.handle(line)
78             
79
80 class HttpParseError(Exception):
81     """
82     Error parsing HTTP request.
83     """
84
85
86 class HttpRequestParser(object):
87     """
88     A simple state machine for parsing HTTP requests.
89     """
90     HTTP_NONE, HTTP_REQUEST, HTTP_HEADERS, HTTP_EMPTY, HTTP_MESSAGE = range(5)
91     HTTP_STATES = ['NONE', 'REQUEST', 'HEADERS', 'EMPTY', 'MESSAGE']
92     
93     def __init__(self):
94         super(HttpRequestParser, self).__init__()
95         self.reset()
96
97     def reset(self):
98         """
99         Reset parser to initial state.
100         """
101         # Initial values for request data.
102         self.method = None
103         self.request_path = None
104         self.http_version = None
105         self.headers = {}
106         self.data = {}
107         
108         # Set initial state.
109         self.state = self.HTTP_NONE        
110
111     def handle(self, line):
112         """
113         Dispatch line to current state handler.
114         """
115         for state in self.HTTP_STATES:
116             if getattr(self, 'HTTP_%s' % state) == self.state:
117                 print self.state, line
118                 getattr(self, 'handle%s' % state.title())(line)
119                 break
120         else:
121             raise HttpParseError('Unknown HTTP state')
122                 
123     def handleNone(self, line):
124         """
125         Pass line to next state.
126         """
127         self.state += 1
128         self.handle(line)
129
130     def handleRequest(self, line):
131         """
132         Retrieve HTTP method, request path and HTTP version from request.
133         """
134         self.method, self.request_path, self.http_version = line.split(' ')
135         self.state += 1
136
137     def handleHeaders(self, line):
138         """
139         Parse headers while not found an empty line.
140         """
141         if line:
142             key, value = line.split(': ')
143             self.headers[key] = value
144         else:
145             self.state += 1
146             self.handle(line)
147
148     def handleEmpty(self, line):
149         """
150         Empty line separator is found - proceed to next state.
151         """
152         self.state += 1
153
154     def handleMessage(self, line):
155         """
156         Append to message body.
157         """
158         self.data = dict(pair.split('=', 2) for pair in line.split('&'))
159         print self.data