Example #1
0
 def test_scan_sub_dir(self):
     disp = dispatch.Dispatcher(_TEST_HANDLERS_DIR, _TEST_HANDLERS_SUB_DIR)
     self.assertEqual(2, len(disp._handler_suite_map))
     self.failIf('/origin_check' in disp._handler_suite_map)
     self.failUnless(
         '/sub/exception_in_transfer' in disp._handler_suite_map)
     self.failUnless('/sub/plain' in disp._handler_suite_map)
Example #2
0
 def test_scan_dir(self):
     disp = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None)
     self.assertEqual(4, len(disp._handler_suite_map))
     self.assertTrue('/origin_check' in disp._handler_suite_map)
     self.assertTrue(
         '/sub/exception_in_transfer' in disp._handler_suite_map)
     self.assertTrue('/sub/plain' in disp._handler_suite_map)
Example #3
0
def _create_dispatcher():
    _LOGGER.info('Initializing Dispatcher')

    options = apache.main_server.get_options()

    handler_root = options.get(_PYOPT_HANDLER_ROOT, None)
    if not handler_root:
        raise Exception('PythonOption %s is not defined' % _PYOPT_HANDLER_ROOT,
                        apache.APLOG_ERR)

    handler_scan = options.get(_PYOPT_HANDLER_SCAN, handler_root)

    allow_handlers_outside_root = _parse_option(
        _PYOPT_ALLOW_HANDLERS_OUTSIDE_ROOT,
        options.get(_PYOPT_ALLOW_HANDLERS_OUTSIDE_ROOT),
        _PYOPT_ALLOW_HANDLERS_OUTSIDE_ROOT_DEFINITION)

    dispatcher = dispatch.Dispatcher(
        handler_root, handler_scan, allow_handlers_outside_root)

    for warning in dispatcher.source_warnings():
        apache.log_error(
            'mod_pywebsocket: Warning in source loading: %s' % warning,
            apache.APLOG_WARNING)

    return dispatcher
Example #4
0
    def __init__(self, server_address, transfer_data, RequestHandlerClass):
        """Override SocketServer.TCPServer.__init__ to set SSL enabled
        socket object to self.socket before server_bind and server_activate,
        if necessary.
        """
        options = Options()
        options.use_tls = False
        options.cgi_directories = []
        options.is_executable_method = None
        options.dispatcher = dispatch.Dispatcher('.',None)
        options.dispatcher._handler_suite_map['/echo'] = dispatch._HandlerSuite(self.extra_handshake, self.web_socket_transfer_data, self.closing_handshake)
         # add an echo handler for testing purposes
        options.dispatcher._handler_suite_map['/'] = dispatch._HandlerSuite(self.extra_handshake, transfer_data, self.closing_handshake)
         # add the supplied transfer method as the default handler
        options.allow_draft75 = True
        options.strict = False
        self.request_queue_size = 128
        self.__ws_is_shut_down = threading.Event()
        self.__ws_serving = False

        SocketServer.BaseServer.__init__(
            self, server_address, RequestHandlerClass)

        # Expose the options object to allow handler objects access it. We name
        # it with websocket_ prefix to avoid conflict.
        self.websocket_server_options = options

        self._create_sockets()
        self.server_bind()
        self.server_activate()
