Quellcode durchsuchen

Import/merge gimite/web-socket-js up to da7caff96496c7d7bfb3.

Bug fixes, restore RFC2817 proxy for non wss://, and handle new
closing handshake from WebSockets 76.
Joel Martin vor 15 Jahren
Ursprung
Commit
b9633f8bfd

+ 3 - 2
include/web-socket-js/README.txt

@@ -67,10 +67,11 @@ The class RFC2817Socket (by Christian Cantrell) effectively lets us implement th
 
 * How to build WebSocketMain.swf
 
-Install Flex SDK.
+Install Flex 4 SDK:
+http://opensource.adobe.com/wiki/display/flexsdk/Download+Flex+4
 
 $ cd flash-src
-$ mxmlc -output=../WebSocketMain.swf WebSocketMain.as
+$ ./build.sh
 
 
 * License

+ 75 - 49
include/web-socket-js/flash-src/WebSocket.as

@@ -1,7 +1,7 @@
 // Copyright: Hiroshi Ichikawa <http://gimite.net/en/>
 // License: New BSD License
 // Reference: http://dev.w3.org/html5/websockets/
-// Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-31
+// Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76
 
 package {
 
@@ -34,7 +34,6 @@ public class WebSocket extends EventDispatcher {
   private static var CLOSING:int = 2;
   private static var CLOSED:int = 3;
   
-  //private var rawSocket:RFC2817Socket;
   private var rawSocket:Socket;
   private var tlsSocket:TLSSocket;
   private var tlsConfig:TLSConfig;
@@ -76,51 +75,49 @@ public class WebSocket extends EventDispatcher {
     // "Header1: xxx\r\nHeader2: yyyy\r\n"
     this.headers = headers;
     
-    /*
-    socket = new RFC2817Socket();
-            
-    // if no proxy information is supplied, it acts like a normal Socket
-    // @see RFC2817Socket::connect
-    if (proxyHost != null && proxyPort != 0){      
-      socket.setProxyInfo(proxyHost, proxyPort);
-    } 
-    */
-
-    ExternalInterface.call("console.log", "[WebSocket] scheme: " + scheme);
-    rawSocket = new Socket();
-
-    rawSocket.addEventListener(Event.CLOSE, onSocketClose);
-    rawSocket.addEventListener(Event.CONNECT, onSocketConnect);
-    rawSocket.addEventListener(IOErrorEvent.IO_ERROR, onSocketIoError);
-    rawSocket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSocketSecurityError);
-    if (scheme == "wss") {
+    if (proxyHost != null && proxyPort != 0){
+      if (scheme == "wss") {
+        main.fatal("wss with proxy is not supported");
+      }
+      var proxySocket:RFC2817Socket = new RFC2817Socket();
+      proxySocket.setProxyInfo(proxyHost, proxyPort);
+      proxySocket.addEventListener(ProgressEvent.SOCKET_DATA, onSocketData);
+      rawSocket = socket = proxySocket;
+    } else {
+      rawSocket = new Socket();
+      if (scheme == "wss") {
         tlsConfig= new TLSConfig(TLSEngine.CLIENT,
             null, null, null, null, null,
             TLSSecurityParameters.PROTOCOL_VERSION);
         tlsConfig.trustSelfSignedCertificates = true;
         tlsConfig.ignoreCommonNameMismatch = true;
-
         tlsSocket = new TLSSocket();
         tlsSocket.addEventListener(ProgressEvent.SOCKET_DATA, onSocketData);
-        socket = (tlsSocket as Socket);
-    } else {
+        socket = tlsSocket;
+      } else {
         rawSocket.addEventListener(ProgressEvent.SOCKET_DATA, onSocketData);
-        socket = (rawSocket as Socket);
+        socket = rawSocket;
+      }
     }
+    rawSocket.addEventListener(Event.CLOSE, onSocketClose);
+    rawSocket.addEventListener(Event.CONNECT, onSocketConnect);
+    rawSocket.addEventListener(IOErrorEvent.IO_ERROR, onSocketIoError);
+    rawSocket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSocketSecurityError);
     rawSocket.connect(host, port);
   }
   
-  public function send(data:String):int {
+  public function send(encData:String):int {
+    var data:String = decodeURIComponent(encData);
     if (readyState == OPEN) {
       socket.writeByte(0x00);
-      socket.writeUTFBytes(decodeURIComponent(data));
+      socket.writeUTFBytes(data);
       socket.writeByte(0xff);
       socket.flush();
       main.log("sent: " + data);
       return -1;
     } else if (readyState == CLOSED) {
       var bytes:ByteArray = new ByteArray();
-      bytes.writeUTFBytes(decodeURIComponent(data));
+      bytes.writeUTFBytes(data);
       bufferedAmount += bytes.length; // not sure whether it should include \x00 and \xff
       // We use return value to let caller know bufferedAmount because we cannot fire
       // stateChange event here which causes weird error:
@@ -136,6 +133,9 @@ public class WebSocket extends EventDispatcher {
     main.log("close");
     dataQueue = [];
     try {
+      socket.writeByte(0xff);
+      socket.writeByte(0x00);
+      socket.flush();
       socket.close();
     } catch (ex:Error) { }
     readyState = CLOSED;
@@ -156,8 +156,8 @@ public class WebSocket extends EventDispatcher {
     main.log("connected");
 
     if (scheme == "wss") {
-        ExternalInterface.call("console.log", "[WebSocket] starting SSL/TLS");
-        tlsSocket.startTLS(rawSocket, host, tlsConfig);
+      main.log("starting SSL/TLS");
+      tlsSocket.startTLS(rawSocket, host, tlsConfig);
     }
     
     dataQueue = [];
@@ -190,7 +190,6 @@ public class WebSocket extends EventDispatcher {
     main.log("request header:\n" + req);
     socket.writeUTFBytes(req);
     main.log("sent key3: " + key3);
-    main.log("expected digest: " + expectedDigest);
     writeBytes(key3);
     socket.flush();
   }
@@ -247,39 +246,49 @@ public class WebSocket extends EventDispatcher {
           headerState = 0;
         }
         if (headerState == 4) {
+          buffer.position = 0;
           var headerStr:String = buffer.readUTFBytes(pos + 1);
           main.log("response header:\n" + headerStr);
           if (!validateHeader(headerStr)) return;
-          makeBufferCompact();
+          removeBufferBefore(pos + 1);
           pos = -1;
         }
       } else if (headerState == 4) {
-        var replyDigest:String = readBytes(buffer, 16);
-        main.log("reply digest: " + replyDigest);
-        if (replyDigest != expectedDigest) {
-          onError("digest doesn't match: " + replyDigest + " != " + expectedDigest);
-          return;
+        if (pos == 15) {
+          buffer.position = 0;
+          var replyDigest:String = readBytes(buffer, 16);
+          main.log("reply digest: " + replyDigest);
+          if (replyDigest != expectedDigest) {
+            onError("digest doesn't match: " + replyDigest + " != " + expectedDigest);
+            return;
+          }
+          headerState = 5;
+          removeBufferBefore(pos + 1);
+          pos = -1;
+          readyState = OPEN;
+          notifyStateChange();
+          dispatchEvent(new Event("open"));
         }
-        headerState = 5;
-        makeBufferCompact();
-        pos = -1;
-        readyState = OPEN;
-        notifyStateChange();
-        dispatchEvent(new Event("open"));
       } else {
-        if (buffer[pos] == 0xff) {
-        //if (buffer.bytesAvailable > 1) {
-          if (buffer.readByte() != 0x00) {
+        if (buffer[pos] == 0xff && pos > 0) {
+          if (buffer[0] != 0x00) {
             onError("data must start with \\x00");
             return;
           }
+          buffer.position = 1;
           var data:String = buffer.readUTFBytes(pos - 1);
           main.log("received: " + data);
           dataQueue.push(encodeURIComponent(data));
           dispatchEvent(new WebSocketMessageEvent("message", data.length.toString()));
-          buffer.readByte();
-          makeBufferCompact();
+          removeBufferBefore(pos + 1);
           pos = -1;
+        } else if (pos == 1 && buffer[0] == 0xff && buffer[1] == 0x00) { // closing
+          main.log("received closing packet");
+          removeBufferBefore(pos + 1);
+          pos = -1;
+          close();
+          notifyStateChange();
+          dispatchEvent(new Event("close"));
         }
       }
     }
@@ -318,6 +327,18 @@ public class WebSocket extends EventDispatcher {
       onError("invalid Connection: " + header["Connection"]);
       return false;
     }
+    if (!header["Sec-WebSocket-Origin"]) {
+      if (header["WebSocket-Origin"]) {
+        onError(
+          "The WebSocket server speaks old WebSocket protocol, " +
+          "which is not supported by web-socket-js. " +
+          "It requires WebSocket protocol 76 or later. " +
+          "Try newer version of the server if available.");
+      } else {
+        onError("header Sec-WebSocket-Origin is missing");
+      }
+      return false;
+    }
     var resOrigin:String = header["Sec-WebSocket-Origin"].toLowerCase();
     if (resOrigin != origin) {
       onError("origin doesn't match: '" + resOrigin + "' != '" + origin + "'");
@@ -331,9 +352,10 @@ public class WebSocket extends EventDispatcher {
     return true;
   }
 
-  private function makeBufferCompact():void {
-    if (buffer.position == 0) return;
+  private function removeBufferBefore(pos:int):void {
+    if (pos == 0) return;
     var nextBuffer:ByteArray = new ByteArray();
+    buffer.position = pos;
     buffer.readBytes(nextBuffer);
     buffer = nextBuffer;
   }
@@ -399,12 +421,16 @@ public class WebSocket extends EventDispatcher {
     return bytes;
   }
   
+  // Writes byte sequence to socket.
+  // bytes is String in special format where bytes[i] is i-th byte, not i-th character.
   private function writeBytes(bytes:String):void {
     for (var i:int = 0; i < bytes.length; ++i) {
       socket.writeByte(bytes.charCodeAt(i));
     }
   }
   
+  // Reads specified number of bytes from buffer, and returns it as special format String
+  // where bytes[i] is i-th byte (not i-th character).
   private function readBytes(buffer:ByteArray, numBytes:int):String {
     var bytes:String = "";
     for (var i:int = 0; i < numBytes; ++i) {

+ 1 - 1
include/web-socket-js/flash-src/WebSocketMain.as

@@ -76,7 +76,7 @@ public class WebSocketMain extends Sprite {
 
   public function log(message:String):void {
     if (debug) {
-        ExternalInterface.call("webSocketLog", encodeURIComponent("[WebSocket] " + message));
+      ExternalInterface.call("webSocketLog", encodeURIComponent("[WebSocket] " + message));
     }
   }
 

+ 2 - 0
include/web-socket-js/sample.html

@@ -15,6 +15,8 @@
     
     // Set URL of your WebSocketMain.swf here:
     WebSocket.__swfLocation = "WebSocketMain.swf";
+    // Set this to dump debug message from Flash to console.log:
+    WebSocket.__debug = true;
 
     var ws;
     

+ 8 - 10
include/web-socket-js/web_socket.js

@@ -292,7 +292,7 @@
 
   WebSocket.__tasks = [];
 
-  WebSocket.__initialize = function(debug) {
+  WebSocket.__initialize = function() {
     if (!WebSocket.__swfLocation) {
       console.error("[WebSocket] set WebSocket.__swfLocation to location of WebSocketMain.swf");
       return;
@@ -320,9 +320,7 @@
         //console.log("[WebSocket] FABridge initializad");
         WebSocket.__flash = FABridge.webSocket.root();
         WebSocket.__flash.setCallerUrl(location.href);
-        if (typeof debug !== "undefined") {
-          WebSocket.__flash.setDebug(debug);
-        }
+        WebSocket.__flash.setDebug(!!WebSocket.__debug);
         for (var i = 0; i < WebSocket.__tasks.length; ++i) {
           WebSocket.__tasks[i]();
         }
@@ -351,12 +349,12 @@
     console.error(decodeURIComponent(message));
   };
 
-  /*
-  if (window.addEventListener) {
-    window.addEventListener("load", WebSocket.__initialize, false);
-  } else {
-    window.attachEvent("onload", WebSocket.__initialize);
+  if (!WebSocket.__disableAutoInitialization) {
+    if (window.addEventListener) {
+      window.addEventListener("load", WebSocket.__initialize, false);
+    } else {
+      window.attachEvent("onload", WebSocket.__initialize);
+    }
   }
-  */
   
 })();