Sfoglia il codice sorgente

Flesh out + test sys.net.Socket on all sys targets (#6887)

* [python] implement Socket setFastSend/waitForRead

* [php] complete Socket implementation

* Add Socket unit test

* [cpp] fix cpp Socket.read return value

* [cs] implement Socket.select

* fix eval

* [lua] add std socket and pass tests

* [lua] fix sys args for new Boot.defArray method

* Disable Socket Java tests for now

* [php] Add php.net.Socket to fix php.net.SslSocket

* Add php socket extention to appveyor
Ben Morris 7 anni fa
parent
commit
0f1633503d

+ 1 - 0
appveyor.yml

@@ -50,6 +50,7 @@ install:
     - choco install php --version 7.2.0 -y
     - echo extension=php_openssl.dll >> C:\tools\php72\php.ini
     - echo extension=php_mbstring.dll >> C:\tools\php72\php.ini
+    - echo extension=php_sockets.dll >> C:\tools\php72\php.ini
     - RefreshEnv
     # setup python
     - cmd: mklink C:\Python34-x64\python3.exe C:\Python34-x64\python.exe

+ 9 - 4
src/macro/eval/evalStdLib.ml

@@ -1796,12 +1796,17 @@ module StdSocket = struct
 		let proto = get_instance_prototype (get_ctx()) key_sys_net_Socket null_pos in
 		let i = get_instance_field_index proto key_socket null_pos in
 		let pair = function
-			| VInstance vi as v -> this vi.iproto.pfields.(i),v
+			| VInstance vi as v -> this vi.ifields.(i),v
 			| v -> unexpected_value v "NativeSocket"
 		in
-		let read = List.map pair (decode_array read) in
-		let write = List.map pair (decode_array write) in
-		let others = List.map pair (decode_array others) in
+		let decode_optional_array = function
+			| VNull -> []
+			| VArray va -> EvalArray.to_list va
+			| v -> unexpected_value v "array"
+		in
+		let read = List.map pair (decode_optional_array read) in
+		let write = List.map pair (decode_optional_array write) in
+		let others = List.map pair (decode_optional_array others) in
 		let timeout = match timeout with VNull -> 0. | VInt32 i -> Int32.to_float i | VFloat f -> f | _ -> unexpected_value timeout "number" in
 		let read',write',others' = Unix.select (List.map fst read) (List.map fst write) (List.map fst others) timeout in
 		let read = List.map (fun sock -> List.assq sock read) read' in

+ 4 - 1
std/cpp/_std/sys/net/Socket.hx

@@ -23,6 +23,8 @@ package sys.net;
 
 import haxe.io.Error;
 import cpp.NativeSocket;
+import cpp.NativeString;
+import cpp.Pointer;
 
 private class SocketInput extends haxe.io.Input {
 
@@ -144,7 +146,8 @@ class Socket {
    public function read() : String {
       var bytes:haxe.io.BytesData = NativeSocket.socket_read(__s);
       if (bytes==null) return "";
-      return bytes.toString();
+      var arr:Array<cpp.Char> = cast bytes;
+      return NativeString.fromPointer(Pointer.ofArray(arr));
    }
 
    public function write( content : String ) : Void {

+ 43 - 6
std/cs/_std/sys/net/Socket.hx

@@ -19,9 +19,11 @@
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  */
- 
+
 package sys.net;
 
+import cs.NativeArray;
+import cs.system.collections.ArrayList;
 import cs.system.net.IPEndPoint;
 import cs.system.net.sockets.AddressFamily;
 import cs.system.net.sockets.NetworkStream;
@@ -30,6 +32,7 @@ import cs.system.net.sockets.SocketFlags;
 import cs.system.net.sockets.SocketShutdown;
 import cs.system.net.sockets.SocketType;
 import cs.system.threading.Thread;
+import cs.system.net.sockets.Socket in NativeSocket;
 import haxe.io.Bytes;
 import haxe.io.Error;
 import haxe.io.Input;
@@ -40,7 +43,7 @@ import haxe.io.Output;
 **/
 @:coreapi
 class Socket {
-	private var sock : cs.system.net.sockets.Socket = null;
+	private var sock : NativeSocket = null;
 
 	/**
 		The stream on which you can read available data. By default the stream is blocking until the requested data is available,
@@ -62,7 +65,7 @@ class Socket {
 		Creates a new unconnected socket.
 	**/
 	public function new() : Void {
-		sock = new cs.system.net.sockets.Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp );
+		sock = new NativeSocket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp );
 		sock.Blocking = true;
 	}
 
@@ -130,7 +133,7 @@ class Socket {
 		Bind the socket to the given host/port so it can afterwards listen for connections there.
 	**/
 	public function bind( host : Host, port : Int ) : Void {
-		sock = new cs.system.net.sockets.Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp );
+		sock = new NativeSocket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp );
 		sock.Bind( new IPEndPoint(host.ipAddress, port) );
 	}
 
@@ -201,8 +204,42 @@ class Socket {
 		In case a `timeout` (in seconds) is specified, select might wait at worse until the timeout expires.
 	**/
 	static public function select(read : Array<Socket>, write : Array<Socket>, others : Array<Socket>, ?timeout : Float) : { read: Array<Socket>,write: Array<Socket>,others: Array<Socket> } {
-		throw "Not implemented yet.";
-		return null;
+		var map:Map<Int, Socket> = new Map();
+		inline function addSockets(sockets:Array<Socket>) {
+			if (sockets != null) for (s in sockets) map[s.sock.Handle.ToInt32()] = s;
+		}
+		inline function getRaw(sockets:Array<Socket>):ArrayList {
+			var a = new ArrayList(sockets == null ? 0 : sockets.length);
+			if (sockets != null) for (s in sockets) {
+				a.Add(s.sock);
+			}
+			return a;
+		}
+		inline function getOriginal(result:ArrayList) {
+			var a:Array<Socket> = [];
+			for (i in 0 ... result.Count) {
+				var s:NativeSocket = result[i];
+				a.push(map[s.Handle.ToInt32()]);
+			}
+			return a;
+		}
+
+		addSockets(read);
+		addSockets(write);
+		addSockets(others);
+
+		// unwrap Sockets into native sockets
+		var rawRead:ArrayList = getRaw(read),
+			rawWrite:ArrayList = getRaw(write),
+			rawOthers:ArrayList = getRaw(others);
+		var microsec = timeout == null ? -1 : Std.int(timeout * 1000000);
+		NativeSocket.Select(rawRead, rawWrite, rawOthers, microsec);
+		// convert native sockets back to Socket objects
+		return {
+			read: getOriginal(rawRead),
+			write: getOriginal(rawWrite),
+			others: getOriginal(rawOthers),
+		}
 	}
 
 }

+ 13 - 2
std/lua/Boot.hx

@@ -226,8 +226,19 @@ class Boot {
 	   Define an array from the given table
 	*/
 	public inline static function defArray<T>(tab: Table<Int,T>, ?length : Int) : Array<T> {
-		if (length == null) length = TableTools.maxn(tab) + 1; // maxn doesn't count 0 index
-		return untyped _hx_tab_array(tab, length);
+		if (length == null){
+			length = TableTools.maxn(tab);
+			if (length > 0){
+				var head = tab[1];
+				Table.remove(tab, 1);
+				tab[0] =  head;
+				return untyped _hx_tab_array(tab, length);
+			} else {
+				return [];
+			}
+		} else {
+			return untyped _hx_tab_array(tab, length);
+		}
 	}
 
 	/*

+ 2 - 2
std/lua/Lib.hx

@@ -25,8 +25,8 @@ import lua.Io;
 import lua.NativeStringTools;
 
 /**
-	Platform-specific Lua Library. Provides some platform-specific functions 
-	for the Lua target, such as conversion from Haxe types to native types 
+	Platform-specific Lua Library. Provides some platform-specific functions
+	for the Lua target, such as conversion from Haxe types to native types
 	and vice-versa.
 **/
 class Lib {

+ 2 - 2
std/lua/_std/Sys.hx

@@ -43,8 +43,8 @@ class Sys {
 		return lua.Lib.println(v);
 	}
 	public inline static function args() : Array<String> {
-		var args = lua.Lib.tableToArray(lua.Lua.arg).copy();
-		args.shift();
+		var targs = lua.PairTools.copy(lua.Lua.arg);
+		var args = lua.Lib.tableToArray(targs);
 		return args;
 	}
 	public static function command( cmd : String, ?args : Array<String> ) : Int  {

+ 264 - 0
std/lua/_std/sys/net/Socket.hx

@@ -0,0 +1,264 @@
+/*
+ * Copyright (C)2005-2018 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+package sys.net;
+
+import lua.lib.luasocket.Socket as LuaSocket;
+import lua.lib.luasocket.socket.*;
+import lua.*;
+
+import haxe.io.Bytes;
+
+/**
+	A TCP socket class : allow you to both connect to a given server and exchange messages or start your own server and wait for connections.
+**/
+
+class Socket {
+
+	/**
+		The stream on which you can read available data. By default the stream is blocking until the requested data is available,
+		use `setBlocking(false)` or `setTimeout` to prevent infinite waiting.
+	**/
+	public var input(default,null) : haxe.io.Input;
+
+	/**
+		The stream on which you can send data. Please note that in case the output buffer you will block while writing the data, use `setBlocking(false)` or `setTimeout` to prevent that.
+	**/
+	public var output(default,null) : haxe.io.Output;
+
+
+	/**
+		A custom value that can be associated with the socket. Can be used to retrieve your custom infos after a `select`.
+	***/
+	var custom : Dynamic;
+
+	/**
+	  A Lua specific datatype for a tcp server instance
+	**/
+	var _socket : LuaSocket;
+
+
+	var blocking = false;
+
+	/**
+		Creates a new unconnected socket.
+	**/
+	public function new() : Void { }
+
+	/**
+		Closes the socket : make sure to properly close all your sockets or you will crash when you run out of file descriptors.
+	**/
+	public function close() : Void {
+		_socket.close();
+	}
+
+	/**
+		Read the whole data available on the socket.
+	**/
+	public function read() : String {
+		return input.readAll().toString();
+	}
+
+	/**
+		Write the whole data to the socket output.
+	**/
+	public function write( content : String ) : Void {
+		output.writeString(content);
+	}
+
+	/**
+		Connect to the given server host/port. Throw an exception in case we couldn't successfully connect.
+	**/
+	public function connect( host : Host, port : Int ) : Void {
+		var res = LuaSocket.connect(host.host, port);
+		if (res.message != null) throw 'Socket Error : ${res.message}';
+		input = new SocketInput(res.result);
+		output = new SocketOutput(res.result);
+		_socket = res.result;
+	}
+
+	/**
+		Allow the socket to listen for incoming questions. The parameter tells how many pending connections we can have until they get refused. Use `accept()` to accept incoming connections.
+	**/
+	public function listen( connections : Int ) : Void {
+		var res = LuaSocket.tcp();
+		if (res.message != null) throw 'Socket Listen Error : ${res.message}';
+		res.result.listen(connections);
+		_socket = res.result;
+	}
+
+	/**
+		Shutdown the socket, either for reading or writing.
+	**/
+	public function shutdown( read : Bool, write : Bool ) : Void {
+		var client : TcpClient = cast _socket;
+		switch [read, write] {
+			case [true  ,true]  : client.shutdown(Both);
+			case [true  ,false] : client.shutdown(Receive);
+			case [false ,true]  : client.shutdown(Send);
+			default : null;
+		}
+	}
+
+	/**
+		Bind the socket to the given host/port so it can afterwards listen for connections there.
+	**/
+	public function bind( host : Host, port : Int ) : Void {
+		var res = LuaSocket.bind(host.host, port);
+		if (res.message != null) throw 'Socket Bind Error : ${res.message}';
+		_socket = res.result;
+	}
+
+	/**
+		Accept a new connected client. This will return a connected socket on which you can read/write some data.
+	**/
+	public function accept() : Socket {
+		var server : TcpServer = cast _socket;
+		var res = server.accept();
+		if (res.message != null) throw 'Error : ${res.message}';
+		var sock = new Socket();
+		sock._socket = res.result;
+		sock.input  = new SocketInput(res.result);
+		sock.output = new SocketOutput(res.result);
+		return sock;
+	}
+
+	/**
+		Return the information about the other side of a connected socket.
+	**/
+	public function peer() : { host : Host, port : Int } {
+		var client : TcpClient = cast _socket;
+		var res = client.getpeername();
+		var host = new Host(res.address);
+		return  { host : host, port : Std.parseInt(res.port)};
+	}
+
+	/**
+		Return the information about our side of a connected socket.
+	**/
+	public function host() : { host : Host, port : Int } {
+		var server : TcpServer = cast _socket;
+		var res = server.getsockname();
+		var host = new Host(res.address);
+		return {host : host, port : Std.parseInt(res.port)};
+	}
+
+	/**
+		Gives a timeout after which blocking socket operations (such as reading and writing) will abort and throw an exception.
+	**/
+	public inline function setTimeout( timeout : Float ) : Void {
+		var client : TcpClient = cast _socket;
+		client.settimeout(timeout);
+	}
+
+	/**
+		Block until some data is available for read on the socket.
+	**/
+	public function waitForRead() : Void {
+		select([this], null, null);
+	}
+
+	/**
+		Change the blocking mode of the socket. A blocking socket is the default behavior. A non-blocking socket will abort blocking operations immediately by throwing a haxe.io.Error.Blocked value.
+	**/
+	public function setBlocking( b : Bool ) : Void {
+		blocking = b;
+	}
+
+	/**
+		Allows the socket to immediately send the data when written to its output : this will cause less ping but might increase the number of packets / data size, especially when doing a lot of small writes.
+	**/
+	public function setFastSend( b : Bool ) : Void {
+		var client : TcpClient = cast _socket;
+		client.setoption(TcpNoDelay, true);
+	}
+
+	/**
+		Wait until one of the sockets groups is ready for the given operation :
+		 - `read`contains sockets on which we want to wait for available data to be read,
+		 - `write` contains sockets on which we want to wait until we are allowed to write some data to their output buffers,
+		 - `others` contains sockets on which we want to wait for exceptional conditions.
+		 - `select` will block until one of the condition is met, in which case it will return the sockets for which the condition was true.
+		In case a `timeout` (in seconds) is specified, select might wait at worse until the timeout expires.
+	**/
+	static public function select(read : Array<Socket>, write : Array<Socket>, others : Array<Socket>, ?timeout : Float) : { read: Array<Socket>, write: Array<Socket>, others: Array<Socket> } {
+		var read_tbl  = read == null ? Table.create()   : Table.fromArray([for (r in read) cast r._socket]);
+		var write_tbl  = write == null ? Table.create()  : Table.fromArray(([for (r in write) cast r._socket]));
+		var res = LuaSocket.select(read_tbl, write_tbl, timeout);
+
+		var convert_socket = function(x : LuaSocket){
+			var sock = new Socket();
+			sock.input = new SocketInput(cast x);
+			sock.output = new SocketOutput(cast x);
+			return sock;
+		}
+		var read_arr =  res.read == null ? [] : lua.Lib.tableToArray(res.read).map(convert_socket);
+
+		var write_arr = res.write == null ? [] : lua.Lib.tableToArray(res.write).map(convert_socket);
+		return {read : read_arr, write : write_arr, others : []};
+	}
+
+}
+
+private class SocketInput extends haxe.io.Input {
+	var s : TcpClient;
+	public function new(s : TcpClient) {
+		this.s = s;
+	}
+	override public function readByte() : Int {
+		var res =  s.receive(1);
+		if (res.message == "closed") throw new haxe.io.Eof();
+		else if (res.message != null) throw 'Error : ${res.message}';
+		return res.result.charCodeAt(0);
+	}
+
+	override public function readBytes( s : Bytes, pos : Int, len : Int ) : Int {
+		var k = len;
+		var b =  s.getData();
+		if( pos < 0 || len < 0 || pos + len > s.length )
+			throw haxe.io.Error.OutsideBounds;
+		try{
+			while( k > 0 ) {
+				b[pos] = cast readByte();
+				pos++;
+				k--;
+			}
+		} catch(e:haxe.io.Eof){
+			if (pos == 0){
+				throw e;
+			}
+		}
+		return len-k;
+	}
+}
+
+
+private class SocketOutput extends haxe.io.Output {
+	var s : TcpClient;
+	public function new(s  : TcpClient) {
+		this.s = s;
+	}
+	override public function writeByte(c : Int ) : Void {
+		s.send(String.fromCharCode(c));
+	}
+
+}
+

+ 12 - 1
std/lua/lib/luasocket/Socket.hx

@@ -21,7 +21,18 @@
  */
 
 package lua.lib.luasocket;
+import lua.lib.luasocket.socket.*;
+
 @:luaRequire("socket")
 extern class Socket {
-	public static function gettime() : Float; // TODO : find a smaller lib for this? 
+	public static var _DEBUG : Bool;
+	public static var _VERSION : String;
+	public static function tcp() : Result<TcpMaster>;
+	public static function bind(address : String, port : Int, ?backlog : Int) : Result<TcpServer>;
+	public static function connect(address : String, port : Int, ?locaddr : String, ?locport : Int) : Result<TcpClient>;
+	public static function gettime() : Float;
+	public static function select(recvt : Table<Int, Socket>, sendt : Table<Int, Socket>, ?timeout : Float) : SelectResult;
+	public function close() : Void;
+	public function getsockname() : AddrInfo;
+
 }

+ 28 - 0
std/lua/lib/luasocket/socket/AddrInfo.hx

@@ -0,0 +1,28 @@
+/*
+ * Copyright (C)2005-2018 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package lua.lib.luasocket.socket;
+
+@:multiReturn extern class AddrInfo{
+	var address : String;
+	var port : String;
+}

+ 27 - 0
std/lua/lib/luasocket/socket/ReceivePattern.hx

@@ -0,0 +1,27 @@
+/*
+ * Copyright (C)2005-2018 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package lua.lib.luasocket.socket;
+@:enum abstract ReceivePattern(String)  {
+	var All  = "*a";
+	var Line = "*l";
+}

+ 27 - 0
std/lua/lib/luasocket/socket/SelectResult.hx

@@ -0,0 +1,27 @@
+/*
+ * Copyright (C)2005-2018 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package lua.lib.luasocket.socket;
+@:multiReturn extern class SelectResult {
+	var read : Table<Int, Socket>;
+	var write : Table<Int, Socket>;
+}

+ 28 - 0
std/lua/lib/luasocket/socket/ShutdownMode.hx

@@ -0,0 +1,28 @@
+/*
+ * Copyright (C)2005-2018 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package lua.lib.luasocket.socket;
+@:enum abstract ShutdownMode(String)  {
+	var Both    = "both";
+	var Send    = "send";
+	var Receive = "receive";
+}

+ 35 - 0
std/lua/lib/luasocket/socket/TcpClient.hx

@@ -0,0 +1,35 @@
+/*
+ * Copyright (C)2005-2018 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package lua.lib.luasocket.socket;
+import haxe.extern.EitherType;
+
+extern class TcpClient extends Socket {
+	public function getpeername() :  AddrInfo;
+	public function receive(pattern : EitherType<ReceivePattern,Int>, ?prefix : String) : Result<String>;
+	public function send(data : String, ?i : Int, ?j : Int) : Result<Int>;
+	public function shutdown(mode : ShutdownMode) : Result<Int>;
+	public function settimeout(value  : Float, ?mode : TimeoutMode) : Void;
+	public function setoption(option : TcpOption , value : EitherType<Bool, {on : Bool, timeout : Float}>) : Void;
+}
+
+

+ 34 - 0
std/lua/lib/luasocket/socket/TcpMaster.hx

@@ -0,0 +1,34 @@
+/*
+ * Copyright (C)2005-2018 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package lua.lib.luasocket.socket;
+import haxe.extern.EitherType;
+
+extern class TcpMaster extends Socket {
+	// transforms master to TcpServer
+	public function listen(backlog : Int) : Void;
+	// transforms master to TcpClient
+	public function connect(address : String, port :Int) : Void;
+	public function bind(address : String, port : Int) : Void;
+}
+
+

+ 29 - 0
std/lua/lib/luasocket/socket/TcpOption.hx

@@ -0,0 +1,29 @@
+/*
+ * Copyright (C)2005-2018 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package lua.lib.luasocket.socket;
+@:enum abstract TcpOption(String)  {
+	var KeepAlive    = "keepalive";
+	var Linger       = "linger";
+	var ReuseAddress = "reuseaddr";
+	var TcpNoDelay   = "tcp-nodelay";
+}

+ 31 - 0
std/lua/lib/luasocket/socket/TcpServer.hx

@@ -0,0 +1,31 @@
+/*
+ * Copyright (C)2005-2018 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package lua.lib.luasocket.socket;
+
+import lua.*;
+
+extern class TcpServer extends Socket {
+	public function accept() : Result<TcpClient>;
+	public function settimeout(value  : Int, ?mode : TimeoutMode) : Void;
+	public function setoption(option : String , value : TcpOption) : Void;
+}

+ 28 - 0
std/lua/lib/luasocket/socket/TimeoutMode.hx

@@ -0,0 +1,28 @@
+/*
+ * Copyright (C)2005-2018 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+
+package lua.lib.luasocket.socket;
+@:enum abstract TimeoutMode(String) {
+	var Block = "b";
+	var Total = "t";
+}

+ 5 - 2
std/php/Const.hx

@@ -156,7 +156,7 @@ extern class Const {
 	static var STREAM_CLIENT_ASYNC_CONNECT : Int;
 	static var STREAM_CLIENT_PERSISTENT : Int;
 	/**
-		@see http://php.net/manual/en/function.socket-create.php
+		@see http://php.net/manual/en/sockets.constants.php
 	**/
 	static var SOCK_STREAM : Int;
 	static var SOCK_DGRAM : Int;
@@ -166,6 +166,9 @@ extern class Const {
 	static var AF_INET : Int;
 	static var AF_INET6 : Int;
 	static var AF_UNIX : Int;
+	static var SOL_TCP : Int;
+	static var TCP_NODELAY : Int;
+	static var PHP_BINARY_READ : Int;
 	/**
 		@see http://php.net/manual/en/json.constants.php
 	**/
@@ -329,4 +332,4 @@ extern class Const {
 	static var ZLIB_SYNC_FLUSH : Int;
 	static var ZLIB_FULL_FLUSH : Int;
 	static var ZLIB_FINISH : Int;
-}
+}

+ 86 - 1
std/php/Global.hx

@@ -1027,6 +1027,91 @@ extern class Global {
 	**/
 	static function stream_socket_get_name( stream:Resource, want_peer:Bool ) : EitherType<String,Bool>;
 
+	/**
+		@see http://php.net/manual/en/function.socket-create.php
+	**/
+	static function socket_create( domain:Int, type:Int, protocol:Int ) : Resource;
+
+	/**
+		@see http://php.net/manual/en/function.socket-connect.php
+	**/
+	static function socket_connect( stream:Resource, host:String, port:Int ) : Bool;
+
+	/**
+		@see http://php.net/manual/en/function.socket-listen.php
+	**/
+	static function socket_listen( stream:Resource, connections:Int ) : Bool;
+
+	/**
+		@see http://php.net/manual/en/function.socket-shutdown.php
+	**/
+	static function socket_shutdown( stream:Resource, rw:Int ) : Bool;
+
+	/**
+		@see http://php.net/manual/en/function.socket-bind.php
+	**/
+	static function socket_bind( stream:Resource, host:String, port:Int ) : Bool;
+
+	/**
+		@see http://php.net/manual/en/function.socket-accept.php
+	**/
+	static function socket_accept( stream:Resource ) : EitherType<Resource,Bool>;
+
+	/**
+		@see http://php.net/manual/en/function.socket-getpeername.php
+	**/
+	static function socket_getpeername( stream:Resource, host:Ref<String>, port:Ref<Int> ) : Bool;
+
+	/**
+		@see http://php.net/manual/en/function.socket-getsockname.php
+	**/
+	static function socket_getsockname( stream:Resource, host:Ref<String>, port:Ref<Int> ) : Bool;
+
+	/**
+		@see http://php.net/manual/en/function.socket-set-block.php
+	**/
+	static function socket_set_block( stream:Resource ) : Bool;
+
+	/**
+		@see http://php.net/manual/en/function.socket-set-nonblock.php
+	**/
+	static function socket_set_nonblock( stream:Resource ) : Bool;
+
+	/**
+		@see http://php.net/manual/en/function.socket-set-option.php
+	**/
+	static function socket_set_option( stream:Resource, level:Int, option:Int, val:Bool ) : Bool;
+
+	/**
+		@see http://php.net/manual/en/function.socket-select.php
+	**/
+	static function socket_select( read:NativeIndexedArray<Resource>, write:NativeIndexedArray<Resource>, other:NativeIndexedArray<Resource>, tv_sec:Int=0, tv_usec:Int=0 ) : EitherType<Bool, Int>;
+
+	/**
+		@see http://php.net/manual/en/function.socket-read.php
+	**/
+	static function socket_read( resource:Resource, length:Int, type:Int=Const.PHP_BINARY_READ ) : EitherType<Bool, String>;
+
+	/**
+		@see http://php.net/manual/en/function.socket-write.php
+	**/
+	static function socket_write( resource:Resource, buffer:String, len:Int=0 ) : EitherType<Bool, Int>;
+
+	/**
+		@see http://php.net/manual/en/function.socket-import-stream.php
+	**/
+	static function socket_import_stream( resource:Resource ) : EitherType<Bool, Resource>;
+
+	/**
+		@see http://php.net/manual/en/function.socket-export-stream.php
+	**/
+	static function socket_export_stream( resource:Resource ) : EitherType<Bool, Resource>;
+
+	/**
+		@see http://php.net/manual/en/function.socket-close.php
+	**/
+	static function socket_close( resource:Resource ) : Void;
+
 	/**
 		@see http://php.net/manual/en/function.ini-get.php
 	**/
@@ -1237,4 +1322,4 @@ extern class Global {
 		@see http://php.net/manual/en/function.is-uploaded-file.php
 	**/
 	static function is_uploaded_file( filename:String ) : Bool;
-}
+}

+ 1 - 1
std/php/_std/sys/net/Host.hx

@@ -56,6 +56,6 @@ class Host {
 	}
 
 	public static function localhost() : String {
-		return _SERVER['HTTP_HOST'];
+		return php.Syntax.coalesce(_SERVER['HTTP_HOST'], "localhost");
 	}
 }

+ 63 - 55
std/php/_std/sys/net/Socket.hx

@@ -31,24 +31,32 @@ import sys.io.FileOutput;
 class Socket {
 
 	private var __s : Resource;
-	private var protocol : String;
+	private var stream : Resource;
 	public var input(default,null) : haxe.io.Input;
 	public var output(default,null) : haxe.io.Output;
 	public var custom : Dynamic;
 
+	var protocol : String;
+
 	public function new() : Void {
 		input = @:privateAccess new sys.io.FileInput(null);
 		output = @:privateAccess new sys.io.FileOutput(null);
+		initSocket();
 		protocol = "tcp";
 	}
 
+	private function initSocket() : Void {
+		__s = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
+	}
+
 	private function assignHandler() : Void {
-		@:privateAccess (cast input:FileInput).__f = __s;
-		@:privateAccess (cast output:FileOutput).__f = __s;
+		stream = socket_export_stream(__s);
+		@:privateAccess (cast input:FileInput).__f = stream;
+		@:privateAccess (cast output:FileOutput).__f = stream;
 	}
 
 	public function close() : Void {
-		fclose(__s);
+		socket_close(__s);
 		@:privateAccess (cast input:FileInput).__f = null;
 		@:privateAccess (cast output:FileOutput).__f = null;
 		input.close();
@@ -57,79 +65,59 @@ class Socket {
 
 	public function read() : String {
 		var b = '';
-		while(!feof(__s)) b += fgets(__s);
+		while(!feof(stream)) b += fgets(stream);
 		return b;
 	}
 
 	public function write( content : String ) : Void {
-		fwrite(__s, content);
+		fwrite(stream, content);
 	}
 
 	public function connect(host : Host, port : Int) : Void {
-		var errs = null;
-		var errn = null;
-		var r = stream_socket_client(protocol + '://' + host.host + ':' + port, errn, errs);
-		Socket.checkError(r, errn, errs);
-		__s = r;
+		var r = socket_connect(__s, host.host, port);
+		checkError(r, 0, 'Unable to connect');
 		assignHandler();
 	}
 
 	public function listen(connections : Int) : Void {
-		throw "Not implemented";
-/* TODO: ??????
 		var r = socket_listen(__s, connections);
-		checkError(r);
-*/
+		checkError(r, 0, 'Unable to listen on socket');
+		assignHandler();
 	}
 
 	public function shutdown( read : Bool, write : Bool ) : Void {
 		var rw = read && write ? 2 : (write ? 1 : (read ? 0 : 2));
-		var r = stream_socket_shutdown(__s, rw);
-		checkError(r, 0, 'Unable to Shutdown');
+		var r = socket_shutdown(__s, rw);
+		checkError(r, 0, 'Unable to shutdown');
 	}
 
 	public function bind(host : Host, port : Int) : Void {
-		var errs = Boot.deref(null);
-		var errn = Boot.deref(null);
-		var r = stream_socket_server(
-			protocol + '://' + host.host + ':' + port,
-			errn,
-			errs,
-			(protocol == "udp" ? STREAM_SERVER_BIND : STREAM_SERVER_BIND | STREAM_SERVER_LISTEN)
-		);
-		Socket.checkError(r, errn, errs);
-		__s = cast r;
-		assignHandler();
+		var r = socket_bind(__s, host.host, port);
+		checkError(r, 0, "Unable to bind socket");
 	}
 
 	public function accept() : Socket {
-		var r = stream_socket_accept(__s);
+		var r = socket_accept(__s);
 		checkError(r, 0, 'Unable to accept connections on socket');
 		var s = new Socket();
+		// FIXME: wasted resource here
 		s.__s = r;
 		s.assignHandler();
 		return s;
 	}
 
-	private function hpOfString(s : String) : { host : Host, port : Int } {
-		var parts = s.split(':');
-		if(parts.length == 2) {
-			return { host : new Host(parts[0]), port : Std.parseInt(parts[1]) };
-		} else {
-			return { host : new Host(parts[1].substr(2)), port : Std.parseInt(parts[2]) };
-		}
-	}
-
 	public function peer() : { host : Host, port : Int } {
-		var r = stream_socket_get_name(__s, true);
+		var host:String = "", port:Int = 0;
+		var r = socket_getpeername(__s, host, port);
 		checkError(r, 0, 'Unable to retrieve the peer name');
-		return hpOfString(r);
+		return {host: new Host(host), port: port};
 	}
 
 	public function host() : { host : Host, port : Int } {
-		var r = stream_socket_get_name(__s, false);
+		var host:String = "", port:Int = 0;
+		var r = socket_getsockname(__s, host, port);
 		checkError(r, 0, 'Unable to retrieve the host name');
-		return hpOfString(r);
+		return {host: new Host(host), port: port};
 	}
 
 	public function setTimeout( timeout : Float ) : Void {
@@ -140,12 +128,13 @@ class Socket {
 	}
 
 	public function setBlocking( b : Bool ) : Void {
-		var r = stream_set_blocking(__s, b);
-		checkError(r, 0, 'Unable to block');
+		var r = b ? socket_set_block(__s) : socket_set_nonblock(__s);
+		checkError(r, 0, 'Unable to set blocking');
 	}
 
 	public function setFastSend( b : Bool ) : Void {
-		throw "Not implemented";
+		var r = socket_set_option(__s, SOL_TCP, TCP_NODELAY, true);
+		checkError(r, 0, "Unable to set TCP_NODELAY on socket");
 	}
 
 	public function waitForRead() : Void {
@@ -157,18 +146,37 @@ class Socket {
 		throw haxe.io.Error.Custom('Error [$code]: $msg');
 	}
 
-	private static function getType(isUdp : Bool) : Int {
-		return isUdp ? SOCK_DGRAM : SOCK_STREAM;
-	}
-
-	private static function getProtocol(protocol : String) : Int {
-		return getprotobyname(protocol);
- 	}
-
 	public static function select(read : Array<Socket>, write : Array<Socket>, others : Array<Socket>, ?timeout : Float) : { read: Array<Socket>,write: Array<Socket>,others: Array<Socket> }
 	{
-		throw "Not implemented";
-		return null;
+		var map:Map<Int, Socket> = new Map();
+		inline function addSockets(sockets:Array<Socket>) {
+			if (sockets != null) for (s in sockets) map[Syntax.int(s.__s)] = s;
+		}
+		inline function getRaw(sockets:Array<Socket>):Array<Resource> {
+			return sockets == null ? [] : [for (s in sockets) s.__s];
+		}
+		inline function getOriginal(result:Array<Resource>) {
+			return [for (r in result) map[Syntax.int(r)]];
+		}
+
+		addSockets(read);
+		addSockets(write);
+		addSockets(others);
+
+		// unwrap Sockets into Resources
+		var rawRead:NativeIndexedArray<Resource> = getRaw(read),
+			rawWrite:NativeIndexedArray<Resource> = getRaw(write),
+			rawOthers:NativeIndexedArray<Resource> = getRaw(others);
+		var sec = Std.int(timeout),
+			usec = Std.int((timeout % 1) * 1000000);
+		var result = socket_select(rawRead, rawWrite, rawOthers, sec, usec);
+		checkError(result, 0, "Error during select call");
+		// convert raw resources back to Socket objects
+		return {
+			read: getOriginal(rawRead),
+			write: getOriginal(rawWrite),
+			others: getOriginal(rawOthers),
+		}
 	}
 
 }

+ 145 - 0
std/php/net/Socket.hx

@@ -0,0 +1,145 @@
+/*
+ * Copyright (C)2005-2018 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+package php.net;
+
+import php.*;
+import php.Global.*;
+import php.Const.*;
+import sys.io.FileInput;
+import sys.io.FileOutput;
+import sys.net.Host;
+
+class Socket extends sys.net.Socket {
+
+	public function new() : Void {
+		super();
+		protocol = "tcp";
+	}
+
+	override private function assignHandler() : Void {
+		@:privateAccess (cast input:FileInput).__f = __s;
+		@:privateAccess (cast output:FileOutput).__f = __s;
+	}
+
+	override public function close() : Void {
+		fclose(__s);
+		@:privateAccess (cast input:FileInput).__f = null;
+		@:privateAccess (cast output:FileOutput).__f = null;
+		input.close();
+		output.close();
+	}
+
+	override public function read() : String {
+		var b = '';
+		while(!feof(__s)) b += fgets(__s);
+		return b;
+	}
+
+	override public function write( content : String ) : Void {
+		fwrite(__s, content);
+	}
+
+	override public function connect(host : Host, port : Int) : Void {
+		var errs = null;
+		var errn = null;
+		var r = stream_socket_client(protocol + '://' + host.host + ':' + port, errn, errs);
+		checkError(r, errn, errs);
+		__s = r;
+		assignHandler();
+	}
+
+	override public function listen(connections : Int) : Void {
+		throw "Not implemented";
+/* TODO: ??????
+		var r = socket_listen(__s, connections);
+		checkError(r);
+*/
+	}
+
+	override public function shutdown( read : Bool, write : Bool ) : Void {
+		var rw = read && write ? 2 : (write ? 1 : (read ? 0 : 2));
+		var r = stream_socket_shutdown(__s, rw);
+		checkError(r, 0, 'Unable to Shutdown');
+	}
+
+	override public function bind(host : Host, port : Int) : Void {
+		var errs = Boot.deref(null);
+		var errn = Boot.deref(null);
+		var r = stream_socket_server(
+			protocol + '://' + host.host + ':' + port,
+			errn,
+			errs,
+			(protocol == "udp" ? STREAM_SERVER_BIND : STREAM_SERVER_BIND | STREAM_SERVER_LISTEN)
+		);
+		Socket.checkError(r, errn, errs);
+		__s = cast r;
+		assignHandler();
+	}
+
+	override public function accept() : Socket {
+		var r = stream_socket_accept(__s);
+		checkError(r, 0, 'Unable to accept connections on socket');
+		var s = new Socket();
+		s.__s = r;
+		s.assignHandler();
+		return s;
+	}
+
+	private function hpOfString(s : String) : { host : Host, port : Int } {
+		var parts = s.split(':');
+		if(parts.length == 2) {
+				return { host : new Host(parts[0]), port : Std.parseInt(parts[1]) };
+		} else {
+				return { host : new Host(parts[1].substr(2)), port : Std.parseInt(parts[2]) };
+		}
+	}
+
+	override public function peer() : { host : Host, port : Int } {
+		var r = stream_socket_get_name(__s, true);
+		checkError(r, 0, 'Unable to retrieve the peer name');
+		return hpOfString(r);
+	}
+
+	override public function host() : { host : Host, port : Int } {
+		var r = stream_socket_get_name(__s, false);
+		checkError(r, 0, 'Unable to retrieve the host name');
+		return hpOfString(r);
+	}
+	private static function getType(isUdp : Bool) : Int {
+		return isUdp ? SOCK_DGRAM : SOCK_STREAM;
+	}
+
+	private static function getProtocol(protocol : String) : Int {
+		return getprotobyname(protocol);
+ 	}
+
+	public static function select(read : Array<Socket>, write : Array<Socket>, others : Array<Socket>, ?timeout : Float) : { read: Array<Socket>,write: Array<Socket>,others: Array<Socket> }
+	{
+		throw "Not implemented";
+		return null;
+	}
+
+	private static inline function checkError(r : Bool, code : Int, msg : String) : Void {
+		return sys.net.Socket.checkError(r, code, msg);
+	}
+
+}

+ 1 - 1
std/php/net/SslSocket.hx

@@ -21,7 +21,7 @@
  */
 package php.net;
 
-class SslSocket extends sys.net.Socket {
+class SslSocket extends php.net.Socket {
 
 	public function new() : Void {
 		super();

+ 8 - 11
std/python/_std/sys/net/Socket.hx

@@ -134,18 +134,15 @@ private class SocketOutput extends haxe.io.Output {
         Creates a new unconnected socket.
     **/
     public function new() : Void {
-    }
-
-    function __initSocket ():Void {
-        __s = new PSocket();
-    }
-
-    function __init() : Void  {
         __initSocket();
         input = new SocketInput(__s);
         output = new SocketOutput(__s);
     }
 
+    function __initSocket() : Void {
+        __s = new PSocket();
+    }
+
     /**
         Closes the socket : make sure to properly close all your sockets or you will crash when you run out of file descriptors.
     **/
@@ -171,7 +168,6 @@ private class SocketOutput extends haxe.io.Output {
         Connect to the given server host/port. Throw an exception in case we couldn't successfully connect.
     **/
     public function connect( host : Host, port : Int ) : Void {
-        __init();
         var host_str = host.toString();
         __s.connect(Tuple2.make(host_str,port));
     }
@@ -193,7 +189,6 @@ private class SocketOutput extends haxe.io.Output {
         Bind the socket to the given host/port so it can afterwards listen for connections there.
     **/
     public function bind( host : Host, port : Int ) : Void {
-        __init();
         var host_str = host.toString();
         __s.bind(Tuple2.make(host_str,port));
     }
@@ -237,7 +232,7 @@ private class SocketOutput extends haxe.io.Output {
         Block until some data is available for read on the socket.
     **/
     public function waitForRead() : Void {
-
+        Select.select([this],[],[]);
     }
 
     /**
@@ -250,7 +245,9 @@ private class SocketOutput extends haxe.io.Output {
     /**
         Allows the socket to immediately send the data when written to its output : this will cause less ping but might increase the number of packets / data size, especially when doing a lot of small writes.
     **/
-    public function setFastSend( b : Bool ) : Void {}
+    public function setFastSend( b : Bool ) : Void {
+        __s.setsockopt(PSocketModule.SOL_TCP, PSocketModule.TCP_NODELAY, b);
+    }
 
     @:keep function fileno():Int return __s.fileno();
 

+ 1 - 1
std/python/net/SslSocket.hx

@@ -39,4 +39,4 @@ class SslSocket extends sys.net.Socket {
 	public override function bind( host : Host, port : Int ) : Void {
 		throw "not implemented";
 	}
-}
+}

+ 60 - 0
tests/unit/src/unitstd/sys/net/Socket.unit.hx

@@ -0,0 +1,60 @@
+#if sys
+// bind & listen
+var s = new sys.net.Socket();
+var host = new sys.net.Host("127.0.0.1");
+s.bind(host, 0);
+s.listen(1);
+var port = s.host().port;
+port > 0;
+
+// connect
+var c = new sys.net.Socket();
+c.connect(host, port);
+c.input != null;
+c.output != null;
+
+#if !java
+// select when accept() would succeed
+var select = sys.net.Socket.select([s], [s], [s], 0.01);
+select.read.length == 1;
+select.write.length == 0;
+select.others.length == 0;
+
+// multiple selects without reading
+var select = sys.net.Socket.select([s], [s], [s], 0.01);
+select.read.length == 1;
+select.write.length == 0;
+select.others.length == 0;
+
+// accept
+var w = s.accept();
+w != null;
+w.input != null;
+w.output != null;
+w.setFastSend(true);
+s.setBlocking(false);
+
+// select after accept
+var select = sys.net.Socket.select([s], [s], [s], 0.01);
+select.read.length == 0;
+select.write.length == 0;
+select.others.length == 0;
+
+// write
+w.output.writeByte(97);
+w.output.writeByte(98);
+w.output.writeByte(99);
+w.close();
+
+// read
+c.waitForRead();
+var select = sys.net.Socket.select([c], [c], [c]);
+select.read.length == 1;
+select.write.length == 1;
+select.others.length == 0;
+c.read() == "abc";
+#end
+
+c.close();
+s.close();
+#end