Explorar o código

finished remoting changes

Nicolas Cannasse %!s(int64=17) %!d(string=hai) anos
pai
achega
142d9153ee

+ 27 - 26
std/haxe/remoting/NekoSocketConnection.hx

@@ -23,62 +23,63 @@
  * DAMAGE.
  */
 package haxe.remoting;
-import haxe.remoting.SocketProtocol;
 
-class NekoSocketConnection extends Connection {
+class NekoSocketConnection implements Connection, implements Dynamic<Connection> {
 
-	var __r : neko.net.RemotingServer;
+	var __path : Array<String>;
+	var __proto : SocketProtocol;
 
-	public override function __resolve(field) : Connection {
-		var s = new NekoSocketConnection(__data,__path.copy());
-		s.__r = __r;
-		s.__path.push(field);
+	function new(proto,path) {
+		__proto = proto;
+		__path = path;
+	}
+
+	public function resolve( name ) : Connection {
+		var s = new NekoSocketConnection(__proto,__path.copy());
+		s.__path.push(name);
 		return s;
 	}
 
-	override public function call( params : Array<Dynamic> ) : Dynamic {
-		var proto = getProtocol();
+	public function call( params : Array<Dynamic> ) : Dynamic {
+		var proto = __proto;
 		proto.sendRequest(__path,params);
 		while( true ) {
 			var data = proto.readMessage();
 			if( proto.isRequest(data) ) {
-				if( __r == null )
+				if( proto.context == null )
 					throw "Request received";
-				proto.processRequest(data,__r.resolvePath,onRequestError);
+				proto.processRequest(data,onRequestError);
 				continue;
 			}
 			return proto.processAnswer(data);
 		}
-		return null;
+		return null; // never reached
 	}
 
 	public function processRequest() {
-		var proto = getProtocol();
-		if( __r == null )
-			throw "No RemotingServer defined";
-		var data = proto.readMessage();
-		proto.processRequest(data,__r.resolvePath,onRequestError);
+		if( __proto.context == null )
+			throw "Can't process request";
+		var data = __proto.readMessage();
+		__proto.processRequest(data,onRequestError);
 	}
 
-	public function onRequestError( path : Array<String>, method : String, args : Array<Dynamic>, exc : Dynamic ) {
+	public function onRequestError( path : Array<String>, args : Array<Dynamic>, exc : Dynamic ) {
 	}
 
 	public function setProtocol( p : SocketProtocol ) {
-		__data = p;
+		__proto = p;
 	}
 
 	public function getProtocol() : SocketProtocol {
-		return __data;
+		return __proto;
 	}
 
-	public function closeConnection() {
-		try getProtocol().socket.close() catch( e : Dynamic ) { };
+	public function close() {
+		try __proto.socket.close() catch( e : Dynamic ) { };
 	}
 
-	public static function socketConnect( s : neko.net.Socket, ?r : neko.net.RemotingServer ) {
-		var sc = new NekoSocketConnection(new SocketProtocol(s),[]);
-		sc.__r = r;
-		return sc;
+	public static function create( s : neko.net.Socket, ?ctx : Context ) {
+		return new NekoSocketConnection(new SocketProtocol(s,ctx),[]);
 	}
 
 }

+ 78 - 72
std/haxe/remoting/SocketConnection.hx

@@ -23,134 +23,141 @@
  * DAMAGE.
  */
 package haxe.remoting;
-import haxe.remoting.SocketProtocol;
+import haxe.remoting.SocketProtocol.Socket;
 
-class SocketConnection extends AsyncConnection {
+class SocketConnection implements AsyncConnection, implements Dynamic<AsyncConnection> {
 
-	var __funs : List<Dynamic -> Void>;
-	#if neko
-	var __r : neko.net.RemotingServer;
-	#end
+	var __path : Array<String>;
+	var __data : {
+		protocol : SocketProtocol,
+		results : List<{ onResult : Dynamic -> Void, onError : Dynamic -> Void }>,
+		log : Array<String> -> Array<Dynamic> -> Dynamic -> Void,
+		error : Dynamic -> Void,
+		#if !flash9
+		#if (flash || js)
+		queue : haxe.TimerQueue,
+		#end
+		#end
+	};
+
+	function new(data,path) {
+		__data = data;
+		__path = path;
+	}
 
-	public override function __resolve(field) : AsyncConnection {
+	public function resolve(name) : AsyncConnection {
 		var s = new SocketConnection(__data,__path.copy());
-		s.__error = __error;
-		s.__funs = __funs;
-		#if neko
-		s.__r = __r;
-		#end
-		s.__path.push(field);
+		s.__path.push(name);
 		return s;
 	}
 
-	override public function call( params : Array<Dynamic>, ?onData : Dynamic -> Void ) : Void {
+	public function call( params : Array<Dynamic>, ?onResult : Dynamic -> Void ) {
 		try {
-			getProtocol().sendRequest(__path,params);
-			__funs.add(onData);
+			__data.protocol.sendRequest(__path,params);
+			__data.results.add({ onResult : onResult, onError : __data.error });
 		} catch( e : Dynamic ) {
-			__error.ref(e);
+			__data.error(e);
 		}
 	}
 
+	public function setErrorHandler(h) {
+		__data.error = h;
+	}
+
+	public function setErrorLogger(h) {
+		__data.log = h;
+	}
+
 	public function setProtocol( p : SocketProtocol ) {
-		__data = p;
+		__data.protocol = p;
 	}
 
 	public function getProtocol() : SocketProtocol {
-		return __data;
+		return __data.protocol;
 	}
 
-	public function closeConnection() {
-		try getProtocol().socket.close() catch( e : Dynamic ) { };
+	public function close() {
+		try __data.protocol.socket.close() catch( e : Dynamic ) { };
 	}
 
 	public function processMessage( data : String ) {
 		var request;
-		var proto = getProtocol();
+		var proto = __data.protocol;
 		data = proto.decodeData(data);
 		try {
 			request = proto.isRequest(data);
 		} catch( e : Dynamic ) {
 			var msg = Std.string(e) + " (in "+StringTools.urlEncode(data)+")";
-			__error.ref(msg); // protocol error
+			__data.error(msg); // protocol error
 			return;
 		}
 		// request
 		if( request ) {
-			try {
-				var me = this;
-				var eval =
-					#if neko
-						__r.resolvePath
-					#else flash
-						function(path:Array<String>) { return flash.Lib.eval(path.join(".")); }
-					#else js
-						function(path:Array<String>) { return js.Lib.eval(path.join(".")); }
-					#end
-				;
-				proto.processRequest(data,eval,function(path,name,args,e) {
-					// exception inside the called method
-					var astr, estr;
-					try astr = args.join(",") catch( e : Dynamic ) astr = "???";
-					try estr = Std.string(e) catch( e : Dynamic ) estr = "???";
-					var header = "Error in call to "+path.join(".")+"."+name+"("+astr+") : ";
-					me.__error.ref(header + estr);
-				});
-			} catch( e : Dynamic ) {
-				__error.ref(e); // protocol error or invalid object/method
-			}
+			try proto.processRequest(data,__data.log) catch( e : Dynamic ) __data.error(e);
 			return;
 		}
 		// answer
-		var f, v;
+		var f = __data.results.pop();
+		if( f == null ) {
+			__data.error("No response excepted ("+data+")");
+			return;
+		}
+		var ret;
 		try {
-			if( __funs.isEmpty() )
-				throw "No response excepted ("+data+")";
-			f = __funs.pop();
-			v = proto.processAnswer(data);
+			ret = proto.processAnswer(data);
 		} catch( e : Dynamic ) {
-			__error.ref(e); // protocol error or answer exception
+			f.onError(e);
 			return;
 		}
-		if( f != null )
-			f(v); // answer callback exception
+		if( f.onResult != null ) f.onResult(ret);
 	}
 
-	#if neko
+	#if (flash || js || neko)
 
-	public static function socketConnect( s : neko.net.Socket, r : neko.net.RemotingServer ) {
-		var sc = new SocketConnection(new SocketProtocol(s),[]);
-		sc.__funs = new List();
-		sc.__r = r;
-		return sc;
+	function defaultLog(path,args,e) {
+		// exception inside the called method
+		var astr, estr;
+		try astr = args.join(",") catch( e : Dynamic ) astr = "???";
+		try estr = Std.string(e) catch( e : Dynamic ) estr = "???";
+		var header = "Error in call to "+path.join(".")+"("+astr+") : ";
+		__data.error(header + estr);
 	}
 
-	#else (flash || js)
-
-	public static function socketConnect( s : Socket ) {
-		var sc = new SocketConnection(new SocketProtocol(s),[]);
-		sc.__funs = new List();
+	public static function create( s : Socket, ctx : Context ) {
+		var data = {
+			protocol : new SocketProtocol(s,ctx),
+			results : new List(),
+			error : function(e) throw e,
+			log : null,
+			#if !flash9
+			#if (flash || js)
+			queue : new haxe.TimerQueue(),
+			#end
+			#end
+		};
+		var sc = new SocketConnection(data,[]);
+		data.log = sc.defaultLog;
 		#if flash9
 		s.addEventListener(flash.events.DataEvent.DATA, function(e : flash.events.DataEvent) {
 			var data = e.data;
-			var msgLen = sc.getProtocol().messageLength(data.charCodeAt(0),data.charCodeAt(1));
+			var msgLen = sc.__data.protocol.messageLength(data.charCodeAt(0),data.charCodeAt(1));
 			if( msgLen == null || data.length != msgLen - 1 ) {
-				sc.__error.ref("Invalid message header");
+				sc.__data.error("Invalid message header");
 				return;
 			}
 			sc.processMessage(e.data.substr(2,e.data.length-2));
 		});
-		#else true
+		#elseif (flash || js)
 		// we can't deliver directly the message
 		// since it might trigger a blocking action on JS side
 		// and in that case this will trigger a Flash bug
 		// where a new onData is called is a parallel thread
 		// ...with the buffer of the previous onData (!)
-		s.onData = function(data : String) {
-			haxe.Timer.queue(function() {
-				var msgLen = sc.getProtocol().messageLength(data.charCodeAt(0),data.charCodeAt(1));
+		s.onData = function( data : String ) {
+			sc.__data.queue.add(function() {
+				var msgLen = sc.__data.protocol.messageLength(data.charCodeAt(0),data.charCodeAt(1));
 				if( msgLen == null || data.length != msgLen - 1 ) {
-					sc.__error.ref("Invalid message header");
+					sc.__data.error("Invalid message header");
 					return;
 				}
 				sc.processMessage(data.substr(2,data.length-2));
@@ -160,7 +167,6 @@ class SocketConnection extends AsyncConnection {
 		return sc;
 	}
 
-	#else error
 	#end
 
 }

+ 23 - 24
std/haxe/remoting/SocketProtocol.hx

@@ -27,12 +27,14 @@ package haxe.remoting;
 typedef Socket =
 	#if flash9
 		flash.net.XMLSocket
-	#else flash
+	#elseif flash
 		flash.XMLSocket
-	#else js
+	#elseif js
 		js.XMLSocket
-	#else neko
+	#elseif neko
 		neko.net.Socket
+	#else
+		Dynamic
 	#end
 
 /**
@@ -52,9 +54,11 @@ typedef Socket =
 class SocketProtocol {
 
 	public var socket : Socket;
+	public var context : Context;
 
-	public function new( sock ) {
+	public function new( sock, ctx ) {
 		this.socket = sock;
+		this.context = ctx;
 	}
 
 	function decodeChar(c) : Null<Int> {
@@ -134,16 +138,17 @@ class SocketProtocol {
 	public function sendMessage( msg : String ) {
 		var e = encodeMessageLength(msg.length + 3);
 		#if neko
-		socket.output.writeChar(e.c1);
-		socket.output.writeChar(e.c2);
-		socket.output.write(msg);
-		socket.output.writeChar(0);
-		#else true
+		var o = socket.output;
+		o.writeByte(e.c1);
+		o.writeByte(e.c2);
+		o.writeString(msg);
+		o.writeByte(0);
+		#else
 		socket.send(Std.chr(e.c1)+Std.chr(e.c2)+msg);
 		#end
 	}
 
-	public function decodeData( data : String ) {
+	public dynamic function decodeData( data : String ) {
 		return data;
 	}
 
@@ -155,7 +160,7 @@ class SocketProtocol {
 		}
 	}
 
-	public function processRequest( data : String, eval : Array<String> -> Dynamic, ?onError : Array<String> -> String -> Array<Dynamic> -> Dynamic -> Void ) {
+	public function processRequest( data : String, ?onError : Array<String> -> Array<Dynamic> -> Dynamic -> Void ) {
 		var s = new haxe.Unserializer(data);
 		var result : Dynamic;
 		var isException = false;
@@ -163,15 +168,8 @@ class SocketProtocol {
 			throw "Not a request";
 		var path : Array<String> = s.unserialize();
 		var args : Array<Dynamic> = s.unserialize();
-		var fname = path.pop();
-		var obj = eval(path);
-		if( obj == null )
-			throw "Object does not exists '"+path.join(".")+"'";
-		var fptr = Reflect.field(obj,fname);
-		if( !Reflect.isFunction(fptr) )
-			throw "Calling not-a-function '"+path.join(".")+"."+fname+"'";
 		try {
-			result = Reflect.callMethod(obj,fptr,args);
+			result = context.call(path,args);
 		} catch( e : Dynamic ) {
 			result = e;
 			isException = true;
@@ -180,7 +178,7 @@ class SocketProtocol {
 		sendAnswer(result,isException);
 		// send the error event
 		if( isException && onError != null )
-			onError(path,fname,args,result);
+			onError(path,args,result);
 	}
 
 	public function processAnswer( data : String ) : Dynamic {
@@ -193,13 +191,14 @@ class SocketProtocol {
 	#if neko
 
 	public function readMessage() {
-		var c1 = socket.input.readChar();
-		var c2 = socket.input.readChar();
+		var i = socket.input;
+		var c1 = i.readByte();
+		var c2 = i.readByte();
 		var len = messageLength(c1,c2);
 		if( len == null )
 			throw "Invalid header";
-		var data = socket.input.read(len - 3);
-		if( socket.input.readChar() != 0 )
+		var data = i.readString(len - 3);
+		if( i.readByte() != 0 )
 			throw "Invalid message";
 		return decodeData(data);
 	}

+ 37 - 12
std/haxe/remoting/SocketWrapper.hx

@@ -23,35 +23,60 @@
  * DAMAGE.
  */
 package haxe.remoting;
+import haxe.remoting.SocketProtocol.Socket;
 
 /**
 	See [js.XMLSocket]
 **/
 class SocketWrapper {
 
-	static var sockets = Reflect.empty();
+	static var ID = 0;
 
-	static function create( id : String ) {
+	static function create( prefix : String ) : String {
+		var id = prefix + "WrappedSocket"+(ID++);
+		var s = new Socket();
+		var ctx = new Context();
+		var cnx = haxe.remoting.ExternalConnection.jsConnect(id,ctx);
+		ctx.addObject("sock",s);
+		ctx.addObject("api",{ close : cnx.close });
 		#if flash9
-		throw "Not implemented";
-		#else true
-		var s = new flash.XMLSocket();
-		var cnx = haxe.remoting.Connection.jsConnect().js.XMLSocket.sockets.__resolve(id);
-		Reflect.setField(sockets,id,s);
+		var connected = false;
+		s.addEventListener(flash.events.Event.CONNECT,function(_) {
+			connected = true;
+			cnx.api.onConnect.call([true]);
+		});
+		s.addEventListener(flash.events.SecurityErrorEvent.SECURITY_ERROR,function(_) {
+			if( connected )
+				cnx.api.onClose.call([]);
+			else
+				cnx.api.onConnect.call([false]);
+		});
+		s.addEventListener(flash.events.Event.CLOSE,function(_) {
+			cnx.api.onClose.call([]);
+		});
+		s.addEventListener(flash.events.DataEvent.DATA,function(e:flash.events.DataEvent) {
+			cnx.api.onData.call([e.data]);
+		});
+		#elseif flash
 		s.onConnect = function(b) {
-			cnx.onConnect.call([b]);
+			cnx.api.onConnect.call([b]);
 		};
 		s.onData = function(data) {
-			cnx.onData.call([data]);
+			cnx.api.onData.call([data]);
 		};
 		s.onClose = function() {
-			cnx.onClose.call([]);
+			cnx.api.onClose.call([]);
 		};
 		#end
+		return id;
 	}
 
-	static function destroy( id : String ) {
-		Reflect.deleteField(sockets,id);
+	static function init() {
+		var ctx = new Context();
+		ctx.addObject("api",{ create : create });
+		haxe.remoting.ExternalConnection.jsConnect("SocketWrapper",ctx);
 	}
 
+	static var _ = init();
+
 }

+ 12 - 19
std/js/XMLSocket.hx

@@ -29,36 +29,29 @@ package js;
 **/
 class XMLSocket {
 
-	static var sockets = Reflect.empty();
-	static var ID = 0;
-
-	var id : String;
-	var rcnx : haxe.remoting.Connection;
-	var cnx : haxe.remoting.Connection;
+	var cnx : haxe.remoting.ExternalConnection;
 
 	public function new( flashObject : String ) {
-		id = "s"+(ID++);
-		rcnx = haxe.remoting.Connection.flashConnect(flashObject).haxe.remoting.SocketWrapper;
-		rcnx.create.call([id]);
-		this.cnx = rcnx.sockets.__resolve(Std.string(id));
-		Reflect.setField(sockets,id,this);
-	}
-
-	public function destroy() {
-		rcnx.destroy.call([id]);
-		Reflect.deleteField(sockets,id);
+		var ctx = new haxe.remoting.Context();
+		var cnx = haxe.remoting.ExternalConnection.flashConnect("SocketWrapper",flashObject,ctx);
+		var sockId = cnx.api.create.call([flashObject]);
+		cnx.close();
+		ctx.addObject("api",this,false);
+		this.cnx = haxe.remoting.ExternalConnection.flashConnect(sockId,flashObject,ctx);
 	}
 
 	public function connect( host : String, port : Int ) {
-		cnx.connect.call([host,port]);
+		cnx.sock.connect.call([host,port]);
 	}
 
 	public function send( data : String ) {
-		cnx.send.call([data]);
+		cnx.sock.send.call([data]);
 	}
 
 	public function close() {
-		cnx.close.call([]);
+		cnx.sock.close.call([]);
+		cnx.api.destroy.call([]);
+		cnx.close();
 	}
 
 	public dynamic function onData( data : String ) {

+ 0 - 101
std/neko/net/RemotingServer.hx

@@ -1,101 +0,0 @@
-/*
- * Copyright (c) 2005, The haXe Project Contributors
- * All rights reserved.
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- *   - Redistributions of source code must retain the above copyright
- *     notice, this list of conditions and the following disclaimer.
- *   - Redistributions in binary form must reproduce the above copyright
- *     notice, this list of conditions and the following disclaimer in the
- *     documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- */
-package neko.net;
-
-class RemotingServer {
-
-	var objects : Hash<Dynamic>;
-	var prefix : String;
-	var log : String -> Void;
-
-	public function new() {
-		objects = new Hash();
-		log = null;
-	}
-
-	public function addObject( name : String, obj : Dynamic ) {
-		objects.set(name,obj);
-	}
-
-	public function setLogger( l ) {
-		log = l;
-	}
-
-	public function setPrivatePrefix( p : String ) {
-		prefix = p;
-	}
-
-	public function resolvePath( path : Array<String> ) : Dynamic {
-		var it = path.iterator();
-		var objname = it.next();
-		if( objname == null )
-			throw "Empty path";
-		var obj = objects.get(objname);
-		if( obj == null )
-			throw "Object '"+objname+"' is not accessible";
-		for( x in it ) {
-			if( obj == null || (prefix != null && x.indexOf(prefix,0) == 0) )
-				return null;
-			obj = Reflect.field(obj,x);
-		}
-		return obj;
-	}
-
-	public function handleRequest() {
-		if( neko.Web.getClientHeader("X-Haxe-Remoting") == null )
-			return false;
-		var v = neko.Web.getParams().get("__x");
-		try {
-			if( v == null )
-				throw "Missing remoting data";
-			var u = new haxe.Unserializer(v);
-			var path : Array<String> = u.unserialize();
-			var args : Array<Dynamic> = u.unserialize();
-			var f = path.pop();
-			var obj = resolvePath(path);
-			var funptr = Reflect.field(obj,f);
-			if( !Reflect.isFunction(funptr) )
-				throw "Calling not-a-function '"+f+"'";
-			var v = Reflect.callMethod(obj,funptr,args);
-			var s = new haxe.Serializer();
-			s.serialize(v);
-			neko.Lib.print("hxr");
-			neko.Lib.print(s.toString());
-		} catch( e : Dynamic ) {
-			if( log != null ) {
-				log(haxe.Stack.toString(haxe.Stack.exceptionStack()));
-				log(Std.string(e));
-				log("\n\n");
-			}
-			var s = new haxe.Serializer();
-			s.serializeException(e);
-			neko.Lib.print("hxr");
-			neko.Lib.print(s.toString());
-		}
-		return true;
-	}
-
-}
-

+ 26 - 22
std/neko/net/ThreadRemotingServer.hx

@@ -31,52 +31,56 @@ class ThreadRemotingServer extends ThreadServer<haxe.remoting.SocketConnection,S
 		messageHeaderSize = 2;
 	}
 
-	public function initClientApi( cnx, server ) {
+	public function initClientApi( cnx : haxe.remoting.SocketConnection, ctx : haxe.remoting.Context ) {
 		throw "Not implemented";
 	}
 
+	public function onXml( cnx : haxe.remoting.SocketConnection, data : String ) {
+		throw "Unhandled XML data '"+data+"'";
+	}
+
 	public override function clientConnected( s : neko.net.Socket ) {
-		var r = new neko.net.RemotingServer();
-		var cnx = haxe.remoting.SocketConnection.socketConnect(s,r);
+		var ctx = new haxe.remoting.Context();
+		var cnx = haxe.remoting.SocketConnection.create(s,ctx);
 		var me = this;
-		cnx.onError = function(e) {
-			if( !Std.is(e,neko.io.Eof) && !Std.is(e,neko.io.Error) )
+		cnx.setErrorHandler(function(e) {
+			if( !Std.is(e,haxe.io.Eof) && !Std.is(e,haxe.io.Error) )
 				me.logError(e);
 			me.stopClient(s);
-		};
-		initClientApi(cnx,r);
+		});
+		initClientApi(cnx,ctx);
 		return cnx;
 	}
 
-	public override function readClientMessage( cnx : haxe.remoting.SocketConnection, buf : String, pos : Int, len : Int ) {
-		var msgLen = cnx.getProtocol().messageLength(buf.charCodeAt(pos),buf.charCodeAt(pos+1));
+	override function readClientMessage( cnx : haxe.remoting.SocketConnection, buf : haxe.io.Bytes, pos : Int, len : Int ) {
+		var msgLen = cnx.getProtocol().messageLength(buf.get(pos),buf.get(pos+1));
 		if( msgLen == null ) {
-			if( buf.charCodeAt(pos) != 60 )
-				throw "Invalid remoting message '"+buf.substr(pos,len)+"'";
-			// XML handling
-			var p = buf.indexOf("\x00",pos);
-			if( p == -1 || p >= len )
+			if( buf.get(pos) != 60 )
+				throw "Invalid remoting message '"+buf.readString(pos,len)+"'";
+			var p = pos;
+			while( p < len ) {
+				if( buf.get(p) == 0 )
+					break;
+				p++;
+			}
+			if( p == len )
 				return null;
 			p -= pos;
 			return {
-				msg : buf.substr(pos,p),
+				msg : buf.readString(pos,p),
 				bytes : p + 1,
 			};
 		}
 		if( len < msgLen )
 			return null;
-		if( buf.charCodeAt(pos + msgLen-1) != 0 )
+		if( buf.get(pos + msgLen-1) != 0 )
 			throw "Truncated message";
 		return {
-			msg : buf.substr(pos+2,msgLen-3),
+			msg : buf.readString(pos+2,msgLen-3),
 			bytes : msgLen,
 		};
 	}
 
-	public function onXml(cnx,data) {
-		throw "Unhandled XML data '"+data+"'";
-	}
-
 	public override function clientMessage( cnx : haxe.remoting.SocketConnection, msg : String ) {
 		if( msg.charCodeAt(0) == 60 ) {
 			onXml(cnx,msg);
@@ -85,7 +89,7 @@ class ThreadRemotingServer extends ThreadServer<haxe.remoting.SocketConnection,S
 		try {
 			cnx.processMessage(msg);
 		} catch( e : Dynamic ) {
-			if( !Std.is(e,neko.io.Eof) && !Std.is(e,neko.io.Error) )
+			if( !Std.is(e,haxe.io.Eof) && !Std.is(e,haxe.io.Error) )
 				logError(e);
 			stopClient(cnx.getProtocol().socket);
 		}

+ 2 - 2
tests/unit/RemotingApi.hx

@@ -25,8 +25,8 @@ class RemotingApi {
 			throw v;
 	}
 
-	public static function context() {
-		var ctx = new haxe.remoting.Context();
+	public static function context( ?ctx ) {
+		if( ctx == null ) ctx = new haxe.remoting.Context();
 		ctx.addObject("api",new RemotingApi());
 		ctx.addObject("apirec",new RemotingApi(),true);
 		return ctx;

+ 32 - 4
tests/unit/RemotingServer.hx

@@ -1,11 +1,39 @@
 package unit;
 
-class RemotingServer {
+class RemotingServer extends neko.net.ThreadRemotingServer {
+
+	static var HOST = "dev.unit-tests";
+	static var PORT = 1999;
+
+	override function initClientApi( cnx : haxe.remoting.SocketConnection, ctx : haxe.remoting.Context ) {
+		RemotingApi.context(ctx);
+		cnx.setErrorLogger(function(path,args,e) {
+			// ignore invalid calls or exceptions in methods
+		});
+	}
+
+	override function onXml( cnx : haxe.remoting.SocketConnection, data : String ) {
+		if( data == "<policy-file-request/>" ) {
+			var str = "<cross-domain-policy>";
+			str += '<allow-access-from domain="'+HOST+'" to-ports="'+PORT+'"/>';
+			str += "</cross-domain-policy>";
+			str += "\x00";
+			cnx.getProtocol().socket.write(str);
+			return;
+		}
+		super.onXml(cnx,data);
+	}
 
 	static function main() {
-		var ctx = RemotingApi.context();
-		if( !haxe.remoting.HttpConnection.handleRequest(ctx) )
-			throw "Invalid request";
+		if( neko.Web.isModNeko ) {
+			var ctx = RemotingApi.context();
+			if( !haxe.remoting.HttpConnection.handleRequest(ctx) )
+				throw "Invalid request";
+			return;
+		}
+		var s = new RemotingServer();
+		trace("Starting server on "+HOST+":"+PORT);
+		s.run(HOST,PORT);
 	}
 
 }

+ 41 - 6
tests/unit/Test.hx

@@ -40,6 +40,10 @@ class Test {
 	}
 
 	function async<Args,T>( f : Args -> (T -> Void) -> Void, args : Args, v : T, ?pos : haxe.PosInfos ) {
+		if( asyncWaits.length >= AMAX ) {
+			asyncCache.push(callback(async,f,args,v,pos));
+			return;
+		}
 		asyncWaits.push(pos);
 		f(args,function(v2) {
 			count++;
@@ -54,26 +58,40 @@ class Test {
 	}
 
 	function asyncExc<Args>( seterror : (Dynamic -> Void) -> Void, f : Args -> (Dynamic -> Void) -> Void, args : Args, ?pos : haxe.PosInfos ) {
+		if( asyncWaits.length >= AMAX ) {
+			asyncCache.push(callback(asyncExc,seterror,f,args,pos));
+			return;
+		}
 		asyncWaits.push(pos);
 		seterror(function(e) {
 			count++;
-			if( !asyncWaits.remove(pos) )
+			if( asyncWaits.remove(pos) )
+				checkDone();
+			else
 				report("Multiple async events",pos);
 		});
 		f(args,function(v) {
 			count++;
-			if( asyncWaits.remove(pos) )
+			if( asyncWaits.remove(pos) ) {
 				report("No exception occured",pos);
-			else
+				checkDone();
+			} else
 				report("Multiple async events",pos);
 		});
 	}
 
+	function log( msg, ?pos : haxe.PosInfos ) {
+		haxe.Log.trace(msg,pos);
+	}
+
 	static var count = 0;
 	static var reportInfos = null;
 	static var reportCount = 0;
 	static var checkCount = 0;
 	static var asyncWaits = new Array<haxe.PosInfos>();
+	static var asyncCache = new Array<Void -> Void>();
+	static var AMAX = 3;
+	static var timer : haxe.Timer;
 
 	dynamic static function report( msg : String, pos : haxe.PosInfos ) {
 		if( reportInfos != null ) {
@@ -89,22 +107,39 @@ class Test {
 	}
 
 	static function checkDone() {
-		if( asyncWaits.length == 0 )
+		if( asyncWaits.length != 0 ) return;
+		if( asyncCache.length == 0 ) {
 			report("DONE ["+count+" tests]",here);
+			return;
+		}
+		resetTimer();
+		while( asyncCache.length > 0 && asyncWaits.length < AMAX )
+			asyncCache.shift()();
 	}
 
 	static function asyncTimeout() {
+		if( asyncWaits.length == 0 )
+			return;
 		for( pos in asyncWaits )
 			report("TIMEOUT",pos);
+		asyncWaits = new Array();
+		checkDone();
+	}
+
+	static function resetTimer() {
+		#if !neko
+		if( timer != null ) timer.stop();
+		timer = new haxe.Timer(10000);
+		timer.run = asyncTimeout;
+		#end
 	}
 
 	static function main() {
 		#if neko
 		if( neko.Web.isModNeko )
 			neko.Lib.print("<pre>");
-		#else
-		haxe.Timer.delay(asyncTimeout,10000);
 		#end
+		resetTimer();
 		var classes = [
 			new TestBytes(),
 			new TestInt32(),

+ 66 - 3
tests/unit/TestRemoting.hx

@@ -1,7 +1,14 @@
 package unit;
+import haxe.remoting.SocketProtocol;
+#if flash
+import haxe.remoting.SocketWrapper;
+#end
 
 class TestRemoting extends Test {
 
+	static var HOST = "dev.unit-tests";
+	static var PORT = 1999;
+
 	static var _ = init();
 	static var ecnx : haxe.remoting.ExternalConnection;
 	static var ecnx2 : haxe.remoting.ExternalConnection;
@@ -14,7 +21,7 @@ class TestRemoting extends Test {
 		#if flash
 		ecnx = haxe.remoting.ExternalConnection.jsConnect("cnx",ctx);
 		ecnx3 = haxe.remoting.ExternalConnection.jsConnect("unknown",ctx);
-		lcnx = haxe.remoting.LocalConnection.connect("local",ctx,["dev.unit-tests"]);
+		lcnx = haxe.remoting.LocalConnection.connect("local",ctx,[HOST]);
 		fjscnx = haxe.remoting.FlashJsConnection.connect(#if flash9 "haxeFlash8" #else "haxeFlash9" #end,"cnx",ctx);
 		#elseif js
 		ecnx = haxe.remoting.ExternalConnection.flashConnect("cnx","haxeFlash8",ctx);
@@ -24,6 +31,7 @@ class TestRemoting extends Test {
 	}
 
 	public function test() {
+		// external connection
 		#if (flash || js)
 		doTestConnection(ecnx);
 		#end
@@ -31,24 +39,79 @@ class TestRemoting extends Test {
 		#if js
 		doTestConnection(ecnx2);
 		#end
+
 		#if flash
+		// local connection
 		doTestAsyncConnection(lcnx);
+		// flash-flash through-js connection
 		doTestAsyncConnection(fjscnx);
 		#end
 		#if (js || neko)
-		var hcnx = haxe.remoting.HttpConnection.urlConnect("http://dev.unit-tests/remoting.n");
+		// http sync connection
+		var hcnx = haxe.remoting.HttpConnection.urlConnect("http://"+HOST+"/remoting.n");
 		doTestConnection(hcnx);
+		// test wrappers
 		var dcnx = haxe.remoting.AsyncDebugConnection.create(haxe.remoting.AsyncAdapter.create(hcnx));
 		dcnx.setErrorDebug(function(path,args,e) {});
 		dcnx.setResultDebug(function(path,args,ret) {});
 		dcnx.setCallDebug(function(path,args) {});
 		doTestAsyncConnection(dcnx);
 		#end
-		var hcnx = haxe.remoting.HttpAsyncConnection.urlConnect("http://dev.unit-tests/remoting.n");
+
+		// http async connection
+		var hcnx = haxe.remoting.HttpAsyncConnection.urlConnect("http://"+HOST+"/remoting.n");
 		doTestAsyncConnection(hcnx);
 		var dcnx = haxe.remoting.DelayedConnection.create();
 		dcnx.connection = hcnx;
 		doTestAsyncConnection(dcnx);
+
+		// socket connection
+		#if (flash || neko)
+		async( doConnect, new Socket(), true );
+		#elseif js
+		async( doConnect, new Socket("haxeFlash8"), true );
+		async( doConnect, new Socket("haxeFlash9"), true );
+		#end
+	}
+
+	function doConnect( s : Socket, onResult : Bool -> Void ) {
+		var me = this;
+		#if flash9
+		var connected = false;
+		s.addEventListener(flash.events.Event.CONNECT,function(e) {
+			connected = true;
+			me.doTestSocket(s);
+			onResult(true);
+		});
+		s.addEventListener(flash.events.SecurityErrorEvent.SECURITY_ERROR,function(e) {
+			onResult(false);
+		});
+		s.addEventListener(flash.events.Event.CLOSE,function(e) {
+			if( !connected )
+				onResult(false);
+		});
+		s.connect(HOST,PORT);
+		#elseif (flash || js)
+		s.onConnect = function(success) {
+			if( success ) me.doTestSocket(s);
+			onResult(success);
+		};
+		s.connect(HOST,PORT);
+		#elseif neko
+		var ret = try { s.connect(new neko.net.Host(HOST),PORT); true; } catch( e : Dynamic ) false;
+		if( ret ) doTestSocket(s);
+		onResult(ret);
+		#end
+	}
+
+	function doTestSocket( s : Socket ) {
+		#if neko
+		var scnx = haxe.remoting.NekoSocketConnection.create(s,new haxe.remoting.Context());
+		doTestConnection(scnx);
+		#else
+		var scnx = haxe.remoting.SocketConnection.create(s,new haxe.remoting.Context());
+		doTestAsyncConnection(scnx);
+		#end
 	}
 
 	function doTestConnection( cnx : haxe.remoting.Connection ) {

+ 3 - 0
tests/unit/server.bat

@@ -0,0 +1,3 @@
+@echo off
+neko remoting.n
+pause