xref: /aosp_15_r20/external/cronet/net/tools/testserver/testserver.py (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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