summaryrefslogtreecommitdiffstats
path: root/module/lib/mod_pywebsocket/dispatch.py
diff options
context:
space:
mode:
Diffstat (limited to 'module/lib/mod_pywebsocket/dispatch.py')
-rw-r--r--module/lib/mod_pywebsocket/dispatch.py387
1 files changed, 0 insertions, 387 deletions
diff --git a/module/lib/mod_pywebsocket/dispatch.py b/module/lib/mod_pywebsocket/dispatch.py
deleted file mode 100644
index 25905f180..000000000
--- a/module/lib/mod_pywebsocket/dispatch.py
+++ /dev/null
@@ -1,387 +0,0 @@
-# Copyright 2012, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-"""Dispatch WebSocket request.
-"""
-
-
-import logging
-import os
-import re
-
-from mod_pywebsocket import common
-from mod_pywebsocket import handshake
-from mod_pywebsocket import msgutil
-from mod_pywebsocket import mux
-from mod_pywebsocket import stream
-from mod_pywebsocket import util
-
-
-_SOURCE_PATH_PATTERN = re.compile(r'(?i)_wsh\.py$')
-_SOURCE_SUFFIX = '_wsh.py'
-_DO_EXTRA_HANDSHAKE_HANDLER_NAME = 'web_socket_do_extra_handshake'
-_TRANSFER_DATA_HANDLER_NAME = 'web_socket_transfer_data'
-_PASSIVE_CLOSING_HANDSHAKE_HANDLER_NAME = (
- 'web_socket_passive_closing_handshake')
-
-
-class DispatchException(Exception):
- """Exception in dispatching WebSocket request."""
-
- def __init__(self, name, status=common.HTTP_STATUS_NOT_FOUND):
- super(DispatchException, self).__init__(name)
- self.status = status
-
-
-def _default_passive_closing_handshake_handler(request):
- """Default web_socket_passive_closing_handshake handler."""
-
- return common.STATUS_NORMAL_CLOSURE, ''
-
-
-def _normalize_path(path):
- """Normalize path.
-
- Args:
- path: the path to normalize.
-
- Path is converted to the absolute path.
- The input path can use either '\\' or '/' as the separator.
- The normalized path always uses '/' regardless of the platform.
- """
-
- path = path.replace('\\', os.path.sep)
- path = os.path.realpath(path)
- path = path.replace('\\', '/')
- return path
-
-
-def _create_path_to_resource_converter(base_dir):
- """Returns a function that converts the path of a WebSocket handler source
- file to a resource string by removing the path to the base directory from
- its head, removing _SOURCE_SUFFIX from its tail, and replacing path
- separators in it with '/'.
-
- Args:
- base_dir: the path to the base directory.
- """
-
- base_dir = _normalize_path(base_dir)
-
- base_len = len(base_dir)
- suffix_len = len(_SOURCE_SUFFIX)
-
- def converter(path):
- if not path.endswith(_SOURCE_SUFFIX):
- return None
- # _normalize_path must not be used because resolving symlink breaks
- # following path check.
- path = path.replace('\\', '/')
- if not path.startswith(base_dir):
- return None
- return path[base_len:-suffix_len]
-
- return converter
-
-
-def _enumerate_handler_file_paths(directory):
- """Returns a generator that enumerates WebSocket Handler source file names
- in the given directory.
- """
-
- for root, unused_dirs, files in os.walk(directory):
- for base in files:
- path = os.path.join(root, base)
- if _SOURCE_PATH_PATTERN.search(path):
- yield path
-
-
-class _HandlerSuite(object):
- """A handler suite holder class."""
-
- def __init__(self, do_extra_handshake, transfer_data,
- passive_closing_handshake):
- self.do_extra_handshake = do_extra_handshake
- self.transfer_data = transfer_data
- self.passive_closing_handshake = passive_closing_handshake
-
-
-def _source_handler_file(handler_definition):
- """Source a handler definition string.
-
- Args:
- handler_definition: a string containing Python statements that define
- handler functions.
- """
-
- global_dic = {}
- try:
- exec handler_definition in global_dic
- except Exception:
- raise DispatchException('Error in sourcing handler:' +
- util.get_stack_trace())
- passive_closing_handshake_handler = None
- try:
- passive_closing_handshake_handler = _extract_handler(
- global_dic, _PASSIVE_CLOSING_HANDSHAKE_HANDLER_NAME)
- except Exception:
- passive_closing_handshake_handler = (
- _default_passive_closing_handshake_handler)
- return _HandlerSuite(
- _extract_handler(global_dic, _DO_EXTRA_HANDSHAKE_HANDLER_NAME),
- _extract_handler(global_dic, _TRANSFER_DATA_HANDLER_NAME),
- passive_closing_handshake_handler)
-
-
-def _extract_handler(dic, name):
- """Extracts a callable with the specified name from the given dictionary
- dic.
- """
-
- if name not in dic:
- raise DispatchException('%s is not defined.' % name)
- handler = dic[name]
- if not callable(handler):
- raise DispatchException('%s is not callable.' % name)
- return handler
-
-
-class Dispatcher(object):
- """Dispatches WebSocket requests.
-
- This class maintains a map from resource name to handlers.
- """
-
- def __init__(
- self, root_dir, scan_dir=None,
- allow_handlers_outside_root_dir=True):
- """Construct an instance.
-
- Args:
- root_dir: The directory where handler definition files are
- placed.
- scan_dir: The directory where handler definition files are
- searched. scan_dir must be a directory under root_dir,
- including root_dir itself. If scan_dir is None,
- root_dir is used as scan_dir. scan_dir can be useful
- in saving scan time when root_dir contains many
- subdirectories.
- allow_handlers_outside_root_dir: Scans handler files even if their
- canonical path is not under root_dir.
- """
-
- self._logger = util.get_class_logger(self)
-
- self._handler_suite_map = {}
- self._source_warnings = []
- if scan_dir is None:
- scan_dir = root_dir
- if not os.path.realpath(scan_dir).startswith(
- os.path.realpath(root_dir)):
- raise DispatchException('scan_dir:%s must be a directory under '
- 'root_dir:%s.' % (scan_dir, root_dir))
- self._source_handler_files_in_dir(
- root_dir, scan_dir, allow_handlers_outside_root_dir)
-
- def add_resource_path_alias(self,
- alias_resource_path, existing_resource_path):
- """Add resource path alias.
-
- Once added, request to alias_resource_path would be handled by
- handler registered for existing_resource_path.
-
- Args:
- alias_resource_path: alias resource path
- existing_resource_path: existing resource path
- """
- try:
- handler_suite = self._handler_suite_map[existing_resource_path]
- self._handler_suite_map[alias_resource_path] = handler_suite
- except KeyError:
- raise DispatchException('No handler for: %r' %
- existing_resource_path)
-
- def source_warnings(self):
- """Return warnings in sourcing handlers."""
-
- return self._source_warnings
-
- def do_extra_handshake(self, request):
- """Do extra checking in WebSocket handshake.
-
- Select a handler based on request.uri and call its
- web_socket_do_extra_handshake function.
-
- Args:
- request: mod_python request.
-
- Raises:
- DispatchException: when handler was not found
- AbortedByUserException: when user handler abort connection
- HandshakeException: when opening handshake failed
- """
-
- handler_suite = self.get_handler_suite(request.ws_resource)
- if handler_suite is None:
- raise DispatchException('No handler for: %r' % request.ws_resource)
- do_extra_handshake_ = handler_suite.do_extra_handshake
- try:
- do_extra_handshake_(request)
- except handshake.AbortedByUserException, e:
- raise
- except Exception, e:
- util.prepend_message_to_exception(
- '%s raised exception for %s: ' % (
- _DO_EXTRA_HANDSHAKE_HANDLER_NAME,
- request.ws_resource),
- e)
- raise handshake.HandshakeException(e, common.HTTP_STATUS_FORBIDDEN)
-
- def transfer_data(self, request):
- """Let a handler transfer_data with a WebSocket client.
-
- Select a handler based on request.ws_resource and call its
- web_socket_transfer_data function.
-
- Args:
- request: mod_python request.
-
- Raises:
- DispatchException: when handler was not found
- AbortedByUserException: when user handler abort connection
- """
-
- # TODO(tyoshino): Terminate underlying TCP connection if possible.
- try:
- if mux.use_mux(request):
- mux.start(request, self)
- else:
- handler_suite = self.get_handler_suite(request.ws_resource)
- if handler_suite is None:
- raise DispatchException('No handler for: %r' %
- request.ws_resource)
- transfer_data_ = handler_suite.transfer_data
- transfer_data_(request)
-
- if not request.server_terminated:
- request.ws_stream.close_connection()
- # Catch non-critical exceptions the handler didn't handle.
- except handshake.AbortedByUserException, e:
- self._logger.debug('%s', e)
- raise
- except msgutil.BadOperationException, e:
- self._logger.debug('%s', e)
- request.ws_stream.close_connection(common.STATUS_ABNORMAL_CLOSURE)
- except msgutil.InvalidFrameException, e:
- # InvalidFrameException must be caught before
- # ConnectionTerminatedException that catches InvalidFrameException.
- self._logger.debug('%s', e)
- request.ws_stream.close_connection(common.STATUS_PROTOCOL_ERROR)
- except msgutil.UnsupportedFrameException, e:
- self._logger.debug('%s', e)
- request.ws_stream.close_connection(common.STATUS_UNSUPPORTED_DATA)
- except stream.InvalidUTF8Exception, e:
- self._logger.debug('%s', e)
- request.ws_stream.close_connection(
- common.STATUS_INVALID_FRAME_PAYLOAD_DATA)
- except msgutil.ConnectionTerminatedException, e:
- self._logger.debug('%s', e)
- except Exception, e:
- util.prepend_message_to_exception(
- '%s raised exception for %s: ' % (
- _TRANSFER_DATA_HANDLER_NAME, request.ws_resource),
- e)
- raise
-
- def passive_closing_handshake(self, request):
- """Prepare code and reason for responding client initiated closing
- handshake.
- """
-
- handler_suite = self.get_handler_suite(request.ws_resource)
- if handler_suite is None:
- return _default_passive_closing_handshake_handler(request)
- return handler_suite.passive_closing_handshake(request)
-
- def get_handler_suite(self, resource):
- """Retrieves two handlers (one for extra handshake processing, and one
- for data transfer) for the given request as a HandlerSuite object.
- """
-
- fragment = None
- if '#' in resource:
- resource, fragment = resource.split('#', 1)
- if '?' in resource:
- resource = resource.split('?', 1)[0]
- handler_suite = self._handler_suite_map.get(resource)
- if handler_suite and fragment:
- raise DispatchException('Fragment identifiers MUST NOT be used on '
- 'WebSocket URIs',
- common.HTTP_STATUS_BAD_REQUEST)
- return handler_suite
-
- def _source_handler_files_in_dir(
- self, root_dir, scan_dir, allow_handlers_outside_root_dir):
- """Source all the handler source files in the scan_dir directory.
-
- The resource path is determined relative to root_dir.
- """
-
- # We build a map from resource to handler code assuming that there's
- # only one path from root_dir to scan_dir and it can be obtained by
- # comparing realpath of them.
-
- # Here we cannot use abspath. See
- # https://bugs.webkit.org/show_bug.cgi?id=31603
-
- convert = _create_path_to_resource_converter(root_dir)
- scan_realpath = os.path.realpath(scan_dir)
- root_realpath = os.path.realpath(root_dir)
- for path in _enumerate_handler_file_paths(scan_realpath):
- if (not allow_handlers_outside_root_dir and
- (not os.path.realpath(path).startswith(root_realpath))):
- self._logger.debug(
- 'Canonical path of %s is not under root directory' %
- path)
- continue
- try:
- handler_suite = _source_handler_file(open(path).read())
- except DispatchException, e:
- self._source_warnings.append('%s: %s' % (path, e))
- continue
- resource = convert(path)
- if resource is None:
- self._logger.debug(
- 'Path to resource conversion on %s failed' % path)
- else:
- self._handler_suite_map[convert(path)] = handler_suite
-
-
-# vi:sts=4 sw=4 et