Exemple #1
0
 def send(self, message, binary=False):
     """
     Send a frame over the websocket with message as its payload
     """
     if binary is None:
         binary = not isinstance(message, six.string_types)
     opcode = self.OPCODE_BINARY if binary else self.OPCODE_TEXT
     try:
         self.send_frame(message, opcode)
     except WebSocketError, e:
         logger.info('Socket is dead {}'.format(e))
def run(addr, port, wsgi_handler, ipv6=False, threading=False):
    """
    Function to monkey patch the internal Django command: manage.py runserver
    """
    logger.info("Websocket support is enabled")
    server_address = (addr, port)
    if not threading:
        raise Exception("Django's Websocket server must run with threading enabled")
    httpd_cls = type("WSGIServer", (socketserver.ThreadingMixIn, WSGIServer), {"daemon_threads": True})
    httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
    httpd.set_app(wsgi_handler)
    httpd.serve_forever()
def run(addr, port, wsgi_handler, ipv6=False, threading=False):
    """
    Function to monkey patch the internal Django command: manage.py runserver
    """
    logger.info('Websocket support is enabled')
    server_address = (addr, port)
    if not threading:
        raise Exception('Django\'s Websocket server must run with threading enabled')
    httpd_cls = type('WSGIServer', (socketserver.ThreadingMixIn, WSGIServer), { 'daemon_threads': True })
    httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
    httpd.set_app(wsgi_handler)
    httpd.serve_forever()
Exemple #4
0
    def receive(self,clientip):
        """
        Read and return a message from the stream. If `None` is returned, then
        the socket is considered closed/errored.
        """
        if self._closed:
            raise WebSocketError("Connection is already closed")
        try:
            message= self.read_message()
            #deal with zombie messages
            try:
                msg = ZombieMessage(message,clientip)

                if 'heartbeat' in msg.get():
                    message = settings.WS4REDIS_HEARTBEAT
                    ZombieWebsocket().heartbeat(msg)
                elif 'endpoint' in msg.get():
                    if msg.get()['endpoint'] == "register":
                      ZombieWebsocket().register(msg)
                    elif msg.get()['endpoint'] == "update":
                      ZombieWebsocket().update(msg)
            except Exception:
                pass

            return message
        except UnicodeError as e:
            logger.info('websocket.receive: UnicodeError {}'.format(e))
            self.close(1007)
        except WebSocketError as e:
            logger.info('websocket.receive: WebSocketError {}'.format(e))
            self.close(1002)
        except Exception as e:
            pdb.set_trace()
            logger.info('websocket.receive: Unknown error {}'.format(e))
            raise e
 def receive(self):
     """
     Read and return a message from the stream. If `None` is returned, then
     the socket is considered closed/errored.
     """
     if self._closed:
         raise WebSocketError("Connection is already closed")
     try:
         return self.read_message()
     except UnicodeError as e:
         logger.info('websocket.receive: UnicodeError {}'.format(e))
         self.close(1007)
     except WebSocketError as e:
         logger.info('websocket.receive: WebSocketError {}'.format(e))
         self.close(1002)
     except Exception as e:
         logger.info('websocket.receive: Unknown error {}'.format(e))
         raise e
