[connect] add package
This commit is contained in:
52
src/main/hw/connect/BaseConnection.hx
Executable file
52
src/main/hw/connect/BaseConnection.hx
Executable file
@@ -0,0 +1,52 @@
|
||||
package hw.connect;
|
||||
|
||||
import haxe.io.Bytes;
|
||||
import hw.connect.IConnection;
|
||||
import hw.signal.Signal;
|
||||
import promhx.Deferred;
|
||||
import promhx.Promise;
|
||||
import protohx.Message;
|
||||
|
||||
class BaseConnection<O:Message, I:Message> implements IConnection<O, I> {
|
||||
public var handler(default, null):Signal<ConnectionEvent>;
|
||||
public var sendHandler(default, null):Signal<O>;
|
||||
public var receiveHandler(default, null):Signal<I>;
|
||||
public var connected(default, null):Bool;
|
||||
public var queue(default, null):PacketQueue<I>;
|
||||
|
||||
private var connectDeferred:Deferred<IConnection<O, I>>;
|
||||
|
||||
public function new(inputFactory:Class<I>) {
|
||||
queue = new PacketQueue<I>(inputFactory);
|
||||
handler = new Signal<ConnectionEvent>();
|
||||
sendHandler = new Signal<O>();
|
||||
receiveHandler = new Signal<I>();
|
||||
}
|
||||
|
||||
public function connect():Promise<IConnection<O, I>> {
|
||||
throw "Not implemented";
|
||||
}
|
||||
|
||||
public function disconnect():Void {
|
||||
throw "Not implemented";
|
||||
}
|
||||
|
||||
public function pushData(bytes:Bytes):Void {
|
||||
#if proto_debug L.d('Proto', 'pushData: ${bytes.length}'); #end
|
||||
queue.addBytes(bytes);
|
||||
while (queue.hasMsg()) {
|
||||
var packet:I = queue.popMsg();
|
||||
receive(packet);
|
||||
}
|
||||
}
|
||||
|
||||
public function send(packet:O):Void {
|
||||
#if proto_debug L.d('Proto', 'send: ${packet}'); #end
|
||||
sendHandler.emit(packet);
|
||||
}
|
||||
|
||||
public function receive(packet:I):Void {
|
||||
#if proto_debug L.d('Proto', 'receive: ${packet}'); #end
|
||||
receiveHandler.emit(packet);
|
||||
}
|
||||
}
|
||||
16
src/main/hw/connect/ConnectionFactory.hx
Normal file
16
src/main/hw/connect/ConnectionFactory.hx
Normal file
@@ -0,0 +1,16 @@
|
||||
package hw.connect;
|
||||
|
||||
import protohx.Message;
|
||||
|
||||
class ConnectionFactory {
|
||||
|
||||
public static function buildClientConnection<O:Message, I:Message>(host: String, port:Int, inputFactory:Class<I>):IConnection<O, I> {
|
||||
#if flash
|
||||
return new hw.connect.flash.FlashConnection<O, I>(host, port, inputFactory);
|
||||
#elseif html5
|
||||
return new hw.connect.js.JsConnection<O, I>(host, port + (hw.connect.js.JsConnection.isSecured() ? 1 : 0), inputFactory);
|
||||
#else
|
||||
return new hw.connect.desktop.DesktopConnection<O, I>(host, port, inputFactory);
|
||||
#end
|
||||
}
|
||||
}
|
||||
24
src/main/hw/connect/IConnection.hx
Executable file
24
src/main/hw/connect/IConnection.hx
Executable file
@@ -0,0 +1,24 @@
|
||||
package hw.connect;
|
||||
|
||||
import haxe.io.Bytes;
|
||||
import hw.signal.Signal;
|
||||
import promhx.Promise;
|
||||
import protohx.Message;
|
||||
|
||||
enum ConnectionEvent {
|
||||
CONNECTED;
|
||||
DISCONNECTED;
|
||||
ERROR(error:Dynamic);
|
||||
}
|
||||
|
||||
interface IConnection<O:Message, I:Message> {
|
||||
public var connected(default, null):Bool;
|
||||
public var handler(default, null):Signal<ConnectionEvent>;
|
||||
public var sendHandler(default, null):Signal<O>;
|
||||
public var receiveHandler(default, null):Signal<I>;
|
||||
|
||||
public function connect():Promise<IConnection<O, I>>;
|
||||
public function disconnect():Void;
|
||||
public function send(packet:O):Void;
|
||||
public function pushData(bytes:Bytes):Void;
|
||||
}
|
||||
70
src/main/hw/connect/PacketQueue.hx
Executable file
70
src/main/hw/connect/PacketQueue.hx
Executable file
@@ -0,0 +1,70 @@
|
||||
package hw.connect;
|
||||
|
||||
import haxe.io.Bytes;
|
||||
import haxe.io.BytesBuffer;
|
||||
import haxe.io.BytesInput;
|
||||
import protohx.Message;
|
||||
|
||||
class PacketQueue<P:Message> {
|
||||
|
||||
public var packetClass(default, null):Class<P>;
|
||||
|
||||
private var buffer:BytesBuffer;
|
||||
private var msgs:List<P>;
|
||||
|
||||
public function new(packetClass:Class<P>) {
|
||||
this.packetClass = packetClass;
|
||||
msgs = new List<P>();
|
||||
buffer = new BytesBuffer();
|
||||
}
|
||||
|
||||
public inline function hasMsg():Bool {
|
||||
return !msgs.isEmpty();
|
||||
}
|
||||
|
||||
public inline function popMsg():P {
|
||||
return msgs.pop();
|
||||
}
|
||||
|
||||
public inline function addMsg(msg:P):Void {
|
||||
msgs.add(msg);
|
||||
}
|
||||
|
||||
private function readPackage():Null<P> {
|
||||
var bytes = buffer.getBytes();
|
||||
var input = new BytesInput(bytes);
|
||||
input.bigEndian = false;
|
||||
if (input.length > 1) {
|
||||
var packetSize = input.readUInt16();
|
||||
if (input.length >= packetSize + 2) {
|
||||
var packet:P = Type.createInstance(packetClass, []);
|
||||
try {
|
||||
packet.mergeFrom(input.read(packetSize));
|
||||
buffer = new BytesBuffer();
|
||||
buffer.add(input.read(input.length - (packetSize + 2)));
|
||||
return packet;
|
||||
} catch (error:Dynamic) {
|
||||
L.w("PacketQueue", "readPackage ", error);
|
||||
buffer = new BytesBuffer();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
buffer = new BytesBuffer();
|
||||
buffer.add(bytes);
|
||||
return null;
|
||||
}
|
||||
|
||||
public function addBytes(bytes:Bytes):Void {
|
||||
buffer.add(bytes);
|
||||
var packet = readPackage();
|
||||
while (packet != null) {
|
||||
msgs.add(packet);
|
||||
packet = readPackage();
|
||||
}
|
||||
}
|
||||
|
||||
public function clean():Void {
|
||||
buffer = new BytesBuffer();
|
||||
}
|
||||
}
|
||||
20
src/main/hw/connect/PacketUtil.hx
Normal file
20
src/main/hw/connect/PacketUtil.hx
Normal file
@@ -0,0 +1,20 @@
|
||||
package hw.connect;
|
||||
|
||||
import haxe.io.Bytes;
|
||||
import haxe.io.BytesOutput;
|
||||
import protohx.Message;
|
||||
|
||||
class PacketUtil {
|
||||
|
||||
public static function fromBytes<P:Message>(bytes:Bytes, factory:Class<P>):P {
|
||||
var packet:P = Type.createInstance(factory, []);
|
||||
packet.mergeFrom(bytes);
|
||||
return packet;
|
||||
}
|
||||
|
||||
public static function toBytes<P:Message>(packet:P):Bytes {
|
||||
var out = new BytesOutput();
|
||||
packet.writeTo(out);
|
||||
return out.getBytes();
|
||||
}
|
||||
}
|
||||
24
src/main/hw/connect/WebSocketTools.hx
Normal file
24
src/main/hw/connect/WebSocketTools.hx
Normal file
@@ -0,0 +1,24 @@
|
||||
package hw.connect;
|
||||
|
||||
import haxe.io.BytesOutput;
|
||||
|
||||
|
||||
class WebSocketTools {
|
||||
|
||||
public static function packet2string(packet:Message):String {
|
||||
var b = new BytesOutput();
|
||||
packet.writeTo(b);
|
||||
var data = b.getBytes();
|
||||
var res = new BytesOutput();
|
||||
//res.writeUInt16(data.length);
|
||||
res.write(data);
|
||||
return Base64.encodeBase64(res.getBytes());
|
||||
}
|
||||
|
||||
public static function string2packet<P:Message>(data:String, packetClass:Class<P>):P {
|
||||
var bytes = Base64.decodeBase64(data);
|
||||
var packet:P = Type.createInstance(packetClass, []);
|
||||
packet.mergeFrom(bytes);
|
||||
return packet;
|
||||
}
|
||||
}
|
||||
76
src/main/hw/connect/desktop/DesktopConnection.hx
Normal file
76
src/main/hw/connect/desktop/DesktopConnection.hx
Normal file
@@ -0,0 +1,76 @@
|
||||
package hw.connect.desktop;
|
||||
|
||||
import cpp.vm.Thread;
|
||||
import haxe.Timer;
|
||||
import hw.connect.IConnection;
|
||||
import promhx.Deferred;
|
||||
import promhx.Promise;
|
||||
import protohx.Message;
|
||||
import sys.net.Host;
|
||||
import sys.net.Socket;
|
||||
|
||||
class DesktopConnection<O:Message, I:Message> extends BaseConnection<O, I> {
|
||||
|
||||
private var host:String;
|
||||
private var port:Int;
|
||||
private var socket:Socket;
|
||||
private var reader:Thread;
|
||||
|
||||
public function new(host:String, port:Int, inputFactory:Class<I>) {
|
||||
super(inputFactory);
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
connected = false;
|
||||
socket = new Socket();
|
||||
socket.setFastSend(true);
|
||||
socket.output.bigEndian = false;
|
||||
socket.input.bigEndian = false;
|
||||
sendHandler.connect(_send);
|
||||
}
|
||||
|
||||
override public function connect():Promise<IConnection<O, I>> {
|
||||
connectDeferred = new Deferred();
|
||||
try {
|
||||
if (connected) {
|
||||
connectDeferred.resolve(this);
|
||||
} else {
|
||||
socket.connect(new Host(host), port);
|
||||
connected = true;
|
||||
reader = Thread.create(_read);
|
||||
connectDeferred.resolve(this);
|
||||
handler.emit(ConnectionEvent.CONNECTED);
|
||||
}
|
||||
} catch (error:Dynamic) {
|
||||
handler.emit(ConnectionEvent.ERROR(error));
|
||||
Timer.delay(function() connectDeferred.throwError(error), 1);
|
||||
}
|
||||
return connectDeferred.promise();
|
||||
}
|
||||
|
||||
override public function disconnect():Void {
|
||||
socket.close();
|
||||
connected = false;
|
||||
handler.emit(DISCONNECTED);
|
||||
}
|
||||
|
||||
private function _read():Void {
|
||||
try {
|
||||
while (connected) {
|
||||
socket.waitForRead();
|
||||
var size = socket.input.readUInt16();
|
||||
var data = socket.input.read(size);
|
||||
var packet:I = PacketUtil.fromBytes(data, queue.packetClass);
|
||||
receiveHandler.emit(packet);
|
||||
}
|
||||
} catch (error:Dynamic) {
|
||||
handler.emit(ERROR(error));
|
||||
}
|
||||
}
|
||||
|
||||
private function _send(packet:O):Void {
|
||||
var bytes = PacketUtil.toBytes(packet);
|
||||
socket.output.writeUInt16(bytes.length);
|
||||
socket.output.write(bytes);
|
||||
socket.output.flush();
|
||||
}
|
||||
}
|
||||
17
src/main/hw/connect/fake/FakeConnection.hx
Normal file
17
src/main/hw/connect/fake/FakeConnection.hx
Normal file
@@ -0,0 +1,17 @@
|
||||
package hw.connect.fake;
|
||||
|
||||
import promhx.Promise;
|
||||
import protohx.Message;
|
||||
|
||||
class FakeConnection<O:Message, I:Message> extends BaseConnection<O, I> {
|
||||
|
||||
override public function connect():Promise<IConnection<O, I>> {
|
||||
handler.emit(ConnectionEvent.CONNECTED);
|
||||
var promise:Promise<IConnection<O, I>> = cast Promise.promise(this);
|
||||
return promise;
|
||||
}
|
||||
|
||||
override public function disconnect():Void {
|
||||
handler.emit(ConnectionEvent.DISCONNECTED);
|
||||
}
|
||||
}
|
||||
89
src/main/hw/connect/flash/FlashConnection.hx
Executable file
89
src/main/hw/connect/flash/FlashConnection.hx
Executable file
@@ -0,0 +1,89 @@
|
||||
package hw.connect.flash;
|
||||
|
||||
import flash.events.ErrorEvent;
|
||||
import flash.events.Event;
|
||||
import flash.events.IOErrorEvent;
|
||||
import flash.events.ProgressEvent;
|
||||
import flash.events.SecurityErrorEvent;
|
||||
import flash.net.Socket;
|
||||
import flash.utils.Endian;
|
||||
import haxe.io.Bytes;
|
||||
import promhx.Deferred;
|
||||
import promhx.Promise;
|
||||
import protohx.Message;
|
||||
import hw.connect.IConnection;
|
||||
|
||||
class FlashConnection<O:Message, I:Message> extends BaseConnection<O, I> {
|
||||
|
||||
private var host:String;
|
||||
private var port:Int;
|
||||
private var socket:Socket;
|
||||
|
||||
public function new(host:String, port:Int, inputFactory:Class<I>) {
|
||||
super(inputFactory);
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
connected = false;
|
||||
socket = new Socket();
|
||||
socket.addEventListener(IOErrorEvent.IO_ERROR, onError);
|
||||
socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
|
||||
socket.addEventListener(Event.CLOSE, onClose);
|
||||
socket.addEventListener(Event.CONNECT, onConnect);
|
||||
socket.addEventListener(ProgressEvent.SOCKET_DATA, onSocketData);
|
||||
socket.endian = Endian.LITTLE_ENDIAN;
|
||||
sendHandler.connect(_send);
|
||||
}
|
||||
|
||||
override public function connect():Promise<IConnection<O, I>> {
|
||||
socket.connect(host, port);
|
||||
connectDeferred = new Deferred();
|
||||
return connectDeferred.promise();
|
||||
}
|
||||
|
||||
override public function disconnect():Void {
|
||||
if (socket.connected) {
|
||||
socket.close();
|
||||
connected = false;
|
||||
handler.emit(ConnectionEvent.DISCONNECTED);
|
||||
}
|
||||
}
|
||||
|
||||
private function onError(event:ErrorEvent):Void {
|
||||
socket.close();
|
||||
connected = false;
|
||||
handler.emit(ConnectionEvent.ERROR(event));
|
||||
if (connectDeferred != null) {
|
||||
connectDeferred.throwError(event);
|
||||
connectDeferred = null;
|
||||
}
|
||||
}
|
||||
|
||||
private function onConnect(_):Void {
|
||||
connected = true;
|
||||
handler.emit(ConnectionEvent.CONNECTED);
|
||||
if (connectDeferred != null) {
|
||||
connectDeferred.resolve(this);
|
||||
connectDeferred = null;
|
||||
}
|
||||
}
|
||||
|
||||
private function onClose(_):Void {
|
||||
socket.close();
|
||||
connected = false;
|
||||
handler.emit(ConnectionEvent.DISCONNECTED);
|
||||
}
|
||||
|
||||
private function onSocketData(_):Void {
|
||||
var data = new flash.utils.ByteArray();
|
||||
socket.readBytes(data);
|
||||
var bytes = Bytes.ofData(data);
|
||||
pushData(bytes);
|
||||
}
|
||||
|
||||
private function _send(packet:O):Void {
|
||||
var bytes = PacketUtil.toBytes(packet);
|
||||
socket.writeShort(bytes.length);
|
||||
socket.writeBytes(bytes.getData());
|
||||
socket.flush();
|
||||
}
|
||||
}
|
||||
91
src/main/hw/connect/js/JsConnection.hx
Normal file
91
src/main/hw/connect/js/JsConnection.hx
Normal file
@@ -0,0 +1,91 @@
|
||||
package hw.connect.js;
|
||||
|
||||
import haxe.io.Bytes;
|
||||
import hw.connect.IConnection;
|
||||
import js.Browser;
|
||||
import js.html.BinaryType;
|
||||
import js.html.WebSocket;
|
||||
import promhx.Deferred;
|
||||
import promhx.Promise;
|
||||
import protohx.Message;
|
||||
|
||||
class JsConnection<O:Message, I:Message> extends BaseConnection<O, I> {
|
||||
|
||||
private var host:String;
|
||||
private var port:Int;
|
||||
private var socket:WebSocket;
|
||||
|
||||
public function new(host:String, port:Int, inputFactory:Class<I>) {
|
||||
super(inputFactory);
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
connected = false;
|
||||
}
|
||||
|
||||
public static function isSecured():Bool {
|
||||
return Browser.location.protocol == "https:";
|
||||
}
|
||||
|
||||
private function buildSocket(host:String, port:Int):WebSocket {
|
||||
var protocol = isSecured() ? "wss:" : "ws:";
|
||||
return new WebSocket('$protocol//$host:$port');
|
||||
}
|
||||
|
||||
override public function connect():Promise<IConnection<O, I>> {
|
||||
var self = this;
|
||||
socket = buildSocket(host, port);
|
||||
socket.binaryType = BinaryType.ARRAYBUFFER;
|
||||
socket.onopen = this.onConnect;
|
||||
socket.onclose = this.onClose;
|
||||
socket.onerror = this.onError;
|
||||
socket.onmessage = this.onSocketData;
|
||||
connectDeferred = new Deferred();
|
||||
return connectDeferred.promise();
|
||||
}
|
||||
|
||||
override public function disconnect():Void {
|
||||
socket.close(1000);
|
||||
connected = false;
|
||||
}
|
||||
|
||||
private function onError(event:Dynamic):Void {
|
||||
socket.close(1000);
|
||||
connected = false;
|
||||
handler.emit(ConnectionEvent.ERROR(event));
|
||||
}
|
||||
|
||||
private function onConnect(_):Void {
|
||||
connected = true;
|
||||
handler.emit(ConnectionEvent.CONNECTED);
|
||||
connectDeferred.resolve(this);
|
||||
}
|
||||
|
||||
private function onClose(_):Void {
|
||||
socket.close();
|
||||
connected = false;
|
||||
handler.emit(ConnectionEvent.DISCONNECTED);
|
||||
}
|
||||
|
||||
private function onSocketData(event:Dynamic):Void {
|
||||
var packet:I = null;
|
||||
try {
|
||||
var bytes = Bytes.ofData(event.data);
|
||||
packet = PacketUtil.fromBytes(bytes, queue.packetClass);
|
||||
} catch (error:Dynamic) {
|
||||
handler.emit(ConnectionEvent.ERROR(error));
|
||||
}
|
||||
if (packet != null) {
|
||||
receive(packet);
|
||||
}
|
||||
}
|
||||
|
||||
override public function send(packet:O):Void {
|
||||
if (connected) {
|
||||
super.send(packet);
|
||||
var bytes = PacketUtil.toBytes(packet);
|
||||
socket.send(bytes.getData());
|
||||
} else {
|
||||
L.w("Connection", "closed");
|
||||
}
|
||||
}
|
||||
}
|
||||
57
src/main/hw/connect/neko/NekoConnection.hx
Executable file
57
src/main/hw/connect/neko/NekoConnection.hx
Executable file
@@ -0,0 +1,57 @@
|
||||
package hw.connect.neko;
|
||||
|
||||
import haxe.Timer;
|
||||
import protohx.Message;
|
||||
import sys.net.Socket;
|
||||
|
||||
class NekoConnection<O:Message, I:Message> extends BaseConnection<O, I> {
|
||||
|
||||
public var socket(default, null):Socket;
|
||||
|
||||
private var sendQueue:Array<O>;
|
||||
private var timer:Timer;
|
||||
|
||||
public function new(socket:Socket, i:Class<I>) {
|
||||
super(i);
|
||||
this.socket = socket;
|
||||
socket.setFastSend(true);
|
||||
socket.output.bigEndian = false;
|
||||
socket.input.bigEndian = false;
|
||||
sendHandler.connect(pushPacket);
|
||||
sendQueue = [];
|
||||
timer = new Timer(1);
|
||||
timer.run = sendRun;
|
||||
}
|
||||
|
||||
private function sendPacket(packet:O):Void {
|
||||
try {
|
||||
var bytes = PacketUtil.toBytes(packet);
|
||||
socket.output.writeUInt16(bytes.length);
|
||||
socket.output.write(bytes);
|
||||
socket.output.flush();
|
||||
} catch (error:Dynamic) {
|
||||
L.e('Proto', 'Error send packet: ${packet}', error);
|
||||
}
|
||||
}
|
||||
|
||||
private function sendRun():Void {
|
||||
if (sendQueue.length > 0) {
|
||||
for (packet in sendQueue) {
|
||||
sendPacket(packet);
|
||||
}
|
||||
sendQueue = [];
|
||||
}
|
||||
}
|
||||
|
||||
private function pushPacket(packet:O):Void {
|
||||
sendQueue.push(packet);
|
||||
}
|
||||
|
||||
override public function disconnect():Void {
|
||||
if (timer != null) {
|
||||
timer.stop();
|
||||
timer = null;
|
||||
}
|
||||
super.disconnect();
|
||||
}
|
||||
}
|
||||
178
src/main/hw/connect/neko/NekoWSConnection.hx
Normal file
178
src/main/hw/connect/neko/NekoWSConnection.hx
Normal file
@@ -0,0 +1,178 @@
|
||||
package hw.connect.neko;
|
||||
|
||||
import haxe.crypto.BaseCode;
|
||||
import haxe.crypto.Sha1;
|
||||
import haxe.io.Bytes;
|
||||
import haxe.io.BytesBuffer;
|
||||
import protohx.Message;
|
||||
import sys.net.Socket;
|
||||
|
||||
class NekoWSConnection<O:Message, I:Message> extends NekoConnection<O, I> {
|
||||
|
||||
private var opened:Bool;
|
||||
|
||||
public function new(socket:Socket, i:Class<I>) {
|
||||
super(socket, i);
|
||||
opened = false;
|
||||
}
|
||||
|
||||
override private function sendPacket(packet:O):Void {
|
||||
var data = PacketUtil.toBytes(packet);
|
||||
writeData(data, socket);
|
||||
}
|
||||
|
||||
override public function pushData(bytes:Bytes):Void {
|
||||
if (!opened) {
|
||||
var str:String = bytes.getString(0, bytes.length);
|
||||
if (StringTools.startsWith(str, "GET")) {
|
||||
var r = ~/Sec-WebSocket-Key:\s*([A-z0-9=+\/]+)/;
|
||||
r.match(str);
|
||||
opened = true;
|
||||
sendServerHandShake(socket, r.matched(1));
|
||||
}
|
||||
} else {
|
||||
var data = parseData(bytes);
|
||||
if (data != null) {
|
||||
var packet:I = PacketUtil.fromBytes(data, queue.packetClass);
|
||||
receive(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function encodeBase64(content:String):String {
|
||||
var suffix = switch (content.length % 3)
|
||||
{
|
||||
case 2: "=";
|
||||
case 1: "==";
|
||||
default: "";
|
||||
};
|
||||
return BaseCode.encode(content, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") + suffix;
|
||||
}
|
||||
|
||||
private function hex2data(hex:String):String {
|
||||
var data = "";
|
||||
for (i in 0...Std.int(hex.length / 2)) {
|
||||
data += String.fromCharCode(Std.parseInt("0x" + hex.substr(i * 2, 2)));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private function sendServerHandShake(socket:sys.net.Socket, inpKey:String) {
|
||||
var outKey = encodeBase64(hex2data(Sha1.encode(StringTools.trim(inpKey) + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")));
|
||||
|
||||
var s = "HTTP/1.1 101 Switching Protocols\r\n"
|
||||
+ "Upgrade: websocket\r\n"
|
||||
+ "Connection: Upgrade\r\n"
|
||||
+ "Sec-WebSocket-Accept: " + outKey + "\r\n"
|
||||
+ "\r\n";
|
||||
|
||||
socket.output.writeString(s);
|
||||
}
|
||||
|
||||
private function writeData(data:Bytes, socket:sys.net.Socket, isServer = true):Void {
|
||||
socket.output.writeByte(130);
|
||||
|
||||
var len = 0;
|
||||
if (data.length < 126) len = data.length;
|
||||
else if (data.length < 65536) len = 126;
|
||||
else len = 127;
|
||||
|
||||
socket.output.writeByte(len | (!isServer ? 0x80 : 0x00));
|
||||
|
||||
if (data.length >= 126) {
|
||||
if (data.length < 65536) {
|
||||
socket.output.writeByte((data.length >> 8) & 0xFF);
|
||||
socket.output.writeByte(data.length & 0xFF);
|
||||
}
|
||||
else {
|
||||
socket.output.writeByte((data.length >> 24) & 0xFF);
|
||||
socket.output.writeByte((data.length >> 16) & 0xFF);
|
||||
socket.output.writeByte((data.length >> 8) & 0xFF);
|
||||
socket.output.writeByte(data.length & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
if (isServer) {
|
||||
socket.output.writeBytes(data, 0, data.length);
|
||||
}
|
||||
else {
|
||||
var mask = [ Std.random(256), Std.random(256), Std.random(256), Std.random(256) ];
|
||||
socket.output.writeByte(mask[0]);
|
||||
socket.output.writeByte(mask[1]);
|
||||
socket.output.writeByte(mask[2]);
|
||||
socket.output.writeByte(mask[3]);
|
||||
var maskedData = new BytesBuffer();
|
||||
for (i in 0...data.length) {
|
||||
maskedData.addByte(data.get(i) ^ mask[i % 4]);
|
||||
}
|
||||
socket.output.writeBytes(maskedData.getBytes(), 0, maskedData.length);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseData(bytes:Bytes):Bytes {
|
||||
var p = 0;
|
||||
var opcode = bytes.get(p++);
|
||||
|
||||
if (opcode == 0x00) {
|
||||
var data = new BytesBuffer();
|
||||
var b:Int;
|
||||
while ((b = bytes.get(p++)) != 0xFF) {
|
||||
data.addByte(b);
|
||||
}
|
||||
return data.getBytes();
|
||||
}
|
||||
|
||||
// 130 = binary data
|
||||
if (opcode == 130) {
|
||||
var len = bytes.get(p++);
|
||||
|
||||
// mask
|
||||
if (len & 0x80 != 0) {
|
||||
len &= 0x7F;
|
||||
|
||||
if (len == 126) {
|
||||
var b2 = bytes.get(p++);
|
||||
var b3 = bytes.get(p++);
|
||||
len = (b2 << 8) + b3;
|
||||
}
|
||||
else if (len == 127) {
|
||||
var b2 = bytes.get(p++);
|
||||
var b3 = bytes.get(p++);
|
||||
var b4 = bytes.get(p++);
|
||||
var b5 = bytes.get(p++);
|
||||
len = (b2 << 24) + (b3 << 16) + (b4 << 8) + b5;
|
||||
}
|
||||
|
||||
//Lib.println("len = " + len);
|
||||
|
||||
// direct array init not work corectly!
|
||||
var mask = [];
|
||||
mask.push(bytes.get(p++));
|
||||
mask.push(bytes.get(p++));
|
||||
mask.push(bytes.get(p++));
|
||||
mask.push(bytes.get(p++));
|
||||
|
||||
//Lib.println("mask = " + mask);
|
||||
|
||||
var data = new BytesBuffer();
|
||||
for (i in 0...len) {
|
||||
data.addByte(bytes.get(p++) ^ mask[i % 4]);
|
||||
}
|
||||
|
||||
//Lib.println("readed = " + data.toString());
|
||||
return data.getBytes();
|
||||
} else {
|
||||
throw "Expected masked data.";
|
||||
}
|
||||
}
|
||||
|
||||
if (opcode == 136) {
|
||||
//socket.close();
|
||||
opened = false;
|
||||
return null;
|
||||
} else {
|
||||
throw "Unsupported websocket opcode: " + opcode;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
9
src/main/hw/connect/session/ISession.hx
Normal file
9
src/main/hw/connect/session/ISession.hx
Normal file
@@ -0,0 +1,9 @@
|
||||
package hw.connect.session;
|
||||
|
||||
import haxe.io.Bytes;
|
||||
|
||||
interface ISession {
|
||||
public var id(default, null):Int;
|
||||
public function pushData(bytes:Bytes):Void;
|
||||
public function disconnect():Void;
|
||||
}
|
||||
71
src/main/hw/connect/session/ProtoSession.hx
Normal file
71
src/main/hw/connect/session/ProtoSession.hx
Normal file
@@ -0,0 +1,71 @@
|
||||
package hw.connect.session;
|
||||
|
||||
import haxe.io.Bytes;
|
||||
import hw.connect.neko.NekoConnection;
|
||||
import hw.connect.neko.NekoWSConnection;
|
||||
import protohx.Message;
|
||||
import sys.net.Socket;
|
||||
|
||||
class ProtoSession<O:Message, I:Message> implements ISession {
|
||||
private static inline var TAG = "Session";
|
||||
|
||||
private static var POLICY_FILE:String = [
|
||||
"<?xml version=\"1.0\"?>",
|
||||
"<!DOCTYPE cross-domain-policy SYSTEM \"http://www.adobe.com/xml/dtds/cross-domain-policy.dtd\">",
|
||||
"<cross-domain-policy>",
|
||||
"<site-control permitted-cross-domain-policies=\"master-only\"/>",
|
||||
"<allow-access-from domain=\"*\" to-ports=\"*\"/>",
|
||||
"</cross-domain-policy>"
|
||||
].join("\n");
|
||||
|
||||
private static var idCounter:Int = 0;
|
||||
|
||||
public var id(default, null):Int;
|
||||
public var connection(default, null):IConnection<O, I>;
|
||||
private var socket:Socket;
|
||||
private var request:Class<I>;
|
||||
|
||||
public function new(socket:Socket, request:Class<I>) {
|
||||
this.id = ++idCounter;
|
||||
this.socket = socket;
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
private function buildConnection(bytes:Bytes):IConnection<O, I> {
|
||||
var str:String = bytes.getString(0, bytes.length);
|
||||
if (str == "<policy-file-request/>" + String.fromCharCode(0)) {
|
||||
L.d(TAG, "policy-file-request");
|
||||
socket.output.writeString(POLICY_FILE + String.fromCharCode(0));
|
||||
socket.output.flush();
|
||||
return null;
|
||||
}
|
||||
if (StringTools.startsWith(str, "GET")) {
|
||||
connection = new NekoWSConnection<O, I>(socket, request);
|
||||
} else {
|
||||
connection = new NekoConnection<O, I>(socket, request);
|
||||
}
|
||||
connection.receiveHandler.connect(onRequest);
|
||||
return connection;
|
||||
}
|
||||
|
||||
public function send(packet:O):Void {
|
||||
connection.send(packet);
|
||||
}
|
||||
|
||||
public function pushData(bytes:Bytes):Void {
|
||||
if (connection == null) {
|
||||
connection = buildConnection(bytes);
|
||||
}
|
||||
if (connection != null) {
|
||||
connection.pushData(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
public function disconnect():Void {
|
||||
connection.disconnect();
|
||||
}
|
||||
|
||||
private function onRequest(request:I):Void {
|
||||
L.d(TAG, 'onRequest: ${request}');
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
package hw.geom;
|
||||
|
||||
import flash.geom.Point as FlashPoint;
|
||||
|
||||
abstract Point(Array<Float>) {
|
||||
public var x(get, set):Float;
|
||||
public var y(get, set):Float;
|
||||
@@ -33,8 +31,4 @@ abstract Point(Array<Float>) {
|
||||
public function toString():String {
|
||||
return 'Point{x=$x,y=$y}';
|
||||
}
|
||||
|
||||
@:from public static function fromFlashPoint(value:FlashPoint):Point {
|
||||
return new Point(value.x, value.y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ class TraceLogger extends BaseLogger {
|
||||
#elseif php
|
||||
untyped __call__('_hx_trace', v);
|
||||
#elseif cpp
|
||||
Stdio.printf(ConstCharStar.fromString(Std.string(v)));
|
||||
Stdio.printf(ConstCharStar.fromString(Std.string(v)+'\n'));
|
||||
//untyped __trace(v, null);
|
||||
#elseif cs
|
||||
cs.system.Console.WriteLine(v);
|
||||
|
||||
Reference in New Issue
Block a user