Config server fixes
[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         while self.socket.canReadLine():
76             line = str(self.socket.readLine()).strip()
77             
78
79 class HttpParseError(Exception):
80     """
81     Error parsing HTTP request.
82     """
83
84
85 class HttpRequestParser(object):
86     """
87     A simple state machine for parsing HTTP requests.
88     """
89     HTTP_NONE, HTTP_REQUEST, HTTP_HEADERS, HTTP_EMPTY, HTTP_MESSAGE = range(5)
90     HTTP_STATES = ['NONE', 'REQUEST', 'HEADERS', 'EMPTY', 'MESSAGE']
91     
92     def __init__(self):
93         super(HttpRequestParser, self).__init__()
94
95     def reset(self):
96         """
97         Reset parser to initial state.
98         """
99         # Initial values for request data.
100         self.method = None
101         self.request_path = None
102         self.http_version = None
103         self.message = ''
104         
105         # Set initial state.
106         self.state = HTTP_NONE        
107
108     def handle(self, line):
109         """
110         Dispatch line to current state handler.
111         """
112         for state in HTTP_STATES:
113             if getattr(self, 'HTTP_%s' % state) == self.state:
114                 getattr(self, 'handle%s' % state.title())(line)
115                 break
116         else:
117             raise HttpParseError('Unknown HTTP state')
118                 
119     def handleNone(self, line):
120         """
121         Pass line to next state.
122         """
123         self.state += 1
124         self.handle(line)
125
126     def handleRequest(self, line):
127         """
128         Retrieve HTTP method, request path and HTTP version from request.
129         """
130         self.method, self.request_path, self.http_version = line.split(' ')
131         self.state += 1
132
133     def handleHeaders(self, line):
134         """
135         Parse headers while not found an empty line.
136         """
137         if line:
138             key, value = line.split(': ')
139             self.headers[key] = value
140         else:
141             self.state += 1
142
143     def handleEmpty(self, line):
144         """
145         Empty line separator is found - proceed to next state.
146         """
147         self.state += 1
148
149     def handleMessage(self, line):
150         """
151         Append to message body.
152         """
153         self.message += line