Chromium Code Reviews| 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 |