Anonymous avatar Anonymous committed 99fcb0b

Changed to (partially) support version 10

Comments (0)

Files changed (2)

geventwebsocket/handler.py

 import re
 import struct
 from hashlib import md5
+import hashlib
+import base64
 
 from gevent.pywsgi import WSGIHandler
 from geventwebsocket import WebSocket
         # In case the client doesn't want to initialize a WebSocket connection
         # we will proceed with the default PyWSGI functionality.
         if self.environ.get("HTTP_CONNECTION") != "Upgrade" or \
-           self.environ.get("HTTP_UPGRADE") != "WebSocket" or \
-           not self.environ.get("HTTP_ORIGIN") or \
+           self.environ.get("HTTP_UPGRADE").lower() != "websocket" or \
+           not (self.environ.get("HTTP_ORIGIN") or
+                self.environ.get("HTTP_SEC_WEBSOCKET_ORIGIN")) or\
            not self.accept_upgrade():
             return super(WebSocketHandler, self).handle_one_response()
         else:
         self.websocket = WebSocket(self.socket, self.rfile, self.environ)
         self.environ['wsgi.websocket'] = self.websocket
 
-        # Detect the Websocket protocol
-        if "HTTP_SEC_WEBSOCKET_KEY1" in self.environ:
-            version = 76
-        else:
-            version = 75
-
-        if version == 75:
+        if self.websocket.version == 75:
             headers = [
                 ("Upgrade", "WebSocket"),
                 ("Connection", "Upgrade"),
                 ("WebSocket-Location", "ws://" + self.environ.get('HTTP_HOST') + self.websocket.path),
             ]
             self.start_response("101 Web Socket Protocol Handshake", headers)
-        elif version == 76:
+        elif self.websocket.version == 76:
             challenge = self._get_challenge()
             headers = [
                 ("Upgrade", "WebSocket"),
             ]
 
             self.start_response("101 Web Socket Protocol Handshake", headers)
-            self.write([challenge])
+            self.write(challenge)
+        elif self.websocket.version == 10:
+            key = self.environ.get("HTTP_SEC_WEBSOCKET_KEY")
+            magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
+            accept = hashlib.sha1("%s%s" % (key, magic) ).digest()
+            accept = base64.b64encode(accept)
+
+            headers = [
+                ("Upgrade", "websocket"),
+                ("Connection", "Upgrade"),
+                ("Sec-WebSocket-Protocol", self.websocket.protocol),
+                ("Sec-WebSocket-Accept", accept),
+            ]
+
+            self.start_response("101 Web Socket Protocol Handshake", headers)
         else:
             raise Exception("Version not supported")
 
 
     def write(self, data):
         if self.websocket_connection:
-            self.wfile.writelines(data)
+            self.socket.sendall(data)
         else:
             super(WebSocketHandler, self).write(data)
 
                 towrite.append("%s: %s\r\n" % header)
 
             towrite.append("\r\n")
-            self.wfile.writelines(towrite)
+            msg = ''.join(towrite)
+            self.socket.sendall(msg)
             self.headers_sent = True
         else:
             super(WebSocketHandler, self).start_response(status, headers, exc_info)
             message = "Client using old/invalid protocol implementation"
             headers = [("Content-Length", str(len(message))),]
             self.start_response("400 Bad Request", headers)
-            self.write([message])
+            self.write(message)
             self.close_connection = True
             return
 

geventwebsocket/websocket.py

     def __init__(self, sock, rfile, environ):
         self.rfile = rfile
         self.socket = sock
-        self.origin = environ.get('HTTP_ORIGIN')
+        if "HTTP_SEC_WEBSOCKET_ORIGIN" in environ:
+            self.origin = environ.get('HTTP_SEC_WEBSOCKET_ORIGIN')
+        else:
+            self.origin = environ.get('HTTP_ORIGIN')
         self.protocol = environ.get('HTTP_SEC_WEBSOCKET_PROTOCOL', 'unknown')
         self.path = environ.get('PATH_INFO')
         self.websocket_closed = False
+        self.version = self.get_version(environ)
+
+    def get_version(self, environ):
+        # Detect the Websocket protocol
+        if environ.get("HTTP_SEC_WEBSOCKET_VERSION") == '8':
+            version = 10
+        elif "HTTP_SEC_WEBSOCKET_KEY1" in environ:
+            version = 76
+        else:
+            version = 75
+
+        return version
 
     def send(self, message):
         if self.websocket_closed:
         else:
             raise Exception("Invalid message encoding")
 
-        self.socket.sendall("\x00" + message + "\xFF")
+        if self.version in [75, 76]:
+            self.socket.sendall("\x00" + message + "\xFF")
+        else:
+            # XXX TODO Make longer messages possible, see wait()
+            max_length = 125
+            opcode = -1
+            while True:
+                part = message[:max_length]
+                message = message[max_length:]
+                opcode = 1 if opcode == -1 else 0
+                opcode += 128 if len(part) < max_length else 0
+
+                self.socket.sendall(chr(opcode) + chr(len(part)) + part)
+
+                if opcode & 0x80 == 0x80:
+                    break
 
     def close_connection(self):
         if not self.websocket_closed:
         else:
             return
 
+    def _read_until(self):
+        bytes = []
+
+        while True:
+            byte = self.rfile.read(1)
+            if ord(byte) != 0xff:
+                bytes.append(byte)
+            else:
+                break
+
+        return ''.join(bytes)
+
     def _message_length(self):
+        if self.version in [75, 76]:
+            return self._message_length_hixie()
+        else:
+            return self._message_length_hybi()
+
+    def _message_length_hixie(self):
         # TODO: buildin security agains lengths greater than 2**31 or 2**32
         length = 0
 
 
         return length
 
-    def _read_until(self):
-        bytes = []
+    def _message_length_hybi(self):
+        byte_str = self.rfile.read(1)
+        if not byte_str:
+            return 0
 
-        while True:
-            byte = self.rfile.read(1)
-            if ord(byte) != 0xff:
-                bytes.append(byte)
-            else:
-                break
+        byte = ord(byte_str)
 
-        return ''.join(bytes)
+        # Assert mask bit set
+        assert(byte & 0x80 == 0x80)
+
+        length = byte & 0x7F
+        if length == 126:
+            length = 0
+            for i in range(2):
+                factor = ord(self.rfile.read(1))
+                length += factor * pow(256, 1-i)
+        elif length == 127:
+            length = 0
+            for i in range(8):
+                factor = ord(self.rfile.read(1))
+                length += factor * pow(256, 7-i)
+
+        return length
 
     def wait(self):
+        if self.version in [75, 76]:
+            return self.wait_hixie()
+        else:
+            return self.wait_hybi()
+
+    def wait_hixie(self):
         while True:
             if self.websocket_closed:
                 return None
                         self.rfile.read(length) # discard the bytes
             else:
                 raise IOError("Reveiced an invalid message")
+
+    def wait_hybi(self):
+        # XXX TODO 01, 00, 81, what if not text?
+        frame_str = self.rfile.read(1)
+        first = ord(frame_str)
+
+        length = self._message_length()
+
+        mask = []
+        for i in range(4):
+            mask.append(self.rfile.read(1))
+
+        payload = []
+        for i in range(length):
+            byte = self.rfile.read(1)
+            data = ord(byte) ^ ord(mask[i%4])
+            payload.append(chr(data))
+
+        return ''.join(payload)
+
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.