mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-20 04:20:55 +01:00
84731e8f00
Summary: This is purely a prototype at the moment, but the basic functionality sort of works. I'm not sure how far I want to go with this but I think we might be able to get somewhere without it being gross. The idea here is to build a notification server WITHOUT using Comet, since Comet is extremely difficult and complicated. Instead, I use Flash on the client. LocalConnection allows flash instances to talk to each other and connect() can be used as a locking primitive. This allows all the instances to elect a master instance in a race-safe way. The master is responsible for opening a single connnection to the server. On the server, I use Node.js since PHP is pretty unsuitable for this task. See Github Issue #3: https://github.com/facebook/phabricator/issues/3 One thing I need to figure out next is if I can reasonably do SSL/TSL over Flash (it looks like I can, in theory, with the as3crypto library) or if the server needs to just send down version information and trigger a separate Ajax call on the client. Test Plan: Created a client pool and connected it to the server, with election and failover apparently working correctly. Reviewed By: aran Reviewers: Girish, aran, jungejason, tuomaspelkonen, davidrecordon Commenters: Girish, davidrecordon CC: aran, epriestley, Girish, davidrecordon Differential Revision: 284
162 lines
No EOL
4.1 KiB
ActionScript
162 lines
No EOL
4.1 KiB
ActionScript
package {
|
|
|
|
import flash.net.*;
|
|
import flash.utils.*;
|
|
import flash.media.*;
|
|
import flash.display.*;
|
|
import flash.events.*;
|
|
import flash.external.ExternalInterface;
|
|
|
|
import com.phabricator.*;
|
|
|
|
import vegas.strings.JSON;
|
|
|
|
public class Aphlict extends Sprite {
|
|
|
|
private var client:String;
|
|
|
|
private var master:LocalConnection;
|
|
private var recv:LocalConnection;
|
|
private var send:LocalConnection;
|
|
|
|
private var receiver:AphlictReceiver;
|
|
private var loyalUntil:Number = 0;
|
|
private var subjects:Array;
|
|
private var frequency:Number = 100;
|
|
|
|
private var socket:Socket;
|
|
private var readBuffer:ByteArray;
|
|
|
|
public function Aphlict() {
|
|
super();
|
|
|
|
this.master = null;
|
|
this.receiver = new AphlictReceiver(this);
|
|
this.subjects = [];
|
|
|
|
this.send = new LocalConnection();
|
|
|
|
this.recv = new LocalConnection();
|
|
this.recv.client = this.receiver;
|
|
for (var ii:Number = 0; ii < 32; ii++) {
|
|
try {
|
|
this.recv.connect('aphlict_subject_' + ii);
|
|
this.client = 'aphlict_subject_' + ii;
|
|
} catch (x:Error) {
|
|
// Some other Aphlict client is holding that ID.
|
|
}
|
|
}
|
|
|
|
if (!this.client) {
|
|
// Too many clients open already, just exit.
|
|
return;
|
|
}
|
|
|
|
this.usurp();
|
|
}
|
|
|
|
private function usurp():void {
|
|
if (this.master) {
|
|
for (var ii:Number = 0; ii < this.subjects.length; ii++) {
|
|
if (this.subjects[ii] == this.client) {
|
|
continue;
|
|
}
|
|
this.send.send(this.subjects[ii], 'remainLoyal');
|
|
}
|
|
} else if (this.loyalUntil < new Date().getTime()) {
|
|
var recv:LocalConnection = new LocalConnection();
|
|
recv.client = this.receiver;
|
|
try {
|
|
recv.connect('aphlict_master');
|
|
this.master = recv;
|
|
this.subjects = [this.client];
|
|
|
|
this.connectToServer();
|
|
|
|
} catch (x:Error) {
|
|
// Can't become the master.
|
|
}
|
|
|
|
if (!this.master) {
|
|
this.send.send('aphlict_master', 'becomeLoyal', this.client);
|
|
this.remainLoyal();
|
|
}
|
|
}
|
|
setTimeout(this.usurp, this.frequency);
|
|
}
|
|
|
|
public function connectToServer():void {
|
|
var socket:Socket = new Socket();
|
|
|
|
socket.addEventListener(Event.CONNECT, didConnectSocket);
|
|
socket.addEventListener(Event.CLOSE, didCloseSocket);
|
|
socket.addEventListener(IOErrorEvent.IO_ERROR, didErrorSocket);
|
|
socket.addEventListener(ProgressEvent.SOCKET_DATA, didReceiveSocket);
|
|
|
|
socket.connect('127.0.0.1', 2600);
|
|
|
|
this.readBuffer = new ByteArray();
|
|
this.socket = socket;
|
|
}
|
|
|
|
private function didConnectSocket(event:Event):void {
|
|
this.log("Connect!");
|
|
}
|
|
|
|
private function didCloseSocket(event:Event):void {
|
|
this.log("Close!");
|
|
}
|
|
|
|
private function didErrorSocket(event:Event):void {
|
|
this.log("Error!");
|
|
}
|
|
|
|
private function didReceiveSocket(event:Event):void {
|
|
var b:ByteArray = this.readBuffer;
|
|
this.socket.readBytes(b, b.length);
|
|
|
|
do {
|
|
b = this.readBuffer;
|
|
b.position = 0;
|
|
|
|
if (b.length <= 8) {
|
|
break;
|
|
}
|
|
|
|
var msg_len:Number = parseInt(b.readUTFBytes(8), 10);
|
|
if (b.length >= msg_len + 8) {
|
|
var bytes:String = b.readUTFBytes(msg_len);
|
|
var data:Object = JSON.deserialize(bytes);
|
|
var t:ByteArray = new ByteArray();
|
|
t.writeBytes(b, msg_len + 8);
|
|
this.readBuffer = t;
|
|
|
|
for (var ii:Number = 0; ii < this.subjects.length; ii++) {
|
|
this.send.send(this.subjects[ii], 'receiveMessage', data);
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
} while (true);
|
|
|
|
}
|
|
|
|
public function remainLoyal():void {
|
|
this.loyalUntil = new Date().getTime() + (2 * this.frequency);
|
|
}
|
|
|
|
public function becomeLoyal(subject:String):void {
|
|
this.subjects.push(subject);
|
|
}
|
|
|
|
public function receiveMessage(msg:Object):void {
|
|
this.log("Received message!");
|
|
}
|
|
|
|
public function log(msg:String):void {
|
|
ExternalInterface.call('console.log', msg);
|
|
}
|
|
|
|
}
|
|
|
|
} |