Ejemplo n.º 1
0
    def _select_loop(self):
        # efficient I/O multiplexing
        self._selector = selectors.DefaultSelector()
        # register server socket file descriptor
        self._selector.register(
            self._server_fd, selectors.EVENT_READ, (self._accept_client, None))

        try:
            while True:
                events = self._selector.select()

                for key, mask in events:
                    # accept new client
                    callback, namespace = key.data
                    try:
                        if namespace is None:
                            callback(key.fileobj)
                        else:
                            callback(key.fileobj, namespace)
                    except exceptions.ExitWrite:
                        # on client/server closed
                        pass
                # clean all socket write buffer
                self._clean_write_queue()
        except KeyboardInterrupt:  # when start debug mode listen Ctrl-C
            logger.info('<Ctrl + C> Bye, Never BUG')
            exit()
        except Exception as e:
            logger.error('Fatal Error occurs for {}'.format(repr(e)))
            raise
Ejemplo n.º 2
0
 def register_controller(self, namespace, controller_name):
     exceptions.raise_parameter_error('namespace', str, namespace)
     if not issubclass(controller_name, base_controller.BaseController):
         raise exceptions.ParameterError(
             'handlers must be derived with WebSocketHandlerProtocol')
     self._router.register(namespace, 'controller', controller_name)
     logger.info("Controller: {namespace} => {controller}".format(
         namespace=namespace, controller=controller_name))
Ejemplo n.º 3
0
    def register_default_handler(self, class_object):
        if not issubclass(class_object, handler.WebSocketHandlerProtocol):
            raise exceptions.ParameterError(
                'handlers must be derived with WebSocketHandlerProtocol')
        logger.info('Default handler: {}'.format(class_object))
        self._router.register_default('handler', class_object)

        @functools.wraps(class_object)
        def _handler_wrapper(*args, **kwargs):
            return class_object(*args, **kwargs)
        return _handler_wrapper
Ejemplo n.º 4
0
        def _decorator_wrapper(class_object):
            if not issubclass(class_object, handler.WebSocketHandlerProtocol):
                raise exceptions.ParameterError(
                    'handlers must be derived with WebSocketHandlerProtocol')
            self._router.register(namespace, 'handler', class_object)
            class_object.__namespace__ = namespace
            logger.info("Handler: '{namespace}' => {handler}".format(
                namespace=namespace, handler=class_object))

            @functools.wraps(class_object)
            def _handler_wrapper(*args, **kwargs):
                return class_object(*args, **kwargs)
            return _handler_wrapper
Ejemplo n.º 5
0
 def run_forever(self):
     # Start deamon on background
     super(WebSocketServerBase, self).run_forever()
     # Create server socket file descriptor
     self._server_fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
     # Set socket option, REUSEADDR = True
     self._server_fd.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
     # Set using non-block socket
     self._server_fd.setblocking(False)
     # Bind server listen port
     self._server_fd.bind(self._server_address)
     # Max connect queue size
     self._server_fd.listen(WebSocketServer.LISTEN_SIZE)
     logger.info('Server running in {}:{}'.format(*self._server_address))
     # enter the main loop
     self._select_loop()
Ejemplo n.º 6
0
    def _start_deamon(self):
        if not self._debug and (os.name == 'nt' or not hasattr(os, 'fork')):
            raise exceptions.DeamonError('Windows does not support fork')
        # double fork create a deamon
        try:
            pid = os.fork()  # fork #1
            if pid > 0:  # parent exit
                exit()
        except OSError as e:
            raise exceptions.FatalError(
                'Fork #1 error occurs, reason({})'.format(e))

        os.chdir('/')
        os.setsid()
        os.umask(0)

        try:
            pid = os.fork()  # fork #2
            if pid > 0:  # parent exit
                exit()
        except OSError as e:
            raise exceptions.FatalError(
                'Fork #2 error occurs, reason({})'.format(e))

        # redirect all std file descriptor
        sys.stdout.flush()
        sys.stderr.flush()
        _stdin = open(self._stdin, 'r')
        _stdout = open(self._stdout, 'a')
        # if require non-buffer, open mode muse be `b`
        _stderr = open(self._stderr, 'wb+', buffering=0)
        os.dup2(_stdin.fileno(), sys.stdin.fileno())
        os.dup2(_stdout.fileno(), sys.stdout.fileno())
        os.dup2(_stderr.fileno(), sys.stderr.fileno())

        # terminal signal
        signal.signal(signal.SIGTERM, self._signal_handler)
        # kill signal
        signal.signal(signal.SIGILL, self._signal_handler)
        # system interrupt
        signal.signal(signal.SIGINT, signal.SIG_IGN)
        # register function at exit
        atexit.register(self._remove_pid_file)
        # write pid file
        with open(self._pid_file, 'w') as fd:
            fd.write('{pid}\n'.format(pid=os.getpid()))
        logger.info('Daemon has been started')
Ejemplo n.º 7
0
    def _socket_ready_receive(self, socket_fd, namespace):
        try:
            controller = \
                self._client_list[namespace][socket_fd]  # type: BaseController
            controller.ready_receive()
        except exceptions.ConnectClosed as e:
            # from server close
            if self._close_information[socket_fd][0] is None:
                # TODO. handler send close-frame
                if e.args[0][0] == 1000:
                    # client first send close-frame
                    self._close_information[socket_fd] = ('client', False)
            else:
                self._close_information[socket_fd] = \
                            (self._close_information[socket_fd][0], True)
                self._close_client(socket_fd, namespace)

            if e.args[0][0] != 1000:
                logger.info('Server active close-frame, reason({})'.format(
                    e.args[0][0]))

            self._write_queue[socket_fd].append(ws_frame.generate_close_frame(
                extra_data=e.args[0][1], errno=e.args[0][0]))
