12da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#!/usr/bin/env python
22da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#
32da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# Copyright 2012, Google Inc.
42da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# All rights reserved.
52da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#
62da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# Redistribution and use in source and binary forms, with or without
72da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# modification, are permitted provided that the following conditions are
82da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# met:
92da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#
102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#     * Redistributions of source code must retain the above copyright
112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# notice, this list of conditions and the following disclaimer.
122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#     * Redistributions in binary form must reproduce the above
132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# copyright notice, this list of conditions and the following disclaimer
142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# in the documentation and/or other materials provided with the
152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# distribution.
162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#     * Neither the name of Google Inc. nor the names of its
172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# contributors may be used to endorse or promote products derived from
182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# this software without specific prior written permission.
192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#
202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis"""Standalone WebSocket server.
342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
352da489cd246702bee5938545b18a6f710ed214bcJamie GennisBASIC USAGE
362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
372da489cd246702bee5938545b18a6f710ed214bcJamie GennisUse this server to run mod_pywebsocket without Apache HTTP Server.
382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
392da489cd246702bee5938545b18a6f710ed214bcJamie GennisUsage:
402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    python standalone.py [-p <ws_port>] [-w <websock_handlers>]
412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                         [-s <scan_dir>]
422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                         [-d <document_root>]
432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                         [-m <websock_handlers_map_file>]
442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                         ... for other options, see _main below ...
452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis<ws_port> is the port number to use for ws:// connection.
472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis<document_root> is the path to the root directory of HTML files.
492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis<websock_handlers> is the path to the root directory of WebSocket handlers.
512da489cd246702bee5938545b18a6f710ed214bcJamie GennisSee __init__.py for details of <websock_handlers> and how to write WebSocket
522da489cd246702bee5938545b18a6f710ed214bcJamie Gennishandlers. If this path is relative, <document_root> is used as the base.
532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis<scan_dir> is a path under the root directory. If specified, only the
552da489cd246702bee5938545b18a6f710ed214bcJamie Gennishandlers under scan_dir are scanned. This is useful in saving scan time.
562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
582da489cd246702bee5938545b18a6f710ed214bcJamie GennisSUPPORTING TLS
592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
602da489cd246702bee5938545b18a6f710ed214bcJamie GennisTo support TLS, run standalone.py with -t, -k, and -c options.
612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
632da489cd246702bee5938545b18a6f710ed214bcJamie GennisSUPPORTING CLIENT AUTHENTICATION
642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
652da489cd246702bee5938545b18a6f710ed214bcJamie GennisTo support client authentication with TLS, run standalone.py with -t, -k, -c,
662da489cd246702bee5938545b18a6f710ed214bcJamie Gennisand --tls-client-auth, and --tls-client-ca options.
672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
682da489cd246702bee5938545b18a6f710ed214bcJamie GennisE.g., $./standalone.py -d ../example -p 10443 -t -c ../test/cert/cert.pem -k
692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis../test/cert/key.pem --tls-client-auth --tls-client-ca=../test/cert/cacert.pem
702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
722da489cd246702bee5938545b18a6f710ed214bcJamie GennisCONFIGURATION FILE
732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
742da489cd246702bee5938545b18a6f710ed214bcJamie GennisYou can also write a configuration file and use it by specifying the path to
752da489cd246702bee5938545b18a6f710ed214bcJamie Gennisthe configuration file by --config option. Please write a configuration file
762da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfollowing the documentation of the Python ConfigParser library. Name of each
772da489cd246702bee5938545b18a6f710ed214bcJamie Gennisentry must be the long version argument name. E.g. to set log level to debug,
782da489cd246702bee5938545b18a6f710ed214bcJamie Gennisadd the following line:
792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
802da489cd246702bee5938545b18a6f710ed214bcJamie Gennislog_level=debug
812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
822da489cd246702bee5938545b18a6f710ed214bcJamie GennisFor options which doesn't take value, please add some fake value. E.g. for
832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis--tls option, add the following line:
842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
852da489cd246702bee5938545b18a6f710ed214bcJamie Gennistls=True
862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
872da489cd246702bee5938545b18a6f710ed214bcJamie GennisNote that tls will be enabled even if you write tls=False as the value part is
882da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfake.
892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
902da489cd246702bee5938545b18a6f710ed214bcJamie GennisWhen both a command line argument and a configuration file entry are set for
912da489cd246702bee5938545b18a6f710ed214bcJamie Gennisthe same configuration item, the command line value will override one in the
922da489cd246702bee5938545b18a6f710ed214bcJamie Gennisconfiguration file.
932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
952da489cd246702bee5938545b18a6f710ed214bcJamie GennisTHREADING
962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
972da489cd246702bee5938545b18a6f710ed214bcJamie GennisThis server is derived from SocketServer.ThreadingMixIn. Hence a thread is
982da489cd246702bee5938545b18a6f710ed214bcJamie Gennisused for each request.
992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1012da489cd246702bee5938545b18a6f710ed214bcJamie GennisSECURITY WARNING
1022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1032da489cd246702bee5938545b18a6f710ed214bcJamie GennisThis uses CGIHTTPServer and CGIHTTPServer is not secure.
1042da489cd246702bee5938545b18a6f710ed214bcJamie GennisIt may execute arbitrary Python code or external programs. It should not be
1052da489cd246702bee5938545b18a6f710ed214bcJamie Gennisused outside a firewall.
1062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis"""
1072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1082da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport BaseHTTPServer
1092da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport CGIHTTPServer
1102da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport SimpleHTTPServer
1112da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport SocketServer
1122da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport ConfigParser
1132da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport base64
1142da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport httplib
1152da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport logging
1162da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport logging.handlers
1172da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport optparse
1182da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport os
1192da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport re
1202da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport select
1212da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport socket
1222da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport sys
1232da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport threading
1242da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport time
1252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis_HAS_SSL = False
1272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis_HAS_OPEN_SSL = False
1282da489cd246702bee5938545b18a6f710ed214bcJamie Gennistry:
1292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    import ssl
1302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    _HAS_SSL = True
1312da489cd246702bee5938545b18a6f710ed214bcJamie Gennisexcept ImportError:
1322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    try:
1332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        import OpenSSL.SSL
1342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        _HAS_OPEN_SSL = True
1352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    except ImportError:
1362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        pass
1372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# Find our local mod_pywebsocket module
1392da489cd246702bee5938545b18a6f710ed214bcJamie Gennissys.path.append(os.path.join(os.path.dirname(__file__),
1402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                "../../third_party/pywebsocket/src"))
1412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1422da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom mod_pywebsocket import common
1432da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom mod_pywebsocket import dispatch
1442da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom mod_pywebsocket import handshake
1452da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom mod_pywebsocket import http_header_util
1462da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom mod_pywebsocket import memorizingfile
1472da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom mod_pywebsocket import util
1482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis_DEFAULT_LOG_MAX_BYTES = 1024 * 256
1512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis_DEFAULT_LOG_BACKUP_COUNT = 5
1522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis_DEFAULT_REQUEST_QUEUE_SIZE = 128
1542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# 1024 is practically large enough to contain WebSocket handshake lines.
1562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis_MAX_MEMORIZED_LINES = 1024
1572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1592da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass _StandaloneConnection(object):
1602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Mimic mod_python mp_conn."""
1612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def __init__(self, request_handler):
1632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Construct an instance.
1642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        Args:
1662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            request_handler: A WebSocketRequestHandler instance.
1672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """
1682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._request_handler = request_handler
1702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def get_local_addr(self):
1722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Getter to mimic mp_conn.local_addr."""
1732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return (self._request_handler.server.server_name,
1752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self._request_handler.server.server_port)
1762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    local_addr = property(get_local_addr)
1772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def get_remote_addr(self):
1792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Getter to mimic mp_conn.remote_addr.
1802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        Setting the property in __init__ won't work because the request
1822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        handler is not initialized yet there."""
1832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return self._request_handler.client_address
1852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    remote_addr = property(get_remote_addr)
1862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def write(self, data):
1882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Mimic mp_conn.write()."""
1892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return self._request_handler.wfile.write(data)
1912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def read(self, length):
1932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Mimic mp_conn.read()."""
1942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return self._request_handler.rfile.read(length)
1962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def get_memorized_lines(self):
1982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Get memorized lines."""
1992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return self._request_handler.rfile.get_memorized_lines()
2012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2032da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass _StandaloneRequest(object):
2042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Mimic mod_python request."""
2052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def __init__(self, request_handler, use_tls):
2072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Construct an instance.
2082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        Args:
2102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            request_handler: A WebSocketRequestHandler instance.
2112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """
2122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._logger = util.get_class_logger(self)
2142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._request_handler = request_handler
2162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.connection = _StandaloneConnection(request_handler)
2172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._use_tls = use_tls
2182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.headers_in = request_handler.headers
2192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def get_uri(self):
2212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Getter to mimic request.uri."""
2222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return self._request_handler.path
2242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    uri = property(get_uri)
2252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def get_method(self):
2272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Getter to mimic request.method."""
2282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return self._request_handler.command
2302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    method = property(get_method)
2312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def is_https(self):
2332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Mimic request.is_https()."""
2342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return self._use_tls
2362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def _drain_received_data(self):
2382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Don't use this method from WebSocket handler. Drains unread data
2392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        in the receive buffer.
2402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """
2412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        raw_socket = self._request_handler.connection
2432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        drained_data = util.drain_received_data(raw_socket)
2442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if drained_data:
2462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._logger.debug(
2472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                'Drained data following close frame: %r', drained_data)
2482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2502da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass _StandaloneSSLConnection(object):
2512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """A wrapper class for OpenSSL.SSL.Connection to provide makefile method
2522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    which is not supported by the class.
2532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
2542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def __init__(self, connection):
2562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._connection = connection
2572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def __getattribute__(self, name):
2592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if name in ('_connection', 'makefile'):
2602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            return object.__getattribute__(self, name)
2612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return self._connection.__getattribute__(name)
2622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def __setattr__(self, name, value):
2642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if name in ('_connection', 'makefile'):
2652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            return object.__setattr__(self, name, value)
2662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return self._connection.__setattr__(name, value)
2672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def makefile(self, mode='r', bufsize=-1):
2692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return socket._fileobject(self._connection, mode, bufsize)
2702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2722da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass WebSocketServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
2732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """HTTPServer specialized for WebSocket."""
2742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # Overrides SocketServer.ThreadingMixIn.daemon_threads
2762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    daemon_threads = True
2772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # Overrides BaseHTTPServer.HTTPServer.allow_reuse_address
2782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    allow_reuse_address = True
2792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def __init__(self, options):
2812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Override SocketServer.TCPServer.__init__ to set SSL enabled
2822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        socket object to self.socket before server_bind and server_activate,
2832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if necessary.
2842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """
2852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._logger = util.get_class_logger(self)
2872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.request_queue_size = options.request_queue_size
2892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.__ws_is_shut_down = threading.Event()
2902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.__ws_serving = False
2912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        SocketServer.BaseServer.__init__(
2932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self, (options.server_host, options.port), WebSocketRequestHandler)
2942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # Expose the options object to allow handler objects access it. We name
2962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # it with websocket_ prefix to avoid conflict.
2972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.websocket_server_options = options
2982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._create_sockets()
3002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.server_bind()
3012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.server_activate()
3022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def _create_sockets(self):
3042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.server_name, self.server_port = self.server_address
3052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._sockets = []
3062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if not self.server_name:
3072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            # On platforms that doesn't support IPv6, the first bind fails.
3082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            # On platforms that supports IPv6
3092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            # - If it binds both IPv4 and IPv6 on call with AF_INET6, the
3102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            #   first bind succeeds and the second fails (we'll see 'Address
3112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            #   already in use' error).
3122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            # - If it binds only IPv6 on call with AF_INET6, both call are
3132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            #   expected to succeed to listen both protocol.
3142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            addrinfo_array = [
3152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                (socket.AF_INET6, socket.SOCK_STREAM, '', '', ''),
3162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                (socket.AF_INET, socket.SOCK_STREAM, '', '', '')]
3172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        else:
3182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            addrinfo_array = socket.getaddrinfo(self.server_name,
3192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                                self.server_port,
3202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                                socket.AF_UNSPEC,
3212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                                socket.SOCK_STREAM,
3222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                                socket.IPPROTO_TCP)
3232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        for addrinfo in addrinfo_array:
3242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._logger.info('Create socket on: %r', addrinfo)
3252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            family, socktype, proto, canonname, sockaddr = addrinfo
3262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            try:
3272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                socket_ = socket.socket(family, socktype)
3282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            except Exception, e:
3292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self._logger.info('Skip by failure: %r', e)
3302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                continue
3312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            if self.websocket_server_options.use_tls:
3322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                if _HAS_SSL:
3332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    if self.websocket_server_options.tls_client_auth:
3342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                        client_cert_ = ssl.CERT_REQUIRED
3352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    else:
3362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                        client_cert_ = ssl.CERT_NONE
3372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    socket_ = ssl.wrap_socket(socket_,
3382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                        keyfile=self.websocket_server_options.private_key,
3392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                        certfile=self.websocket_server_options.certificate,
3402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                        ssl_version=ssl.PROTOCOL_SSLv23,
3412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                        ca_certs=self.websocket_server_options.tls_client_ca,
3422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                        cert_reqs=client_cert_)
3432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                if _HAS_OPEN_SSL:
3442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    ctx = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
3452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    ctx.use_privatekey_file(
3462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                        self.websocket_server_options.private_key)
3472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    ctx.use_certificate_file(
3482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                        self.websocket_server_options.certificate)
3492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    socket_ = OpenSSL.SSL.Connection(ctx, socket_)
3502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._sockets.append((socket_, addrinfo))
3512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def server_bind(self):
3532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Override SocketServer.TCPServer.server_bind to enable multiple
3542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        sockets bind.
3552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """
3562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        failed_sockets = []
3582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        for socketinfo in self._sockets:
3602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            socket_, addrinfo = socketinfo
3612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._logger.info('Bind on: %r', addrinfo)
3622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            if self.allow_reuse_address:
3632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                socket_.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
3642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            try:
3652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                socket_.bind(self.server_address)
3662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            except Exception, e:
3672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self._logger.info('Skip by failure: %r', e)
3682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                socket_.close()
3692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                failed_sockets.append(socketinfo)
3702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        for socketinfo in failed_sockets:
3722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._sockets.remove(socketinfo)
3732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def server_activate(self):
3752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Override SocketServer.TCPServer.server_activate to enable multiple
3762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        sockets listen.
3772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """
3782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        failed_sockets = []
3802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        for socketinfo in self._sockets:
3822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            socket_, addrinfo = socketinfo
3832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._logger.info('Listen on: %r', addrinfo)
3842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            try:
3852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                socket_.listen(self.request_queue_size)
3862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            except Exception, e:
3872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self._logger.info('Skip by failure: %r', e)
3882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                socket_.close()
3892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                failed_sockets.append(socketinfo)
3902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        for socketinfo in failed_sockets:
3922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._sockets.remove(socketinfo)
3932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if len(self._sockets) == 0:
3952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._logger.critical(
3962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                'No sockets activated. Use info log level to see the reason.')
3972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def server_close(self):
3992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Override SocketServer.TCPServer.server_close to enable multiple
4002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        sockets close.
4012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """
4022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        for socketinfo in self._sockets:
4042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            socket_, addrinfo = socketinfo
4052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._logger.info('Close on: %r', addrinfo)
4062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            socket_.close()
4072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def fileno(self):
4092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Override SocketServer.TCPServer.fileno."""
4102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._logger.critical('Not supported: fileno')
4122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return self._sockets[0][0].fileno()
4132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def handle_error(self, rquest, client_address):
4152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Override SocketServer.handle_error."""
4162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._logger.error(
4182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            'Exception in processing request from: %r\n%s',
4192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            client_address,
4202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            util.get_stack_trace())
4212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # Note: client_address is a tuple.
4222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def get_request(self):
4242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Override TCPServer.get_request to wrap OpenSSL.SSL.Connection
4252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        object with _StandaloneSSLConnection to provide makefile method. We
4262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        cannot substitute OpenSSL.SSL.Connection.makefile since it's readonly
4272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        attribute.
4282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """
4292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        accepted_socket, client_address = self.socket.accept()
4312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if self.websocket_server_options.use_tls and _HAS_OPEN_SSL:
4322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            accepted_socket = _StandaloneSSLConnection(accepted_socket)
4332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return accepted_socket, client_address
4342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def serve_forever(self, poll_interval=0.5):
4362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Override SocketServer.BaseServer.serve_forever."""
4372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.__ws_serving = True
4392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.__ws_is_shut_down.clear()
4402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        handle_request = self.handle_request
4412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if hasattr(self, '_handle_request_noblock'):
4422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            handle_request = self._handle_request_noblock
4432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        else:
4442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._logger.warning('Fallback to blocking request handler')
4452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        try:
4462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            while self.__ws_serving:
4472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                r, w, e = select.select(
4482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    [socket_[0] for socket_ in self._sockets],
4492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    [], [], poll_interval)
4502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                for socket_ in r:
4512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    self.socket = socket_
4522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    handle_request()
4532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self.socket = None
4542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        finally:
4552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self.__ws_is_shut_down.set()
4562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def shutdown(self):
4582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Override SocketServer.BaseServer.shutdown."""
4592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.__ws_serving = False
4612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.__ws_is_shut_down.wait()
4622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4642da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass WebSocketRequestHandler(CGIHTTPServer.CGIHTTPRequestHandler):
4652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """CGIHTTPRequestHandler specialized for WebSocket."""
4662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # Use httplib.HTTPMessage instead of mimetools.Message.
4682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    MessageClass = httplib.HTTPMessage
4692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def setup(self):
4712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Override SocketServer.StreamRequestHandler.setup to wrap rfile
4722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        with MemorizingFile.
4732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        This method will be called by BaseRequestHandler's constructor
4752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        before calling BaseHTTPRequestHandler.handle.
4762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        BaseHTTPRequestHandler.handle will call
4772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        BaseHTTPRequestHandler.handle_one_request and it will call
4782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        WebSocketRequestHandler.parse_request.
4792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """
4802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # Call superclass's setup to prepare rfile, wfile, etc. See setup
4822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # definition on the root class SocketServer.StreamRequestHandler to
4832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # understand what this does.
4842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        CGIHTTPServer.CGIHTTPRequestHandler.setup(self)
4852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.rfile = memorizingfile.MemorizingFile(
4872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self.rfile,
4882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            max_memorized_lines=_MAX_MEMORIZED_LINES)
4892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def __init__(self, request, client_address, server):
4912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._logger = util.get_class_logger(self)
4922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._options = server.websocket_server_options
4942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # Overrides CGIHTTPServerRequestHandler.cgi_directories.
4962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.cgi_directories = self._options.cgi_directories
4972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # Replace CGIHTTPRequestHandler.is_executable method.
4982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if self._options.is_executable_method is not None:
4992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self.is_executable = self._options.is_executable_method
5002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # This actually calls BaseRequestHandler.__init__.
5022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        CGIHTTPServer.CGIHTTPRequestHandler.__init__(
5032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self, request, client_address, server)
5042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def parse_request(self):
5062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Override BaseHTTPServer.BaseHTTPRequestHandler.parse_request.
5072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        Return True to continue processing for HTTP(S), False otherwise.
5092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        See BaseHTTPRequestHandler.handle_one_request method which calls
5112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        this method to understand how the return value will be handled.
5122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """
5132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # We hook parse_request method, but also call the original
5152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # CGIHTTPRequestHandler.parse_request since when we return False,
5162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # CGIHTTPRequestHandler.handle_one_request continues processing and
5172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # it needs variables set by CGIHTTPRequestHandler.parse_request.
5182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        #
5192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # Variables set by this method will be also used by WebSocket request
5202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # handling (self.path, self.command, self.requestline, etc. See also
5212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # how _StandaloneRequest's members are implemented using these
5222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # attributes).
5232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if not CGIHTTPServer.CGIHTTPRequestHandler.parse_request(self):
5242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            return False
5252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if self._options.use_basic_auth:
5272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            auth = self.headers.getheader('Authorization')
5282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            if auth != self._options.basic_auth_credential:
5292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self.send_response(401)
5302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self.send_header('WWW-Authenticate',
5312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                 'Basic realm="Pywebsocket"')
5322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self.end_headers()
5332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self._logger.info('Request basic authentication')
5342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                return True
5352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        host, port, resource = http_header_util.parse_uri(self.path)
5372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if resource is None:
5382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._logger.info('Invalid URI: %r', self.path)
5392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._logger.info('Fallback to CGIHTTPRequestHandler')
5402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            return True
5412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        server_options = self.server.websocket_server_options
5422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if host is not None:
5432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            validation_host = server_options.validation_host
5442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            if validation_host is not None and host != validation_host:
5452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self._logger.info('Invalid host: %r (expected: %r)',
5462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                  host,
5472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                  validation_host)
5482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self._logger.info('Fallback to CGIHTTPRequestHandler')
5492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                return True
5502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if port is not None:
5512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            validation_port = server_options.validation_port
5522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            if validation_port is not None and port != validation_port:
5532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self._logger.info('Invalid port: %r (expected: %r)',
5542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                  port,
5552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                  validation_port)
5562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self._logger.info('Fallback to CGIHTTPRequestHandler')
5572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                return True
5582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.path = resource
5592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        request = _StandaloneRequest(self, self._options.use_tls)
5612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        try:
5632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            # Fallback to default http handler for request paths for which
5642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            # we don't have request handlers.
5652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            if not self._options.dispatcher.get_handler_suite(self.path):
5662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self._logger.info('No handler for resource: %r',
5672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                  self.path)
5682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self._logger.info('Fallback to CGIHTTPRequestHandler')
5692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                return True
5702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        except dispatch.DispatchException, e:
5712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._logger.info('%s', e)
5722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self.send_error(e.status)
5732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            return False
5742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # If any Exceptions without except clause setup (including
5762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # DispatchException) is raised below this point, it will be caught
5772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # and logged by WebSocketServer.
5782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        try:
5802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            try:
5812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                handshake.do_handshake(
5822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    request,
5832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    self._options.dispatcher,
5842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    allowDraft75=self._options.allow_draft75,
5852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    strict=self._options.strict)
5862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            except handshake.VersionException, e:
5872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self._logger.info('%s', e)
5882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self.send_response(common.HTTP_STATUS_BAD_REQUEST)
5892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self.send_header(common.SEC_WEBSOCKET_VERSION_HEADER,
5902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                 e.supported_versions)
5912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self.end_headers()
5922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                return False
5932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            except handshake.HandshakeException, e:
5942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                # Handshake for ws(s) failed.
5952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self._logger.info('%s', e)
5962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self.send_error(e.status)
5972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                return False
5982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            request._dispatcher = self._options.dispatcher
6002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._options.dispatcher.transfer_data(request)
6012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        except handshake.AbortedByUserException, e:
6022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._logger.info('%s', e)
6032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return False
6042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def log_request(self, code='-', size='-'):
6062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Override BaseHTTPServer.log_request."""
6072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._logger.info('"%s" %s %s',
6092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                          self.requestline, str(code), str(size))
6102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def log_error(self, *args):
6122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Override BaseHTTPServer.log_error."""
6132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # Despite the name, this method is for warnings than for errors.
6152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # For example, HTTP status code is logged by this method.
6162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._logger.warning('%s - %s',
6172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                             self.address_string(),
6182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                             args[0] % args[1:])
6192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def is_cgi(self):
6212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Test whether self.path corresponds to a CGI script.
6222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        Add extra check that self.path doesn't contains ..
6242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        Also check if the file is a executable file or not.
6252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        If the file is not executable, it is handled as static file or dir
6262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        rather than a CGI script.
6272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """
6282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if CGIHTTPServer.CGIHTTPRequestHandler.is_cgi(self):
6302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            if '..' in self.path:
6312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                return False
6322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            # strip query parameter from request path
6332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            resource_name = self.path.split('?', 2)[0]
6342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            # convert resource_name into real path name in filesystem.
6352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            scriptfile = self.translate_path(resource_name)
6362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            if not os.path.isfile(scriptfile):
6372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                return False
6382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            if not self.is_executable(scriptfile):
6392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                return False
6402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            return True
6412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return False
6422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6442da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef _get_logger_from_class(c):
6452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return logging.getLogger('%s.%s' % (c.__module__, c.__name__))
6462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6482da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef _configure_logging(options):
6492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    logging.addLevelName(common.LOGLEVEL_FINE, 'FINE')
6502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    logger = logging.getLogger()
6522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    logger.setLevel(logging.getLevelName(options.log_level.upper()))
6532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if options.log_file:
6542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        handler = logging.handlers.RotatingFileHandler(
6552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                options.log_file, 'a', options.log_max, options.log_count)
6562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    else:
6572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        handler = logging.StreamHandler()
6582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    formatter = logging.Formatter(
6592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            '[%(asctime)s] [%(levelname)s] %(name)s: %(message)s')
6602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    handler.setFormatter(formatter)
6612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    logger.addHandler(handler)
6622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    deflate_log_level_name = logging.getLevelName(
6642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        options.deflate_log_level.upper())
6652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    _get_logger_from_class(util._Deflater).setLevel(
6662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        deflate_log_level_name)
6672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    _get_logger_from_class(util._Inflater).setLevel(
6682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        deflate_log_level_name)
6692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6712da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef _alias_handlers(dispatcher, websock_handlers_map_file):
6722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Set aliases specified in websock_handler_map_file in dispatcher.
6732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Args:
6752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        dispatcher: dispatch.Dispatcher instance
6762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        websock_handler_map_file: alias map file
6772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
6782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    fp = open(websock_handlers_map_file)
6802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    try:
6812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        for line in fp:
6822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            if line[0] == '#' or line.isspace():
6832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                continue
6842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            m = re.match('(\S+)\s+(\S+)', line)
6852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            if not m:
6862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                logging.warning('Wrong format in map file:' + line)
6872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                continue
6882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            try:
6892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                dispatcher.add_resource_path_alias(
6902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    m.group(1), m.group(2))
6912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            except dispatch.DispatchException, e:
6922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                logging.error(str(e))
6932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    finally:
6942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        fp.close()
6952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6972da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef _build_option_parser():
6982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    parser = optparse.OptionParser()
6992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    parser.add_option('--config', dest='config_file', type='string',
7012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      default=None,
7022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      help=('Path to configuration file. See the file comment '
7032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                            'at the top of this file for the configuration '
7042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                            'file format'))
7052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    parser.add_option('-H', '--server-host', '--server_host',
7062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      dest='server_host',
7072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      default='',
7082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      help='server hostname to listen to')
7092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    parser.add_option('-V', '--validation-host', '--validation_host',
7102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      dest='validation_host',
7112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      default=None,
7122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      help='server hostname to validate in absolute path.')
7132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    parser.add_option('-p', '--port', dest='port', type='int',
7142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      default=common.DEFAULT_WEB_SOCKET_PORT,
7152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      help='port to listen to')
7162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    parser.add_option('-P', '--validation-port', '--validation_port',
7172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      dest='validation_port', type='int',
7182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      default=None,
7192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      help='server port to validate in absolute path.')
7202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    parser.add_option('-w', '--websock-handlers', '--websock_handlers',
7212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      dest='websock_handlers',
7222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      default='.',
7232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      help='WebSocket handlers root directory.')
7242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    parser.add_option('-m', '--websock-handlers-map-file',
7252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      '--websock_handlers_map_file',
7262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      dest='websock_handlers_map_file',
7272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      default=None,
7282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      help=('WebSocket handlers map file. '
7292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                            'Each line consists of alias_resource_path and '
7302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                            'existing_resource_path, separated by spaces.'))
7312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    parser.add_option('-s', '--scan-dir', '--scan_dir', dest='scan_dir',
7322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      default=None,
7332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      help=('WebSocket handlers scan directory. '
7342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                            'Must be a directory under websock_handlers.'))
7352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    parser.add_option('--allow-handlers-outside-root-dir',
7362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      '--allow_handlers_outside_root_dir',
7372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      dest='allow_handlers_outside_root_dir',
7382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      action='store_true',
7392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      default=False,
7402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      help=('Scans WebSocket handlers even if their canonical '
7412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                            'path is not under websock_handlers.'))
7422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    parser.add_option('-d', '--document-root', '--document_root',
7432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      dest='document_root', default='.',
7442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      help='Document root directory.')
7452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    parser.add_option('-x', '--cgi-paths', '--cgi_paths', dest='cgi_paths',
7462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      default=None,
7472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      help=('CGI paths relative to document_root.'
7482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                            'Comma-separated. (e.g -x /cgi,/htbin) '
7492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                            'Files under document_root/cgi_path are handled '
7502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                            'as CGI programs. Must be executable.'))
7512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    parser.add_option('-t', '--tls', dest='use_tls', action='store_true',
7522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      default=False, help='use TLS (wss://)')
7532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    parser.add_option('-k', '--private-key', '--private_key',
7542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      dest='private_key',
7552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      default='', help='TLS private key file.')
7562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    parser.add_option('-c', '--certificate', dest='certificate',
7572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      default='', help='TLS certificate file.')
7582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    parser.add_option('--tls-client-auth', dest='tls_client_auth',
7592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      action='store_true', default=False,
7602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      help='Requires TLS client auth on every connection.')
7612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    parser.add_option('--tls-client-ca', dest='tls_client_ca', default='',
7622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      help=('Specifies a pem file which contains a set of '
7632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                            'concatenated CA certificates which are used to '
7642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                            'validate certificates passed from clients'))
7652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    parser.add_option('--basic-auth', dest='use_basic_auth',
7662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      action='store_true', default=False,
7672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      help='Requires Basic authentication.')
7682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    parser.add_option('--basic-auth-credential',
7692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      dest='basic_auth_credential', default='test:test',
7702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      help='Specifies the credential of basic authentication '
7712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      'by username:password pair (e.g. test:test).')
7722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    parser.add_option('-l', '--log-file', '--log_file', dest='log_file',
7732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      default='', help='Log file.')
7742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # Custom log level:
7752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # - FINE: Prints status of each frame processing step
7762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    parser.add_option('--log-level', '--log_level', type='choice',
7772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      dest='log_level', default='warn',
7782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      choices=['fine',
7792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                               'debug', 'info', 'warning', 'warn', 'error',
7802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                               'critical'],
7812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      help='Log level.')
7822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    parser.add_option('--deflate-log-level', '--deflate_log_level',
7832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      type='choice',
7842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      dest='deflate_log_level', default='warn',
7852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      choices=['debug', 'info', 'warning', 'warn', 'error',
7862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                               'critical'],
7872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      help='Log level for _Deflater and _Inflater.')
7882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    parser.add_option('--thread-monitor-interval-in-sec',
7892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      '--thread_monitor_interval_in_sec',
7902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      dest='thread_monitor_interval_in_sec',
7912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      type='int', default=-1,
7922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      help=('If positive integer is specified, run a thread '
7932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                            'monitor to show the status of server threads '
7942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                            'periodically in the specified inteval in '
7952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                            'second. If non-positive integer is specified, '
7962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                            'disable the thread monitor.'))
7972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    parser.add_option('--log-max', '--log_max', dest='log_max', type='int',
7982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      default=_DEFAULT_LOG_MAX_BYTES,
7992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      help='Log maximum bytes')
8002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    parser.add_option('--log-count', '--log_count', dest='log_count',
8012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      type='int', default=_DEFAULT_LOG_BACKUP_COUNT,
8022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      help='Log backup count')
8032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    parser.add_option('--allow-draft75', dest='allow_draft75',
8042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      action='store_true', default=False,
8052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      help='Allow draft 75 handshake')
8062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    parser.add_option('--strict', dest='strict', action='store_true',
8072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      default=False, help='Strictly check handshake request')
8082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    parser.add_option('-q', '--queue', dest='request_queue_size', type='int',
8092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      default=_DEFAULT_REQUEST_QUEUE_SIZE,
8102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                      help='request queue size')
8112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return parser
8132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8152da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass ThreadMonitor(threading.Thread):
8162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    daemon = True
8172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def __init__(self, interval_in_sec):
8192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        threading.Thread.__init__(self, name='ThreadMonitor')
8202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._logger = util.get_class_logger(self)
8222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._interval_in_sec = interval_in_sec
8242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def run(self):
8262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        while True:
8272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            thread_name_list = []
8282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            for thread in threading.enumerate():
8292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                thread_name_list.append(thread.name)
8302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._logger.info(
8312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                "%d active threads: %s",
8322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                threading.active_count(),
8332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                ', '.join(thread_name_list))
8342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            time.sleep(self._interval_in_sec)
8352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8372da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef _parse_args_and_config(args):
8382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    parser = _build_option_parser()
8392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # First, parse options without configuration file.
8412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    temporary_options, temporary_args = parser.parse_args(args=args)
8422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if temporary_args:
8432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        logging.critical(
8442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            'Unrecognized positional arguments: %r', temporary_args)
8452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        sys.exit(1)
8462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if temporary_options.config_file:
8482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        try:
8492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            config_fp = open(temporary_options.config_file, 'r')
8502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        except IOError, e:
8512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            logging.critical(
8522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                'Failed to open configuration file %r: %r',
8532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                temporary_options.config_file,
8542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                e)
8552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            sys.exit(1)
8562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        config_parser = ConfigParser.SafeConfigParser()
8582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        config_parser.readfp(config_fp)
8592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        config_fp.close()
8602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        args_from_config = []
8622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        for name, value in config_parser.items('pywebsocket'):
8632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            args_from_config.append('--' + name)
8642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            args_from_config.append(value)
8652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if args is None:
8662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            args = args_from_config
8672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        else:
8682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            args = args_from_config + args
8692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return parser.parse_args(args=args)
8702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    else:
8712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return temporary_options, temporary_args
8722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8742da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef _main(args=None):
8752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    options, args = _parse_args_and_config(args=args)
8762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    os.chdir(options.document_root)
8782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    _configure_logging(options)
8802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # TODO(tyoshino): Clean up initialization of CGI related values. Move some
8822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # of code here to WebSocketRequestHandler class if it's better.
8832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    options.cgi_directories = []
8842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    options.is_executable_method = None
8852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if options.cgi_paths:
8862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        options.cgi_directories = options.cgi_paths.split(',')
8872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if sys.platform in ('cygwin', 'win32'):
8882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            cygwin_path = None
8892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            # For Win32 Python, it is expected that CYGWIN_PATH
8902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            # is set to a directory of cygwin binaries.
8912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            # For example, websocket_server.py in Chromium sets CYGWIN_PATH to
8922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            # full path of third_party/cygwin/bin.
8932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            if 'CYGWIN_PATH' in os.environ:
8942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                cygwin_path = os.environ['CYGWIN_PATH']
8952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            util.wrap_popen3_for_win(cygwin_path)
8962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            def __check_script(scriptpath):
8982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                return util.get_script_interp(scriptpath, cygwin_path)
8992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
9002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            options.is_executable_method = __check_script
9012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
9022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if options.use_tls:
9032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if not (_HAS_SSL or _HAS_OPEN_SSL):
9042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            logging.critical('TLS support requires ssl or pyOpenSSL module.')
9052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            sys.exit(1)
9062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if not options.private_key or not options.certificate:
9072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            logging.critical(
9082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    'To use TLS, specify private_key and certificate.')
9092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            sys.exit(1)
9102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
9112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if options.tls_client_auth:
9122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if not options.use_tls:
9132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            logging.critical('TLS must be enabled for client authentication.')
9142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            sys.exit(1)
9152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if not _HAS_SSL:
9162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            logging.critical('Client authentication requires ssl module.')
9172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
9182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if not options.scan_dir:
9192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        options.scan_dir = options.websock_handlers
9202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
9212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if options.use_basic_auth:
9222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        options.basic_auth_credential = 'Basic ' + base64.b64encode(
9232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            options.basic_auth_credential)
9242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
9252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    try:
9262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if options.thread_monitor_interval_in_sec > 0:
9272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            # Run a thread monitor to show the status of server threads for
9282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            # debugging.
9292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            ThreadMonitor(options.thread_monitor_interval_in_sec).start()
9302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
9312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # Share a Dispatcher among request handlers to save time for
9322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # instantiation.  Dispatcher can be shared because it is thread-safe.
9332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        options.dispatcher = dispatch.Dispatcher(
9342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            options.websock_handlers,
9352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            options.scan_dir,
9362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            options.allow_handlers_outside_root_dir)
9372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if options.websock_handlers_map_file:
9382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            _alias_handlers(options.dispatcher,
9392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                            options.websock_handlers_map_file)
9402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        warnings = options.dispatcher.source_warnings()
9412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if warnings:
9422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            for warning in warnings:
9432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                logging.warning('mod_pywebsocket: %s' % warning)
9442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
9452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        server = WebSocketServer(options)
9462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        server.serve_forever()
9472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    except Exception, e:
9482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        logging.critical('mod_pywebsocket: %s' % e)
9492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        logging.critical('mod_pywebsocket: %s' % util.get_stack_trace())
9502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        sys.exit(1)
9512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
9522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
9532da489cd246702bee5938545b18a6f710ed214bcJamie Gennisif __name__ == '__main__':
9542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    _main(sys.argv[1:])
9552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
9562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
9572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# vi:sts=4 sw=4 et
958