Example #5
0
    def test_transfer_data(self):
        dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None)

        request = mock.MockRequest(connection=mock.MockConn('\xff\x00'))
        request.ws_resource = '/origin_check'
        request.ws_protocol = 'p1'
        dispatcher.transfer_data(request)
        self.assertEqual(
            'origin_check_wsh.py is called for /origin_check, p1'
            '\xff\x00', request.connection.written_data())

        request = mock.MockRequest(connection=mock.MockConn('\xff\x00'))
        request.ws_resource = '/sub/plain'
        request.ws_protocol = None
        dispatcher.transfer_data(request)
        self.assertEqual(
            'sub/plain_wsh.py is called for /sub/plain, None'
            '\xff\x00', request.connection.written_data())

        request = mock.MockRequest(connection=mock.MockConn('\xff\x00'))
        request.ws_resource = '/sub/plain?'
        request.ws_protocol = None
        dispatcher.transfer_data(request)
        self.assertEqual(
            'sub/plain_wsh.py is called for /sub/plain?, None'
            '\xff\x00', request.connection.written_data())

        request = mock.MockRequest(connection=mock.MockConn('\xff\x00'))
        request.ws_resource = '/sub/plain?q=v'
        request.ws_protocol = None
        dispatcher.transfer_data(request)
        self.assertEqual(
            'sub/plain_wsh.py is called for /sub/plain?q=v, None'
            '\xff\x00', request.connection.written_data())
Example #6
0
 def test_scan_sub_dir_as_root(self):
     disp = dispatch.Dispatcher(_TEST_HANDLERS_SUB_DIR,
                                _TEST_HANDLERS_SUB_DIR)
     self.assertEqual(2, len(disp._handlers))
     self.failIf(disp._handlers.has_key('/origin_check'))
     self.failIf(disp._handlers.has_key('/sub/exception_in_transfer'))
     self.failIf(disp._handlers.has_key('/sub/plain'))
     self.failUnless(disp._handlers.has_key('/exception_in_transfer'))
     self.failUnless(disp._handlers.has_key('/plain'))
Example #7
0
    def test_do_extra_handshake(self):
        dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None)
        request = mock.MockRequest()
        request.ws_resource = '/origin_check'
        request.ws_origin = 'http://example.com'
        dispatcher.do_extra_handshake(request)  # Must not raise exception.

        request.ws_origin = 'http://bad.example.com'
        self.assertRaises(Exception, dispatcher.do_extra_handshake, request)
Example #8
0
 def test_transfer_data_handler_exception(self):
     dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None)
     request = mock.MockRequest(connection=mock.MockConn(''))
     request.ws_resource = '/sub/exception_in_transfer'
     request.ws_protocol = 'p3'
     try:
         dispatcher.transfer_data(request)
         self.fail()
     except Exception, e:
         self.failUnless(str(e).find('Intentional') != -1)
Example #9
0
 def test_scan_sub_dir_as_root(self):
     disp = dispatch.Dispatcher(_TEST_HANDLERS_SUB_DIR,
                                _TEST_HANDLERS_SUB_DIR)
     self.assertEqual(2, len(disp._handler_suite_map))
     self.assertFalse('/origin_check' in disp._handler_suite_map)
     self.assertFalse(
         '/sub/exception_in_transfer' in disp._handler_suite_map)
     self.assertFalse('/sub/plain' in disp._handler_suite_map)
     self.assertTrue('/exception_in_transfer' in disp._handler_suite_map)
     self.assertTrue('/plain' in disp._handler_suite_map)
Example #10
0
 def test_resource_path_alias(self):
     disp = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None)
     disp.add_resource_path_alias('/', '/origin_check')
     self.assertEqual(4, len(disp._handlers))
     self.failUnless(disp._handlers.has_key('/origin_check'))
     self.failUnless(disp._handlers.has_key('/sub/exception_in_transfer'))
     self.failUnless(disp._handlers.has_key('/sub/plain'))
     self.failUnless(disp._handlers.has_key('/'))
     self.assertRaises(dispatch.DispatchError, disp.add_resource_path_alias,
                       '/alias', '/not-exist')
Example #11
0
 def test_resource_path_alias(self):
     disp = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None)
     disp.add_resource_path_alias('/', '/origin_check')
     self.assertEqual(5, len(disp._handler_suite_map))
     self.failUnless('/origin_check' in disp._handler_suite_map)
     self.failUnless(
         '/sub/exception_in_transfer' in disp._handler_suite_map)
     self.failUnless('/sub/plain' in disp._handler_suite_map)
     self.failUnless('/' in disp._handler_suite_map)
     self.assertRaises(dispatch.DispatchException,
                       disp.add_resource_path_alias, '/alias', '/not-exist')
