Source code for ginsfsm.protocols.sockjs.server.c_sockjs_server

# -*- encoding: utf-8 -*-
""" GSockjsServer GObj

.. autoclass:: GSockjsServer
    :members: start_up

"""

from pyramid.security import (
    Allow,
    Everyone,
    DENY_ALL,
)

from ginsfsm.gobj import GObj
from ginsfsm.protocols.sockjs.server.c_static import (
    GInfo,
    GIFrame,
)
from ginsfsm.c_timer import GTimer
from ginsfsm.deferred import Deferred
from ginsfsm.protocols.sockjs.server.c_transport_websocket import GRawWebsocket
from ginsfsm.protocols.sockjs.server import session, sessioncontainer
from ginsfsm.protocols.sockjs.server import stats
from ginsfsm.protocols.sockjs.server.c_session import GSockjsSession
from ginsfsm.protocols.sockjs.server import proto


#-------------------------------------------#
#               Actions
#-------------------------------------------#
def ac_timeout(self, event):
    self.counter += 1
    self.send_event(self.timer, 'EV_SET_TIMER', seconds=self.seconds)


def ac_sample(self, event):
    """ Event attributes:
        * :attr:`data`: example.

    """
    self.broadcast_event('EV_OUTPUT_EVENT', data=event.data)


GSOCKJSSERVER_FSM = {
    'event_list': (
        'EV_INPUT_EVENT:top input',
        'EV_OUTPUT_EVENT:top output',
    ),
    'state_list': (
        'ST_IDLE',
    ),
    'machine': {
        'ST_IDLE':
        (
            ('EV_INPUT_EVENT',      ac_sample,      None),
        ),
    }
}

GSOCKJSSERVER_GCONFIG = {
    'subscriber': [
        None, None, 0, None,
        "subcriber of all output-events."
        "Default is ``None``, i.e., the parent"
    ],
    'sockjs_app_class': [
        None, None, 0, None,
        "Class to receive sockjs events."
        "GSockjsServer will create a instance of this class."
    ],

    # like tornado settings
    'session_check_interval': [
        int, 5, 0, None, "Sessions check interval in seconds"
    ],
    'disconnect_delay': [
        int, 10, 0, None, "Session expiration in seconds"
    ],
    'heartbeat_delay': [
        int, 25, 0, None,
        "Heartbeat time in seconds. Do not change this value unless "
        "you absolutely sure that new value will work."
    ],
    'disabled_transports': [tuple, (), 0, None, "Transports disabled"],
    'sockjs_url': [
        str, 'http://cdn.sockjs.org/sockjs-0.3.min.js', 0, None,
        "SockJS location"
    ],
    'response_limit': [int, 128 * 1024, 0, None, "Max response body size"],
    'jsessionid': [
        bool, True, 0, None,
        "Enable or disable JSESSIONID cookie handling"
    ],
    'immediate_flush': [
        bool, True, 0, None,
        "Should sockjs-tornado flush messages immediately or queue then and"
        " flush on next ioloop tick"
    ],
    'verify_ip': [
        bool, True, 0, None,
        "Enable IP checks for polling transports. If enabled, "
        "all subsequent polling calls should be from the same IP address."
    ],
    'per_user': [
        bool, False, 0, None,
        "TODO: default is True in pyramid_sockjs"
    ],
}


