Permalink
Browse files

Switching to single event "event" instead of "open", "message", etc. …

…in Flash interface, to keep order of all events and avoid recursive call error.

Using setTimeout() for event callback to avoid recursive call error.
Removing FABridge.EventsToCallLater. Hopefully setTimeout() above is more confident alternative.
  • Loading branch information...
1 parent 6640d9d commit 20f837425d44144f0df7020a5f98350be2b83745 @gimite gimite committed Jan 11, 2011
View
Binary file not shown.
View
Binary file not shown.
View
@@ -22,11 +22,7 @@ import com.hurlant.crypto.tls.TLSEngine;
import com.hurlant.crypto.tls.TLSSecurityParameters;
import com.gsolo.encryption.MD5;
-[Event(name="message", type="flash.events.Event")]
-[Event(name="open", type="flash.events.Event")]
-[Event(name="close", type="flash.events.Event")]
-[Event(name="error", type="flash.events.Event")]
-[Event(name="stateChange", type="WebSocketStateEvent")]
+[Event(name="event", type="flash.events.Event")]
public class WebSocket extends EventDispatcher {
private static var CONNECTING:int = 0;
@@ -47,10 +43,9 @@ public class WebSocket extends EventDispatcher {
private var origin:String;
private var protocol:String;
private var buffer:ByteArray = new ByteArray();
- private var dataQueue:Array;
+ private var eventQueue:Array = [];
private var headerState:int = 0;
private var readyState:int = CONNECTING;
- private var bufferedAmount:int = 0;
private var headers:String;
private var noiseChars:Array;
private var expectedDigest:String;
@@ -115,23 +110,19 @@ public class WebSocket extends EventDispatcher {
socket.flush();
main.log("sent: " + data);
return -1;
- } else if (readyState == CLOSED) {
+ } else if (readyState == CLOSING || readyState == CLOSED) {
var bytes:ByteArray = new ByteArray();
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:
- // > You are trying to call recursively into the Flash Player which is not allowed.
- return bufferedAmount;
+ return bytes.length; // not sure whether it should include \x00 and \xff
} else {
- main.fatal("INVALID_STATE_ERR: invalid state");
+ main.fatal("invalid state");
return 0;
}
}
public function close():void {
main.log("close");
- dataQueue = [];
+ eventQueue = [];
try {
if (readyState == OPEN) {
socket.writeByte(0xff);
@@ -146,14 +137,6 @@ public class WebSocket extends EventDispatcher {
// We do something equivalent in JavaScript WebSocket#close instead.
}
- public function getReadyState():int {
- return readyState;
- }
-
- public function getBufferedAmount():int {
- return bufferedAmount;
- }
-
private function onSocketConnect(event:Event):void {
main.log("connected");
@@ -162,7 +145,6 @@ public class WebSocket extends EventDispatcher {
tlsSocket.startTLS(rawSocket, host, tlsConfig);
}
- dataQueue = [];
var hostValue:String = host + (port == 80 ? "" : ":" + port);
var cookie:String = "";
if (main.getCallerHost() == host) {
@@ -199,8 +181,7 @@ public class WebSocket extends EventDispatcher {
private function onSocketClose(event:Event):void {
main.log("closed");
readyState = CLOSED;
- notifyStateChange();
- dispatchEvent(new Event("close"));
+ fireEvent({type: "close"}, true);
}
private function onSocketIoError(event:IOErrorEvent):void {
@@ -230,8 +211,7 @@ public class WebSocket extends EventDispatcher {
if (state == CLOSED) return;
main.error(message);
close();
- notifyStateChange();
- dispatchEvent(new Event(state == CONNECTING ? "close" : "error"));
+ fireEvent({type: state == CONNECTING ? "close" : "error"}, true);
}
private function onSocketData(event:ProgressEvent):void {
@@ -266,8 +246,7 @@ public class WebSocket extends EventDispatcher {
removeBufferBefore(pos + 1);
pos = -1;
readyState = OPEN;
- notifyStateChange();
- dispatchEvent(new Event("open"));
+ fireEvent({type: "open"}, true);
}
} else {
if (buffer[pos] == 0xff && pos > 0) {
@@ -277,27 +256,23 @@ public class WebSocket extends EventDispatcher {
}
var data:String = readUTFBytes(buffer, 1, pos - 1);
main.log("received: " + data);
- dataQueue.push(encodeURIComponent(data));
- dispatchEvent(new Event("message"));
+ fireEvent({type: "message", data: encodeURIComponent(data)}, false);
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"));
+ fireEvent({type: "close"}, true);
}
}
}
}
- public function readSocketData():Array {
- var q:Array = dataQueue;
- if (dataQueue.length > 0) {
- dataQueue = [];
- }
+ public function receiveEvents():Array {
+ var q:Array = eventQueue;
+ eventQueue = [];
return q;
}
@@ -360,8 +335,12 @@ public class WebSocket extends EventDispatcher {
buffer = nextBuffer;
}
- private function notifyStateChange():void {
- dispatchEvent(new WebSocketStateEvent("stateChange", readyState, bufferedAmount));
+ private function fireEvent(event:Object, stateChanged:Boolean):void {
+ if (stateChanged) {
+ event.readyState = readyState;
+ }
+ eventQueue.push(event);
+ dispatchEvent(new Event("event"));
}
private function initNoiseChars():void {
View
@@ -24,19 +24,9 @@ public class WebSocketMain extends Sprite {
private var manualPolicyFileLoaded:Boolean = false;
public function WebSocketMain() {
-
- // This is to avoid "You are trying to call recursively into the Flash Player ..."
- // error which (I heard) happens when you pass bunch of messages.
- // This workaround was written here:
- // http://www.themorphicgroup.com/blog/2009/02/14/fabridge-error-you-are-trying-to-call-recursively-into-the-flash-player-which-is-not-allowed/
- FABridge.EventsToCallLater["flash.events::Event"] = "true";
- FABridge.EventsToCallLater["WebSocketMessageEvent"] = "true";
- FABridge.EventsToCallLater["WebSocketStateEvent"] = "true";
-
var fab:FABridge = new FABridge();
fab.rootObject = this;
//log("Flash initialized");
-
}
public function setCallerUrl(url:String):void {
@@ -1,32 +0,0 @@
-// 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
-
-package {
-
-import flash.display.*;
-import flash.events.*;
-import flash.external.*;
-import flash.net.*;
-import flash.system.*;
-import flash.utils.*;
-import mx.core.*;
-import mx.controls.*;
-import mx.events.*;
-import mx.utils.*;
-
-public class WebSocketStateEvent extends Event {
-
- public var readyState:int;
- public var bufferedAmount:int;
-
- public function WebSocketStateEvent(type:String, readyState:int, bufferedAmount:int) {
- super(type);
- this.readyState = readyState;
- this.bufferedAmount = bufferedAmount;
- }
-
-}
-
-}
View
@@ -33,73 +33,22 @@
WebSocket.__addTask(function() {
self.__createFlash(url, protocol, proxyHost, proxyPort, headers);
});
- }, 1);
+ }, 0);
};
WebSocket.prototype.__createFlash = function(url, protocol, proxyHost, proxyPort, headers) {
var self = this;
self.__flash =
WebSocket.__flash.create(url, protocol, proxyHost || null, proxyPort || 0, headers || null);
-
- self.__flash.addEventListener("open", function(fe) {
- try {
- self.readyState = self.__flash.getReadyState();
- if (self.__timer) clearInterval(self.__timer);
- if (window.opera) {
- // Workaround for weird behavior of Opera which sometimes drops events.
- self.__timer = setInterval(function () {
- self.__handleMessages();
- }, 500);
- }
- if (self.onopen) self.onopen();
- } catch (e) {
- console.error(e.toString());
- }
- });
-
- self.__flash.addEventListener("close", function(fe) {
- try {
- self.readyState = self.__flash.getReadyState();
- if (self.__timer) clearInterval(self.__timer);
- if (self.onclose) self.onclose();
- } catch (e) {
- console.error(e.toString());
- }
- });
-
- self.__flash.addEventListener("message", function() {
- try {
- self.__handleMessages();
- } catch (e) {
- console.error(e.toString());
- }
+ self.__flash.addEventListener("event", function(fe) {
+ // Uses setTimeout() to workaround the error:
+ // > You are trying to call recursively into the Flash Player which is not allowed.
+ setTimeout(function() { self.__handleEvents(); }, 0);
});
-
- self.__flash.addEventListener("error", function(fe) {
- try {
- if (self.__timer) clearInterval(self.__timer);
- if (self.onerror) self.onerror();
- } catch (e) {
- console.error(e.toString());
- }
- });
-
- self.__flash.addEventListener("stateChange", function(fe) {
- try {
- self.readyState = self.__flash.getReadyState();
- self.bufferedAmount = fe.getBufferedAmount();
- } catch (e) {
- console.error(e.toString());
- }
- });
-
//console.log("[WebSocket] Flash object is ready");
};
WebSocket.prototype.send = function(data) {
- if (this.__flash) {
- this.readyState = this.__flash.getReadyState();
- }
if (!this.__flash || this.readyState == WebSocket.CONNECTING) {
throw "INVALID_STATE_ERR: Web Socket connection has not been established";
}
@@ -113,15 +62,14 @@
if (result < 0) { // success
return true;
} else {
- this.bufferedAmount = result;
+ this.bufferedAmount += result;
return false;
}
};
WebSocket.prototype.close = function() {
var self = this;
if (!self.__flash) return;
- self.readyState = self.__flash.getReadyState();
if (self.readyState == WebSocket.CLOSED || self.readyState == WebSocket.CLOSING) return;
self.__flash.close();
// Sets/calls them manually here because Flash WebSocketConnection.close cannot fire events
@@ -132,7 +80,7 @@
if (self.onclose) {
// Make it asynchronous so that it looks more like an actual
// close event
- setTimeout(self.onclose, 1);
+ setTimeout(self.onclose, 0);
}
};
@@ -201,30 +149,62 @@
}
};
- WebSocket.prototype.__handleMessages = function() {
- // Gets data using readSocketData() instead of getting it from event object
+ WebSocket.prototype.__handleEvents = function() {
+ // Gets events using receiveEvents() instead of getting it from event object
// of Flash event. This is to make sure to keep message order.
// It seems sometimes Flash events don't arrive in the same order as they are sent.
- var arr = this.__flash.readSocketData();
- for (var i = 0; i < arr.length; i++) {
- var data = decodeURIComponent(arr[i]);
+ var events = this.__flash.receiveEvents();
+ for (var i = 0; i < events.length; i++) {
try {
- if (this.onmessage) {
- var e;
- if (window.MessageEvent && !window.opera) {
- e = document.createEvent("MessageEvent");
- e.initMessageEvent("message", false, false, data, null, null, window, null);
- } else { // IE and Opera, the latter one truncates the data parameter after any 0x00 bytes
- e = {data: data};
+ var event = events[i];
+ if ("readyState" in event) {
+ this.readyState = event.readyState;
+ }
+ if (event.type == "open") {
+
+ if (this.__timer) clearInterval(this.__timer);
+ if (window.opera) {
+ // Workaround for weird behavior of Opera which sometimes drops events.
+ this.__timer = setInterval(function () {
+ this.__handleEvents();
+ }, 500);
+ }
+ if (this.onopen) this.onopen();
+
+ } else if (event.type == "close") {
+
+ if (this.__timer) clearInterval(this.__timer);
+ if (this.onclose) this.onclose();
+
+ } else if (event.type == "message") {
+
+ if (this.onmessage) {
+ var data = decodeURIComponent(event.data);
+ var e;
+ if (window.MessageEvent && !window.opera) {
+ e = document.createEvent("MessageEvent");
+ e.initMessageEvent("message", false, false, data, null, null, window, null);
+ } else {
+ // IE and Opera, the latter one truncates the data parameter after any 0x00 bytes.
+ e = {data: data};
+ }
+ this.onmessage(e);
}
- this.onmessage(e);
+
+ } else if (event.type == "error") {
+
+ if (this.__timer) clearInterval(this.__timer);
+ if (this.onerror) this.onerror();
+
+ } else {
+ throw "unknown event type: " + event.type;
}
} catch (e) {
console.error(e.toString());
}
}
};
-
+
/**
* @param {object} object
* @param {string} type

0 comments on commit 20f8374

Please sign in to comment.