Example #12
0
def _create_dispatcher():
    _HANDLER_ROOT = apache.main_server.get_options().get(
        _PYOPT_HANDLER_ROOT, None)
    if not _HANDLER_ROOT:
        raise Exception('PythonOption %s is not defined' % _PYOPT_HANDLER_ROOT,
                        apache.APLOG_ERR)
    _HANDLER_SCAN = apache.main_server.get_options().get(
        _PYOPT_HANDLER_SCAN, _HANDLER_ROOT)
    dispatcher = dispatch.Dispatcher(_HANDLER_ROOT, _HANDLER_SCAN)
    for warning in dispatcher.source_warnings():
        apache.log_error('mod_pywebsocket: %s' % warning, apache.APLOG_WARNING)
    return dispatcher
Example #13
0
    def test_do_extra_handshake(self):
        dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None)
        request = mock.MockRequest()
        request.ws_resource = '/origin_check'
        request.ws_origin = 'http://example.com'
        dispatcher.do_extra_handshake(request)  # Must not raise exception.

        request.ws_origin = 'http://bad.example.com'
        try:
            dispatcher.do_extra_handshake(request)
            self.fail('Could not catch HandshakeException with 403 status')
        except handshake.HandshakeException, e:
            self.assertEquals(403, e.status)
Example #14
0
 def test_transfer_data_no_handler(self):
     dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None)
     for resource in ['/blank', '/sub/non_callable',
                      '/sub/no_wsh_at_the_end', '/does/not/exist']:
         request = mock.MockRequest(connection=mock.MockConn(''))
         request.ws_resource = resource
         request.ws_protocol = 'p2'
         try:
             dispatcher.transfer_data(request)
             self.fail()
         except dispatch.DispatchError, e:
             self.failUnless(str(e).find('No handler') != -1)
         except Exception:
             self.fail()
Example #15
0
 def test_source_warnings(self):
     dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None)
     warnings = dispatcher.source_warnings()
     warnings.sort()
     expected_warnings = [
         (os.path.join(_TEST_HANDLERS_DIR, 'blank_wsh.py') +
          ': web_socket_do_extra_handshake is not defined.'),
         (os.path.join(_TEST_HANDLERS_DIR, 'sub', 'non_callable_wsh.py') +
          ': web_socket_do_extra_handshake is not callable.'),
         (os.path.join(_TEST_HANDLERS_DIR, 'sub',
                       'wrong_handshake_sig_wsh.py') +
          ': web_socket_do_extra_handshake is not defined.'),
         (os.path.join(_TEST_HANDLERS_DIR, 'sub',
                       'wrong_transfer_sig_wsh.py') +
          ': web_socket_transfer_data is not defined.'),
     ]
     self.assertEquals(4, len(warnings))
     for expected, actual in zip(expected_warnings, warnings):
         self.assertEquals(expected, actual)
    def __init__(self, options):
        """Override SocketServer.TCPServer.__init__ to set SSL enabled
        socket object to self.socket before server_bind and server_activate,
        if necessary.
        """

        print "Starting Server..."
        # Share a Dispatcher among request handlers to save time for
        # instantiation.  Dispatcher can be shared because it is thread-safe.
        options.dispatcher = dispatch.Dispatcher(
            options.websock_handlers, options.scan_dir,
            options.allow_handlers_outside_root_dir)
        if options.websock_handlers_map_file:
            _alias_handlers(options.dispatcher,
                            options.websock_handlers_map_file)
        warnings = options.dispatcher.source_warnings()
        if warnings:
            for warning in warnings:
                logging.warning('Warning in source loading: %s' % warning)

        self._logger = util.get_class_logger(self)

        self.request_queue_size = options.request_queue_size
        self.__ws_is_shut_down = threading.Event()
        self.__ws_serving = False

        SocketServer.BaseServer.__init__(self,
                                         (options.server_host, options.port),
                                         WebSocketRequestHandler)

        # Expose the options object to allow handler objects access it. We name
        # it with websocket_ prefix to avoid conflict.
        self.websocket_server_options = options

        self._create_sockets()
        self.server_bind()
        self.server_activate()