[docs]class GSockjsServer(GObj): """ GSockjsServer GObj. This gobj only works with `Pyramid <http://www.pylonsproject.org/>`_ framework. .. ginsfsm:: :fsm: GSOCKJSSERVER_FSM :gconfig: GSOCKJSSERVER_GCONFIG *Input-Events:* * :attr:`'EV_INPUT_EVENT'`: sample input event. Event attributes: * ``data``: sample event attribute. *Output-Events:* * :attr:`'EV_OUTPUT_EVENT'`: sample output event. Event attributes: * ``data``: sample event attribute. """ __acl__ = [ (Allow, Everyone, 'view'), (DENY_ALL), ] def __init__(self): GObj.__init__(self, GSOCKJSSERVER_FSM, GSOCKJSSERVER_GCONFIG) # Store connection class
[docs] def start_up(self): """ Initialization zone. """ self.logger.info( "==> Resource path of sockjs server: %s\n" "==> Test connection with => %s%s/%s" % ( self.resource_path(), 'schema://host:port', # TODO: get real values self.resource_path(), 'info')) if self.config.subscriber is None: self.config.subscriber = self.parent self.subscribe_event(None, self.config.subscriber) if not self.config.sockjs_app_class: raise Exception('You must supply a connection application.') self._connection = self.config.sockjs_app_class #----------------------------------------------# # Static handlers #----------------------------------------------# # Info self.create_gobj( 'info', GInfo, self, sockjs_server=self, ) # IFrame self.create_gobj( 'iframe', GIFrame, self, re_name='iframe[0-9-.a-z_]*.html', sockjs_server=self, ) # RawWebsocket self.create_gobj( 'websocket', GRawWebsocket, self, sockjs_server=self, ) #----------------------------------------------# # Session URLs #----------------------------------------------# self.sockjs_session = self.create_gobj( 'sockjs_session', GSockjsSession, self, re_name='^[^/.]+$', sockjs_server=self, ) #----------------------------------------------# # Setup #----------------------------------------------# # Store connection class #self._connection = connection # Initialize io_loop #self.io_loop = io_loop or ioloop.IOLoop.instance() disabled_transports = self.config.disabled_transports self.websockets_enabled = 'websocket' not in disabled_transports self.cookie_needed = self.config.jsessionid # Sessions self._sessions = sessioncontainer.SessionContainer() check_interval = self.config.session_check_interval self.timer = self.create_gobj( None, # unnamed gobj GTimer, # gclass self # parent ) self.timer.delete_all_subscriptions() deferred = Deferred(None, self._sessions.expire) self.timer.subscribe_event( 'EV_TIMEOUT', deferred, __deferred_witout_oevent__=True, ) self.send_event( self.timer, 'EV_SET_TIMER', seconds=check_interval, autostart=True) #self._sessions_cleanup = PeriodicCallback( # self._sessions.expire, # check_interval, # self.gaplic) #self._sessions_cleanup.start() # Stats self.stats = stats.StatsCollector()
def create_session(self, session_id, register=True): """Creates new session object and returns it. `request` Request that created the session. Will be used to get query string parameters and cookies `register` Should be session registered in a storage. Websockets don't need it. """ # TODO: Possible optimization here for settings.get s = session.Session( self._connection, self, session_id, self.config.disconnect_delay ) self.logger.debug("CREATE SESSION: id=%s, s=%r" % (session_id, s,)) if register: self._sessions.add(s) return s def get_session(self, session_id): """Get session by session id `session_id` Session id """ s = self._sessions.get(session_id) self.logger.debug("GET SESSION: id=%s, s=%r" % (session_id, s,)) return s def get_connection_class(self): """Return associated connection class""" return self._connection # Broadcast helper def broadcast(self, clients, msg): """Optimized `broadcast` implementation. Depending on type of the session, will json-encode message once and will call either `send_message` or `send_jsonifed`. `clients` Clients iterable `msg` Message to send """ json_msg = None count = 0 for c in clients: sess = c.session if not sess.is_closed: if sess.send_expects_json: if json_msg is None: json_msg = proto.json_encode(msg) sess.send_jsonified(json_msg, False) else: sess.send_message(msg, stats=False) count += 1 self.stats.on_pack_sent(count)
#----------------------------------------------------------------# # Views #----------------------------------------------------------------# from ginsfsm.protocols.sockjs.server.basehandler import BaseHandler from pyramid.view import view_config @view_config( context=GSockjsServer, name='', attr='get', request_method='GET', ) class GreetingsHandler(BaseHandler): """SockJS greetings page handler""" def get(self): response = self.response self.enable_cache() response.content_type = 'text/plain' response.charset = 'UTF-8' data = 'Welcome to SockJS!\n'.encode() response.body = data return response