Commits

Ginés Martínez Sánchez  committed a90e508 Draft

SessionURLs test sockjs passed!

  • Participants
  • Parent commits fc327f1

Comments (0)

Files changed (17)

File ginsfsm/c_sock.py

 
 
 def ac_drop(self, event):
-    self._mt_drop()
+    self.mt_drop()
 
 
 def ac_send_data(self, event):
                 peername=self.addr,
             )
 
-    def _mt_drop(self):
+    def mt_drop(self):
         """ Drop the connexion.
         """
         logging.info(

File ginsfsm/gaplic.py

         self.ini_settings = ini_settings.copy()
         # Call shutdown() to stop gaplic
         self.do_exit = multiprocessing.Event()
-        """threading.Event() or multiprocessing.Event() object
+        """ threading.Event() or multiprocessing.Event() object
         to signal the shutdown of gaplic."""
         self.loop_timeout = 0.5     # timeout to select(),poll() function.
-        """Loop timeout. Default 0.5 seconds."""
+        """ Loop timeout. Default 0.5 seconds.
+            It's the minimun timer resolution you can have.
+        """
         self._impl_poll = _poll()   # Used by gsock. epoll() implementation.
         self._socket_map = {}       # Used by gsock. Dict {fd:Gobj}
         self._gotter_timers = {}    # Dict with timers  {_XTimer:timer value}
         self._qevent = deque()      # queue for post events.
+        self._callbacks = []        # callbacks compatible with tornado.io_loop
         self._inside = 0            # to tab machine trace.
         self._unique_named_gobjs = {}
-        self.thread_ident = 0
-        self.thread_name = 0
+        self._thread_ident = None
+        self._thread_name = 0
         self.gaplic = self
         self.deferred_list = DeferredList()
 
         Return True if there is some remain event for be proccessed.
         Useful for testing purposes.
         """
-        if not self.thread_ident:
-            self.thread_ident = threading.current_thread().ident
-            self.thread_name = threading.current_thread().name
+        if not self._thread_ident:
+            self._thread_ident = threading.current_thread().ident
+            self._thread_name = threading.current_thread().name
+
+        timeout = self.loop_timeout  # iniatially wait loop_timeout seconds
+
+        # Callbacks compatible with tornado.io_loop
+        # Prevent IO event starvation by delaying new callbacks
+        # to the next iteration of the event loop.
+        callbacks = self._callbacks
+        self._callbacks = []
+        for callback in callbacks:
+            self._run_callback(callback)
+
+        if self._callbacks:
+            # If any callbacks or timeouts called add_callback,
+            # we don't want to wait in poll() before we run them.
+            timeout = 0.0
+
         remain = self._process_qevent()
         if remain:
-            timeout = 0.01
-        else:
-            timeout = self.loop_timeout
-        some_event = poll_loop(self._socket_map, self._impl_poll, timeout)
-        if some_event:
-            remain = True
-        remain |= self._process_timer()
+            # They are remain events,
+            # we don't want to wait in poll() before we run them.
+            # wait some time, to avoid recursive send events that puts 100% cpu.
+            timeout = 0.1
 
+        poll_loop(self._socket_map, self._impl_poll, timeout)
+
+        #
+        self._process_timer()
+
+        # oportunity for subclass.
         self.mt_subprocess()
+
         return remain
 
     def mt_process(self):
                 #TODO consider names of another gaplics
                 destination = self._resolv_destination(event.destination)
                 cur_ident = threading.current_thread().ident
-                dst_ident = destination.gaplic.thread_ident
+                if cur_ident != self._thread_ident:
+                    logging.error("????????????????????????????????????")
+
+                dst_ident = destination.gaplic._thread_ident
                 if cur_ident == dst_ident:
                     self.send_event(destination, event.event_name, **event.kw)
                 else:
             self._gotter_timers[xtimer] = 0
             self._gotter_timers.pop(xtimer)
 
+    def add_timeout(self, deadline, callback):
+        """ Compatible with tornado.io_loop
+
+        ``deadline`` only seconds please.
+
+        Calls the given callback at the time deadline from the I/O loop.
+
+        Returns a handle that may be passed to remove_timeout to cancel.
+
+        ``deadline`` may be a number denoting a unix timestamp (as returned
+        by ``time.time()`` or a ``datetime.timedelta`` object for a deadline
+        relative to the current time.
+
+        Note that it is not safe to call `add_timeout` from other threads.
+        Instead, you must use `add_callback` to transfer control to the
+        IOLoop's thread, and then call `add_timeout` from there.
+        """
+        timer_id = self._setTimeout(deadline, callback)
+        return timer_id
+
+    def remove_timeout(self, timeout):
+        """ Compatible with tornado.io_loop
+
+        Cancels a pending timeout.
+
+        The argument is a handle as returned by add_timeout.
+        """
+        # Removing from a heap is complicated, so just leave the defunct
+        # timeout object in the queue (see discussion in
+        # http://docs.python.org/library/heapq.html).
+        # If this turns out to be a problem, we could add a garbage
+        # collection pass whenever there are too many dead timeouts.
+        timeout.callback = None
+        self._clearTimeout(timeout)
+
+    def add_callback(self, callback):
+        """ Compatible with tornado.io_loop
+
+        Calls the given callback on the next I/O loop iteration.
+
+        It is safe to call this method from any thread at any time.
+        Note that this is the *only* method in IOLoop that makes this
+        guarantee; all other interaction with the IOLoop must be done
+        from that IOLoop's thread.  add_callback() may be used to transfer
+        control from other threads to the IOLoop's thread.
+        """
+        list_empty = not self._callbacks
+        self._callbacks.append(callback)
+        if list_empty:
+            if threading.current_thread().ident != self._thread_ident:
+                # If we're in the IOLoop's thread, we know it's not currently
+                # polling.  If we're not, and we added the first callback to an
+                # empty list, we may need to wake it up (it may wake up on its
+                # own, but an occasional extra wake is harmless).  Waking
+                # up a polling IOLoop is relatively expensive, so we try to
+                # avoid it when we can.
+                pass
+                # TODO: study this from tornado
+                # self._waker.wake()
+
+    def _run_callback(self, callback):
+        try:
+            callback()
+        except Exception:
+            self.handle_callback_exception(callback)
+
+    def handle_callback_exception(self, callback):
+        """This method is called whenever a callback run by the IOLoop
+        throws an exception.
+
+        By default simply logs the exception as an error.  Subclasses
+        may override this method to customize reporting of exceptions.
+
+        The exception itself is not passed explicitly, but is available
+        in sys.exc_info.
+        """
+        logging.error("Exception in callback %r", callback, exc_info=True)
+
 
 #===============================================================
 #                   Thread wrapper for gaplic

File ginsfsm/gobj.py

 # Attributes that a gaplic can update.
 GOBJ_GCONFIG = {
     'gaplic': [None, None, 0, None, ''],
+    're_name': [str, None, 0, None,
+        'Regular expression name to search the gobj in the resource tree.'
+        'Used in Pyramid traversal.'],
     'ini_settings': [dict, {}, 0, None,
         'The ini settings will be set to all new created gobj'
         ' by overwrite_parameters() function'],
     '_increase_inside': [None, None, 0, None, ''],
     '_decrease_inside': [None, None, 0, None, ''],
     '_tab': [None, None, 0, None, ''],
-    '__re_name__': [bool, False, 0, None,
-        'GObj name is a Regular expression.'
-        'Used in Pyramid traversal.'],
 }
 
 _urandom_name = 0
         self._dl_subscriptions = set()      # uauuu, how many fans!!
         self._some_subscriptions = False
         self._destroyed = False  # mark as destroyed when destroy_gobj()
-        self._re_name = ''  # re compiled when using __re_name__
-        self.real_name = ''  # real name when using __re_name__
+        self._re_compiled_name = ''  # re compiled name when using re_name
+        self.re_matched_name = ''  # matched name when using re_name
 
         gconfig = add_gconfig(gconfig, GOBJ_GCONFIG)
         GConfig.__init__(self, gconfig)
             by each `gclass` type.
             The attributes must be defined in the gclass GCONFIG,
             otherwise they are ignored.
-            TODO: doc __re_name__
+            TODO: doc re_name
 
         :rtype: new gobj instance.
 
         if parent is not None:
             parent._add_child(gobj)
 
-        if name and gobj.__re_name__:
-            gobj._re_name = re.compile(name)
+        if gobj.re_name:
+            gobj._re_compiled_name = re.compile(gobj.re_name)
 
         gobj.start_up()
 
         if self.gaplic:
             cur_ident = threading.current_thread().ident
             cur_name = threading.current_thread().name
-            if self.gaplic.thread_ident and \
-                    cur_ident != self.gaplic.thread_ident:
+            if self.gaplic._thread_ident and \
+                    cur_ident != self.gaplic._thread_ident:
                 logging.error("ERROR internal: "
                     "current thread '%s' is not the sender thread '%s'",
                     cur_name, self.gaplic.thread_name
                 )
 
-            dst_ident = destination.gaplic.thread_ident
+            dst_ident = destination.gaplic._thread_ident
             if dst_ident and cur_ident != dst_ident:
                 return self.post_event(destination, event)
 
         if name is None:
             raise KeyError('No such child named None')
         for gobj in self.dl_childs:
-            if gobj.__re_name__:
-                if gobj._re_name.match(name) is not None:
-                    gobj.real_name = name
+            if gobj.re_name:
+                if gobj._re_compiled_name.match(name) is not None:
+                    # this gobj has been got as re_matched_name.
+                    print "matched------------->", name, "-", gobj.re_name
+                    gobj.re_matched_name = name
                     return gobj
             else:
                 if gobj.name == name:

File ginsfsm/protocols/http/common/response.py

                 [x.capitalize() for x in headername.split('-')]
                 )
             if headername == 'Content-Length':
