OLD | NEW |
---|---|
1 #!/usr/bin/python2.4 | 1 #!/usr/bin/python2.4 |
2 # Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """This is a simple HTTP server used for testing Chrome. | 6 """This is a simple HTTP server used for testing Chrome. |
7 | 7 |
8 It supports several test URLs, as specified by the handlers in TestPageHandler. | 8 It supports several test URLs, as specified by the handlers in TestPageHandler. |
9 By default, it listens on an ephemeral port and sends the port number back to | 9 By default, it listens on an ephemeral port and sends the port number back to |
10 the originating process over a pipe. The originating process can specify an | 10 the originating process over a pipe. The originating process can specify an |
11 explicit port if necessary. | 11 explicit port if necessary. |
12 It can use https if you specify the flag --https=CERT where CERT is the path | 12 It can use https if you specify the flag --https=CERT where CERT is the path |
13 to a pem file containing the certificate and private key that should be used. | 13 to a pem file containing the certificate and private key that should be used. |
14 """ | 14 """ |
15 | 15 |
16 import asyncore | |
16 import base64 | 17 import base64 |
17 import BaseHTTPServer | 18 import BaseHTTPServer |
18 import cgi | 19 import cgi |
20 import errno | |
19 import optparse | 21 import optparse |
20 import os | 22 import os |
21 import re | 23 import re |
22 import shutil | 24 import select |
23 import SocketServer | 25 import SocketServer |
26 import socket | |
24 import sys | 27 import sys |
25 import struct | 28 import struct |
26 import time | 29 import time |
27 import urlparse | 30 import urlparse |
28 import warnings | 31 import warnings |
29 | 32 |
30 if sys.version_info < (2, 6): | 33 if sys.version_info < (2, 6): |
31 import simplejson as json | 34 import simplejson as json |
32 else: | 35 else: |
33 import json | 36 import json |
(...skipping 17 matching lines...) Expand all Loading... | |
51 | 54 |
52 SERVER_HTTP = 0 | 55 SERVER_HTTP = 0 |
53 SERVER_FTP = 1 | 56 SERVER_FTP = 1 |
54 SERVER_SYNC = 2 | 57 SERVER_SYNC = 2 |
55 | 58 |
56 debug_output = sys.stderr | 59 debug_output = sys.stderr |
57 def debug(str): | 60 def debug(str): |
58 debug_output.write(str + "\n") | 61 debug_output.write(str + "\n") |
59 debug_output.flush() | 62 debug_output.flush() |
60 | 63 |
64 class Error(Exception): | |
65 """Error class for this module.""" | |
66 pass | |
67 | |
68 class UnexpectedSocketType(Error): | |
69 """Raised when a value of a socket map is of unknown type.""" | |
70 pass | |
71 | |
61 class StoppableHTTPServer(BaseHTTPServer.HTTPServer): | 72 class StoppableHTTPServer(BaseHTTPServer.HTTPServer): |
62 """This is a specialization of of BaseHTTPServer to allow it | 73 """This is a specialization of of BaseHTTPServer to allow it |
63 to be exited cleanly (by setting its "stop" member to True).""" | 74 to be exited cleanly (by setting its "stop" member to True).""" |
64 | 75 |
65 def serve_forever(self): | 76 def serve_forever(self): |
66 self.stop = False | 77 self.stop = False |
67 self.nonce_time = None | 78 self.nonce_time = None |
68 while not self.stop: | 79 while not self.stop: |
69 self.handle_request() | 80 self.handle_request() |
70 self.socket.close() | 81 self.socket.close() |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
113 return False | 124 return False |
114 | 125 |
115 | 126 |
116 class SyncHTTPServer(StoppableHTTPServer): | 127 class SyncHTTPServer(StoppableHTTPServer): |
117 """An HTTP server that handles sync commands.""" | 128 """An HTTP server that handles sync commands.""" |
118 | 129 |
119 def __init__(self, server_address, request_handler_class): | 130 def __init__(self, server_address, request_handler_class): |
120 # We import here to avoid pulling in chromiumsync's dependencies | 131 # We import here to avoid pulling in chromiumsync's dependencies |
121 # unless strictly necessary. | 132 # unless strictly necessary. |
122 import chromiumsync | 133 import chromiumsync |
134 import xmppserver | |
135 StoppableHTTPServer.__init__(self, server_address, request_handler_class) | |
123 self._sync_handler = chromiumsync.TestServer() | 136 self._sync_handler = chromiumsync.TestServer() |
124 StoppableHTTPServer.__init__(self, server_address, request_handler_class) | 137 self._socket_map = { self.fileno(): self } |
138 self._xmpp_server = xmppserver.XmppServer( | |
cbentzel
2010/11/18 11:59:09
Should xmpp_server be run a separate thread so you
akalin
2010/11/18 23:32:58
I think the complexity of this select loop (especi
| |
139 self._socket_map, ('localhost', 0)) | |
140 self.xmpp_port = self._xmpp_server.getsockname()[1] | |
125 | 141 |
126 def HandleCommand(self, query, raw_request): | 142 def HandleCommand(self, query, raw_request): |
127 return self._sync_handler.HandleCommand(query, raw_request) | 143 return self._sync_handler.HandleCommand(query, raw_request) |
128 | 144 |
145 def serve_forever(self): | |
146 """This is a merge of asyncore.loop() and SocketServer.serve_forever(). | |
147 """ | |
148 | |
149 def HandleSocketServerRequestNoBlock(socket_server): | |
150 """Handles a single request for a SocketServer.BaseServer. | |
151 | |
152 Copied from SocketServer._handle_request_noblock(). | |
153 """ | |
154 try: | |
155 request, client_address = socket_server.get_request() | |
156 except socket.error: | |
157 return | |
158 if socket_server.verify_request(request, client_address): | |
159 try: | |
160 socket_server.process_request(request, client_address) | |
161 except: | |
162 socket_server.handle_error(request, client_address) | |
163 socket_server.close_request(request) | |
164 | |
165 def RunDispatcherHandler(dispatcher, handler): | |
166 """Handles a single event for an asyncore.dispatcher. | |
167 | |
168 Adapted from asyncore.read() et al. | |
169 """ | |
170 try: | |
171 handler(dispatcher) | |
172 except (asyncore.ExitNow, KeyboardInterrupt, SystemExit): | |
173 raise | |
174 except: | |
175 dispatcher.handle_error() | |
176 | |
177 poll_interval = 0.5 | |
178 | |
179 while map: | |
180 r = []; w = []; e = [] | |
181 for fd, obj in self._socket_map.items(): | |
182 if isinstance(obj, SocketServer.BaseServer): | |
183 r.append(fd) | |
184 elif isinstance(obj, asyncore.dispatcher): | |
185 is_r = obj.readable() | |
186 is_w = obj.writable() | |
187 if is_r: | |
188 r.append(fd) | |
189 if is_w: | |
190 w.append(fd) | |
191 if is_r or is_w: | |
192 e.append(fd) | |
193 else: | |
194 raise UnexpectedSocketType() | |
195 | |
196 if [] == r == w == e: | |
197 time.sleep(poll_interval) | |
Paweł Hajdan Jr.
2010/11/18 11:58:45
hmmm, why? Can we wait for an event that would mak
akalin
2010/11/18 23:32:58
Actually, this case should never happen. Removed
| |
198 continue | |
199 | |
200 try: | |
201 r, w, e = select.select(r, w, e, poll_interval) | |
202 except select.error, err: | |
203 if err.args[0] != errno.EINTR: | |
204 raise | |
205 else: | |
206 continue | |
207 | |
208 for fd in r: | |
209 obj = self._socket_map.get(fd) | |
210 if obj is None: | |
211 continue | |
212 if isinstance(obj, SocketServer.BaseServer): | |
213 HandleSocketServerRequestNoBlock(obj) | |
214 elif isinstance(obj, asyncore.dispatcher): | |
215 RunDispatcherHandler(obj, asyncore.dispatcher.handle_read_event) | |
216 else: | |
217 raise UnexpectedSocketType() | |
218 | |
219 for fd in w: | |
220 obj = self._socket_map.get(fd) | |
221 if obj is None: | |
222 continue | |
223 if isinstance(obj, asyncore.dispatcher): | |
224 RunDispatcherHandler(obj, asyncore.dispatcher.handle_write_event) | |
225 else: | |
226 raise UnexpectedSocketType() | |
227 | |
228 for fd in e: | |
229 obj = self._socket_map.get(fd) | |
230 if obj is None: | |
231 continue | |
232 if isinstance(obj, asyncore.dispatcher): | |
233 RunDispatcherHandler(obj, asyncore.dispatcher.handle_expt_event) | |
234 else: | |
235 raise UnexpectedSocketType() | |
236 | |
129 | 237 |
130 class BasePageHandler(BaseHTTPServer.BaseHTTPRequestHandler): | 238 class BasePageHandler(BaseHTTPServer.BaseHTTPRequestHandler): |
131 | 239 |
132 def __init__(self, request, client_address, socket_server, | 240 def __init__(self, request, client_address, socket_server, |
133 connect_handlers, get_handlers, post_handlers, put_handlers): | 241 connect_handlers, get_handlers, post_handlers, put_handlers): |
134 self._connect_handlers = connect_handlers | 242 self._connect_handlers = connect_handlers |
135 self._get_handlers = get_handlers | 243 self._get_handlers = get_handlers |
136 self._post_handlers = post_handlers | 244 self._post_handlers = post_handlers |
137 self._put_handlers = put_handlers | 245 self._put_handlers = put_handlers |
138 BaseHTTPServer.BaseHTTPRequestHandler.__init__( | 246 BaseHTTPServer.BaseHTTPRequestHandler.__init__( |
(...skipping 1103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1242 self.__fd1.flush() | 1350 self.__fd1.flush() |
1243 self.__fd2.flush() | 1351 self.__fd2.flush() |
1244 | 1352 |
1245 def main(options, args): | 1353 def main(options, args): |
1246 logfile = open('testserver.log', 'w') | 1354 logfile = open('testserver.log', 'w') |
1247 sys.stdout = FileMultiplexer(sys.stdout, logfile) | 1355 sys.stdout = FileMultiplexer(sys.stdout, logfile) |
1248 sys.stderr = FileMultiplexer(sys.stderr, logfile) | 1356 sys.stderr = FileMultiplexer(sys.stderr, logfile) |
1249 | 1357 |
1250 port = options.port | 1358 port = options.port |
1251 | 1359 |
1360 server_data = {} | |
1361 | |
1252 if options.server_type == SERVER_HTTP: | 1362 if options.server_type == SERVER_HTTP: |
1253 if options.cert: | 1363 if options.cert: |
1254 # let's make sure the cert file exists. | 1364 # let's make sure the cert file exists. |
1255 if not os.path.isfile(options.cert): | 1365 if not os.path.isfile(options.cert): |
1256 print 'specified server cert file not found: ' + options.cert + \ | 1366 print 'specified server cert file not found: ' + options.cert + \ |
1257 ' exiting...' | 1367 ' exiting...' |
1258 return | 1368 return |
1259 for ca_cert in options.ssl_client_ca: | 1369 for ca_cert in options.ssl_client_ca: |
1260 if not os.path.isfile(ca_cert): | 1370 if not os.path.isfile(ca_cert): |
1261 print 'specified trusted client CA file not found: ' + ca_cert + \ | 1371 print 'specified trusted client CA file not found: ' + ca_cert + \ |
1262 ' exiting...' | 1372 ' exiting...' |
1263 return | 1373 return |
1264 server = HTTPSServer(('127.0.0.1', port), TestPageHandler, options.cert, | 1374 server = HTTPSServer(('127.0.0.1', port), TestPageHandler, options.cert, |
1265 options.ssl_client_auth, options.ssl_client_ca, | 1375 options.ssl_client_auth, options.ssl_client_ca, |
1266 options.ssl_bulk_cipher) | 1376 options.ssl_bulk_cipher) |
1267 print 'HTTPS server started on port %d...' % server.server_port | 1377 print 'HTTPS server started on port %d...' % server.server_port |
1268 else: | 1378 else: |
1269 server = StoppableHTTPServer(('127.0.0.1', port), TestPageHandler) | 1379 server = StoppableHTTPServer(('127.0.0.1', port), TestPageHandler) |
1270 print 'HTTP server started on port %d...' % server.server_port | 1380 print 'HTTP server started on port %d...' % server.server_port |
1271 | 1381 |
1272 server.data_dir = MakeDataDir() | 1382 server.data_dir = MakeDataDir() |
1273 server.file_root_url = options.file_root_url | 1383 server.file_root_url = options.file_root_url |
1274 listen_port = server.server_port | 1384 server_data['port'] = server.server_port |
cbentzel
2010/11/18 11:59:09
Should this move to unique key per server type?
akalin
2010/11/18 23:32:58
Hmm I don't think so. Then run_testserver would h
| |
1275 server._device_management_handler = None | 1385 server._device_management_handler = None |
1276 elif options.server_type == SERVER_SYNC: | 1386 elif options.server_type == SERVER_SYNC: |
1277 server = SyncHTTPServer(('127.0.0.1', port), SyncPageHandler) | 1387 server = SyncHTTPServer(('127.0.0.1', port), SyncPageHandler) |
1278 print 'Sync HTTP server started on port %d...' % server.server_port | 1388 print 'Sync HTTP server started on port %d...' % server.server_port |
1279 listen_port = server.server_port | 1389 print 'Sync XMPP server started on port %d...' % server.xmpp_port |
1390 server_data['port'] = server.server_port | |
1391 server_data['xmpp_port'] = server.xmpp_port | |
1280 # means FTP Server | 1392 # means FTP Server |
1281 else: | 1393 else: |
1282 my_data_dir = MakeDataDir() | 1394 my_data_dir = MakeDataDir() |
1283 | 1395 |
1284 # Instantiate a dummy authorizer for managing 'virtual' users | 1396 # Instantiate a dummy authorizer for managing 'virtual' users |
1285 authorizer = pyftpdlib.ftpserver.DummyAuthorizer() | 1397 authorizer = pyftpdlib.ftpserver.DummyAuthorizer() |
1286 | 1398 |
1287 # Define a new user having full r/w permissions and a read-only | 1399 # Define a new user having full r/w permissions and a read-only |
1288 # anonymous user | 1400 # anonymous user |
1289 authorizer.add_user('chrome', 'chrome', my_data_dir, perm='elradfmw') | 1401 authorizer.add_user('chrome', 'chrome', my_data_dir, perm='elradfmw') |
1290 | 1402 |
1291 authorizer.add_anonymous(my_data_dir) | 1403 authorizer.add_anonymous(my_data_dir) |
1292 | 1404 |
1293 # Instantiate FTP handler class | 1405 # Instantiate FTP handler class |
1294 ftp_handler = pyftpdlib.ftpserver.FTPHandler | 1406 ftp_handler = pyftpdlib.ftpserver.FTPHandler |
1295 ftp_handler.authorizer = authorizer | 1407 ftp_handler.authorizer = authorizer |
1296 | 1408 |
1297 # Define a customized banner (string returned when client connects) | 1409 # Define a customized banner (string returned when client connects) |
1298 ftp_handler.banner = ("pyftpdlib %s based ftpd ready." % | 1410 ftp_handler.banner = ("pyftpdlib %s based ftpd ready." % |
1299 pyftpdlib.ftpserver.__ver__) | 1411 pyftpdlib.ftpserver.__ver__) |
1300 | 1412 |
1301 # Instantiate FTP server class and listen to 127.0.0.1:port | 1413 # Instantiate FTP server class and listen to 127.0.0.1:port |
1302 address = ('127.0.0.1', port) | 1414 address = ('127.0.0.1', port) |
1303 server = pyftpdlib.ftpserver.FTPServer(address, ftp_handler) | 1415 server = pyftpdlib.ftpserver.FTPServer(address, ftp_handler) |
1304 listen_port = server.socket.getsockname()[1] | 1416 server_data['port'] = server.socket.getsockname()[1] |
1305 print 'FTP server started on port %d...' % listen_port | 1417 print 'FTP server started on port %d...' % server_data['port'] |
1306 | 1418 |
1307 # Notify the parent that we've started. (BaseServer subclasses | 1419 # Notify the parent that we've started. (BaseServer subclasses |
1308 # bind their sockets on construction.) | 1420 # bind their sockets on construction.) |
1309 if options.startup_pipe is not None: | 1421 if options.startup_pipe is not None: |
1310 server_data = { | |
1311 'port': listen_port | |
1312 } | |
1313 server_data_json = json.dumps(server_data) | 1422 server_data_json = json.dumps(server_data) |
1314 print 'sending server_data: %s' % server_data_json | 1423 print 'sending server_data: %s' % server_data_json |
1315 server_data_len = len(server_data_json) | 1424 server_data_len = len(server_data_json) |
1316 if sys.platform == 'win32': | 1425 if sys.platform == 'win32': |
1317 fd = msvcrt.open_osfhandle(options.startup_pipe, 0) | 1426 fd = msvcrt.open_osfhandle(options.startup_pipe, 0) |
1318 else: | 1427 else: |
1319 fd = options.startup_pipe | 1428 fd = options.startup_pipe |
1320 startup_pipe = os.fdopen(fd, "w") | 1429 startup_pipe = os.fdopen(fd, "w") |
1321 # First write the data length as an unsigned 4-byte value. This | 1430 # First write the data length as an unsigned 4-byte value. This |
1322 # is _not_ using network byte ordering since the other end of the | 1431 # is _not_ using network byte ordering since the other end of the |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1367 'option may appear multiple times, indicating ' | 1476 'option may appear multiple times, indicating ' |
1368 'multiple algorithms should be enabled.'); | 1477 'multiple algorithms should be enabled.'); |
1369 option_parser.add_option('', '--file-root-url', default='/files/', | 1478 option_parser.add_option('', '--file-root-url', default='/files/', |
1370 help='Specify a root URL for files served.') | 1479 help='Specify a root URL for files served.') |
1371 option_parser.add_option('', '--startup-pipe', type='int', | 1480 option_parser.add_option('', '--startup-pipe', type='int', |
1372 dest='startup_pipe', | 1481 dest='startup_pipe', |
1373 help='File handle of pipe to parent process') | 1482 help='File handle of pipe to parent process') |
1374 options, args = option_parser.parse_args() | 1483 options, args = option_parser.parse_args() |
1375 | 1484 |
1376 sys.exit(main(options, args)) | 1485 sys.exit(main(options, args)) |
OLD | NEW |