1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/testing/mochitest/pywebsocket/mod_pywebsocket/headerparserhandler.py Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,243 @@ 1.4 +# Copyright 2011, Google Inc. 1.5 +# All rights reserved. 1.6 +# 1.7 +# Redistribution and use in source and binary forms, with or without 1.8 +# modification, are permitted provided that the following conditions are 1.9 +# met: 1.10 +# 1.11 +# * Redistributions of source code must retain the above copyright 1.12 +# notice, this list of conditions and the following disclaimer. 1.13 +# * Redistributions in binary form must reproduce the above 1.14 +# copyright notice, this list of conditions and the following disclaimer 1.15 +# in the documentation and/or other materials provided with the 1.16 +# distribution. 1.17 +# * Neither the name of Google Inc. nor the names of its 1.18 +# contributors may be used to endorse or promote products derived from 1.19 +# this software without specific prior written permission. 1.20 +# 1.21 +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.22 +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.23 +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1.24 +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1.25 +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 1.26 +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 1.27 +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1.28 +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 1.29 +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.30 +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 1.31 +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.32 + 1.33 + 1.34 +"""PythonHeaderParserHandler for mod_pywebsocket. 1.35 + 1.36 +Apache HTTP Server and mod_python must be configured such that this 1.37 +function is called to handle WebSocket request. 1.38 +""" 1.39 + 1.40 + 1.41 +import logging 1.42 + 1.43 +from mod_python import apache 1.44 + 1.45 +from mod_pywebsocket import common 1.46 +from mod_pywebsocket import dispatch 1.47 +from mod_pywebsocket import handshake 1.48 +from mod_pywebsocket import util 1.49 + 1.50 + 1.51 +# PythonOption to specify the handler root directory. 1.52 +_PYOPT_HANDLER_ROOT = 'mod_pywebsocket.handler_root' 1.53 + 1.54 +# PythonOption to specify the handler scan directory. 1.55 +# This must be a directory under the root directory. 1.56 +# The default is the root directory. 1.57 +_PYOPT_HANDLER_SCAN = 'mod_pywebsocket.handler_scan' 1.58 + 1.59 +# PythonOption to allow handlers whose canonical path is 1.60 +# not under the root directory. It's disallowed by default. 1.61 +# Set this option with value of 'yes' to allow. 1.62 +_PYOPT_ALLOW_HANDLERS_OUTSIDE_ROOT = ( 1.63 + 'mod_pywebsocket.allow_handlers_outside_root_dir') 1.64 +# Map from values to their meanings. 'Yes' and 'No' are allowed just for 1.65 +# compatibility. 1.66 +_PYOPT_ALLOW_HANDLERS_OUTSIDE_ROOT_DEFINITION = { 1.67 + 'off': False, 'no': False, 'on': True, 'yes': True} 1.68 + 1.69 +# PythonOption to specify to allow draft75 handshake. 1.70 +# The default is None (Off) 1.71 +_PYOPT_ALLOW_DRAFT75 = 'mod_pywebsocket.allow_draft75' 1.72 +# Map from values to their meanings. 1.73 +_PYOPT_ALLOW_DRAFT75_DEFINITION = {'off': False, 'on': True} 1.74 + 1.75 + 1.76 +class ApacheLogHandler(logging.Handler): 1.77 + """Wrapper logging.Handler to emit log message to apache's error.log.""" 1.78 + 1.79 + _LEVELS = { 1.80 + logging.DEBUG: apache.APLOG_DEBUG, 1.81 + logging.INFO: apache.APLOG_INFO, 1.82 + logging.WARNING: apache.APLOG_WARNING, 1.83 + logging.ERROR: apache.APLOG_ERR, 1.84 + logging.CRITICAL: apache.APLOG_CRIT, 1.85 + } 1.86 + 1.87 + def __init__(self, request=None): 1.88 + logging.Handler.__init__(self) 1.89 + self._log_error = apache.log_error 1.90 + if request is not None: 1.91 + self._log_error = request.log_error 1.92 + 1.93 + # Time and level will be printed by Apache. 1.94 + self._formatter = logging.Formatter('%(name)s: %(message)s') 1.95 + 1.96 + def emit(self, record): 1.97 + apache_level = apache.APLOG_DEBUG 1.98 + if record.levelno in ApacheLogHandler._LEVELS: 1.99 + apache_level = ApacheLogHandler._LEVELS[record.levelno] 1.100 + 1.101 + msg = self._formatter.format(record) 1.102 + 1.103 + # "server" parameter must be passed to have "level" parameter work. 1.104 + # If only "level" parameter is passed, nothing shows up on Apache's 1.105 + # log. However, at this point, we cannot get the server object of the 1.106 + # virtual host which will process WebSocket requests. The only server 1.107 + # object we can get here is apache.main_server. But Wherever (server 1.108 + # configuration context or virtual host context) we put 1.109 + # PythonHeaderParserHandler directive, apache.main_server just points 1.110 + # the main server instance (not any of virtual server instance). Then, 1.111 + # Apache follows LogLevel directive in the server configuration context 1.112 + # to filter logs. So, we need to specify LogLevel in the server 1.113 + # configuration context. Even if we specify "LogLevel debug" in the 1.114 + # virtual host context which actually handles WebSocket connections, 1.115 + # DEBUG level logs never show up unless "LogLevel debug" is specified 1.116 + # in the server configuration context. 1.117 + # 1.118 + # TODO(tyoshino): Provide logging methods on request object. When 1.119 + # request is mp_request object (when used together with Apache), the 1.120 + # methods call request.log_error indirectly. When request is 1.121 + # _StandaloneRequest, the methods call Python's logging facility which 1.122 + # we create in standalone.py. 1.123 + self._log_error(msg, apache_level, apache.main_server) 1.124 + 1.125 + 1.126 +def _configure_logging(): 1.127 + logger = logging.getLogger() 1.128 + # Logs are filtered by Apache based on LogLevel directive in Apache 1.129 + # configuration file. We must just pass logs for all levels to 1.130 + # ApacheLogHandler. 1.131 + logger.setLevel(logging.DEBUG) 1.132 + logger.addHandler(ApacheLogHandler()) 1.133 + 1.134 + 1.135 +_configure_logging() 1.136 + 1.137 +_LOGGER = logging.getLogger(__name__) 1.138 + 1.139 + 1.140 +def _parse_option(name, value, definition): 1.141 + if value is None: 1.142 + return False 1.143 + 1.144 + meaning = definition.get(value.lower()) 1.145 + if meaning is None: 1.146 + raise Exception('Invalid value for PythonOption %s: %r' % 1.147 + (name, value)) 1.148 + return meaning 1.149 + 1.150 + 1.151 +def _create_dispatcher(): 1.152 + _LOGGER.info('Initializing Dispatcher') 1.153 + 1.154 + options = apache.main_server.get_options() 1.155 + 1.156 + handler_root = options.get(_PYOPT_HANDLER_ROOT, None) 1.157 + if not handler_root: 1.158 + raise Exception('PythonOption %s is not defined' % _PYOPT_HANDLER_ROOT, 1.159 + apache.APLOG_ERR) 1.160 + 1.161 + handler_scan = options.get(_PYOPT_HANDLER_SCAN, handler_root) 1.162 + 1.163 + allow_handlers_outside_root = _parse_option( 1.164 + _PYOPT_ALLOW_HANDLERS_OUTSIDE_ROOT, 1.165 + options.get(_PYOPT_ALLOW_HANDLERS_OUTSIDE_ROOT), 1.166 + _PYOPT_ALLOW_HANDLERS_OUTSIDE_ROOT_DEFINITION) 1.167 + 1.168 + dispatcher = dispatch.Dispatcher( 1.169 + handler_root, handler_scan, allow_handlers_outside_root) 1.170 + 1.171 + for warning in dispatcher.source_warnings(): 1.172 + apache.log_error('mod_pywebsocket: %s' % warning, apache.APLOG_WARNING) 1.173 + 1.174 + return dispatcher 1.175 + 1.176 + 1.177 +# Initialize 1.178 +_dispatcher = _create_dispatcher() 1.179 + 1.180 + 1.181 +def headerparserhandler(request): 1.182 + """Handle request. 1.183 + 1.184 + Args: 1.185 + request: mod_python request. 1.186 + 1.187 + This function is named headerparserhandler because it is the default 1.188 + name for a PythonHeaderParserHandler. 1.189 + """ 1.190 + 1.191 + handshake_is_done = False 1.192 + try: 1.193 + # Fallback to default http handler for request paths for which 1.194 + # we don't have request handlers. 1.195 + if not _dispatcher.get_handler_suite(request.uri): 1.196 + request.log_error('No handler for resource: %r' % request.uri, 1.197 + apache.APLOG_INFO) 1.198 + request.log_error('Fallback to Apache', apache.APLOG_INFO) 1.199 + return apache.DECLINED 1.200 + except dispatch.DispatchException, e: 1.201 + request.log_error('mod_pywebsocket: %s' % e, apache.APLOG_INFO) 1.202 + if not handshake_is_done: 1.203 + return e.status 1.204 + 1.205 + try: 1.206 + allow_draft75 = _parse_option( 1.207 + _PYOPT_ALLOW_DRAFT75, 1.208 + apache.main_server.get_options().get(_PYOPT_ALLOW_DRAFT75), 1.209 + _PYOPT_ALLOW_DRAFT75_DEFINITION) 1.210 + 1.211 + try: 1.212 + handshake.do_handshake( 1.213 + request, _dispatcher, allowDraft75=allow_draft75) 1.214 + except handshake.VersionException, e: 1.215 + request.log_error('mod_pywebsocket: %s' % e, apache.APLOG_INFO) 1.216 + request.err_headers_out.add(common.SEC_WEBSOCKET_VERSION_HEADER, 1.217 + e.supported_versions) 1.218 + return apache.HTTP_BAD_REQUEST 1.219 + except handshake.HandshakeException, e: 1.220 + # Handshake for ws/wss failed. 1.221 + # Send http response with error status. 1.222 + request.log_error('mod_pywebsocket: %s' % e, apache.APLOG_INFO) 1.223 + return e.status 1.224 + 1.225 + handshake_is_done = True 1.226 + request._dispatcher = _dispatcher 1.227 + _dispatcher.transfer_data(request) 1.228 + except handshake.AbortedByUserException, e: 1.229 + request.log_error('mod_pywebsocket: %s' % e, apache.APLOG_INFO) 1.230 + except Exception, e: 1.231 + # DispatchException can also be thrown if something is wrong in 1.232 + # pywebsocket code. It's caught here, then. 1.233 + 1.234 + request.log_error('mod_pywebsocket: %s\n%s' % 1.235 + (e, util.get_stack_trace()), 1.236 + apache.APLOG_ERR) 1.237 + # Unknown exceptions before handshake mean Apache must handle its 1.238 + # request with another handler. 1.239 + if not handshake_is_done: 1.240 + return apache.DECLINED 1.241 + # Set assbackwards to suppress response header generation by Apache. 1.242 + request.assbackwards = 1 1.243 + return apache.DONE # Return DONE such that no other handlers are invoked. 1.244 + 1.245 + 1.246 +# vi:sts=4 sw=4 et