Example #17
0
def _main(args=None):
    options, args = _parse_args_and_config(args=args)

    os.chdir(options.document_root)

    _configure_logging(options)

    # TODO(tyoshino): Clean up initialization of CGI related values. Move some
    # of code here to WebSocketRequestHandler class if it's better.
    options.cgi_directories = []
    options.is_executable_method = None
    if options.cgi_paths:
        options.cgi_directories = options.cgi_paths.split(',')
        if sys.platform in ('cygwin', 'win32'):
            cygwin_path = None
            # For Win32 Python, it is expected that CYGWIN_PATH
            # is set to a directory of cygwin binaries.
            # For example, websocket_server.py in Chromium sets CYGWIN_PATH to
            # full path of third_party/cygwin/bin.
            if 'CYGWIN_PATH' in os.environ:
                cygwin_path = os.environ['CYGWIN_PATH']
            util.wrap_popen3_for_win(cygwin_path)

            def __check_script(scriptpath):
                return util.get_script_interp(scriptpath, cygwin_path)

            options.is_executable_method = __check_script

    if options.use_tls:
        if not (_HAS_SSL or _HAS_OPEN_SSL):
            logging.critical('TLS support requires ssl or pyOpenSSL module.')
            sys.exit(1)
        if not options.private_key or not options.certificate:
            logging.critical(
                'To use TLS, specify private_key and certificate.')
            sys.exit(1)

    if options.tls_client_auth:
        if not options.use_tls:
            logging.critical('TLS must be enabled for client authentication.')
            sys.exit(1)
        if not _HAS_SSL:
            logging.critical('Client authentication requires ssl module.')

    if not options.scan_dir:
        options.scan_dir = options.websock_handlers

    if options.use_basic_auth:
        options.basic_auth_credential = 'Basic ' + base64.b64encode(
            options.basic_auth_credential)

    try:
        if options.thread_monitor_interval_in_sec > 0:
            # Run a thread monitor to show the status of server threads for
            # debugging.
            ThreadMonitor(options.thread_monitor_interval_in_sec).start()

        # Share a Dispatcher among request handlers to save time for
        # instantiation.  Dispatcher can be shared because it is thread-safe.
        options.dispatcher = dispatch.Dispatcher(
            options.websock_handlers, options.scan_dir,
            options.allow_handlers_outside_root_dir)
        if options.websock_handlers_map_file:
            _alias_handlers(options.dispatcher,
                            options.websock_handlers_map_file)
        warnings = options.dispatcher.source_warnings()
        if warnings:
            for warning in warnings:
                logging.warning('mod_pywebsocket: %s' % warning)

        server = WebSocketServer(options)
        server.serve_forever()
    except Exception, e:
        logging.critical('mod_pywebsocket: %s' % e)
        logging.critical('mod_pywebsocket: %s' % util.get_stack_trace())
        sys.exit(1)
