Explorar o código

Support WebSockets 76 (hixie-76, hybi-00).

Looks like disabling web-socket-js debug messages by default that we
get a minor speedup.

Python proxy should support both 75 and 76 (00) modes. Also, update ws
test to more reliably hit the WebSockets ordering/drop issue.
Joel Martin %!s(int64=15) %!d(string=hai) anos
pai
achega
486cd527f2
Modificáronse 6 ficheiros con 89 adicións e 29 borrados
  1. 0 2
      docs/TODO
  2. 6 0
      docs/notes
  3. BIN=BIN
      include/web-socket-js/flash-src/WebSocketMain.swf
  4. 22 13
      tests/ws.html
  5. 4 3
      tests/ws.py
  6. 57 11
      utils/websocket.py

+ 0 - 2
docs/TODO

@@ -2,8 +2,6 @@ Short Term:
 
 - Test on IE 9 preview 3.
 
-- Support WebSockets version 76 in proxy (Chrome 6 and Safari 5).
-
 - Support Opera 10.60.
 
 - Possibly support IE <= 8.0 using excanvas:

+ 6 - 0
docs/notes

@@ -36,3 +36,9 @@ WebSocket gets converted to UTF-8 and vice-versa. So, one additional
 (and necessary) function of the proxy is base64 encoding/decoding what
 is sent to/from the browser. Another option that I want to explore is
 UTF-8 encoding in the proxy.
+
+
+Building web-socket-js emulator:
+
+cd include/web-socket-js/flash-src
+mxmlc -static-link-runtime-shared-libraries WebSocketMain.as

BIN=BIN
include/web-socket-js/flash-src/WebSocketMain.swf


+ 22 - 13
tests/ws.html

@@ -1,6 +1,18 @@
 <html>
 
-    <head><title>WebSockets Test</title></head>
+    <head>
+        <title>WebSockets Test</title>
+        <script src="include/mootools.js"></script>
+        <script src="include/base64.js"></script>
+        <script src="include/util.js"></script>
+        <!-- Uncomment to activate firebug lite -->
+        <!--
+        <script type='text/javascript' 
+            src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
+        -->
+
+
+    </head>
 
     <body>
 
@@ -31,16 +43,6 @@
     </body>
 
 
