1*6777b538SAndroid Build Coastguard Worker#!/usr/bin/env vpython3 2*6777b538SAndroid Build Coastguard Worker# Copyright 2013 The Chromium Authors 3*6777b538SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be 4*6777b538SAndroid Build Coastguard Worker# found in the LICENSE file. 5*6777b538SAndroid Build Coastguard Worker 6*6777b538SAndroid Build Coastguard Worker"""This is a simple HTTP/TCP/PROXY/BASIC_AUTH_PROXY/WEBSOCKET server used for 7*6777b538SAndroid Build Coastguard Workertesting Chrome. 8*6777b538SAndroid Build Coastguard Worker 9*6777b538SAndroid Build Coastguard WorkerIt supports several test URLs, as specified by the handlers in TestPageHandler. 10*6777b538SAndroid Build Coastguard WorkerBy default, it listens on an ephemeral port and sends the port number back to 11*6777b538SAndroid Build Coastguard Workerthe originating process over a pipe. The originating process can specify an 12*6777b538SAndroid Build Coastguard Workerexplicit port if necessary. 13*6777b538SAndroid Build Coastguard Worker""" 14*6777b538SAndroid Build Coastguard Worker 15*6777b538SAndroid Build Coastguard Workerfrom __future__ import print_function 16*6777b538SAndroid Build Coastguard Worker 17*6777b538SAndroid Build Coastguard Workerimport base64 18*6777b538SAndroid Build Coastguard Workerimport logging 19*6777b538SAndroid Build Coastguard Workerimport os 20*6777b538SAndroid Build Coastguard Workerimport select 21*6777b538SAndroid Build Coastguard Workerfrom six.moves import BaseHTTPServer, socketserver 22*6777b538SAndroid Build Coastguard Workerimport six.moves.urllib.parse as urlparse 23*6777b538SAndroid Build Coastguard Workerimport socket 24*6777b538SAndroid Build Coastguard Workerimport ssl 25*6777b538SAndroid Build Coastguard Workerimport sys 26*6777b538SAndroid Build Coastguard Worker 27*6777b538SAndroid Build Coastguard WorkerBASE_DIR = os.path.dirname(os.path.abspath(__file__)) 28*6777b538SAndroid Build Coastguard WorkerROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(BASE_DIR))) 29*6777b538SAndroid Build Coastguard Worker 30*6777b538SAndroid Build Coastguard Worker# Insert at the beginning of the path, we want to use our copies of the library 31*6777b538SAndroid Build Coastguard Worker# unconditionally (since they contain modifications from anything that might be 32*6777b538SAndroid Build Coastguard Worker# obtained from e.g. PyPi). 33*6777b538SAndroid Build Coastguard Workersys.path.insert(0, os.path.join(ROOT_DIR, 'third_party', 'pywebsocket3', 'src')) 34*6777b538SAndroid Build Coastguard Worker 35*6777b538SAndroid Build Coastguard Workerimport mod_pywebsocket.standalone 36*6777b538SAndroid Build Coastguard Workerfrom mod_pywebsocket.standalone import WebSocketServer 37*6777b538SAndroid Build Coastguard Worker# import manually 38*6777b538SAndroid Build Coastguard Workermod_pywebsocket.standalone.ssl = ssl 39*6777b538SAndroid Build Coastguard Worker 40*6777b538SAndroid Build Coastguard Workerimport testserver_base 41*6777b538SAndroid Build Coastguard Worker 42*6777b538SAndroid Build Coastguard WorkerSERVER_UNSET = 0 43*6777b538SAndroid Build Coastguard WorkerSERVER_BASIC_AUTH_PROXY = 1 44*6777b538SAndroid Build Coastguard WorkerSERVER_WEBSOCKET = 2 45*6777b538SAndroid Build Coastguard WorkerSERVER_PROXY = 3 46*6777b538SAndroid Build Coastguard Worker 47*6777b538SAndroid Build Coastguard Worker# Default request queue size for WebSocketServer. 48*6777b538SAndroid Build Coastguard Worker_DEFAULT_REQUEST_QUEUE_SIZE = 128 49*6777b538SAndroid Build Coastguard Worker 50*6777b538SAndroid Build Coastguard Workerclass WebSocketOptions: 51*6777b538SAndroid Build Coastguard Worker """Holds options for WebSocketServer.""" 52*6777b538SAndroid Build Coastguard Worker 53*6777b538SAndroid Build Coastguard Worker def __init__(self, host, port, data_dir): 54*6777b538SAndroid Build Coastguard Worker self.request_queue_size = _DEFAULT_REQUEST_QUEUE_SIZE 55*6777b538SAndroid Build Coastguard Worker self.server_host = host 56*6777b538SAndroid Build Coastguard Worker self.port = port 57*6777b538SAndroid Build Coastguard Worker self.websock_handlers = data_dir 58*6777b538SAndroid Build Coastguard Worker self.scan_dir = None 59*6777b538SAndroid Build Coastguard Worker self.allow_handlers_outside_root_dir = False 60*6777b538SAndroid Build Coastguard Worker self.websock_handlers_map_file = None 61*6777b538SAndroid Build Coastguard Worker self.cgi_directories = [] 62*6777b538SAndroid Build Coastguard Worker self.is_executable_method = None 63*6777b538SAndroid Build Coastguard Worker 64*6777b538SAndroid Build Coastguard Worker self.use_tls = False 65*6777b538SAndroid Build Coastguard Worker self.private_key = None 66*6777b538SAndroid Build Coastguard Worker self.certificate = None 67*6777b538SAndroid Build Coastguard Worker self.tls_client_auth = False 68*6777b538SAndroid Build Coastguard Worker self.tls_client_ca = None 69*6777b538SAndroid Build Coastguard Worker self.use_basic_auth = False 70*6777b538SAndroid Build Coastguard Worker self.basic_auth_credential = 'Basic ' + base64.b64encode( 71*6777b538SAndroid Build Coastguard Worker b'test:test').decode() 72*6777b538SAndroid Build Coastguard Worker 73*6777b538SAndroid Build Coastguard Worker 74*6777b538SAndroid Build Coastguard Workerclass ThreadingHTTPServer(socketserver.ThreadingMixIn, 75*6777b538SAndroid Build Coastguard Worker testserver_base.ClientRestrictingServerMixIn, 76*6777b538SAndroid Build Coastguard Worker testserver_base.BrokenPipeHandlerMixIn, 77*6777b538SAndroid Build Coastguard Worker testserver_base.StoppableHTTPServer): 78*6777b538SAndroid Build Coastguard Worker """This is a specialization of StoppableHTTPServer that adds client 79*6777b538SAndroid Build Coastguard Worker verification and creates a new thread for every connection. It 80*6777b538SAndroid Build Coastguard Worker should only be used with handlers that are known to be threadsafe.""" 81*6777b538SAndroid Build Coastguard Worker 82*6777b538SAndroid Build Coastguard Worker pass 83*6777b538SAndroid Build Coastguard Worker 84*6777b538SAndroid Build Coastguard Worker 85*6777b538SAndroid Build Coastguard Workerclass TestPageHandler(testserver_base.BasePageHandler): 86*6777b538SAndroid Build Coastguard Worker def __init__(self, request, client_address, socket_server): 87*6777b538SAndroid Build Coastguard Worker connect_handlers = [self.DefaultConnectResponseHandler] 88*6777b538SAndroid Build Coastguard Worker get_handlers = [self.DefaultResponseHandler] 89*6777b538SAndroid Build Coastguard Worker post_handlers = get_handlers 90*6777b538SAndroid Build Coastguard Worker put_handlers = get_handlers 91*6777b538SAndroid Build Coastguard Worker head_handlers = [self.DefaultResponseHandler] 92*6777b538SAndroid Build Coastguard Worker testserver_base.BasePageHandler.__init__(self, request, client_address, 93*6777b538SAndroid Build Coastguard Worker socket_server, connect_handlers, 94*6777b538SAndroid Build Coastguard Worker get_handlers, head_handlers, 95*6777b538SAndroid Build Coastguard Worker post_handlers, put_handlers) 96*6777b538SAndroid Build Coastguard Worker 97*6777b538SAndroid Build Coastguard Worker def DefaultResponseHandler(self): 98*6777b538SAndroid Build Coastguard Worker """This is the catch-all response handler for requests that aren't handled 99*6777b538SAndroid Build Coastguard Worker by one of the special handlers above. 100*6777b538SAndroid Build Coastguard Worker Note that we specify the content-length as without it the https connection 101*6777b538SAndroid Build Coastguard Worker is not closed properly (and the browser keeps expecting data).""" 102*6777b538SAndroid Build Coastguard Worker 103*6777b538SAndroid Build Coastguard Worker contents = "Default response given for path: " + self.path 104*6777b538SAndroid Build Coastguard Worker self.send_response(200) 105*6777b538SAndroid Build Coastguard Worker self.send_header('Content-Type', 'text/html') 106*6777b538SAndroid Build Coastguard Worker self.send_header('Content-Length', len(contents)) 107*6777b538SAndroid Build Coastguard Worker self.end_headers() 108*6777b538SAndroid Build Coastguard Worker if (self.command != 'HEAD'): 109*6777b538SAndroid Build Coastguard Worker self.wfile.write(contents.encode('utf8')) 110*6777b538SAndroid Build Coastguard Worker return True 111*6777b538SAndroid Build Coastguard Worker 112*6777b538SAndroid Build Coastguard Worker def DefaultConnectResponseHandler(self): 113*6777b538SAndroid Build Coastguard Worker """This is the catch-all response handler for CONNECT requests that aren't 114*6777b538SAndroid Build Coastguard Worker handled by one of the special handlers above. Real Web servers respond 115*6777b538SAndroid Build Coastguard Worker with 400 to CONNECT requests.""" 116*6777b538SAndroid Build Coastguard Worker 117*6777b538SAndroid Build Coastguard Worker contents = "Your client has issued a malformed or illegal request." 118*6777b538SAndroid Build Coastguard Worker self.send_response(400) # bad request 119*6777b538SAndroid Build Coastguard Worker self.send_header('Content-Type', 'text/html') 120*6777b538SAndroid Build Coastguard Worker self.send_header('Content-Length', len(contents)) 121*6777b538SAndroid Build Coastguard Worker self.end_headers() 122*6777b538SAndroid Build Coastguard Worker self.wfile.write(contents.encode('utf8')) 123*6777b538SAndroid Build Coastguard Worker return True 124*6777b538SAndroid Build Coastguard Worker 125*6777b538SAndroid Build Coastguard Worker 126*6777b538SAndroid Build Coastguard Workerclass ProxyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): 127*6777b538SAndroid Build Coastguard Worker """A request handler that behaves as a proxy server. Only CONNECT, GET and 128*6777b538SAndroid Build Coastguard Worker HEAD methods are supported. 129*6777b538SAndroid Build Coastguard Worker """ 130*6777b538SAndroid Build Coastguard Worker 131*6777b538SAndroid Build Coastguard Worker redirect_connect_to_localhost = False; 132*6777b538SAndroid Build Coastguard Worker 133*6777b538SAndroid Build Coastguard Worker def _start_read_write(self, sock): 134*6777b538SAndroid Build Coastguard Worker sock.setblocking(0) 135*6777b538SAndroid Build Coastguard Worker self.request.setblocking(0) 136*6777b538SAndroid Build Coastguard Worker rlist = [self.request, sock] 137*6777b538SAndroid Build Coastguard Worker while True: 138*6777b538SAndroid Build Coastguard Worker ready_sockets, _unused, errors = select.select(rlist, [], []) 139*6777b538SAndroid Build Coastguard Worker if errors: 140*6777b538SAndroid Build Coastguard Worker self.send_response(500) 141*6777b538SAndroid Build Coastguard Worker self.end_headers() 142*6777b538SAndroid Build Coastguard Worker return 143*6777b538SAndroid Build Coastguard Worker for s in ready_sockets: 144*6777b538SAndroid Build Coastguard Worker received = s.recv(1024) 145*6777b538SAndroid Build Coastguard Worker if len(received) == 0: 146*6777b538SAndroid Build Coastguard Worker return 147*6777b538SAndroid Build Coastguard Worker if s == self.request: 148*6777b538SAndroid Build Coastguard Worker other = sock 149*6777b538SAndroid Build Coastguard Worker else: 150*6777b538SAndroid Build Coastguard Worker other = self.request 151*6777b538SAndroid Build Coastguard Worker # This will lose data if the kernel write buffer fills up. 152*6777b538SAndroid Build Coastguard Worker # TODO(ricea): Correctly use the return value to track how much was 153*6777b538SAndroid Build Coastguard Worker # written and buffer the rest. Use select to determine when the socket 154*6777b538SAndroid Build Coastguard Worker # becomes writable again. 155*6777b538SAndroid Build Coastguard Worker other.send(received) 156*6777b538SAndroid Build Coastguard Worker 157*6777b538SAndroid Build Coastguard Worker def _do_common_method(self): 158*6777b538SAndroid Build Coastguard Worker url = urlparse.urlparse(self.path) 159*6777b538SAndroid Build Coastguard Worker port = url.port 160*6777b538SAndroid Build Coastguard Worker if not port: 161*6777b538SAndroid Build Coastguard Worker if url.scheme == 'http': 162*6777b538SAndroid Build Coastguard Worker port = 80 163*6777b538SAndroid Build Coastguard Worker elif url.scheme == 'https': 164*6777b538SAndroid Build Coastguard Worker port = 443 165*6777b538SAndroid Build Coastguard Worker if not url.hostname or not port: 166*6777b538SAndroid Build Coastguard Worker self.send_response(400) 167*6777b538SAndroid Build Coastguard Worker self.end_headers() 168*6777b538SAndroid Build Coastguard Worker return 169*6777b538SAndroid Build Coastguard Worker 170*6777b538SAndroid Build Coastguard Worker if len(url.path) == 0: 171*6777b538SAndroid Build Coastguard Worker path = '/' 172*6777b538SAndroid Build Coastguard Worker else: 173*6777b538SAndroid Build Coastguard Worker path = url.path 174*6777b538SAndroid Build Coastguard Worker if len(url.query) > 0: 175*6777b538SAndroid Build Coastguard Worker path = '%s?%s' % (url.path, url.query) 176*6777b538SAndroid Build Coastguard Worker 177*6777b538SAndroid Build Coastguard Worker sock = None 178*6777b538SAndroid Build Coastguard Worker try: 179*6777b538SAndroid Build Coastguard Worker sock = socket.create_connection((url.hostname, port)) 180*6777b538SAndroid Build Coastguard Worker sock.send(('%s %s %s\r\n' % 181*6777b538SAndroid Build Coastguard Worker (self.command, path, self.protocol_version)).encode('utf-8')) 182*6777b538SAndroid Build Coastguard Worker for name, value in self.headers.items(): 183*6777b538SAndroid Build Coastguard Worker if (name.lower().startswith('connection') 184*6777b538SAndroid Build Coastguard Worker or name.lower().startswith('proxy')): 185*6777b538SAndroid Build Coastguard Worker continue 186*6777b538SAndroid Build Coastguard Worker # HTTP headers are encoded in Latin-1. 187*6777b538SAndroid Build Coastguard Worker sock.send(b'%s: %s\r\n' % 188*6777b538SAndroid Build Coastguard Worker (name.encode('latin-1'), value.encode('latin-1'))) 189*6777b538SAndroid Build Coastguard Worker sock.send(b'\r\n') 190*6777b538SAndroid Build Coastguard Worker # This is wrong: it will pass through connection-level headers and 191*6777b538SAndroid Build Coastguard Worker # misbehave on connection reuse. The only reason it works at all is that 192*6777b538SAndroid Build Coastguard Worker # our test servers have never supported connection reuse. 193*6777b538SAndroid Build Coastguard Worker # TODO(ricea): Use a proper HTTP client library instead. 194*6777b538SAndroid Build Coastguard Worker self._start_read_write(sock) 195*6777b538SAndroid Build Coastguard Worker except Exception: 196*6777b538SAndroid Build Coastguard Worker logging.exception('failure in common method: %s %s', self.command, path) 197*6777b538SAndroid Build Coastguard Worker self.send_response(500) 198*6777b538SAndroid Build Coastguard Worker self.end_headers() 199*6777b538SAndroid Build Coastguard Worker finally: 200*6777b538SAndroid Build Coastguard Worker if sock is not None: 201*6777b538SAndroid Build Coastguard Worker sock.close() 202*6777b538SAndroid Build Coastguard Worker 203*6777b538SAndroid Build Coastguard Worker def do_CONNECT(self): 204*6777b538SAndroid Build Coastguard Worker try: 205*6777b538SAndroid Build Coastguard Worker pos = self.path.rfind(':') 206*6777b538SAndroid Build Coastguard Worker host = self.path[:pos] 207*6777b538SAndroid Build Coastguard Worker port = int(self.path[pos+1:]) 208*6777b538SAndroid Build Coastguard Worker except Exception: 209*6777b538SAndroid Build Coastguard Worker self.send_response(400) 210*6777b538SAndroid Build Coastguard Worker self.end_headers() 211*6777b538SAndroid Build Coastguard Worker 212*6777b538SAndroid Build Coastguard Worker if ProxyRequestHandler.redirect_connect_to_localhost: 213*6777b538SAndroid Build Coastguard Worker host = "127.0.0.1" 214*6777b538SAndroid Build Coastguard Worker 215*6777b538SAndroid Build Coastguard Worker sock = None 216*6777b538SAndroid Build Coastguard Worker try: 217*6777b538SAndroid Build Coastguard Worker sock = socket.create_connection((host, port)) 218*6777b538SAndroid Build Coastguard Worker self.send_response(200, 'Connection established') 219*6777b538SAndroid Build Coastguard Worker self.end_headers() 220*6777b538SAndroid Build Coastguard Worker self._start_read_write(sock) 221*6777b538SAndroid Build Coastguard Worker except Exception: 222*6777b538SAndroid Build Coastguard Worker logging.exception('failure in CONNECT: %s', path) 223*6777b538SAndroid Build Coastguard Worker self.send_response(500) 224*6777b538SAndroid Build Coastguard Worker self.end_headers() 225*6777b538SAndroid Build Coastguard Worker finally: 226*6777b538SAndroid Build Coastguard Worker if sock is not None: 227*6777b538SAndroid Build Coastguard Worker sock.close() 228*6777b538SAndroid Build Coastguard Worker 229*6777b538SAndroid Build Coastguard Worker def do_GET(self): 230*6777b538SAndroid Build Coastguard Worker self._do_common_method() 231*6777b538SAndroid Build Coastguard Worker 232*6777b538SAndroid Build Coastguard Worker def do_HEAD(self): 233*6777b538SAndroid Build Coastguard Worker self._do_common_method() 234*6777b538SAndroid Build Coastguard Worker 235*6777b538SAndroid Build Coastguard Workerclass BasicAuthProxyRequestHandler(ProxyRequestHandler): 236*6777b538SAndroid Build Coastguard Worker """A request handler that behaves as a proxy server which requires 237*6777b538SAndroid Build Coastguard Worker basic authentication. 238*6777b538SAndroid Build Coastguard Worker """ 239*6777b538SAndroid Build Coastguard Worker 240*6777b538SAndroid Build Coastguard Worker _AUTH_CREDENTIAL = 'Basic Zm9vOmJhcg==' # foo:bar 241*6777b538SAndroid Build Coastguard Worker 242*6777b538SAndroid Build Coastguard Worker def parse_request(self): 243*6777b538SAndroid Build Coastguard Worker """Overrides parse_request to check credential.""" 244*6777b538SAndroid Build Coastguard Worker 245*6777b538SAndroid Build Coastguard Worker if not ProxyRequestHandler.parse_request(self): 246*6777b538SAndroid Build Coastguard Worker return False 247*6777b538SAndroid Build Coastguard Worker 248*6777b538SAndroid Build Coastguard Worker auth = self.headers.get('Proxy-Authorization', None) 249*6777b538SAndroid Build Coastguard Worker if auth != self._AUTH_CREDENTIAL: 250*6777b538SAndroid Build Coastguard Worker self.send_response(407) 251*6777b538SAndroid Build Coastguard Worker self.send_header('Proxy-Authenticate', 'Basic realm="MyRealm1"') 252*6777b538SAndroid Build Coastguard Worker self.end_headers() 253*6777b538SAndroid Build Coastguard Worker return False 254*6777b538SAndroid Build Coastguard Worker 255*6777b538SAndroid Build Coastguard Worker return True 256*6777b538SAndroid Build Coastguard Worker 257*6777b538SAndroid Build Coastguard Worker 258*6777b538SAndroid Build Coastguard Workerclass ServerRunner(testserver_base.TestServerRunner): 259*6777b538SAndroid Build Coastguard Worker """TestServerRunner for the net test servers.""" 260*6777b538SAndroid Build Coastguard Worker 261*6777b538SAndroid Build Coastguard Worker def __init__(self): 262*6777b538SAndroid Build Coastguard Worker super(ServerRunner, self).__init__() 263*6777b538SAndroid Build Coastguard Worker 264*6777b538SAndroid Build Coastguard Worker def __make_data_dir(self): 265*6777b538SAndroid Build Coastguard Worker if self.options.data_dir: 266*6777b538SAndroid Build Coastguard Worker if not os.path.isdir(self.options.data_dir): 267*6777b538SAndroid Build Coastguard Worker raise testserver_base.OptionError('specified data dir not found: ' + 268*6777b538SAndroid Build Coastguard Worker self.options.data_dir + ' exiting...') 269*6777b538SAndroid Build Coastguard Worker my_data_dir = self.options.data_dir 270*6777b538SAndroid Build Coastguard Worker else: 271*6777b538SAndroid Build Coastguard Worker # Create the default path to our data dir, relative to the exe dir. 272*6777b538SAndroid Build Coastguard Worker my_data_dir = os.path.join(BASE_DIR, "..", "..", "data") 273*6777b538SAndroid Build Coastguard Worker 274*6777b538SAndroid Build Coastguard Worker return my_data_dir 275*6777b538SAndroid Build Coastguard Worker 276*6777b538SAndroid Build Coastguard Worker def create_server(self, server_data): 277*6777b538SAndroid Build Coastguard Worker port = self.options.port 278*6777b538SAndroid Build Coastguard Worker host = self.options.host 279*6777b538SAndroid Build Coastguard Worker 280*6777b538SAndroid Build Coastguard Worker logging.basicConfig() 281*6777b538SAndroid Build Coastguard Worker 282*6777b538SAndroid Build Coastguard Worker # Work around a bug in Mac OS 10.6. Spawning a WebSockets server 283*6777b538SAndroid Build Coastguard Worker # will result in a call to |getaddrinfo|, which fails with "nodename 284*6777b538SAndroid Build Coastguard Worker # nor servname provided" for localhost:0 on 10.6. 285*6777b538SAndroid Build Coastguard Worker # TODO(ricea): Remove this if no longer needed. 286*6777b538SAndroid Build Coastguard Worker if self.options.server_type == SERVER_WEBSOCKET and \ 287*6777b538SAndroid Build Coastguard Worker host == "localhost" and \ 288*6777b538SAndroid Build Coastguard Worker port == 0: 289*6777b538SAndroid Build Coastguard Worker host = "127.0.0.1" 290*6777b538SAndroid Build Coastguard Worker 291*6777b538SAndroid Build Coastguard Worker # Construct the subjectAltNames for any ad-hoc generated certificates. 292*6777b538SAndroid Build Coastguard Worker # As host can be either a DNS name or IP address, attempt to determine 293*6777b538SAndroid Build Coastguard Worker # which it is, so it can be placed in the appropriate SAN. 294*6777b538SAndroid Build Coastguard Worker dns_sans = None 295*6777b538SAndroid Build Coastguard Worker ip_sans = None 296*6777b538SAndroid Build Coastguard Worker ip = None 297*6777b538SAndroid Build Coastguard Worker try: 298*6777b538SAndroid Build Coastguard Worker ip = socket.inet_aton(host) 299*6777b538SAndroid Build Coastguard Worker ip_sans = [ip] 300*6777b538SAndroid Build Coastguard Worker except socket.error: 301*6777b538SAndroid Build Coastguard Worker pass 302*6777b538SAndroid Build Coastguard Worker if ip is None: 303*6777b538SAndroid Build Coastguard Worker dns_sans = [host] 304*6777b538SAndroid Build Coastguard Worker 305*6777b538SAndroid Build Coastguard Worker if self.options.server_type == SERVER_UNSET: 306*6777b538SAndroid Build Coastguard Worker raise testserver_base.OptionError('no server type specified') 307*6777b538SAndroid Build Coastguard Worker elif self.options.server_type == SERVER_WEBSOCKET: 308*6777b538SAndroid Build Coastguard Worker # TODO(toyoshim): Remove following os.chdir. Currently this operation 309*6777b538SAndroid Build Coastguard Worker # is required to work correctly. It should be fixed from pywebsocket side. 310*6777b538SAndroid Build Coastguard Worker os.chdir(self.__make_data_dir()) 311*6777b538SAndroid Build Coastguard Worker websocket_options = WebSocketOptions(host, port, '.') 312*6777b538SAndroid Build Coastguard Worker scheme = "ws" 313*6777b538SAndroid Build Coastguard Worker if self.options.cert_and_key_file: 314*6777b538SAndroid Build Coastguard Worker scheme = "wss" 315*6777b538SAndroid Build Coastguard Worker websocket_options.use_tls = True 316*6777b538SAndroid Build Coastguard Worker key_path = os.path.join(ROOT_DIR, self.options.cert_and_key_file) 317*6777b538SAndroid Build Coastguard Worker if not os.path.isfile(key_path): 318*6777b538SAndroid Build Coastguard Worker raise testserver_base.OptionError( 319*6777b538SAndroid Build Coastguard Worker 'specified server cert file not found: ' + 320*6777b538SAndroid Build Coastguard Worker self.options.cert_and_key_file + ' exiting...') 321*6777b538SAndroid Build Coastguard Worker websocket_options.private_key = key_path 322*6777b538SAndroid Build Coastguard Worker websocket_options.certificate = key_path 323*6777b538SAndroid Build Coastguard Worker 324*6777b538SAndroid Build Coastguard Worker if self.options.ssl_client_auth: 325*6777b538SAndroid Build Coastguard Worker websocket_options.tls_client_cert_optional = False 326*6777b538SAndroid Build Coastguard Worker websocket_options.tls_client_auth = True 327*6777b538SAndroid Build Coastguard Worker if len(self.options.ssl_client_ca) != 1: 328*6777b538SAndroid Build Coastguard Worker raise testserver_base.OptionError( 329*6777b538SAndroid Build Coastguard Worker 'one trusted client CA file should be specified') 330*6777b538SAndroid Build Coastguard Worker if not os.path.isfile(self.options.ssl_client_ca[0]): 331*6777b538SAndroid Build Coastguard Worker raise testserver_base.OptionError( 332*6777b538SAndroid Build Coastguard Worker 'specified trusted client CA file not found: ' + 333*6777b538SAndroid Build Coastguard Worker self.options.ssl_client_ca[0] + ' exiting...') 334*6777b538SAndroid Build Coastguard Worker websocket_options.tls_client_ca = self.options.ssl_client_ca[0] 335*6777b538SAndroid Build Coastguard Worker print('Trying to start websocket server on %s://%s:%d...' % 336*6777b538SAndroid Build Coastguard Worker (scheme, websocket_options.server_host, websocket_options.port)) 337*6777b538SAndroid Build Coastguard Worker server = WebSocketServer(websocket_options) 338*6777b538SAndroid Build Coastguard Worker print('WebSocket server started on %s://%s:%d...' % 339*6777b538SAndroid Build Coastguard Worker (scheme, host, server.server_port)) 340*6777b538SAndroid Build Coastguard Worker server_data['port'] = server.server_port 341*6777b538SAndroid Build Coastguard Worker websocket_options.use_basic_auth = self.options.ws_basic_auth 342*6777b538SAndroid Build Coastguard Worker elif self.options.server_type == SERVER_PROXY: 343*6777b538SAndroid Build Coastguard Worker ProxyRequestHandler.redirect_connect_to_localhost = \ 344*6777b538SAndroid Build Coastguard Worker self.options.redirect_connect_to_localhost 345*6777b538SAndroid Build Coastguard Worker server = ThreadingHTTPServer((host, port), ProxyRequestHandler) 346*6777b538SAndroid Build Coastguard Worker print('Proxy server started on port %d...' % server.server_port) 347*6777b538SAndroid Build Coastguard Worker server_data['port'] = server.server_port 348*6777b538SAndroid Build Coastguard Worker elif self.options.server_type == SERVER_BASIC_AUTH_PROXY: 349*6777b538SAndroid Build Coastguard Worker ProxyRequestHandler.redirect_connect_to_localhost = \ 350*6777b538SAndroid Build Coastguard Worker self.options.redirect_connect_to_localhost 351*6777b538SAndroid Build Coastguard Worker server = ThreadingHTTPServer((host, port), BasicAuthProxyRequestHandler) 352*6777b538SAndroid Build Coastguard Worker print('BasicAuthProxy server started on port %d...' % server.server_port) 353*6777b538SAndroid Build Coastguard Worker server_data['port'] = server.server_port 354*6777b538SAndroid Build Coastguard Worker else: 355*6777b538SAndroid Build Coastguard Worker raise testserver_base.OptionError('unknown server type' + 356*6777b538SAndroid Build Coastguard Worker self.options.server_type) 357*6777b538SAndroid Build Coastguard Worker 358*6777b538SAndroid Build Coastguard Worker return server 359*6777b538SAndroid Build Coastguard Worker 360*6777b538SAndroid Build Coastguard Worker def add_options(self): 361*6777b538SAndroid Build Coastguard Worker testserver_base.TestServerRunner.add_options(self) 362*6777b538SAndroid Build Coastguard Worker self.option_parser.add_option('--proxy', 363*6777b538SAndroid Build Coastguard Worker action='store_const', 364*6777b538SAndroid Build Coastguard Worker const=SERVER_PROXY, 365*6777b538SAndroid Build Coastguard Worker default=SERVER_UNSET, 366*6777b538SAndroid Build Coastguard Worker dest='server_type', 367*6777b538SAndroid Build Coastguard Worker help='start up a proxy server.') 368*6777b538SAndroid Build Coastguard Worker self.option_parser.add_option('--basic-auth-proxy', 369*6777b538SAndroid Build Coastguard Worker action='store_const', 370*6777b538SAndroid Build Coastguard Worker const=SERVER_BASIC_AUTH_PROXY, 371*6777b538SAndroid Build Coastguard Worker default=SERVER_UNSET, 372*6777b538SAndroid Build Coastguard Worker dest='server_type', 373*6777b538SAndroid Build Coastguard Worker help='start up a proxy server which requires ' 374*6777b538SAndroid Build Coastguard Worker 'basic authentication.') 375*6777b538SAndroid Build Coastguard Worker self.option_parser.add_option('--websocket', 376*6777b538SAndroid Build Coastguard Worker action='store_const', 377*6777b538SAndroid Build Coastguard Worker const=SERVER_WEBSOCKET, 378*6777b538SAndroid Build Coastguard Worker default=SERVER_UNSET, 379*6777b538SAndroid Build Coastguard Worker dest='server_type', 380*6777b538SAndroid Build Coastguard Worker help='start up a WebSocket server.') 381*6777b538SAndroid Build Coastguard Worker self.option_parser.add_option('--cert-and-key-file', 382*6777b538SAndroid Build Coastguard Worker dest='cert_and_key_file', help='specify the ' 383*6777b538SAndroid Build Coastguard Worker 'path to the file containing the certificate ' 384*6777b538SAndroid Build Coastguard Worker 'and private key for the server in PEM ' 385*6777b538SAndroid Build Coastguard Worker 'format') 386*6777b538SAndroid Build Coastguard Worker self.option_parser.add_option('--ssl-client-auth', action='store_true', 387*6777b538SAndroid Build Coastguard Worker help='Require SSL client auth on every ' 388*6777b538SAndroid Build Coastguard Worker 'connection.') 389*6777b538SAndroid Build Coastguard Worker self.option_parser.add_option('--ssl-client-ca', action='append', 390*6777b538SAndroid Build Coastguard Worker default=[], help='Specify that the client ' 391*6777b538SAndroid Build Coastguard Worker 'certificate request should include the CA ' 392*6777b538SAndroid Build Coastguard Worker 'named in the subject of the DER-encoded ' 393*6777b538SAndroid Build Coastguard Worker 'certificate contained in the specified ' 394*6777b538SAndroid Build Coastguard Worker 'file. This option may appear multiple ' 395*6777b538SAndroid Build Coastguard Worker 'times, indicating multiple CA names should ' 396*6777b538SAndroid Build Coastguard Worker 'be sent in the request.') 397*6777b538SAndroid Build Coastguard Worker self.option_parser.add_option('--file-root-url', default='/files/', 398*6777b538SAndroid Build Coastguard Worker help='Specify a root URL for files served.') 399*6777b538SAndroid Build Coastguard Worker # TODO(ricea): Generalize this to support basic auth for HTTP too. 400*6777b538SAndroid Build Coastguard Worker self.option_parser.add_option('--ws-basic-auth', action='store_true', 401*6777b538SAndroid Build Coastguard Worker dest='ws_basic_auth', 402*6777b538SAndroid Build Coastguard Worker help='Enable basic-auth for WebSocket') 403*6777b538SAndroid Build Coastguard Worker self.option_parser.add_option('--redirect-connect-to-localhost', 404*6777b538SAndroid Build Coastguard Worker dest='redirect_connect_to_localhost', 405*6777b538SAndroid Build Coastguard Worker default=False, action='store_true', 406*6777b538SAndroid Build Coastguard Worker help='If set, the Proxy server will connect ' 407*6777b538SAndroid Build Coastguard Worker 'to localhost instead of the requested URL ' 408*6777b538SAndroid Build Coastguard Worker 'on CONNECT requests') 409*6777b538SAndroid Build Coastguard Worker 410*6777b538SAndroid Build Coastguard Worker 411*6777b538SAndroid Build Coastguard Workerif __name__ == '__main__': 412*6777b538SAndroid Build Coastguard Worker sys.exit(ServerRunner().main()) 413