Browse Source

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 years ago
parent
commit
0f1633503d

+ 1 - 0
appveyor.yml

@@ -50,6 +50,7 @@ install:
     - choco install php --version 7.2.0 -y
     - choco install php --version 7.2.0 -y
     - echo extension=php_openssl.dll >> C:\tools\php72\php.ini
     - echo extension=php_openssl.dll >> C:\tools\php72\php.ini
     - echo extension=php_mbstring.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
     - RefreshEnv
     # setup python
     # setup python
     - cmd: mklink C:\Python34-x64\python3.exe C:\Python34-x64\python.exe
     - 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 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 i = get_instance_field_index proto key_socket null_pos in
 		let pair = function
 		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"
 			| v -> unexpected_value v "NativeSocket"
 		in
 		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 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',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
 		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 haxe.io.Error;
 import cpp.NativeSocket;
 import cpp.NativeSocket;
+import cpp.NativeString;
+import cpp.Pointer;
 
 
 private class SocketInput extends haxe.io.Input {
 private class SocketInput extends haxe.io.Input {
 
 
@@ -144,7 +146,8 @@ class Socket {
    public function read() : String {
    public function read() : String {
       var bytes:haxe.io.BytesData = NativeSocket.socket_read(__s);
       var bytes:haxe.io.BytesData = NativeSocket.socket_read(__s);
       if (bytes==null) return "";
       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 {
    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
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * DEALINGS IN THE SOFTWARE.
  */
  */
- 
+
 package sys.net;
 package sys.net;
 
 
+import cs.NativeArray;
+import cs.system.collections.ArrayList;
 import cs.system.net.IPEndPoint;
 import cs.system.net.IPEndPoint;
 import cs.system.net.sockets.AddressFamily;
 import cs.system.net.sockets.AddressFamily;
 import cs.system.net.sockets.NetworkStream;
 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.SocketShutdown;
 import cs.system.net.sockets.SocketType;
 import cs.system.net.sockets.SocketType;
 import cs.system.threading.Thread;
 import cs.system.threading.Thread;
+import cs.system.net.sockets.Socket in NativeSocket;
 import haxe.io.Bytes;
 import haxe.io.Bytes;
 import haxe.io.Error;
 import haxe.io.Error;
 import haxe.io.Input;
 import haxe.io.Input;
@@ -40,7 +43,7 @@ import haxe.io.Output;
 **/
 **/
 @:coreapi
 @:coreapi
 class Socket {
 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,
 		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.
 		Creates a new unconnected socket.
 	**/
 	**/
 	public function new() : Void {
 	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;
 		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.
 		Bind the socket to the given host/port so it can afterwards listen for connections there.
 	**/
 	**/
 	public function bind( host : Host, port : Int ) : Void {
 	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) );
 		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.
 		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> } {
 	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
 	   Define an array from the given table
 	*/
 	*/
 	public inline static function defArray<T>(tab: Table<Int,T>, ?length : Int) : Array<T> {
 	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;
 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.
 	and vice-versa.
 **/
 **/
 class Lib {
 class Lib {

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

@@ -43,8 +43,8 @@ class Sys {
 		return lua.Lib.println(v);
 		return lua.Lib.println(v);
 	}
 	}
 	public inline static function args() : Array<String> {
 	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;
 		return args;
 	}
 	}
 	public static function command( cmd : String, ?args : Array<String> ) : Int  {
 	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;
 package lua.lib.luasocket;
+import lua.lib.luasocket.socket.*;
+
 @:luaRequire("socket")
 @:luaRequire("socket")
 extern class 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_ASYNC_CONNECT : Int;
 	static var STREAM_CLIENT_PERSISTENT : 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_STREAM : Int;
 	static var SOCK_DGRAM : Int;
 	static var SOCK_DGRAM : Int;
@@ -166,6 +166,9 @@ extern class Const {
 	static var AF_INET : Int;
 	static var AF_INET : Int;
 	static var AF_INET6 : Int;
 	static var AF_INET6 : Int;
 	static var AF_UNIX : 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
 		@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_SYNC_FLUSH : Int;
 	static var ZLIB_FULL_FLUSH : Int;
 	static var ZLIB_FULL_FLUSH : Int;
 	static var ZLIB_FINISH : 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>;
 	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
 		@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
 		@see http://php.net/manual/en/function.is-uploaded-file.php
 	**/
 	**/
 	static function is_uploaded_file( filename:String ) : Bool;
 	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 {
 	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 {
 class Socket {
 
 
 	private var __s : Resource;
 	private var __s : Resource;
-	private var protocol : String;
+	private var stream : Resource;
 	public var input(default,null) : haxe.io.Input;
 	public var input(default,null) : haxe.io.Input;
 	public var output(default,null) : haxe.io.Output;
 	public var output(default,null) : haxe.io.Output;
 	public var custom : Dynamic;
 	public var custom : Dynamic;
 
 
+	var protocol : String;
+
 	public function new() : Void {
 	public function new() : Void {
 		input = @:privateAccess new sys.io.FileInput(null);
 		input = @:privateAccess new sys.io.FileInput(null);
 		output = @:privateAccess new sys.io.FileOutput(null);
 		output = @:privateAccess new sys.io.FileOutput(null);
+		initSocket();
 		protocol = "tcp";
 		protocol = "tcp";
 	}
 	}
 
 
+	private function initSocket() : Void {
+		__s = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
+	}
+
 	private function assignHandler() : Void {
 	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 {
 	public function close() : Void {
-		fclose(__s);
+		socket_close(__s);
 		@:privateAccess (cast input:FileInput).__f = null;
 		@:privateAccess (cast input:FileInput).__f = null;
 		@:privateAccess (cast output:FileOutput).__f = null;
 		@:privateAccess (cast output:FileOutput).__f = null;
 		input.close();
 		input.close();
@@ -57,79 +65,59 @@ class Socket {
 
 
 	public function read() : String {
 	public function read() : String {
 		var b = '';
 		var b = '';
-		while(!feof(__s)) b += fgets(__s);
+		while(!feof(stream)) b += fgets(stream);
 		return b;
 		return b;
 	}
 	}
 
 
 	public function write( content : String ) : Void {
 	public function write( content : String ) : Void {
-		fwrite(__s, content);
+		fwrite(stream, content);
 	}
 	}
 
 
 	public function connect(host : Host, port : Int) : Void {
 	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();
 		assignHandler();
 	}
 	}
 
 
 	public function listen(connections : Int) : Void {
 	public function listen(connections : Int) : Void {
-		throw "Not implemented";
-/* TODO: ??????
 		var r = socket_listen(__s, connections);
 		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 {
 	public function shutdown( read : Bool, write : Bool ) : Void {
 		var rw = read && write ? 2 : (write ? 1 : (read ? 0 : 2));
 		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 {
 	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 {
 	public function accept() : Socket {
-		var r = stream_socket_accept(__s);
+		var r = socket_accept(__s);
 		checkError(r, 0, 'Unable to accept connections on socket');
 		checkError(r, 0, 'Unable to accept connections on socket');
 		var s = new Socket();
 		var s = new Socket();
+		// FIXME: wasted resource here
 		s.__s = r;
 		s.__s = r;
 		s.assignHandler();
 		s.assignHandler();
 		return s;
 		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 } {
 	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');
 		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 } {
 	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');
 		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 {
 	public function setTimeout( timeout : Float ) : Void {
@@ -140,12 +128,13 @@ class Socket {
 	}
 	}
 
 
 	public function setBlocking( b : Bool ) : Void {
 	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 {
 	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 {
 	public function waitForRead() : Void {
@@ -157,18 +146,37 @@ class Socket {
 		throw haxe.io.Error.Custom('Error [$code]: $msg');
 		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> }
 	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;
 package php.net;
 
 
-class SslSocket extends sys.net.Socket {
+class SslSocket extends php.net.Socket {
 
 
 	public function new() : Void {
 	public function new() : Void {
 		super();
 		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.
         Creates a new unconnected socket.
     **/
     **/
     public function new() : Void {
     public function new() : Void {
-    }
-
-    function __initSocket ():Void {
-        __s = new PSocket();
-    }
-
-    function __init() : Void  {
         __initSocket();
         __initSocket();
         input = new SocketInput(__s);
         input = new SocketInput(__s);
         output = new SocketOutput(__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.
         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.
         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 {
     public function connect( host : Host, port : Int ) : Void {
-        __init();
         var host_str = host.toString();
         var host_str = host.toString();
         __s.connect(Tuple2.make(host_str,port));
         __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.
         Bind the socket to the given host/port so it can afterwards listen for connections there.
     **/
     **/
     public function bind( host : Host, port : Int ) : Void {
     public function bind( host : Host, port : Int ) : Void {
-        __init();
         var host_str = host.toString();
         var host_str = host.toString();
         __s.bind(Tuple2.make(host_str,port));
         __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.
         Block until some data is available for read on the socket.
     **/
     **/
     public function waitForRead() : Void {
     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.
         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();
     @: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 {
 	public override function bind( host : Host, port : Int ) : Void {
 		throw "not implemented";
 		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