Commits

Ginés Martínez Sánchez committed 18c57ef Draft

I cant wsgi-response for websocket

  • Participants
  • Parent commits 763488b

Comments (0)

Files changed (4)

File ginsfsm/c_sock.py

 def ac_send_data(self, event):
     """ Write in the output data buffer and flush it right now.
     """
-    self._write_soon(event.data)
-    self._transmit_ready_event_done = False
-    self._send_some()
+    # TODO: callback when transmit_ready
+    self.mt_send_data(event.data)
 
 
-def ac_write_output_data(self, event):
+def ac_write(self, event):
     """ Write in the output data buffer.
         Equivalent to waitress' write_soon().
         The data are not really sent until _send_some() is called.
     """
-    self._write_soon(event.data)
+    self.mt_write(event.data)
 
 
-def ac_flush_output_data(self, event):
+def ac_flush(self, event):
     """ Flush the output data buffer.
         Equivalent to waitress' send_some().
     """
-    self._transmit_ready_event_done = False
-    self._send_some()
+    # TODO: callback when transmit_ready
+    self.mt_flush()
 
 
 GSOCK_FSM = {
             ('EV_CONNECT',              ac_connect,             None),
             ('EV_DROP',                 ac_drop,                None),
             ('EV_SEND_DATA',            ac_send_data,           None),
-            ('EV_WRITE_OUTPUT_DATA',    ac_write_output_data,   None),
-            ('EV_FLUSH_OUTPUT_DATA',    ac_flush_output_data,   None),
+            ('EV_WRITE_OUTPUT_DATA',    ac_write,   None),
+            ('EV_FLUSH_OUTPUT_DATA',    ac_flush,   None),
         ),
     }
 }
             (not self.connected) or \
             self.will_close
 
-    def _mt_send_data(self, data):
+    def mt_send_data(self, data, callback=None):
         """ Send data.
         :param data: data to send.
         """