Example #18
0
    def _stream_ws_thread(self, stream_id, queue):
        frame = queue.get(True, None)

        if frame is None:
            return

        rfile, wfile = os.pipe()
        rfile, wfile = os.fdopen(rfile, 'rb'), os.fdopen(
            wfile, 'wb', 0)  # needs to be unbuffer for websockets
        stream_handler = H2HandlerCopy(self, frame, rfile)

        h2request = H2Request(stream_handler)
        h2response = H2Response(stream_handler, h2request)

        dispatcher = dispatch.Dispatcher(self.server.ws_doc_root, None, False)
        if not dispatcher.get_handler_suite(stream_handler.path):
            h2response.set_error(404)
            h2response.write()
            return

        request_wrapper = _WebSocketRequest(stream_handler, h2response)

        handshaker = WsH2Handshaker(request_wrapper, dispatcher)
        try:
            handshaker.do_handshake()
        except HandshakeException as e:
            self.logger.info('Handshake failed for error: %s' % e)
            h2response.set_error(e.status)
            h2response.write()
            return
        except AbortedByUserException:
            h2response.write()
            return

        # h2 Handshaker prepares the headers but does not send them down the
        # wire. Flush the headers here.
        try:
            h2response.write_status_headers()
        except StreamClosedError:
            # work around https://github.com/web-platform-tests/wpt/issues/27786
            # The stream was already closed.
            return

        request_wrapper._dispatcher = dispatcher

        # we need two threads:
        # - one to handle the frame queue
        # - one to handle the request (dispatcher.transfer_data is blocking)
        # the alternative is to have only one (blocking) thread. That thread
        # will call transfer_data. That would require a special case in
        # handle_one_request, to bypass the queue and write data to wfile
        # directly.
        t = threading.Thread(
            target=Http2WebTestRequestHandler._stream_ws_sub_thread,
            args=(self, request_wrapper, stream_handler, queue))
        t.start()

        while not self.close_connection:
            try:
                frame = queue.get(True, 1)
            except Empty:
                continue

            if isinstance(frame, DataReceived):
                wfile.write(frame.data)
                if frame.stream_ended:
                    raise NotImplementedError("frame.stream_ended")
                    wfile.close()
            elif frame is None or isinstance(
                    frame, (StreamReset, StreamEnded, ConnectionTerminated)):
                self.logger.debug('(%s - %s) Stream Reset, Thread Closing' %
                                  (self.uid, stream_id))
                break

        t.join()
Example #19
0
 def test_scan_dir(self):
     disp = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None)
     self.assertEqual(3, len(disp._handlers))
     self.failUnless(disp._handlers.has_key('/origin_check'))
     self.failUnless(disp._handlers.has_key('/sub/exception_in_transfer'))
     self.failUnless(disp._handlers.has_key('/sub/plain'))
Example #20
0
 def test_scan_dir_must_under_root(self):
     dispatch.Dispatcher('a/b', 'a/b/c')  # OK
     dispatch.Dispatcher('a/b///', 'a/b')  # OK
     self.assertRaises(dispatch.DispatchException, dispatch.Dispatcher,
                       'a/b/c', 'a/b')