-    <!-- Uncomment to activate firebug lite -->
-    <!--
-    <script type='text/javascript' 
-        src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
-    -->
-
-    <script src="include/mootools.js"></script>
-    <script src="include/base64.js"></script>
-    <script src="include/util.js"></script>
-
     <script>
 
         function error(str) {
@@ -226,7 +228,10 @@
 
 
         /* If no builtin websockets then load web_socket.js */
-        if (! window.WebSocket) {
+        if (window.WebSocket) {
+            VNC_native_ws = true;
+        } else {
+            VNC_native_ws = false;
             console.log("Loading web-socket-js flash bridge");
             var extra = "<script src='include/web-socket-js/swfobject.js'><\/script>";
             extra += "<script src='include/web-socket-js/FABridge.js'><\/script>";
@@ -236,7 +241,11 @@
 
         window.onload = function() {
             console.log("onload");
-            WebSocket.__swfLocation = "include/web-socket-js/WebSocketMain.swf";
+            if (!VNC_native_ws) {
+                console.log("initializing web-socket-js flash bridge");
+                WebSocket.__swfLocation = "include/web-socket-js/WebSocketMain.swf";
+                WebSocket.__initialize();
+            }
             var url = document.location.href;
             $('host').value = (url.match(/host=([^&#]*)/) || ['',''])[1];
             $('port').value = (url.match(/port=([^&#]*)/) || ['',''])[1];

+ 4 - 3
tests/ws.py

@@ -15,6 +15,7 @@ sys.path.insert(0,os.path.dirname(__file__) + "/../utils/")
 from websocket import *
 
 buffer_size = 65536
+max_packet_size = 10000
 recv_cnt = send_cnt = 0
 
 
@@ -76,8 +77,8 @@ def check(buf):
 
 def generate():
     global send_cnt, rand_array
-    length = random.randint(10, 100000)
-    numlist = rand_array[100000-length:]
+    length = random.randint(10, max_packet_size)
+    numlist = rand_array[max_packet_size-length:]
     # Error in length
     #numlist.append(5)
     chksum = sum(numlist)
@@ -156,7 +157,7 @@ if __name__ == '__main__':
 
     print "Prepopulating random array"
     rand_array = []
-    for i in range(0, 100000):
+    for i in range(0, max_packet_size):
         rand_array.append(random.randint(0, 9))
 
     settings['listen_port'] = listen_port

+ 57 - 11
utils/websocket.py

@@ -9,9 +9,10 @@ as taken from http://docs.python.org/dev/library/ssl.html#certificates
 
 '''
 
-import sys, socket, ssl, traceback
+import sys, socket, ssl, struct, traceback
 import os, resource, errno, signal # daemonizing
 from base64 import b64encode, b64decode
+from hashlib import md5
 
 settings = {
     'listen_host' : '',
@@ -30,11 +31,11 @@ send_seq = 0
 server_handshake = """HTTP/1.1 101 Web Socket Protocol Handshake\r
 Upgrade: WebSocket\r
 Connection: Upgrade\r
-WebSocket-Origin: %s\r
-WebSocket-Location: %s://%s%s\r
-WebSocket-Protocol: sample\r
+%sWebSocket-Origin: %s\r
+%sWebSocket-Location: %s://%s%s\r
+%sWebSocket-Protocol: sample\r
 \r
-"""
+%s"""
 
 policy_response = """<cross-domain-policy><allow-access-from domain="*" to-ports="*" /></cross-domain-policy>\n"""
 
@@ -104,13 +105,11 @@ def do_handshake(sock):
         scheme = "ws"
         print "  using plain (not SSL) socket"
     handshake = retsock.recv(4096)
-    req_lines = handshake.split("\r\n")
-    _, path, _ = req_lines[0].split(" ")
-    _, origin = req_lines[4].split(" ")
-    _, host = req_lines[3].split(" ")
+    print "handshake: " + repr(handshake)
+    h = parse_handshake(handshake)
 
     # Parse client settings from the GET path
-    cvars = path.partition('?')[2].partition('#')[0].split('&')
+    cvars = h['path'].partition('?')[2].partition('#')[0].split('&')
     for cvar in [c for c in cvars if c]:
         name, _, val = cvar.partition('=')
         if name not in ['b64encode', 'seq_num']: continue
@@ -118,9 +117,56 @@ def do_handshake(sock):
         client_settings[name] = value
         print "  %s=%s" % (name, value)
 
-    retsock.send(server_handshake % (origin, scheme, host, path))
+    if h.get('key3'):
+        trailer = gen_md5(h)
+        pre = "Sec-"
+    else:
+        trailer = ""
+        pre = ""
+
+    response = server_handshake % (pre, h['Origin'], pre, scheme,
+            h['Host'], h['path'], pre, trailer)
+
+    print "sending response:", repr(response)
+
+    retsock.send(response)
     return retsock
 
+def parse_handshake(handshake):
+    ret = {}
+    req_lines = handshake.split("\r\n")
+    if not req_lines[0].startswith("GET "):
+        raise "Invalid handshake: no GET request line"
+    ret['path'] = req_lines[0].split(" ")[1]
+    for line in req_lines[1:]:
+        if line == "": break
+        var, delim, val = line.partition(": ")
+        ret[var] = val
+
+    if req_lines[-2] == "":
+        ret['key3'] = req_lines[-1]
+
+    return ret
+
+def gen_md5(keys):
+    key1 = keys['Sec-WebSocket-Key1']
+    key2 = keys['Sec-WebSocket-Key2']
+    key3 = keys['key3']
+    spaces1 = key1.count(" ")
+    spaces2 = key2.count(" ")
+    num1 = int("".join([c for c in key1 if c.isdigit()])) / spaces1
+    num2 = int("".join([c for c in key2 if c.isdigit()])) / spaces2
+
+    packed = struct.pack('>II8s', num1, num2, key3)
+    digest = md5(packed).digest()
+    print "num1:", num1
+    print "num2:", num2
+    print "key3:", repr(key3)
+    print "packed:", packed
+    print "digest:", repr(digest)
+
+    return digest
+
 def daemonize():
     os.umask(0)
     os.chdir('/')