-                print "VIENEEEEEE de arriba"
                 content_length_header = headerval
             if headername == 'Date':
                 date_header = headerval

File ginsfsm/protocols/http/common/utilities.py

 import time
 import calendar
 
-logger = logging.getLogger('waitress')
-
 
 def find_double_newline(s):
     """Returns the position just after a double newline in the given string."""
     return retval
 
 
-class logging_dispatcher(asyncore.dispatcher):
-    logger = logger
-
-    def log_info(self, message, type='info'):
-        severity = {
-            'info': logging.INFO,
-            'warning': logging.WARN,
-            'error': logging.ERROR,
-            }
-        self.logger.log(severity.get(type, logging.INFO), message)
-
-
 class Error(object):
     def __init__(self, body):
         self.body = body

File ginsfsm/protocols/sockjs/server/basehandler.py

 
     def finish(self):
         """Finishes this response, ending the HTTP request."""
+        #self._log_disconnect()
+        #super(BaseHandler, self).finish()
 
     # Various helpers
     def set_header(self, name, value):
     def set_status(self, code):
         self.response.status = code
 
+    def set_body(self, body):
+        self.response.body = body
+
     def enable_cache(self):
         """Enable client-side caching for the current request"""
         d = datetime.now() + timedelta(seconds=CACHE_TIME)
         if another flush occurs before the previous flush's callback
         has been run, the previous callback will be discarded.
         """
+        if callback:
+            # TODO: must be executed at on event WRITE
+            # instead next pooling cycle.
+            self.context.sockjs_server.gaplic.add_callback(callback)
 
 
 class PreflightHandler(BaseHandler):

File ginsfsm/protocols/sockjs/server/c_session.py

 from ginsfsm.protocols.sockjs.server.transports.xhrstreaming import (
     GXhrStreaming,
 )
+from ginsfsm.protocols.sockjs.server.transports.xhr import (
+    GXhrPolling,
+    GXhrSend,
+)
 
 #----------------------------------------------------------------#
 #                   GSockjsSession GClass
         """ Initialization zone.
         """
         self.session_id = self.create_gobj(
-            '.+',
+            'session_id',
             GSessionId,
             self,
-            __re_name__=True,
+            re_name='^[^/.]+$',
             sockjs_server=self.sockjs_server,
         )
 
         #               /jsonp_send
         #-------------------------------------------#
 
-        # TODO:
-        # Es mejor no usar views, nos comeriamos la memoria de pyramid.
-        # Que sea parte del path
+        self.create_gobj(
+            'xhr_send',
+            GXhrSend,
+            self,
+            sockjs_server=self.sockjs_server,
+        )
         """
         self.sockjs_server.pyramid_config.add_view(
             context=GSessionId,
-            name='xhr_send',
-            view=transports.XhrSendHandler,
-            attr='get',
-            path_info=self.resource_path(),
-            request_method='GET',
-            permission=self.sockjs_server.permission,
-        )
-        self.sockjs_server.pyramid_config.add_view(
-            context=GSessionId,
             name='jsonp_send',
             view=transports.JSONPSendHandler,
             attr='get',
             request_method='GET',
             permission=self.sockjs_server.permission,
         )