Example #21
0
def _main():
    parser = optparse.OptionParser()
    parser.add_option('-H', '--server-host', '--server_host',
                      dest='server_host',
                      default='',
                      help='server hostname to listen to')
    parser.add_option('-p', '--port', dest='port', type='int',
                      default=handshake.DEFAULT_WEB_SOCKET_PORT,
                      help='port to listen to')
    parser.add_option('-w', '--websock-handlers', '--websock_handlers',
                      dest='websock_handlers',
                      default='.',
                      help='Web Socket handlers root directory.')
    parser.add_option('-m', '--websock-handlers-map-file',
                      '--websock_handlers_map_file',
                      dest='websock_handlers_map_file',
                      default=None,
                      help=('Web Socket handlers map file. '
                            'Each line consists of alias_resource_path and '
                            'existing_resource_path, separated by spaces.'))
    parser.add_option('-s', '--scan-dir', '--scan_dir', dest='scan_dir',
                      default=None,
                      help=('Web Socket handlers scan directory. '
                            'Must be a directory under websock_handlers.'))
    parser.add_option('-d', '--document-root', '--document_root',
                      dest='document_root', default='.',
                      help='Document root directory.')
    parser.add_option('-x', '--cgi-paths', '--cgi_paths', dest='cgi_paths',
                      default=None,
                      help=('CGI paths relative to document_root.'
                            'Comma-separated. (e.g -x /cgi,/htbin) '
                            'Files under document_root/cgi_path are handled '
                            'as CGI programs. Must be executable.'))
    parser.add_option('-t', '--tls', dest='use_tls', action='store_true',
                      default=False, help='use TLS (wss://)')
    parser.add_option('-k', '--private-key', '--private_key',
                      dest='private_key',
                      default='', help='TLS private key file.')
    parser.add_option('-c', '--certificate', dest='certificate',
                      default='', help='TLS certificate file.')
    parser.add_option('-l', '--log-file', '--log_file', dest='log_file',
                      default='', help='Log file.')
    parser.add_option('--log-level', '--log_level', type='choice',
                      dest='log_level', default='warn',
                      choices=['debug', 'info', 'warn', 'error', 'critical'],
                      help='Log level.')
    parser.add_option('--log-max', '--log_max', dest='log_max', type='int',
                      default=_DEFAULT_LOG_MAX_BYTES,
                      help='Log maximum bytes')
    parser.add_option('--log-count', '--log_count', dest='log_count',
                      type='int', default=_DEFAULT_LOG_BACKUP_COUNT,
                      help='Log backup count')
    parser.add_option('--allow-draft75', dest='allow_draft75',
                      action='store_true', default=False,
                      help='Allow draft 75 handshake')
    parser.add_option('--strict', dest='strict', action='store_true',
                      default=False, help='Strictly check handshake request')
    parser.add_option('-q', '--queue', dest='request_queue_size', type='int',
                      default=_DEFAULT_REQUEST_QUEUE_SIZE,
                      help='request queue size')
    options = parser.parse_args()[0]

    os.chdir(options.document_root)

    _configure_logging(options)

    SocketServer.TCPServer.request_queue_size = options.request_queue_size
    CGIHTTPServer.CGIHTTPRequestHandler.cgi_directories = []

    if options.cgi_paths:
        CGIHTTPServer.CGIHTTPRequestHandler.cgi_directories = \
            options.cgi_paths.split(',')
        if sys.platform in ('cygwin', 'win32'):
            cygwin_path = None
            # For Win32 Python, it is expected that CYGWIN_PATH
            # is set to a directory of cygwin binaries.
            # For example, websocket_server.py in Chromium sets CYGWIN_PATH to
            # full path of third_party/cygwin/bin.
            if 'CYGWIN_PATH' in os.environ:
                cygwin_path = os.environ['CYGWIN_PATH']
            util.wrap_popen3_for_win(cygwin_path)
            def __check_script(scriptpath):
                return util.get_script_interp(scriptpath, cygwin_path)
            CGIHTTPServer.executable = __check_script

    if options.use_tls:
        if not _HAS_OPEN_SSL:
            logging.critical('To use TLS, install pyOpenSSL.')
            sys.exit(1)
        if not options.private_key or not options.certificate:
            logging.critical(
                    'To use TLS, specify private_key and certificate.')
            sys.exit(1)

    if not options.scan_dir:
        options.scan_dir = options.websock_handlers

    try:
        # Share a Dispatcher among request handlers to save time for
        # instantiation.  Dispatcher can be shared because it is thread-safe.
        options.dispatcher = dispatch.Dispatcher(options.websock_handlers,
                                                 options.scan_dir)
        if options.websock_handlers_map_file:
            _alias_handlers(options.dispatcher,
                            options.websock_handlers_map_file)
        _print_warnings_if_any(options.dispatcher)

        WebSocketRequestHandler.options = options
        WebSocketServer.options = options

        server = WebSocketServer((options.server_host, options.port),
                                 WebSocketRequestHandler)
        server.serve_forever()
    except Exception, e:
        logging.critical(str(e))
        sys.exit(1)
Example #22
0
 def test_abort_transfer_data(self):
     dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None)
     request = mock.MockRequest()
     request.ws_resource = '/abort_by_user'
     self.assertRaises(handshake.AbortedByUserException,
                       dispatcher.transfer_data, request)