Index: net/tools/testserver/testserver_base.py |
diff --git a/net/tools/testserver/testserver_base.py b/net/tools/testserver/testserver_base.py |
index 835ec35d5961bf5e418f68b56e2784c8b4f42a64..455ca5c32023fd082feeb20f28991de82fead7d3 100644 |
--- a/net/tools/testserver/testserver_base.py |
+++ b/net/tools/testserver/testserver_base.py |
@@ -1,10 +1,15 @@ |
-# Copyright (c) 2012 The Chromium Authors. All rights reserved. |
+# Copyright 2013 The Chromium Authors. All rights reserved. |
# Use of this source code is governed by a BSD-style license that can be |
# found in the LICENSE file. |
+import BaseHTTPServer |
+import errno |
import json |
import optparse |
import os |
+import re |
+import socket |
+import SocketServer |
import struct |
import sys |
import warnings |
@@ -15,6 +20,12 @@ warnings.filterwarnings("ignore", category=DeprecationWarning) |
if sys.platform == 'win32': |
import msvcrt |
+# Using debug() seems to cause hangs on XP: see http://crbug.com/64515. |
+debug_output = sys.stderr |
+def debug(string): |
+ debug_output.write(string + "\n") |
+ debug_output.flush() |
+ |
class Error(Exception): |
"""Error class for this module.""" |
@@ -44,6 +55,47 @@ class FileMultiplexer(object): |
self.__fd2.flush() |
+class ClientRestrictingServerMixIn: |
+ """Implements verify_request to limit connections to our configured IP |
+ address.""" |
+ |
+ def verify_request(self, _request, client_address): |
+ return client_address[0] == self.server_address[0] |
+ |
+ |
+class BrokenPipeHandlerMixIn: |
+ """Allows the server to deal with "broken pipe" errors (which happen if the |
+ browser quits with outstanding requests, like for the favicon). This mix-in |
+ requires the class to derive from SocketServer.BaseServer and not override its |
+ handle_error() method. """ |
+ |
+ def handle_error(self, request, client_address): |
+ value = sys.exc_info()[1] |
+ if isinstance(value, socket.error): |
+ err = value.args[0] |
+ if sys.platform in ('win32', 'cygwin'): |
+ # "An established connection was aborted by the software in your host." |
+ pipe_err = 10053 |
+ else: |
+ pipe_err = errno.EPIPE |
+ if err == pipe_err: |
+ print "testserver.py: Broken pipe" |
+ return |
+ SocketServer.BaseServer.handle_error(self, request, client_address) |
+ |
+ |
+class StoppableHTTPServer(BaseHTTPServer.HTTPServer): |
+ """This is a specialization of BaseHTTPServer to allow it |
+ to be exited cleanly (by setting its "stop" member to True).""" |
+ |
+ def serve_forever(self): |
+ self.stop = False |
+ self.nonce_time = None |
+ while not self.stop: |
+ self.handle_request() |
+ self.socket.close() |
+ |
+ |
def MultiplexerHack(std_fd, log_fd): |
"""Creates a FileMultiplexer that will write to both specified files. |
@@ -57,6 +109,59 @@ def MultiplexerHack(std_fd, log_fd): |
return FileMultiplexer(std_fd, log_fd) |
+class BasePageHandler(BaseHTTPServer.BaseHTTPRequestHandler): |
+ |
+ def __init__(self, request, client_address, socket_server, |
+ connect_handlers, get_handlers, head_handlers, post_handlers, |
+ put_handlers): |
+ self._connect_handlers = connect_handlers |
+ self._get_handlers = get_handlers |
+ self._head_handlers = head_handlers |
+ self._post_handlers = post_handlers |
+ self._put_handlers = put_handlers |
+ BaseHTTPServer.BaseHTTPRequestHandler.__init__( |
+ self, request, client_address, socket_server) |
+ |
+ def log_request(self, *args, **kwargs): |
+ # Disable request logging to declutter test log output. |
+ pass |
+ |
+ def _ShouldHandleRequest(self, handler_name): |
+ """Determines if the path can be handled by the handler. |
+ |
+ We consider a handler valid if the path begins with the |
+ handler name. It can optionally be followed by "?*", "/*". |
+ """ |
+ |
+ pattern = re.compile('%s($|\?|/).*' % handler_name) |
+ return pattern.match(self.path) |
+ |
+ def do_CONNECT(self): |
+ for handler in self._connect_handlers: |
+ if handler(): |
+ return |
+ |
+ def do_GET(self): |
+ for handler in self._get_handlers: |
+ if handler(): |
+ return |
+ |
+ def do_HEAD(self): |
+ for handler in self._head_handlers: |
+ if handler(): |
+ return |
+ |
+ def do_POST(self): |
+ for handler in self._post_handlers: |
+ if handler(): |
+ return |
+ |
+ def do_PUT(self): |
+ for handler in self._put_handlers: |
+ if handler(): |
+ return |
+ |
+ |
class TestServerRunner(object): |
"""Runs a test server and communicates with the controlling C++ test code. |
@@ -71,7 +176,7 @@ class TestServerRunner(object): |
def main(self): |
self.options, self.args = self.option_parser.parse_args() |
- logfile = open('testserver.log', 'w') |
+ logfile = open(self.options.log_file, 'w') |
sys.stderr = MultiplexerHack(sys.stderr, logfile) |
if self.options.log_to_console: |
sys.stdout = MultiplexerHack(sys.stdout, logfile) |
@@ -108,6 +213,9 @@ class TestServerRunner(object): |
dest='log_to_console', |
help='Enables or disables sys.stdout logging ' |
'to the console.') |
+ self.option_parser.add_option('--log-file', default='testserver.log', |
+ dest='log_file', |
+ help='The name of the server log file.') |
self.option_parser.add_option('--port', default=0, type='int', |
help='Port used by the server. If ' |
'unspecified, the server will listen on an ' |
@@ -117,6 +225,9 @@ class TestServerRunner(object): |
help='Hostname or IP upon which the server ' |
'will listen. Client connections will also ' |
'only be allowed from this address.') |
+ self.option_parser.add_option('--data-dir', dest='data_dir', |
+ help='Directory from which to read the ' |
+ 'files.') |
def _notify_startup_complete(self, server_data): |
# Notify the parent that we've started. (BaseServer subclasses |