Exemple #6
0
 def receive(self):
     """
     Read and return a message from the stream. If `None` is returned, then
     the socket is considered closed/errored.
     """
     if self._closed:
         raise WebSocketError("Connection is already closed")
     try:
         return self.read_message()
     except UnicodeError:
         logger.info('websocket.receive: UnicodeError')
         self.close(1007)
     except WebSocketError:
         logger.info('websocket.receive: WebSocketError')
         self.close(1002)
     except Exception as e:
         logger.info('websocket.receive: Unknown error %s', e)
         raise e
 def __call__(self, environ, start_response):
     """ Hijack the main loop from the original thread and listen on events on Redis and Websockets"""
     websocket = None
     request = None
     subscriber = self.Subscriber(self._redis_connection)
     try:
         self.assure_protocol_requirements(environ)
         request = WSGIRequest(environ)
         self.process_request(request)
         channels, echo_message = self.process_subscriptions(request)
         if callable(private_settings.WS4REDIS_ALLOWED_CHANNELS):
             channels = list(private_settings.WS4REDIS_ALLOWED_CHANNELS(request, channels))
         websocket = self.upgrade_websocket(environ, start_response)
         logger.debug('Subscribed to channels: {0}'.format(', '.join(channels)))
         subscriber.set_pubsub_channels(request, channels)
         subscriber.user_connect(request)
         websocket_fd = websocket.get_file_descriptor()
         listening_fds = [websocket_fd]
         redis_fd = subscriber.get_file_descriptor()
         if redis_fd:
             listening_fds.append(redis_fd)
         subscriber.send_persited_messages(websocket)
         recvmsg = None
         while websocket and not websocket.closed:
             ready = self.select(listening_fds, [], [], 4.0)[0]
             if not ready:
                 # flush empty socket
                 websocket.flush()
             for fd in ready:
                 if fd == websocket_fd:
                     recvmsg = RedisMessage(websocket.receive())
                     if recvmsg:
                         subscriber.publish_message(recvmsg)
                 elif fd == redis_fd:
                     sendmsg = RedisMessage(subscriber.parse_response())
                     if sendmsg and (echo_message or sendmsg != recvmsg):
                         websocket.send(sendmsg)
                 else:
                     logger.error('Invalid file descriptor: {0}'.format(fd))
             if private_settings.WS4REDIS_HEARTBEAT:
                 websocket.send(private_settings.WS4REDIS_HEARTBEAT)
     except WebSocketError as excpt:
         logger.warning('WebSocketError: ', exc_info=sys.exc_info())
         response = http.HttpResponse(status=1001, content='Websocket Closed')
     except UpgradeRequiredError as excpt:
         logger.info('Websocket upgrade required')
         response = http.HttpResponseBadRequest(status=426, content=excpt)
     except HandshakeError as excpt:
         logger.warning('HandshakeError: ', exc_info=sys.exc_info())
         response = http.HttpResponseBadRequest(content=excpt)
     except PermissionDenied as excpt:
         logger.warning('PermissionDenied: ', exc_info=sys.exc_info())
         response = http.HttpResponseForbidden(content=excpt)
     except Exception as excpt:
         logger.error('Other Exception: ', exc_info=sys.exc_info())
         response = http.HttpResponseServerError(content=excpt)
     else:
         response = http.HttpResponse()
     if websocket:
         subscriber.user_disconnect(request)
         websocket.close(code=1001, message='Websocket Closed')
     if hasattr(start_response, 'im_self') and not start_response.im_self.headers_sent:
         logger.warning('Staring late response on websocket')
         status_text = STATUS_CODE_TEXT.get(response.status_code, 'UNKNOWN STATUS CODE')
         status = '{0} {1}'.format(response.status_code, status_text)
         start_response(force_str(status), response._headers.values())
     logger.info('Finish long living response with status code: '.format(response.status_code))
     return response
 def __call__(self, environ, start_response):
     """
     Hijack the main loop from the original thread and listen on events on the Redis
     and the Websocket filedescriptors.
     """
     websocket = None
     subscriber = self.Subscriber(self._redis_connection)
     try:
         self.assure_protocol_requirements(environ)
         request = WSGIRequest(environ)
         if callable(private_settings.WS4REDIS_PROCESS_REQUEST):
             private_settings.WS4REDIS_PROCESS_REQUEST(request)
         else:
             self.process_request(request)
         channels, echo_message = self.process_subscriptions(request)
         if callable(private_settings.WS4REDIS_ALLOWED_CHANNELS):
             channels = list(private_settings.WS4REDIS_ALLOWED_CHANNELS(request, channels))
         websocket = self.upgrade_websocket(environ, start_response)
         logger.debug('Subscribed to channels: {0}'.format(', '.join(channels)))
         subscriber.set_pubsub_channels(request, channels)
         websocket_fd = websocket.get_file_descriptor()
         listening_fds = [websocket_fd]
         redis_fd = subscriber.get_file_descriptor()
         if redis_fd:
             listening_fds.append(redis_fd)
         subscriber.send_persited_messages(websocket)
         recvmsg = None
         while websocket and not websocket.closed:
             ready = self.select(listening_fds, [], [], 4.0)[0]
             if not ready:
                 # flush empty socket
                 websocket.flush()
             for fd in ready:
                 if fd == websocket_fd:
                     recvmsg = RedisMessage(websocket.receive())
                     if recvmsg:
                         subscriber.publish_message(recvmsg)
                 elif fd == redis_fd:
                     sendmsg = RedisMessage(subscriber.parse_response())
                     if sendmsg and (echo_message or sendmsg != recvmsg):
                         websocket.send(sendmsg)
                 else:
                     logger.error('Invalid file descriptor: {0}'.format(fd))
             # Check again that the websocket is closed before sending the heartbeat,
             # because the websocket can closed previously in the loop.
             if private_settings.WS4REDIS_HEARTBEAT and not websocket.closed:
                 websocket.send(private_settings.WS4REDIS_HEARTBEAT)
     except WebSocketError as excpt:
         logger.warning('WebSocketError: {}'.format(excpt), exc_info=sys.exc_info())
         response = http.HttpResponse(status=1001, content='Websocket Closed')
     except UpgradeRequiredError as excpt:
         logger.info('Websocket upgrade required')
         response = http.HttpResponseBadRequest(status=426, content=excpt)
     except HandshakeError as excpt:
         logger.warning('HandshakeError: {}'.format(excpt), exc_info=sys.exc_info())
         response = http.HttpResponseBadRequest(content=excpt)
     except PermissionDenied as excpt:
         logger.warning('PermissionDenied: {}'.format(excpt), exc_info=sys.exc_info())
         response = http.HttpResponseForbidden(content=excpt)
     except Exception as excpt:
         logger.error('Other Exception: {}'.format(excpt), exc_info=sys.exc_info())
         response = http.HttpResponseServerError(content=excpt)
     else:
         response = http.HttpResponse()
     finally:
         subscriber.release()
         if websocket:
             websocket.close(code=1001, message='Websocket Closed')
         else:
             logger.warning('Starting late response on websocket')
             status_text = http_client.responses.get(response.status_code, 'UNKNOWN STATUS CODE')
             status = '{0} {1}'.format(response.status_code, status_text)
             headers = response._headers.values()
             if six.PY3:
                 headers = list(headers)
             start_response(force_str(status), headers)
             logger.info('Finish non-websocket response with status code: {}'.format(response.status_code))
     return response
 def __call__(self, environ, start_response):
     """
     Hijack the main loop from the original thread and listen on events on the Redis
     and the Websocket filedescriptors.
     """
     websocket = None
     subscriber = self.Subscriber(self._redis_connection)
     try:
         self.assure_protocol_requirements(environ)
         request = WSGIRequest(environ)
         if callable(private_settings.WS4REDIS_PROCESS_REQUEST):
             private_settings.WS4REDIS_PROCESS_REQUEST(request)
         else:
             self.process_request(request)
         channels, echo_message = self.process_subscriptions(request)
         if callable(private_settings.WS4REDIS_ALLOWED_CHANNELS):
             channels = list(private_settings.WS4REDIS_ALLOWED_CHANNELS(request, channels))
         websocket = self.upgrade_websocket(environ, start_response)
         logger.debug('Subscribed to channels: {0}'.format(', '.join(channels)))
         subscriber.set_pubsub_channels(request, channels)
         websocket_fd = websocket.get_file_descriptor()
         listening_fds = [websocket_fd]
         redis_fd = subscriber.get_file_descriptor()
         if redis_fd:
             listening_fds.append(redis_fd)
         subscriber.send_persited_messages(websocket)
         recvmsg = None
         while websocket and not websocket.closed:
             ready = self.select(listening_fds, [], [], 4.0)[0]
             if not ready:
                 # flush empty socket
                 websocket.flush()
             for fd in ready:
                 if fd == websocket_fd:
                     recvmsg = RedisMessage(websocket.receive())
                     if recvmsg:
                         subscriber.publish_message(recvmsg)
                 elif fd == redis_fd:
                     sendmsg = RedisMessage(subscriber.parse_response())
                     if sendmsg and (echo_message or sendmsg != recvmsg):
                         websocket.send(sendmsg)
                 else:
                     logger.error('Invalid file descriptor: {0}'.format(fd))
             if private_settings.WS4REDIS_HEARTBEAT:
                 websocket.send(private_settings.WS4REDIS_HEARTBEAT)
     except WebSocketError as excpt:
         logger.warning('WebSocketError: {}'.format(excpt), exc_info=sys.exc_info())
         response = http.HttpResponse(status=1001, content='Websocket Closed')
     except UpgradeRequiredError as excpt:
         logger.info('Websocket upgrade required')
         response = http.HttpResponseBadRequest(status=426, content=excpt)
     except HandshakeError as excpt:
         logger.warning('HandshakeError: {}'.format(excpt), exc_info=sys.exc_info())
         response = http.HttpResponseBadRequest(content=excpt)
     except PermissionDenied as excpt:
         logger.warning('PermissionDenied: {}'.format(excpt), exc_info=sys.exc_info())
         response = http.HttpResponseForbidden(content=excpt)
     except Exception as excpt:
         logger.error('Other Exception: {}'.format(excpt), exc_info=sys.exc_info())
         response = http.HttpResponseServerError(content=excpt)
     else:
         response = http.HttpResponse()
     finally:
         subscriber.release()
         if websocket:
             websocket.close(code=1001, message='Websocket Closed')
         else:
             logger.warning('Starting late response on websocket')
             status_text = STATUS_CODE_TEXT.get(response.status_code, 'UNKNOWN STATUS CODE')
             status = '{0} {1}'.format(response.status_code, status_text)
             start_response(force_str(status), response._headers.values())
             logger.info('Finish non-websocket response with status code: {}'.format(response.status_code))
     return response
