From b2d0513ed3788ff902d873be02be7cf4d490d3c0 Mon Sep 17 00:00:00 2001 From: neimanpinchas Date: Mon, 13 May 2024 18:07:03 +0300 Subject: [PATCH 01/20] add cors, options add cors middleware before listen rename hidden cors field to public field so user can opt in update map of allowed methods on every get, post.. on setup generate options response string to avoid runtime cost implicitly add options option --- weblink/Weblink.hx | 30 ++++++++++++++++++++++++++---- weblink/_internal/Server.hx | 8 ++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/weblink/Weblink.hx b/weblink/Weblink.hx index 42642ac..f4c65b3 100644 --- a/weblink/Weblink.hx +++ b/weblink/Weblink.hx @@ -14,6 +14,7 @@ using haxe.io.Path; class Weblink { public var server:Null; public var routeTree:RadixTree; + public var allowed_methods = new Map(); private var middlewareToChain:Array = []; @@ -26,10 +27,16 @@ class Weblink { response.send("Error 404, Route Not found."); } + public function cors_middleware(request:Request, response:Response):Void { + response.headers.add({key: "Access-Control-Allow-Origin", value: cors}); + } + var _serve:Bool = false; var _path:String; var _dir:String; - var _cors:String = "*"; + + public var cors:String = "*"; + public var allowed_methods_string = ""; public function new() { this.routeTree = new RadixTree(); @@ -68,29 +75,44 @@ class Weblink { if (middleware != null) { func = middleware(func); } + allowed_methods[Get] = true; _updateRoute(path, Get, func); } public function post(path:String, func:Handler) { + allowed_methods[Post] = true; _updateRoute(path, Post, func); } public function put(path:String, func:Handler) { + allowed_methods[Put] = true; _updateRoute(path, Put, func); } public function head(path:String, func:Handler) { + allowed_methods[Head] = true; _updateRoute(path, Head, func); } public function listen(port:Int, blocking:Bool = true) { + if (cors.length > 0) + this.middlewareToChain.push(cors_middleware); this.pathNotFound = chainMiddleware(this.pathNotFound); + allowed_methods[Options] = true; + var allowed_methods_array = new Array(); + for (k => v in allowed_methods) { + if (v) { + allowed_methods_array.push(k); + } + } + + allowed_methods_string = allowed_methods_array.join(", "); server = new Server(port, this); server.update(blocking); } public function serve(path:String = "", dir:String = "", cors:String = "*") { - _cors = cors; + this.cors = cors; _path = path; _dir = dir; _serve = true; @@ -127,8 +149,8 @@ class Weblink { var ext = request.path.extension(); var mime = weblink._internal.Mime.types.get(ext); response.headers = new List
(); - if (_cors.length > 0) - response.headers.add({key: "Access-Control-Allow-Origin", value: _cors}); + if (cors.length > 0) + response.headers.add({key: "Access-Control-Allow-Origin", value: cors}); response.contentType = mime == null ? "text/plain" : mime; var path = Path.join([_dir, request.basePath.substr(_path.length)]).normalize(); if (path == "") diff --git a/weblink/_internal/Server.hx b/weblink/_internal/Server.hx index c54ed1d..c90dbdf 100644 --- a/weblink/_internal/Server.hx +++ b/weblink/_internal/Server.hx @@ -8,11 +8,15 @@ import hl.uv.Stream; import sys.net.Host; import weblink._internal.Socket; +using Lambda; + class Server extends SocketServer { // var sockets:Array; var parent:Weblink; var stream:Stream; + public var running:Bool = true; + var loop:hl.uv.Loop; public function new(port:Int, parent:Weblink) { @@ -76,6 +80,10 @@ class Server extends SocketServer { private function complete(request:Request, socket:Socket) { @:privateAccess var response = request.response(this, socket); + if (request.method == Options) { + response.send("Allow: " + parent.allowed_methods_string); + } + if (request.method == Get && @:privateAccess parent._serve && response.status == OK From 1317dbd258d725ab85a67bee992910029590f63c Mon Sep 17 00:00:00 2001 From: neimanpinchas Date: Mon, 13 May 2024 18:10:37 +0300 Subject: [PATCH 02/20] fix nullexp create headers list in cors mw --- weblink/Weblink.hx | 1 + 1 file changed, 1 insertion(+) diff --git a/weblink/Weblink.hx b/weblink/Weblink.hx index f4c65b3..6f886e1 100644 --- a/weblink/Weblink.hx +++ b/weblink/Weblink.hx @@ -28,6 +28,7 @@ class Weblink { } public function cors_middleware(request:Request, response:Response):Void { + response.headers = new List
(); response.headers.add({key: "Access-Control-Allow-Origin", value: cors}); } From de9ae424607e3bd0a7002998052808819e2eacc3 Mon Sep 17 00:00:00 2001 From: neimanpinchas Date: Mon, 13 May 2024 18:14:53 +0300 Subject: [PATCH 03/20] fix bug do not find other routes on options --- weblink/_internal/Server.hx | 1 + 1 file changed, 1 insertion(+) diff --git a/weblink/_internal/Server.hx b/weblink/_internal/Server.hx index c90dbdf..d0090f7 100644 --- a/weblink/_internal/Server.hx +++ b/weblink/_internal/Server.hx @@ -82,6 +82,7 @@ class Server extends SocketServer { if (request.method == Options) { response.send("Allow: " + parent.allowed_methods_string); + return; } if (request.method == Get From 4d5705accd5fa6d9df428a2a8ace11b1f9985284 Mon Sep 17 00:00:00 2001 From: neimanpinchas Date: Mon, 13 May 2024 18:21:26 +0300 Subject: [PATCH 04/20] run cors on options req this was the main intent of this PR now I think we might instead add the options handler as a std route, instead of making a special case --- weblink/_internal/Server.hx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/weblink/_internal/Server.hx b/weblink/_internal/Server.hx index d0090f7..610b3d3 100644 --- a/weblink/_internal/Server.hx +++ b/weblink/_internal/Server.hx @@ -81,6 +81,8 @@ class Server extends SocketServer { @:privateAccess var response = request.response(this, socket); if (request.method == Options) { + if (parent.cors.length > 0) + parent.cors_middleware(request, response); response.send("Allow: " + parent.allowed_methods_string); return; } From 50ac566c0c6dcb8fad2a51864d8f8684fbf1c21d Mon Sep 17 00:00:00 2001 From: neimanpinchas Date: Mon, 13 May 2024 19:01:09 +0300 Subject: [PATCH 05/20] allow headers in cors for remoting Need to think about a proper typedef for cors config with options for headers, methods, origins etc. --- weblink/Weblink.hx | 1 + 1 file changed, 1 insertion(+) diff --git a/weblink/Weblink.hx b/weblink/Weblink.hx index 6f886e1..781904a 100644 --- a/weblink/Weblink.hx +++ b/weblink/Weblink.hx @@ -30,6 +30,7 @@ class Weblink { public function cors_middleware(request:Request, response:Response):Void { response.headers = new List
(); response.headers.add({key: "Access-Control-Allow-Origin", value: cors}); + response.headers.add({key: "Access-Control-Allow-Headers", value: "*"}); } var _serve:Bool = false; From e23a0e96f3e0df34488df393833e142ec7512d5e Mon Sep 17 00:00:00 2001 From: neimanpinchas Date: Mon, 13 May 2024 19:07:30 +0300 Subject: [PATCH 06/20] move cors to seperate method Placing it in listen has no effect because MW is already flattened --- weblink/Weblink.hx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/weblink/Weblink.hx b/weblink/Weblink.hx index 781904a..6755813 100644 --- a/weblink/Weblink.hx +++ b/weblink/Weblink.hx @@ -73,6 +73,11 @@ class Weblink { this.routeTree.put(path, method, chainMiddleware(handler)); } + public function enable_cors(_cors:String) { + cors = _cors; + this.middlewareToChain.push(cors_middleware); + } + public function get(path:String, func:Handler, ?middleware:Middleware) { if (middleware != null) { func = middleware(func); @@ -97,8 +102,6 @@ class Weblink { } public function listen(port:Int, blocking:Bool = true) { - if (cors.length > 0) - this.middlewareToChain.push(cors_middleware); this.pathNotFound = chainMiddleware(this.pathNotFound); allowed_methods[Options] = true; var allowed_methods_array = new Array(); From de5d7ae39dcb5e208a892a3164857cb9ebc97bf8 Mon Sep 17 00:00:00 2001 From: neimanpinchas Date: Tue, 14 May 2024 13:36:23 +0300 Subject: [PATCH 07/20] add res.json --- weblink/Response.hx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/weblink/Response.hx b/weblink/Response.hx index 89e2008..f63bf56 100644 --- a/weblink/Response.hx +++ b/weblink/Response.hx @@ -1,5 +1,6 @@ package weblink; +import haxe.Json; import haxe.http.HttpStatus; import haxe.io.Bytes; import haxe.io.Encoding; @@ -63,6 +64,10 @@ class Response { this.sendBytes(Bytes.ofString(data, Encoding.UTF8)); } + public inline function json(data:Dynamic, pretty = false) { + send(if (pretty) Json.stringify(data, null, " ") else Json.stringify(data)); + } + private function end() { this.server = null; final socket = this.socket; From df28531d08754bc25011f156185d0a812a60a163 Mon Sep 17 00:00:00 2001 From: neimanpinchas Date: Fri, 7 Jun 2024 13:31:06 +0300 Subject: [PATCH 08/20] add js support, for the meantime as hashlink still has some bugs --- tests.hxml | 4 ++- weblink/_internal/Server.hx | 27 +++++++++++++++++--- weblink/_internal/Socket.hx | 31 ++++++++++++++++++++++- weblink/_internal/SocketServer.hx | 42 ++++++++++++++++++++++++++++++- weblink/security/Sign.hx | 4 +++ 5 files changed, 101 insertions(+), 7 deletions(-) diff --git a/tests.hxml b/tests.hxml index d172e5c..8e48dae 100644 --- a/tests.hxml +++ b/tests.hxml @@ -1,4 +1,6 @@ -cp tests -main Test --macro nullSafety("weblink._internal.ds", StrictThreaded) --hl test.hl \ No newline at end of file +#-hl test.hl +--js test.js +-L hxnodejs diff --git a/weblink/_internal/Server.hx b/weblink/_internal/Server.hx index 610b3d3..9bb53fc 100644 --- a/weblink/_internal/Server.hx +++ b/weblink/_internal/Server.hx @@ -3,13 +3,29 @@ package weblink._internal; import haxe.MainLoop; import haxe.http.HttpMethod; import haxe.io.Bytes; -import hl.uv.Loop.LoopRunMode; -import hl.uv.Stream; import sys.net.Host; import weblink._internal.Socket; using Lambda; +#if hl +import hl.uv.Loop.LoopRunMode; +import hl.uv.Loop; +import hl.uv.Stream; +#else +class Loop { + public function new() {} + + public static function getDefault():Loop { + return new Loop(); + } + + public function stop() {} + + public function run(a) {} +} +#end + class Server extends SocketServer { // var sockets:Array; var parent:Weblink; @@ -17,11 +33,11 @@ class Server extends SocketServer { public var running:Bool = true; - var loop:hl.uv.Loop; + var loop:Loop; public function new(port:Int, parent:Weblink) { // sockets = []; - loop = hl.uv.Loop.getDefault(); + loop = Loop.getDefault(); super(loop); bind(new Host("0.0.0.0"), port); noDelay(true); @@ -114,6 +130,9 @@ class Server extends SocketServer { public function update(blocking:Bool = true) { do { @:privateAccess MainLoop.tick(); // for timers + #if js + var NoWait = 0; + #end loop.run(NoWait); } while (running && blocking); } diff --git a/weblink/_internal/Socket.hx b/weblink/_internal/Socket.hx index cb53674..24a65d1 100644 --- a/weblink/_internal/Socket.hx +++ b/weblink/_internal/Socket.hx @@ -1,8 +1,37 @@ package weblink._internal; import haxe.io.Bytes; +import js.node.Buffer; +#if hl +import hl.uv.Stream; +#else +// typedef Stream = js.node.net.Socket; -private typedef Basic = hl.uv.Stream +abstract Stream(js.node.net.Socket) { + // public extern function write(b:Bytes):Void; + // public extern function close(?callb:() -> Void):Void; + public function new(s:js.node.net.Socket) { + this = s; + } + + public function readStart(callb:(data:Bytes) -> Void) { + this.on("data", function(d:Buffer) { + callb(d.hxToBytes()); + }); + } + + public function close() { + this.end(); + } + + // TODO we really want a js buffer + public function write(b:Bytes) { + this.write(b.toString()); + } +} +#end + +private typedef Basic = Stream abstract Socket(Basic) { inline public function new(i:Basic) { diff --git a/weblink/_internal/SocketServer.hx b/weblink/_internal/SocketServer.hx index e42973e..adb3055 100644 --- a/weblink/_internal/SocketServer.hx +++ b/weblink/_internal/SocketServer.hx @@ -1,4 +1,44 @@ package weblink._internal; +import sys.net.Host; + +#if js +class SocketServer +#else class SocketServer extends #if (hl && !nolibuv) hl.uv.Tcp #else sys.net.Socket #end -{} +#end +{ + #if js + public var node_socket:js.node.net.Server; + public var cons = new Array(); + public function new(loop:Server.Loop) {} + + public dynamic function on_connect(con) { + trace("connected", con); + } + + public function bind(host:Host, port:Int) { + node_socket = new js.node.net.Server(); + node_socket.listen(port, host.host); + } + + public function listen(backlog:Int, cb:Void->Void) { + node_socket.on("connection", function(con) { + trace("new con", con); + cons.push(con); + cb(); + }); + } + + public function accept() { + trace("handling"); + return cons.shift(); + } + + public function noDelay(yn:Bool) {} + + public function close(?callb:Null<() -> Void>) { + node_socket.close(callb); + } + #end +} diff --git a/weblink/security/Sign.hx b/weblink/security/Sign.hx index f95b4e9..2234ec8 100644 --- a/weblink/security/Sign.hx +++ b/weblink/security/Sign.hx @@ -36,7 +36,11 @@ class Sign { if (string1.length != string2.length) { return false; } + #if hl var v = @:privateAccess string1.bytes.compare16(string2.bytes, string1.length); return v == 0; + #else + return string1==string2; + #end } } From 2f9a5bf7460c6faf25098c57103152b20505b011 Mon Sep 17 00:00:00 2001 From: neimanpinchas Date: Mon, 10 Jun 2024 18:04:29 +0300 Subject: [PATCH 09/20] remove logs --- weblink/_internal/SocketServer.hx | 2 -- 1 file changed, 2 deletions(-) diff --git a/weblink/_internal/SocketServer.hx b/weblink/_internal/SocketServer.hx index adb3055..f7b58ed 100644 --- a/weblink/_internal/SocketServer.hx +++ b/weblink/_internal/SocketServer.hx @@ -24,14 +24,12 @@ class SocketServer extends #if (hl && !nolibuv) hl.uv.Tcp #else sys.net.Socket # public function listen(backlog:Int, cb:Void->Void) { node_socket.on("connection", function(con) { - trace("new con", con); cons.push(con); cb(); }); } public function accept() { - trace("handling"); return cons.shift(); } From ffdffb546858af35ac23100e94b2fb4b1d02850d Mon Sep 17 00:00:00 2001 From: neimanpinchas Date: Mon, 10 Jun 2024 18:32:01 +0300 Subject: [PATCH 10/20] preserve whole url in url field remove query string from path it is BTW removed from basepath --- weblink/Request.hx | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/weblink/Request.hx b/weblink/Request.hx index eced023..7567a44 100644 --- a/weblink/Request.hx +++ b/weblink/Request.hx @@ -7,12 +7,19 @@ import weblink._internal.Server; class Request { public var cookies:List; + // these exclude the query string public var path:String; + // 1st segment only public var basePath:String; + // this includes the query string + public var url:String; + /** Contains values for parameters declared in the route matched, if there are any. **/ public var routeParams:Map; + public var ip:String; public var baseUrl:String; + public var headers:StringMap; public var text:String; public var method:HttpMethod; @@ -37,18 +44,23 @@ class Request { var first = lines[0]; var index = first.indexOf("/"); path = first.substring(index, first.indexOf(" ", index + 1)); + url = path; // preserve the url path; + var q = path.indexOf("?", 1); + if (q < 0) + q = path.length; + path = path.substring(0, q); var index2 = path.indexOf("/", 1); var index3 = path.indexOf("?", 1); if (index2 == -1) index2 = index3; if (index2 != -1) { - basePath = path.substr(0,index2); - }else{ + basePath = path.substr(0, index2); + } else { basePath = path; } // trace(basePath); // trace(path); - //trace(first.substring(0, index - 1).toUpperCase()); + // trace(first.substring(0, index - 1).toUpperCase()); method = first.substring(0, index - 1).toUpperCase(); for (i in 0...lines.length - 1) { if (lines[i] == "") { @@ -143,11 +155,11 @@ class Request { final r = ~/(?:\?|&|;)([^=]+)=([^&|;]+)/; var obj = {}; var init:Bool = true; - var string:String = path; + var string:String = url; while (r.match(string)) { if (init) { var pos = r.matchedPos().pos; - path = path.substring(0, pos); + url = url.substring(0, pos); init = false; } // 0 entire, 1 name, 2 value From 4b385ac8fe5e8dbdaad3a32586c325b3c9658bc4 Mon Sep 17 00:00:00 2001 From: neimanpinchas Date: Mon, 19 Aug 2024 15:07:06 +0300 Subject: [PATCH 11/20] add error handing to handlers --- weblink/_internal/Server.hx | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/weblink/_internal/Server.hx b/weblink/_internal/Server.hx index 9bb53fc..5e74b1d 100644 --- a/weblink/_internal/Server.hx +++ b/weblink/_internal/Server.hx @@ -112,15 +112,28 @@ class Server extends SocketServer { } } + var execute=(handler)->{ + try { + + handler(request, response); + } catch(ex){ + trace(ex,ex.stack); + //TODO check PRODUCTION env var + response.status=500; + response.send(ex.toString()+"\n"+ex.stack); + } + } + switch (parent.routeTree.tryGet(request.basePath, request.method)) { case Found(handler, params): request.routeParams = params; - handler(request, response); + execute(handler); + case _: switch (parent.routeTree.tryGet(request.path, request.method)) { case Found(handler, params): request.routeParams = params; - handler(request, response); + execute(handler); case _: @:privateAccess parent.pathNotFound(request, response); } From 5303001bc4964ad45c562a59fec9d3638e51c3bb Mon Sep 17 00:00:00 2001 From: neimanpinchas Date: Mon, 19 Aug 2024 15:07:45 +0300 Subject: [PATCH 12/20] fix bug with waiting for end of input --- weblink/_internal/Socket.hx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/weblink/_internal/Socket.hx b/weblink/_internal/Socket.hx index 24a65d1..be69f8c 100644 --- a/weblink/_internal/Socket.hx +++ b/weblink/_internal/Socket.hx @@ -1,7 +1,10 @@ package weblink._internal; import haxe.io.Bytes; +import haxe.io.BytesBuffer; +#if js import js.node.Buffer; +#end #if hl import hl.uv.Stream; #else @@ -15,9 +18,15 @@ abstract Stream(js.node.net.Socket) { } public function readStart(callb:(data:Bytes) -> Void) { + // var bb = new BytesBuffer(); this.on("data", function(d:Buffer) { + // bb.add(d.hxToBytes()); callb(d.hxToBytes()); }); + /* + this.on("end", function(d:Buffer) { + callb(bb.getBytes()); + });*/ } public function close() { From 87c3acb01bdbb8a242999ebd81da989a927be4d3 Mon Sep 17 00:00:00 2001 From: neimanpinchas Date: Mon, 19 Aug 2024 15:11:25 +0300 Subject: [PATCH 13/20] stream only on js --- weblink/_internal/Socket.hx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/weblink/_internal/Socket.hx b/weblink/_internal/Socket.hx index 24a65d1..5a0755b 100644 --- a/weblink/_internal/Socket.hx +++ b/weblink/_internal/Socket.hx @@ -1,10 +1,12 @@ package weblink._internal; import haxe.io.Bytes; +#if js import js.node.Buffer; +#end #if hl import hl.uv.Stream; -#else +#elseif js // typedef Stream = js.node.net.Socket; abstract Stream(js.node.net.Socket) { From 2a85b30928c1bc82787d0c9d2cc73fad825173cc Mon Sep 17 00:00:00 2001 From: neimanpinchas Date: Mon, 19 Aug 2024 15:11:41 +0300 Subject: [PATCH 14/20] add testing for js --- testsjs.hxml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 testsjs.hxml diff --git a/testsjs.hxml b/testsjs.hxml new file mode 100644 index 0000000..8e48dae --- /dev/null +++ b/testsjs.hxml @@ -0,0 +1,6 @@ +-cp tests +-main Test +--macro nullSafety("weblink._internal.ds", StrictThreaded) +#-hl test.hl +--js test.js +-L hxnodejs From 2395dc5510d540bf5077bf1f236ca93cccf41da5 Mon Sep 17 00:00:00 2001 From: neimanpinchas Date: Thu, 19 Sep 2024 14:45:37 +0300 Subject: [PATCH 15/20] trying to make some testing for js target, currently having issues with sync http --- haxe_libraries/hxnodejs.hxml | 7 +++++++ tests/sys/thread/Thread.hx | 5 +++++ testsjs.hxml | 1 + 3 files changed, 13 insertions(+) create mode 100644 haxe_libraries/hxnodejs.hxml create mode 100644 tests/sys/thread/Thread.hx diff --git a/haxe_libraries/hxnodejs.hxml b/haxe_libraries/hxnodejs.hxml new file mode 100644 index 0000000..4fcea51 --- /dev/null +++ b/haxe_libraries/hxnodejs.hxml @@ -0,0 +1,7 @@ +# @install: lix --silent download "haxelib:/hxnodejs#12.2.0" into hxnodejs/12.2.0/haxelib +-cp ${HAXE_LIBCACHE}/hxnodejs/12.2.0/haxelib/src +-D hxnodejs=12.2.0 +--macro allowPackage('sys') +# should behave like other target defines and not be defined in macro context +--macro define('nodejs') +--macro _internal.SuppressDeprecated.run() diff --git a/tests/sys/thread/Thread.hx b/tests/sys/thread/Thread.hx new file mode 100644 index 0000000..80a639e --- /dev/null +++ b/tests/sys/thread/Thread.hx @@ -0,0 +1,5 @@ +#if js +function create(f) { + Timers.setImmdiate(f); +} +#end diff --git a/testsjs.hxml b/testsjs.hxml index 8e48dae..015a190 100644 --- a/testsjs.hxml +++ b/testsjs.hxml @@ -4,3 +4,4 @@ #-hl test.hl --js test.js -L hxnodejs +-D allow_deasync From 3291cacdfef7a9e3043eca7bc30fe6ac00954283 Mon Sep 17 00:00:00 2001 From: neimanpinchas Date: Thu, 19 Sep 2024 14:46:26 +0300 Subject: [PATCH 16/20] allow virtual field access for query params to make it closer to express --- weblink/Request.hx | 3 ++- weblink/_internal/ds/FieldMap.hx | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 weblink/_internal/ds/FieldMap.hx diff --git a/weblink/Request.hx b/weblink/Request.hx index 7567a44..895e7af 100644 --- a/weblink/Request.hx +++ b/weblink/Request.hx @@ -4,6 +4,7 @@ import haxe.ds.StringMap; import haxe.http.HttpMethod; import haxe.io.Bytes; import weblink._internal.Server; +import weblink._internal.ds.FieldMap; class Request { public var cookies:List; @@ -15,7 +16,7 @@ class Request { public var url:String; /** Contains values for parameters declared in the route matched, if there are any. **/ - public var routeParams:Map; + public var routeParams:FieldMap; public var ip:String; public var baseUrl:String; diff --git a/weblink/_internal/ds/FieldMap.hx b/weblink/_internal/ds/FieldMap.hx new file mode 100644 index 0000000..97aa7df --- /dev/null +++ b/weblink/_internal/ds/FieldMap.hx @@ -0,0 +1,18 @@ +package weblink._internal.ds; + +@:forward +@:arrayAccess +@:forwardStatics +abstract FieldMap(Map) { + public function new(m) { + this = m; + } + + @:op(a.b) + public function fieldRead(name:String) + return this.get(name); + + @:op(a.b) + public function fieldWrite(name:String, value:V) + return this.set(name, value); +} From fdbdff73e523e0c266eb79565b4e0d1a0e4fa6ec Mon Sep 17 00:00:00 2001 From: neimanpinchas Date: Thu, 19 Sep 2024 14:47:08 +0300 Subject: [PATCH 17/20] onclose event --- weblink/Response.hx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/weblink/Response.hx b/weblink/Response.hx index f63bf56..d1fb538 100644 --- a/weblink/Response.hx +++ b/weblink/Response.hx @@ -68,9 +68,15 @@ class Response { send(if (pretty) Json.stringify(data, null, " ") else Json.stringify(data)); } + + public dynamic function onclose() { + + } + private function end() { this.server = null; final socket = this.socket; + onclose(); if (socket != null) { if (this.close) { socket.close(); From 84a2ffc4ee8d410ad29fc94c385f207a91afffaf Mon Sep 17 00:00:00 2001 From: neimanpinchas Date: Thu, 19 Sep 2024 14:47:53 +0300 Subject: [PATCH 18/20] fix bug with quick requests make stream variable private, this effects also the hashlink target and may explain the seqfault issues --- weblink/_internal/Server.hx | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/weblink/_internal/Server.hx b/weblink/_internal/Server.hx index 5e74b1d..b803eb0 100644 --- a/weblink/_internal/Server.hx +++ b/weblink/_internal/Server.hx @@ -5,6 +5,7 @@ import haxe.http.HttpMethod; import haxe.io.Bytes; import sys.net.Host; import weblink._internal.Socket; +import weblink._internal.ds.FieldMap; using Lambda; @@ -29,8 +30,8 @@ class Loop { class Server extends SocketServer { // var sockets:Array; var parent:Weblink; - var stream:Stream; + // var stream:Stream; public var running:Bool = true; var loop:Loop; @@ -42,7 +43,7 @@ class Server extends SocketServer { bind(new Host("0.0.0.0"), port); noDelay(true); listen(100, function() { - stream = accept(); + var stream = accept(); var socket:Socket = cast stream; var request:Request = null; var done:Bool = false; @@ -112,27 +113,26 @@ class Server extends SocketServer { } } - var execute=(handler)->{ + var execute = (handler) -> { try { - handler(request, response); - } catch(ex){ - trace(ex,ex.stack); - //TODO check PRODUCTION env var - response.status=500; - response.send(ex.toString()+"\n"+ex.stack); + } catch (ex) { + trace(ex, ex.stack); + // TODO check PRODUCTION env var + response.status = 500; + response.send(ex.toString() + "\n" + ex.stack); } } switch (parent.routeTree.tryGet(request.basePath, request.method)) { case Found(handler, params): - request.routeParams = params; + request.routeParams = new FieldMap(params); execute(handler); - + case _: switch (parent.routeTree.tryGet(request.path, request.method)) { case Found(handler, params): - request.routeParams = params; + request.routeParams = new FieldMap(params); execute(handler); case _: @:privateAccess parent.pathNotFound(request, response); From 537dc1b586fe3007e86420d0d9a94274b7b37bbb Mon Sep 17 00:00:00 2001 From: neimanpinchas Date: Mon, 23 Dec 2024 16:00:48 +0200 Subject: [PATCH 19/20] add cs test file --- testscs.hxml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 testscs.hxml diff --git a/testscs.hxml b/testscs.hxml new file mode 100644 index 0000000..ee379f1 --- /dev/null +++ b/testscs.hxml @@ -0,0 +1,5 @@ +-cp tests +-main Test +--macro nullSafety("weblink._internal.ds", StrictThreaded) +#-hl test.hl +--cs testcs From d58c6d2192394cb7c10b6753afafd5b6871ba815 Mon Sep 17 00:00:00 2001 From: neimanpinchas Date: Mon, 23 Dec 2024 16:01:44 +0200 Subject: [PATCH 20/20] make cs work --- .vscode/settings.json | 2 +- haxe_libraries/hxcs.hxml | 4 +++ weblink/Weblink.hx | 4 +-- weblink/_internal/Server.hx | 2 +- weblink/_internal/Socket.hx | 49 +++++++++++++++++++++++++++++++ weblink/_internal/SocketServer.hx | 45 +++++++++++++++++++++++++++- 6 files changed, 101 insertions(+), 5 deletions(-) create mode 100644 haxe_libraries/hxcs.hxml diff --git a/.vscode/settings.json b/.vscode/settings.json index a0dfe8e..f80075c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,7 +2,7 @@ "[haxe]": { "editor.formatOnSave": true, "editor.codeActionsOnSave": { - "source.sortImports": true + "source.sortImports": "explicit" } }, "testExplorer.codeLens": false diff --git a/haxe_libraries/hxcs.hxml b/haxe_libraries/hxcs.hxml new file mode 100644 index 0000000..9047c02 --- /dev/null +++ b/haxe_libraries/hxcs.hxml @@ -0,0 +1,4 @@ +# @install: lix --silent download "haxelib:/hxcs#4.2.0" into hxcs/4.2.0/haxelib +# @run: haxelib run-dir hxcs "${HAXE_LIBCACHE}/hxcs/4.2.0/haxelib" +-cp ${HAXE_LIBCACHE}/hxcs/4.2.0/haxelib/ +-D hxcs=4.2.0 \ No newline at end of file diff --git a/weblink/Weblink.hx b/weblink/Weblink.hx index 6755813..0e1537b 100644 --- a/weblink/Weblink.hx +++ b/weblink/Weblink.hx @@ -150,14 +150,14 @@ class Weblink { private inline function _serveEvent(request:Request, response:Response):Bool { if (request.path.charAt(0) == "/") - request.path = request.basePath.substr(1); + request.path = request.path.substr(1); var ext = request.path.extension(); var mime = weblink._internal.Mime.types.get(ext); response.headers = new List
(); if (cors.length > 0) response.headers.add({key: "Access-Control-Allow-Origin", value: cors}); response.contentType = mime == null ? "text/plain" : mime; - var path = Path.join([_dir, request.basePath.substr(_path.length)]).normalize(); + var path = Path.join([_dir, request.path.substr(_path.length-1)]).normalize(); if (path == "") path = "."; if (sys.FileSystem.exists(path)) { diff --git a/weblink/_internal/Server.hx b/weblink/_internal/Server.hx index b803eb0..0d2f566 100644 --- a/weblink/_internal/Server.hx +++ b/weblink/_internal/Server.hx @@ -143,7 +143,7 @@ class Server extends SocketServer { public function update(blocking:Bool = true) { do { @:privateAccess MainLoop.tick(); // for timers - #if js + #if (js||cs) var NoWait = 0; #end loop.run(NoWait); diff --git a/weblink/_internal/Socket.hx b/weblink/_internal/Socket.hx index ff18f9c..a9eab56 100644 --- a/weblink/_internal/Socket.hx +++ b/weblink/_internal/Socket.hx @@ -38,6 +38,55 @@ abstract Stream(js.node.net.Socket) { this.write(b.toString()); } } +#elseif cs +abstract Stream(sys.net.Socket) { + public function new(s) { + trace("creating new socket"); + this = s; + } + + public function readStart(callb:(data:Bytes) -> Void) { + // var bb = new BytesBuffer(); + this.setTimeout(3); + while (true) { + trace("waiting for read"); + try { + this.waitForRead(); + trace('done reading '); + var bufsize = (1 << 14); // 16 Ko + + var buf = Bytes.alloc(bufsize); + trace('going to read bytes'); + var len = this.input.readBytes(buf, 0, bufsize); + trace('done reading $len'); + var text = buf.toString(); + callb(Bytes.ofString(text)); + } catch (ex) { + trace(ex); + break; + } + } + /* + this.on("data", function(d:Buffer) { + // bb.add(d.hxToBytes()); + callb(d.hxToBytes()); + }); + */ + /* + this.on("end", function(d:Buffer) { + callb(bb.getBytes()); + });*/ + } + + public function close() { + this.close(); + } + + // TODO we really want a js buffer + public function write(b:Bytes) { + this.write(b.toString()); + } +} #end private typedef Basic = Stream diff --git a/weblink/_internal/SocketServer.hx b/weblink/_internal/SocketServer.hx index f7b58ed..6037f6f 100644 --- a/weblink/_internal/SocketServer.hx +++ b/weblink/_internal/SocketServer.hx @@ -1,12 +1,54 @@ package weblink._internal; +import sys.thread.Thread; import sys.net.Host; - +import weblink._internal.Socket.Stream; #if js class SocketServer + +#elseif cs +class SocketServer { + public var socket:sys.net.Socket; + public var cons = new Array(); + public function new(loop:Server.Loop) { + socket=new sys.net.Socket(); + }; + public function close(?callb:() -> Void) { + socket.close(); + callb(); + } + + + public dynamic function on_connect(con) { + trace("connected", con); + } + + public function bind(host:Host, port:Int) { + socket.bind(host,port); + } + + public function listen(backlog:Int, cb:Void->Void) { + socket.listen(backlog); + Thread.create(()->{ + while(true){ + var next=socket.accept(); + cons.push(new Stream(next)); + cb(); + } + }); + } + + public function accept() { + return cons.shift(); + } + + public function noDelay(yn:Bool) {} + public function noWait(yn:Bool) {} +} #else class SocketServer extends #if (hl && !nolibuv) hl.uv.Tcp #else sys.net.Socket #end #end +#if !cs { #if js public var node_socket:js.node.net.Server; @@ -40,3 +82,4 @@ class SocketServer extends #if (hl && !nolibuv) hl.uv.Tcp #else sys.net.Socket # } #end } +#end \ No newline at end of file