testing/mochitest/pywebsocket/mod_pywebsocket/headerparserhandler.py

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 # Copyright 2011, Google Inc.
michael@0 2 # All rights reserved.
michael@0 3 #
michael@0 4 # Redistribution and use in source and binary forms, with or without
michael@0 5 # modification, are permitted provided that the following conditions are
michael@0 6 # met:
michael@0 7 #
michael@0 8 # * Redistributions of source code must retain the above copyright
michael@0 9 # notice, this list of conditions and the following disclaimer.
michael@0 10 # * Redistributions in binary form must reproduce the above
michael@0 11 # copyright notice, this list of conditions and the following disclaimer
michael@0 12 # in the documentation and/or other materials provided with the
michael@0 13 # distribution.
michael@0 14 # * Neither the name of Google Inc. nor the names of its
michael@0 15 # contributors may be used to endorse or promote products derived from
michael@0 16 # this software without specific prior written permission.
michael@0 17 #
michael@0 18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
michael@0 19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
michael@0 20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
michael@0 21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
michael@0 22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
michael@0 23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
michael@0 24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
michael@0 25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
michael@0 26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
michael@0 27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
michael@0 28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
michael@0 29
michael@0 30
michael@0 31 """PythonHeaderParserHandler for mod_pywebsocket.
michael@0 32
michael@0 33 Apache HTTP Server and mod_python must be configured such that this
michael@0 34 function is called to handle WebSocket request.
michael@0 35 """
michael@0 36
michael@0 37
michael@0 38 import logging
michael@0 39
michael@0 40 from mod_python import apache
michael@0 41
michael@0 42 from mod_pywebsocket import common
michael@0 43 from mod_pywebsocket import dispatch
michael@0 44 from mod_pywebsocket import handshake
michael@0 45 from mod_pywebsocket import util
michael@0 46
michael@0 47
michael@0 48 # PythonOption to specify the handler root directory.
michael@0 49 _PYOPT_HANDLER_ROOT = 'mod_pywebsocket.handler_root'
michael@0 50
michael@0 51 # PythonOption to specify the handler scan directory.
michael@0 52 # This must be a directory under the root directory.
michael@0 53 # The default is the root directory.
michael@0 54 _PYOPT_HANDLER_SCAN = 'mod_pywebsocket.handler_scan'
michael@0 55
michael@0 56 # PythonOption to allow handlers whose canonical path is
michael@0 57 # not under the root directory. It's disallowed by default.
michael@0 58 # Set this option with value of 'yes' to allow.
michael@0 59 _PYOPT_ALLOW_HANDLERS_OUTSIDE_ROOT = (
michael@0 60 'mod_pywebsocket.allow_handlers_outside_root_dir')
michael@0 61 # Map from values to their meanings. 'Yes' and 'No' are allowed just for
michael@0 62 # compatibility.
michael@0 63 _PYOPT_ALLOW_HANDLERS_OUTSIDE_ROOT_DEFINITION = {
michael@0 64 'off': False, 'no': False, 'on': True, 'yes': True}
michael@0 65
michael@0 66 # PythonOption to specify to allow draft75 handshake.
michael@0 67 # The default is None (Off)
michael@0 68 _PYOPT_ALLOW_DRAFT75 = 'mod_pywebsocket.allow_draft75'
michael@0 69 # Map from values to their meanings.
michael@0 70 _PYOPT_ALLOW_DRAFT75_DEFINITION = {'off': False, 'on': True}
michael@0 71
michael@0 72
michael@0 73 class ApacheLogHandler(logging.Handler):
michael@0 74 """Wrapper logging.Handler to emit log message to apache's error.log."""
michael@0 75
michael@0 76 _LEVELS = {
michael@0 77 logging.DEBUG: apache.APLOG_DEBUG,
michael@0 78 logging.INFO: apache.APLOG_INFO,
michael@0 79 logging.WARNING: apache.APLOG_WARNING,
michael@0 80 logging.ERROR: apache.APLOG_ERR,
michael@0 81 logging.CRITICAL: apache.APLOG_CRIT,
michael@0 82 }
michael@0 83
michael@0 84 def __init__(self, request=None):
michael@0 85 logging.Handler.__init__(self)
michael@0 86 self._log_error = apache.log_error
michael@0 87 if request is not None:
michael@0 88 self._log_error = request.log_error
michael@0 89
michael@0 90 # Time and level will be printed by Apache.
michael@0 91 self._formatter = logging.Formatter('%(name)s: %(message)s')
michael@0 92
michael@0 93 def emit(self, record):
michael@0 94 apache_level = apache.APLOG_DEBUG
michael@0 95 if record.levelno in ApacheLogHandler._LEVELS:
michael@0 96 apache_level = ApacheLogHandler._LEVELS[record.levelno]
michael@0 97
michael@0 98 msg = self._formatter.format(record)
michael@0 99
michael@0 100 # "server" parameter must be passed to have "level" parameter work.
michael@0 101 # If only "level" parameter is passed, nothing shows up on Apache's
michael@0 102 # log. However, at this point, we cannot get the server object of the
michael@0 103 # virtual host which will process WebSocket requests. The only server
michael@0 104 # object we can get here is apache.main_server. But Wherever (server
michael@0 105 # configuration context or virtual host context) we put
michael@0 106 # PythonHeaderParserHandler directive, apache.main_server just points
michael@0 107 # the main server instance (not any of virtual server instance). Then,
michael@0 108 # Apache follows LogLevel directive in the server configuration context
michael@0 109 # to filter logs. So, we need to specify LogLevel in the server
michael@0 110 # configuration context. Even if we specify "LogLevel debug" in the
michael@0 111 # virtual host context which actually handles WebSocket connections,
michael@0 112 # DEBUG level logs never show up unless "LogLevel debug" is specified
michael@0 113 # in the server configuration context.
michael@0 114 #
michael@0 115 # TODO(tyoshino): Provide logging methods on request object. When
michael@0 116 # request is mp_request object (when used together with Apache), the
michael@0 117 # methods call request.log_error indirectly. When request is
michael@0 118 # _StandaloneRequest, the methods call Python's logging facility which
michael@0 119 # we create in standalone.py.
michael@0 120 self._log_error(msg, apache_level, apache.main_server)
michael@0 121
michael@0 122
michael@0 123 def _configure_logging():
michael@0 124 logger = logging.getLogger()
michael@0 125 # Logs are filtered by Apache based on LogLevel directive in Apache
michael@0 126 # configuration file. We must just pass logs for all levels to
michael@0 127 # ApacheLogHandler.
michael@0 128 logger.setLevel(logging.DEBUG)
michael@0 129 logger.addHandler(ApacheLogHandler())
michael@0 130
michael@0 131
michael@0 132 _configure_logging()
michael@0 133
michael@0 134 _LOGGER = logging.getLogger(__name__)
michael@0 135
michael@0 136
michael@0 137 def _parse_option(name, value, definition):
michael@0 138 if value is None:
michael@0 139 return False
michael@0 140
michael@0 141 meaning = definition.get(value.lower())
michael@0 142 if meaning is None:
michael@0 143 raise Exception('Invalid value for PythonOption %s: %r' %
michael@0 144 (name, value))
michael@0 145 return meaning
michael@0 146
michael@0 147
michael@0 148 def _create_dispatcher():
michael@0 149 _LOGGER.info('Initializing Dispatcher')
michael@0 150
michael@0 151 options = apache.main_server.get_options()
michael@0 152
michael@0 153 handler_root = options.get(_PYOPT_HANDLER_ROOT, None)
michael@0 154 if not handler_root:
michael@0 155 raise Exception('PythonOption %s is not defined' % _PYOPT_HANDLER_ROOT,
michael@0 156 apache.APLOG_ERR)
michael@0 157
michael@0 158 handler_scan = options.get(_PYOPT_HANDLER_SCAN, handler_root)
michael@0 159
michael@0 160 allow_handlers_outside_root = _parse_option(
michael@0 161 _PYOPT_ALLOW_HANDLERS_OUTSIDE_ROOT,
michael@0 162 options.get(_PYOPT_ALLOW_HANDLERS_OUTSIDE_ROOT),
michael@0 163 _PYOPT_ALLOW_HANDLERS_OUTSIDE_ROOT_DEFINITION)
michael@0 164
michael@0 165 dispatcher = dispatch.Dispatcher(
michael@0 166 handler_root, handler_scan, allow_handlers_outside_root)
michael@0 167
michael@0 168 for warning in dispatcher.source_warnings():
michael@0 169 apache.log_error('mod_pywebsocket: %s' % warning, apache.APLOG_WARNING)
michael@0 170
michael@0 171 return dispatcher
michael@0 172
michael@0 173
michael@0 174 # Initialize
michael@0 175 _dispatcher = _create_dispatcher()
michael@0 176
michael@0 177
michael@0 178 def headerparserhandler(request):
michael@0 179 """Handle request.
michael@0 180
michael@0 181 Args:
michael@0 182 request: mod_python request.
michael@0 183
michael@0 184 This function is named headerparserhandler because it is the default
michael@0 185 name for a PythonHeaderParserHandler.
michael@0 186 """
michael@0 187
michael@0 188 handshake_is_done = False
michael@0 189 try:
michael@0 190 # Fallback to default http handler for request paths for which
michael@0 191 # we don't have request handlers.
michael@0 192 if not _dispatcher.get_handler_suite(request.uri):
michael@0 193 request.log_error('No handler for resource: %r' % request.uri,
michael@0 194 apache.APLOG_INFO)
michael@0 195 request.log_error('Fallback to Apache', apache.APLOG_INFO)
michael@0 196 return apache.DECLINED
michael@0 197 except dispatch.DispatchException, e:
michael@0 198 request.log_error('mod_pywebsocket: %s' % e, apache.APLOG_INFO)
michael@0 199 if not handshake_is_done:
michael@0 200 return e.status
michael@0 201
michael@0 202 try:
michael@0 203 allow_draft75 = _parse_option(
michael@0 204 _PYOPT_ALLOW_DRAFT75,
michael@0 205 apache.main_server.get_options().get(_PYOPT_ALLOW_DRAFT75),
michael@0 206 _PYOPT_ALLOW_DRAFT75_DEFINITION)
michael@0 207
michael@0 208 try:
michael@0 209 handshake.do_handshake(
michael@0 210 request, _dispatcher, allowDraft75=allow_draft75)
michael@0 211 except handshake.VersionException, e:
michael@0 212 request.log_error('mod_pywebsocket: %s' % e, apache.APLOG_INFO)
michael@0 213 request.err_headers_out.add(common.SEC_WEBSOCKET_VERSION_HEADER,
michael@0 214 e.supported_versions)
michael@0 215 return apache.HTTP_BAD_REQUEST
michael@0 216 except handshake.HandshakeException, e:
michael@0 217 # Handshake for ws/wss failed.
michael@0 218 # Send http response with error status.
michael@0 219 request.log_error('mod_pywebsocket: %s' % e, apache.APLOG_INFO)
michael@0 220 return e.status
michael@0 221
michael@0 222 handshake_is_done = True
michael@0 223 request._dispatcher = _dispatcher
michael@0 224 _dispatcher.transfer_data(request)
michael@0 225 except handshake.AbortedByUserException, e:
michael@0 226 request.log_error('mod_pywebsocket: %s' % e, apache.APLOG_INFO)
michael@0 227 except Exception, e:
michael@0 228 # DispatchException can also be thrown if something is wrong in
michael@0 229 # pywebsocket code. It's caught here, then.
michael@0 230
michael@0 231 request.log_error('mod_pywebsocket: %s\n%s' %
michael@0 232 (e, util.get_stack_trace()),
michael@0 233 apache.APLOG_ERR)
michael@0 234 # Unknown exceptions before handshake mean Apache must handle its
michael@0 235 # request with another handler.
michael@0 236 if not handshake_is_done:
michael@0 237 return apache.DECLINED
michael@0 238 # Set assbackwards to suppress response header generation by Apache.
michael@0 239 request.assbackwards = 1
michael@0 240 return apache.DONE # Return DONE such that no other handlers are invoked.
michael@0 241
michael@0 242
michael@0 243 # vi:sts=4 sw=4 et

mercurial