Ejemplo n.º 8
0
    def verify_frame(self, frame: ws_frame.FrameBase):
        # MUST be 0 unless an extension is negotiated that defines meanings
        # for non-zero values.  If a nonzero value is received and none of
        # the negotiated extensions defines the meaning of such a nonzero
        # value, the receiving endpoint MUST _Fail the WebSocket
        # Connection_.
        if frame.flag_rsv1 or frame.flag_rsv2 or frame.flag_rsv3:
            logger.info('{} send frame invalid'.format(self._client_name))
            return False

        # As control frames cannot be fragmented, an intermediary MUST NOT
        # attempt to change the fragmentation of a control frame.
        if frame.flag_opcode >= 8 and frame.flag_fin != 1:
            logger.info('{} send frame invalid'.format(self._client_name))
            return False

        # All control frames MUST have a payload length of 125 bytes or less
        # and MUST NOT be fragmented.
        if frame.flag_opcode >= 0x08 and frame.payload_data_length > 125:
            logger.info('{} send frame invalid'.format(self._client_name))
            return False

        # An unfragmented message consists of a single frame with the FIN
        # bit set and an opcode other than 0.
        if frame.flag_fin == 1 and frame.flag_opcode == 0:
            logger.info('{} send frame invalid'.format(self._client_name))
            return False

        # a client MUST mask all frames that it sends to the server. The server
        # MUST close the connection upon receiving a frame that is not masked.
        if frame.flag_mask == 1 and frame.mask_key is False:
            # In this case, a server MAY send a Close frame with a status code
            # of 1002
            logger.info('{} send frame invalid'.format(self._client_name))
            return False
        return True
Ejemplo n.º 9
0
    def _accept_http_handshake(self, socket_fd):
        _tcp_stream = \
            self._client_list['default'][socket_fd]  # type:tcp_stream.TCPStream
        # receive data from kernel tcp buffer
        pos = _tcp_stream.find_buffer(b'\r\n\r\n')
        if pos is -1:
            return
        http_request = http_message.factory_http_message(
            _tcp_stream.feed_buffer(pos))
        # Verify http request is correct
        support_extension = tuple()
        try:
            support_extension_list = \
                http_verifier.verify_request(
                    socket_fd.getpeername(), http_request)
            support_extension = (
                b'Sec-WebSocket-Extensions',
                b','.join(map(
                    lambda x: generic.to_bytes(x), support_extension_list)))
            if not support_extension[1]:
                support_extension = None
        except exceptions.HttpVerifierError:
            # verify error occurs, return 403 Forbidden
            http_response = http_message.HttpResponse(
                403, (b'X-Forbidden-Reason', b'http-options-invalid'))
            # write directly to the data, and then close the connection
            self._socket_ready_write(
                socket_fd, http_response, http_request.url_path)
            # close connection
            self._close_client(socket_fd, 'default')
        logger.debug('Request: {}'.format(repr(http_request)))
        # TODO. chunk header-field
        if 'Content-Length' in http_request.header:
            logger.info('Request has payload, length = {}'.format(
                http_request.header.get_value('Content-Length')))
            # buffer length is too short
            if _tcp_stream.get_buffer_length() < \
                    http_request.header.get_value('Content-Length'):
                return
            # drop payload data
            _tcp_stream.feed_buffer(http_request['Content-Length'].value)

        ws_key = http_request.header.get_value('Sec-WebSocket-Key')
        # Optionally, other header fields, such as those used to send
        # cookies or request authentication to a server.
        http_response = http_message.HttpResponse(
            101, *(
                (b'Upgrade', b'websocket'),
                (b'Connection', b'Upgrade'),
                (b'Sec-WebSocket-Accept', ws_utils.ws_accept_key(ws_key)),
                support_extension
            )
        )
        try:
            namespace = generic.to_string(http_request.url_path)
            # get handler or default handler
            _handler = self._router.solution(namespace, 'handler')(socket_fd)
            # get controller or default controller
            controller_name = self._router.solution(namespace, 'controller')
            # initial controller
            if namespace not in self._client_list:
                self._client_list[namespace] = dict()
            self._client_list[namespace][socket_fd] = controller_name(
                self._client_list['default'].pop(socket_fd),
                self._write_queue[socket_fd].append,
                _handler)
            # send http-handshake-response
            self._write_queue[socket_fd].append(http_response)
            # notification handler connect event
            response = _handler.on_connect()
            # send connect message
            if hasattr(response, 'pack'):
                self._write_queue[socket_fd].append(response)
            elif hasattr(response, 'generate_frame'):
                self._write_queue[socket_fd].append(response.generate_frame)
            # modify selector data
            self._selector.modify(socket_fd,
                                  selectors.EVENT_READ,
                                  (self._socket_ready_receive, namespace))
        except exceptions.ParameterError:
            raise exceptions.FatalError('handler not found')
Ejemplo n.º 10
0
 def register_default_controller(self, controller_name):
     if not issubclass(controller_name, base_controller.BaseController):
         raise exceptions.ParameterError(
             'handlers must be derived with WebSocketHandlerProtocol')
     logger.info('Default controller: {}'.format(controller_name))
     self._router.register_default('controller', controller_name)
Ejemplo n.º 11
0
 def _remove_pid_file(self):
     logger.info('Daemon has exited')
     if os.path.exists(self._pid_file):
         os.remove(self._pid_file)
Ejemplo n.º 12
0
 def _signal_handler(self, signum, frame):
     logger.info('Daemon receive an exit signal({}: {})'.format(
         signum, frame))
     self._remove_pid_file()
     exit()