Exemple #10
0
class WebsocketWSGIServer(object):
    def __init__(self, redis_connection=None):
        """
        redis_connection can be overriden by a mock object.
        """
        comps = str(redis_settings.WS4REDIS_STORE).split('.')
        module = import_module('.'.join(comps[:-1]))
        RedisStore = getattr(module, comps[-1])
        self.allowed_channels = RedisStore.subscription_channels + RedisStore.publish_channels
        self._redis_connection = redis_connection and redis_connection or StrictRedis(
            **redis_settings.WS4REDIS_CONNECTION)
        self.RedisStore = RedisStore

    def assure_protocol_requirements(self, environ):
        if environ.get('REQUEST_METHOD') != 'GET':
            raise HandshakeError('HTTP method must be a GET')

        if environ.get('SERVER_PROTOCOL') != 'HTTP/1.1':
            raise HandshakeError('HTTP server protocol must be 1.1')

        if environ.get('HTTP_UPGRADE', '').lower() != 'websocket':
            raise HandshakeError(
                'Client does not wish to upgrade to a websocket')

    def process_request(self, request):
        request.session = None
        request.user = None
        if 'django.contrib.sessions.middleware.SessionMiddleware' in settings.MIDDLEWARE_CLASSES:
            engine = import_module(settings.SESSION_ENGINE)
            session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME,
                                              None)
            if session_key:
                request.session = engine.SessionStore(session_key)
                if 'django.contrib.auth.middleware.AuthenticationMiddleware' in settings.MIDDLEWARE_CLASSES:
                    from django.contrib.auth import get_user
                    request.user = SimpleLazyObject(lambda: get_user(request))

    def process_subscriptions(self, request):
        requested_channels = [p.strip().lower() for p in request.GET]
        agreed_channels = [
            p for p in requested_channels if p in self.allowed_channels
        ]
        return agreed_channels

    def __call__(self, environ, start_response):
        """ Hijack the main loop from the original thread and listen on events on Redis and Websockets"""
        websocket = None
        redis_store = self.RedisStore(self._redis_connection)
        try:
            self.assure_protocol_requirements(environ)
            request = WSGIRequest(environ)
            self.process_request(request)
            channels = self.process_subscriptions(request)
            websocket = self.upgrade_websocket(environ, start_response)
            logger.debug('Subscribed to channels: {0}'.format(
                ', '.join(channels)))
            redis_store.subscribe_channels(request, channels)
            websocket_fd = websocket.get_file_descriptor()
            listening_fds = [websocket_fd]
            redis_fd = redis_store.get_file_descriptor()
            if redis_fd:
                listening_fds.append(redis_fd)
            redis_store.send_persited_messages(websocket)
            while websocket and not websocket.closed:
                ready = self.select(listening_fds, [], [], 4.0)[0]
                if not ready:
                    # flush empty socket
                    websocket.flush()
                for fd in ready:
                    if fd == websocket_fd:
                        message = websocket.receive()
                        redis_store.publish_message(message)
                    elif fd == redis_fd:
                        response = redis_store.parse_response()
                        if response[0] == 'message':
                            message = response[2]
                            websocket.send(message)
                    else:
                        logger.error('Invalid file descriptor: {0}'.format(fd))
        except WebSocketError, excpt:
            logger.warning('WebSocketError: ', exc_info=sys.exc_info())
            response = HttpResponse(status=1001, content='Websocket Closed')
        except UpgradeRequiredError:
            logger.info('Websocket upgrade required')
            response = HttpResponseBadRequest(status=426, content=excpt)
                        if response[0] == 'message':
                            message = response[2]
                            websocket.send(message)
                    else:
                        logger.error('Invalid file descriptor: {0}'.format(fd))
                if redis_settings.WS4REDIS_HEARTBEAT:
                    websocket.send(redis_settings.WS4REDIS_HEARTBEAT)
        except WebSocketError, excpt:
            logger.warning('WebSocketError: ', exc_info=sys.exc_info())
            response = HttpResponse(status=1001, content='Websocket Closed')
        except UpgradeRequiredError:
            logger.info('Websocket upgrade required')
            response = HttpResponseBadRequest(status=426, content=excpt)
        except HandshakeError, excpt:
            logger.warning('HandshakeError: ', exc_info=sys.exc_info())
            response = HttpResponseBadRequest(content=excpt)
        except Exception, excpt:
            logger.error('Other Exception: ', exc_info=sys.exc_info())
            response = HttpResponseServerError(content=excpt)
        else:
            response = HttpResponse()
        if websocket:
            websocket.close(code=1001, message='Websocket Closed')
        if hasattr(start_response, 'im_self') and not start_response.im_self.headers_sent:
            logger.warning('Staring late response on websocket')
            status_text = STATUS_CODE_TEXT.get(response.status_code, 'UNKNOWN STATUS CODE')
            status = '{0} {1}'.format(response.status_code, status_text)
            start_response(force_str(status), response._headers.values())
        logger.info('Finish long living response with status code: '.format(response.status_code))
        return response
                    else:
                        logger.error('Invalid file descriptor: {0}'.format(fd))
                if redis_settings.WS4REDIS_HEARTBEAT:
                    websocket.send(redis_settings.WS4REDIS_HEARTBEAT)
        except WebSocketError, excpt:
            logger.warning('WebSocketError: ', exc_info=sys.exc_info())
            response = HttpResponse(status=1001, content='Websocket Closed')
        except UpgradeRequiredError, excpt:
            logger.info('Websocket upgrade required')
            response = HttpResponseBadRequest(status=426, content=excpt)
        except HandshakeError, excpt:
            logger.warning('HandshakeError: ', exc_info=sys.exc_info())
            response = HttpResponseBadRequest(content=excpt)
        except Exception, excpt:
            logger.error('Other Exception: ', exc_info=sys.exc_info())
            response = HttpResponseServerError(content=excpt)
        else:
            response = HttpResponse()
        if websocket:
            websocket.close(code=1001, message='Websocket Closed')
        if hasattr(start_response,
                   'im_self') and not start_response.im_self.headers_sent:
            logger.warning('Staring late response on websocket')
            status_text = STATUS_CODE_TEXT.get(response.status_code,
                                               'UNKNOWN STATUS CODE')
            status = '{0} {1}'.format(response.status_code, status_text)
            start_response(force_str(status), response._headers.values())
        logger.info('Finish long living response with status code: '.format(
            response.status_code))
        return response