+        self.mt_write(data)
+        self.mt_flush(callback)
+
+    def mt_write(self, data):
+        self._write_soon(data)
+
+    def mt_flush(self, callback=None):
+        # TODO: callback when TRANSMIT_READY
+        self._transmit_ready_event_done = False
+        self._send_some()
 
     def _write_soon(self, data):
         """ Write data in the output buffer.

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

 import struct
 import time
 import base64
-from hashlib import sha1
 
 import ginsfsm.escape
 from ginsfsm.protocols.sockjs.server.session import ConnectionInfo
     to_string = lambda d: d.tostring()
 
 
-class WebSocketHandler(WebsocketResponse):
+class WebSocketHandler(object):
     """Subclass this class to create a basic WebSocket handler.
 
     Override on_message to handle incoming messages. You can also override
     This script pops up an alert box that says "You said: Hello, world".
     """
     def __init__(self, context, request):
-        super(WebSocketHandler, self).__init__(context, request)
-        #self.stream = request.connection.stream
-        self.stream = self
-
+        self.context = context
+        self.request = request
+        channel = request.environ['ginsfsm.channel']
+        self.stream = channel.gsock
         self.ws_connection = None
 
     def _execute(self):
             return response
 
         # check client handshake for validity
-        protocol = environ.get('SERVER_PROTOCOL','')
+        protocol = environ.get('SERVER_PROTOCOL', '')
         if not protocol.startswith("HTTP/"):
             return HTTPBadRequest('Protocol is not HTTP')
 
                   protocol.endswith('/1.1')):
             return HTTPBadRequest('HTTP/1.1 is required')
 
+        key = environ.get("HTTP_SEC_WEBSOCKET_KEY")
+        if not key or len(base64.b64decode(key)) != 16:
+            return HTTPBadRequest('HTTP_SEC_WEBSOCKET_KEY is invalid key')
+
         fields = ("Host", "Sec-Websocket-Key", "Sec-Websocket-Version")
         if not all(map(lambda f: self.request.headers.get(f), fields)):
             return HTTPBadRequest('Missing/Invalid WebSocket headers')
 
-        key = environ.get("HTTP_SEC_WEBSOCKET_KEY")
-        if not key or len(base64.b64decode(key)) != 16:
-            return HTTPBadRequest('HTTP_SEC_WEBSOCKET_KEY is invalid key')
+        #------------------------------#
+        #   Accept the connection
+        #------------------------------#
+        self.ws_connection = WebSocketProtocol13(self, self.auto_decode())
+        self._accept_connection()
 
-        KEY = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
-        headers = [
-            ("Upgrade", "websocket"),
-            ("Connection", "Upgrade"),
-            ("Sec-WebSocket-Accept", base64.b64encode(sha1(key + KEY).digest())
-            ),
-        ]
-        self.headers = headers
-        self.status = '101 Switching Protocols'
+        response = WebsocketResponse(self.context, self.request)
+        return response
 
-        self.ws_connection = WebSocketProtocol13(self, self.auto_decode())
+    def _challenge_response(self):
+        sha1 = hashlib.sha1()
+        sha1.update(ginsfsm.escape.utf8(
+                self.request.headers.get("Sec-Websocket-Key")))
+        sha1.update(b("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")) # Magic value
+        return ginsfsm.escape.native_str(base64.b64encode(sha1.digest()))
 
+    def _accept_connection(self):
+        subprotocol_header = ''
         subprotocols = self.request.headers.get("Sec-WebSocket-Protocol", '')
         subprotocols = [s.strip() for s in subprotocols.split(',')]
         if subprotocols:
             selected = self.select_subprotocol(subprotocols)
             if selected:
                 assert selected in subprotocols
-                self.headerlist.append(('Sec-WebSocket-Protocol', selected))
+                subprotocol_header = "Sec-WebSocket-Protocol: %s\r\n" % (
+                    selected)
 
-        # self.open()
+        self.stream.mt_send_data(ginsfsm.escape.utf8(
+            "HTTP/1.1 101 Switching Protocols\r\n"
+            "Upgrade: websocket\r\n"
+            "Connection: Upgrade\r\n"
+            "Sec-WebSocket-Accept: %s\r\n"
+            "%s"
+            "\r\n" % (self._challenge_response(), subprotocol_header)))
+
+        self.open()
         #self._receive_frame()
 
-        return self
-
     def abort_connection(self):
         self.ws_connection._abort()
 
         else:
             frame = STRUCT_BBQ.pack(finbit, 127, l)
         frame += data
-        self.stream.write(frame)
+        self.stream.mt_send_data(frame)
 
     def write_message(self, message, binary=False):
         """Sends the given message to the client of this Web Socket."""

File ginsfsm/protocols/wsgi/common/wsgi_response.py

     'CONTENT_TYPE'   : 'CONTENT_TYPE',
 }
 hop_by_hop = frozenset((
-#    'connection',
+    'connection',
     'keep-alive',
     'proxy-authenticate',
     'proxy-authorization',
     'te',
     'trailers',
     'transfer-encoding',
-#    'upgrade',
+    'upgrade',
 ))
 
 

File ginsfsm/protocols/wsgi/webob/websocket_response.py

 """
     WebsocketResponse -  an asynchronous Websocket WebOb Response over ginsfsm.
 """
-import logging
-
 from ginsfsm.protocols.http.server.c_http_clisrv import ResponseInterrupt
 
 try:
 class WebsocketResponse(Response):
     def __init__(self, context, request):
         super(WebsocketResponse, self).__init__()
-
-        del self.app_iter  # Important trick to remove Content-Length.
-        # WARNING: you cannot use body, because it sets Content-Length.
-
         self.context = context
         self.request = request
-        self.ginsfsm_channel = None
 
     def __call__(self, environ, start_response):
         "Override WSGI call"
-        self.ginsfsm_channel = environ['ginsfsm.channel']
-        start_response(self.status, self._abs_headerlist(environ))
-        self.write(self.body)
-        self.flush()
-
         raise ResponseInterrupt()
 
-    def write(self, data):
-        """ Write data to http channel.
-        """
-        if self.ginsfsm_channel:
-            print "111111111111111", data
-            self.ginsfsm_channel.write(data)
-        else:
-            # write in the sync response by now.
-            # in wsgi response call, all app_iter will be flush.
-            print "222222222222222", data
-            super(WebsocketResponse, self).write(data)
-
-    def flush(self, callback=None):
-        """Flushes the current output buffer to the network.
-
-        The ``callback`` argument, if given, can be used for flow control:
-        it will be run when all flushed data has been written to the socket.
-        Note that only one flush callback can be outstanding at a time;
-        if another flush occurs before the previous flush's callback
-        has been run, the previous callback will be discarded.
-        """
-        if self.ginsfsm_channel:
-            self.ginsfsm_channel.flush(callback)
-        else:
-            logging.error('ERROR async Flush before set ginsfsm_channel')
-
-    def finish(self):
-        """Finishes this response, ending the HTTP request."""
-        if self.ginsfsm_channel:
-            self.ginsfsm_channel.finish()
-        else:
-            logging.error('ERROR async FINISH before set ginsfsm_channel')