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