-        self.sockjs_server.pyramid_config.add_view(
-            context=GSessionId,
-            name='xhr',
-            view=transports.XhrPollingTransport,
-            attr='get',
-            path_info=self.resource_path(),
-            request_method='GET',
-            permission=self.sockjs_server.permission,
+        """
+        self.create_gobj(
+            'xhr',
+            GXhrPolling,
+            self,
+            sockjs_server=self.sockjs_server,
         )
-        """
 
         self.create_gobj(
             'xhr_streaming',

File ginsfsm/protocols/sockjs/server/c_sockjs_server.py

         )
         # IFrame
         self.create_gobj(
-            'iframe[0-9-.a-z_]*.html',
+            'iframe',
             GIFrame,
             self,
-            __re_name__=True,
+            re_name='iframe[0-9-.a-z_]*.html',
             sockjs_server=self,
         )
 
         #           Session URLs
         #----------------------------------------------#
         self.sockjs_session = self.create_gobj(
-            '^\d{3}$',
+            'sockjs_session',
             GSockjsSession,
             self,
-            __re_name__=True,
+            re_name='^[^/.]+$',
             sockjs_server=self,
         )
 

File ginsfsm/protocols/sockjs/server/conn.py

 # -*- coding: utf-8 -*-
 """
-    sockjs.tornado.conn
-    ~~~~~~~~~~~~~~~~~~~
-
     SockJS connection interface
 """
 
     def is_closed(self):
         """Check if connection was closed"""
         return self.session.is_closed
-
-

File ginsfsm/protocols/sockjs/server/migrate.py

 # -*- coding: utf-8 -*-
 """
-    sockjs.tornado.migrate
-    ~~~~~~~~~~~~~~~~~~~~~~
-
-    `tornado.websocket` to `sockjs.tornado` migration helper.
+    `tornado.websocket` to `sockjs` migration helper.
 """
 
-from sockjs.tornado import conn
+from ginsfsm.protocols.sockjs.server import conn
 
 
 class WebsocketHandler(conn.SockJSConnection):

File ginsfsm/protocols/sockjs/server/periodic.py

+# -*- coding: utf-8 -*-
+"""
+    This module implements customized PeriodicCallback from tornado with
+    support of the sliding window.
+"""
+
+import time
+import logging
+
+
+class Callback(object):
+    """Custom implementation of the Tornado.Callback with support
+    of callback timeout delays.
+    """
+    def __init__(self, callback, callback_time, gaplic):
+        """Constructor.
+
+        `callback`
+            Callback function
+        `callback_time`
+            Callback timeout value (in milliseconds)
+        `gaplic`
+            gaplic (io_loop) instance
+        """
+        self.callback = callback
+        self.callback_time = callback_time
+        self.io_loop = gaplic
+        self._running = False
+
+        self.next_run = None
+
+    def calculate_next_run(self):
+        """Caltulate next scheduled run"""
+        return time.time() + self.callback_time / 1000.0
+
+    def start(self, timeout=None):
+        """Start callbacks"""
+        self._running = True
+
+        if timeout is None:
+            timeout = self.calculate_next_run()
+
+        self.io_loop.add_timeout(timeout, self._run)
+
+    def stop(self):
+        """Stop callbacks"""
+        self._running = False
+
+    def delay(self):
+        """Delay callback"""
+        self.next_run = self.calculate_next_run()
+
+    def _run(self):
+        if not self._running:
+            return
+
+        # Support for shifting callback window
+        if self.next_run is not None and time.time() < self.next_run:
+            self.start(self.next_run)
+            self.next_run = None
+            return
+
+        next_call = None
+        try:
+            next_call = self.callback()
+        except (KeyboardInterrupt, SystemExit):
+            raise
+        except:
+            logging.error("Error in periodic callback", exc_info=True)
+
+        if self._running:
+            self.start(next_call)

File ginsfsm/protocols/sockjs/server/proto.py

     return 'c[%d,"%s"]' % (code, reason)
 
 
-decode = json.loads
+json_decode = json.loads
 
 
-def encode(data):
+def json_encode(data):
     return json.dumps(data, **kwargs)
 
 
 def close_frame(code, reason, eol=''):
-    return '%s[%d,%s]%s' % (CLOSE, code, encode(reason), eol)
+    return '%s[%d,%s]%s' % (CLOSE, code, json_encode(reason), eol)
 
 
 def message_frame(data, eol=''):
-    return '%s%s%s' % (MESSAGE, encode(data), eol)
+    return '%s%s%s' % (MESSAGE, json_encode(data), eol)

File ginsfsm/protocols/sockjs/server/session.py

 
 import logging
 
-from sockjs.tornado import sessioncontainer, periodic, proto
+from ginsfsm.protocols.sockjs.server import sessioncontainer, periodic, proto
 
 
 class ConnectionInfo(object):
     """
     _exposed_headers = set(['referer', 'x-client-ip', 'x-forwarded-for',
                             'x-cluster-client-ip', 'via', 'x-real-ip'])
+
     def __init__(self, ip, cookies, arguments, headers, path):
         self.ip = ip
         self.cookies = cookies
     def delayed_close(self):
         """Delayed close - won't close immediately, but on next ioloop tick."""
         self.state = CLOSING
-        self.server.io_loop.add_callback(self.close)
+        self.server.gaplic.add_callback(self.close)
 
     def get_close_reason(self):
         """Return last close reason tuple.
 
         # Heartbeat related stuff
         self._heartbeat_timer = None
-        self._heartbeat_interval = self.server.heartbeat_delay * 1000
+        self._heartbeat_interval = self.server.heartbeat_delay
 
         self._immediate_flush = self.server.immediate_flush
         self._pending_flush = False
             self.send_queue += msg
 
             if not self._pending_flush:
-                self.server.io_loop.add_callback(self.flush)
+                self.server.gaplic.add_callback(self.flush)
                 self._pending_flush = True
 
         if stats:
         """Reset hearbeat timer"""
         self.stop_heartbeat()
 
-        self._heartbeat_timer = periodic.Callback(self._heartbeat,
-                                                  self._heartbeat_interval,
-                                                  self.server.io_loop)
+        self._heartbeat_timer = periodic.Callback(
+            self._heartbeat,
+            self._heartbeat_interval,
+            self.server.gaplic
+        )
         self._heartbeat_timer.start()
 
     def stop_heartbeat(self):
 
     def _heartbeat(self):
         """Heartbeat callback"""
+        print "CALLLLLLLLLLLLLLLLLLLLLLL"
         if self.handler is not None:
             self.handler.send_pack(proto.HEARTBEAT)
         else:

File ginsfsm/protocols/sockjs/server/sessioncontainer.py

 # -*- coding: utf-8 -*-
 """
-    sockjs.tornado.sessioncontainer
-    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
     Simple heapq-based session implementation with sliding expiration window
     support.
 """

File ginsfsm/protocols/sockjs/server/transports/xhr.py

 # -*- coding: utf-8 -*-
 """
-    ginsfsm.protocols.sockjs.server.transports.xhr
-    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
     Xhr-Polling transport implementation
 """
 import logging
 
-from tornado.web import asynchronous
+from pyramid.view import view_config
+from pyramid.security import authenticated_userid
+from pyramid.httpexceptions import HTTPNotFound, HTTPServerError
 
+from ginsfsm.gobj import GObj
 from ginsfsm.protocols.sockjs.server import proto
 from ginsfsm.protocols.sockjs.server.transports import pollingbase
 
+#----------------------------------------------------------------#
+#                   GXhrPolling GClass
+#----------------------------------------------------------------#
+GXHRPOLLING_GCONFIG = {
+    'sockjs_server': [None, None, 0, None, ""],
+}
 
+
+class GXhrPolling(GObj):
+    """  GXhrPolling GObj.
+    """
+    def __init__(self):
+        GObj.__init__(self, {}, GXHRPOLLING_GCONFIG)
+
+    def start_up(self):
+        """ Initialization zone.
+        """
+
+
+#----------------------------------------------------------------#
+#                   GXhrPolling Views
+#----------------------------------------------------------------#
+@view_config(
+    context=GXhrPolling,
+    name='',
+    attr='options',
+    request_method='OPTIONS',
+)
+@view_config(
+    context=GXhrPolling,
+    name='',
+    attr='post',
+    request_method='POST',
+)
 class XhrPollingTransport(pollingbase.PollingTransportBase):
     """xhr-polling transport implementation"""
     name = 'xhr'
 
-    @asynchronous
-    def post(self, session_id):
+    def post(self):
+        response = self.set_response(self.request.response)
+        print "XXXXXXXXXXXXXXXXXXXXX"
         # Start response
         self.preflight()
         self.handle_session_cookie()
         self.disable_cache()
 
+        self.sid = self.context.parent.re_matched_name  # session_id
+        if self.context.sockjs_server.per_user:
+            self.sid = (authenticated_userid(self.request), self.sid)
+
         # Get or create session without starting heartbeat
-        if not self._attach_session(session_id, False):
-            return
+        if not self._attach_session(self.sid, False):
+            return response  # HTTPServerError("Error in attach_session.")
 
         # Might get already detached because connection was closed in on_open
         if not self.session:
-            return
+            return HTTPServerError("Error, no session.")
 
         if not self.session.send_queue:
+            print "HEARTTTTTTTTTTTTTTT"
             self.session.start_heartbeat()
         else:
+            print "FLUSHHHHHHHHHHHHHHHHHH"
             self.session.flush()
 
+        return response
+
     def send_pack(self, message, binary=False):
         if binary:
             raise Exception('binary not supported for XhrPollingTransport')
         self.active = False
 
         try:
-            self.set_header('Content-Type', 'application/javascript; charset=UTF-8')
-            self.set_header('Content-Length', len(message) + 1)
-            self.write(message + '\n')
+            self.content_type = 'application/javascript'
+            self.charset = 'UTF-8'
+            print "zzzzz", message
+            self.set_body(message + '\n')
             self.flush(callback=self.send_complete)
-        except IOError:
-            # If connection dropped, make sure we close offending session instead
-            # of propagating error all way up.
+        except:
+            # If connection dropped, make sure we close offending session
+            # instead of propagating error all way up.
             self.session.delayed_close()
 
 
+#----------------------------------------------------------------#
+#                   GXhrSend GClass
+#----------------------------------------------------------------#
+GXHRSEND_GCONFIG = {
+    'sockjs_server': [None, None, 0, None, ""],
+}
+
+
+class GXhrSend(GObj):
+    """  GXhr GObj.
+    """
+    def __init__(self):
+        GObj.__init__(self, {}, GXHRSEND_GCONFIG)
+
+    def start_up(self):
+        """ Initialization zone.
+        """
+
+
+#----------------------------------------------------------------#
+#                   GXhrSend Views
+#----------------------------------------------------------------#
+@view_config(
+    context=GXhrSend,
+    name='',
+    attr='options',
+    request_method='OPTIONS',
+)
+@view_config(
+    context=GXhrSend,
+    name='',
+    attr='post',
+    request_method='POST',
+)
 class XhrSendHandler(pollingbase.PollingTransportBase):
-    def post(self, session_id):
+    def post(self):
+        response = self.set_response(self.request.response)
+
+        self.sid = self.context.parent.re_matched_name  # session_id
+        if self.context.sockjs_server.per_user:
+            self.sid = (authenticated_userid(self.request), self.sid)
+
+        # Start response
         self.preflight()
         self.handle_session_cookie()
         self.disable_cache()
 
-        session = self._get_session(session_id)
+        session = self._get_session(self.sid)
 
         if session is None:
-            self.set_status(404)
-            return
+            return HTTPNotFound('Session not found')
 
-        #data = self.request.body.decode('utf-8')
-        data = self.request.body
+        data = self.request.body_file.read()
         if not data:
-            self.write("Payload expected.")
-            self.set_status(500)
-            return
+            return HTTPServerError("Payload expected.")
 
         try:
             messages = proto.json_decode(data)
         except:
-            # TODO: Proper error handling
-            self.write("Broken JSON encoding.")
-            self.set_status(500)
-            return
+            return HTTPServerError("Broken JSON encoding.")
 
         try:
             session.on_messages(messages)
         except Exception:
             logging.exception('XHR incoming')
             session.close()
-
-            self.set_status(500)
-            return
+            return HTTPServerError()
 
         self.set_status(204)
-        self.set_header('Content-Type', 'text/plain; charset=UTF-8')
+        self.content_type = 'text/plain'
+        self.charset = 'UTF-8'
+        return response

File ginsfsm/protocols/sockjs/server/transports/xhrstreaming.py

 # -*- coding: utf-8 -*-
 """
-    ginsfsm.protocols.sockjs.server.transports.xhrstreaming
-    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
     Xhr-Streaming transport implementation
 """
 from pyramid.view import view_config
 from ginsfsm.protocols.sockjs.server.transports import streamingbase
 
 #----------------------------------------------------------------#
-#                   GClass
+#                   GXhrStreaming GClass
 #----------------------------------------------------------------#
 GXHRSTREAMING_GCONFIG = {
     'sockjs_server': [None, None, 0, None, ""],
 
 
 class GXhrStreaming(GObj):
-    """  GStreamingBase GObj.
+    """  GStreaming GObj.
     """
     def __init__(self):
         GObj.__init__(self, {}, GXHRSTREAMING_GCONFIG)
     def start_up(self):
         """ Initialization zone.
         """
-        # views
-        """
-        self.sockjs_server.pyramid_config.add_view(
-            context=GXhrStreaming,
-            name='',
-            view=XhrStreamingTransport,
-            attr='post',
-            path_info=self.resource_path(),
-            request_method='POST',
-            permission=self.sockjs_server.permission,
-        )
-        """
 
 
 #----------------------------------------------------------------#
-#                   Info views
+#                   GXhrStreaming Views
 #----------------------------------------------------------------#
 @view_config(
     context=GXhrStreaming,
     name='',
+    attr='options',
+    request_method='OPTIONS',
+)
+@view_config(
+    context=GXhrStreaming,
+    name='',
     attr='post',
     request_method='POST',
 )
-@view_config(
-    context=GXhrStreaming,
-    name='',
-    attr='options',
-    request_method='OPTIONS',
-)
 class XhrStreamingTransport(streamingbase.StreamingTransportBase):
     name = 'xhr_streaming'
 
     def post(self):
-        response = self.set_response(self)
+        response = self.set_response(self)  # Response is self.
         self.headers = ()  # important trick: clean default webob headers.
 
-        self.sid = self.context.real_name  # session_id
+        self.sid = self.context.re_matched_name  # session_id
         if self.context.sockjs_server.per_user:
             self.sid = (authenticated_userid(self.request), self.sid)
 
         #    return HTTPNotFound(headers=(session_cookie(request),))
         #request.environ['wsgi.sockjs_session'] = session
 
-        # Handle cookie
+        # Start response
         self.preflight()
         self.handle_session_cookie()
         self.disable_cache()
         return self  # Webob will execute __call__
 
     def __call__(self, environ, start_response):
-        print ">>>>>>>>>> gins", self._abs_headerlist(environ)
         write = self.write = start_response(
             self.status, self._abs_headerlist(environ))
         write('h' * 2048 + '\n')
 
             self.write(message + '\n')
             self.flush(callback=self.send_complete)
-        except IOError:
+        except:
             # If connection dropped, make sure we close offending session instead
             # of propagating error all way up.
             self.session.delayed_close()

File ginsfsm/protocols/sockjs/server/websocket.py

 """
-sockjs.tornado.websocket
-    ~~~~~~~~~~~~~~~~~~~~~~~~
+    websocket
 
     This module contains modified version of the WebSocketHandler from
     Tornado with some performance fixes and behavior changes for Hybi10
 import tornado.escape
 import tornado.web
 
+# TODO remove
 from tornado import stack_context
 from tornado.util import bytes_type, b