Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"[haxe]": {
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.sortImports": true
"source.sortImports": "explicit"
}
},
"testExplorer.codeLens": false
Expand Down
4 changes: 4 additions & 0 deletions haxe_libraries/hxcs.hxml
Original file line number Diff line number Diff line change
@@ -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
7 changes: 7 additions & 0 deletions haxe_libraries/hxnodejs.hxml
Original file line number Diff line number Diff line change
@@ -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()
4 changes: 3 additions & 1 deletion tests.hxml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
-cp tests
-main Test
--macro nullSafety("weblink._internal.ds", StrictThreaded)
-hl test.hl
#-hl test.hl
--js test.js
-L hxnodejs
5 changes: 5 additions & 0 deletions tests/sys/thread/Thread.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#if js
function create(f) {
Timers.setImmdiate(f);
}
#end
5 changes: 5 additions & 0 deletions testscs.hxml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-cp tests
-main Test
--macro nullSafety("weblink._internal.ds", StrictThreaded)
#-hl test.hl
--cs testcs
7 changes: 7 additions & 0 deletions testsjs.hxml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
-cp tests
-main Test
--macro nullSafety("weblink._internal.ds", StrictThreaded)
#-hl test.hl
--js test.js
-L hxnodejs
-D allow_deasync
25 changes: 19 additions & 6 deletions weblink/Request.hx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,23 @@ 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<Cookie>;
// 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<String, String>;
public var routeParams:FieldMap<String, String>;

public var ip:String;
public var baseUrl:String;

public var headers:StringMap<String>;
public var text:String;
public var method:HttpMethod;
Expand All @@ -37,18 +45,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] == "") {
Expand Down Expand Up @@ -143,11 +156,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
Expand Down
11 changes: 11 additions & 0 deletions weblink/Response.hx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package weblink;

import haxe.Json;
import haxe.http.HttpStatus;
import haxe.io.Bytes;
import haxe.io.Encoding;
Expand Down Expand Up @@ -63,9 +64,19 @@ 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));
}


public dynamic function onclose() {

}

private function end() {
this.server = null;
final socket = this.socket;
onclose();
if (socket != null) {
if (this.close) {
socket.close();
Expand Down
39 changes: 33 additions & 6 deletions weblink/Weblink.hx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ using haxe.io.Path;
class Weblink {
public var server:Null<Server>;
public var routeTree:RadixTree<Handler>;
public var allowed_methods = new Map<HttpMethod, Bool>();

private var middlewareToChain:Array<Middleware> = [];

Expand All @@ -26,10 +27,18 @@ class Weblink {
response.send("Error 404, Route Not found.");
}

public function cors_middleware(request:Request, response:Response):Void {
response.headers = new List<Header>();
response.headers.add({key: "Access-Control-Allow-Origin", value: cors});
response.headers.add({key: "Access-Control-Allow-Headers", value: "*"});
}

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();
Expand Down Expand Up @@ -64,33 +73,51 @@ 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);
}
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) {
this.pathNotFound = chainMiddleware(this.pathNotFound);
allowed_methods[Options] = true;
var allowed_methods_array = new Array<String>();
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;
Expand Down Expand Up @@ -123,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<Header>();
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();
var path = Path.join([_dir, request.path.substr(_path.length-1)]).normalize();
if (path == "")
path = ".";
if (sys.FileSystem.exists(path)) {
Expand Down
63 changes: 53 additions & 10 deletions weblink/_internal/Server.hx
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,47 @@ 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;
import weblink._internal.ds.FieldMap;

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<Socket>;
var parent:Weblink;
var stream:Stream;

// var stream:Stream;
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);
listen(100, function() {
stream = accept();
var stream = accept();
var socket:Socket = cast stream;
var request:Request = null;
var done:Bool = false;
Expand Down Expand Up @@ -76,6 +97,13 @@ class Server extends SocketServer {
private function complete(request:Request, socket:Socket) {
@: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;
}

if (request.method == Get
&& @:privateAccess parent._serve
&& response.status == OK
Expand All @@ -85,15 +113,27 @@ 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);
request.routeParams = new FieldMap(params);
execute(handler);

case _:
switch (parent.routeTree.tryGet(request.path, request.method)) {
case Found(handler, params):
request.routeParams = params;
handler(request, response);
request.routeParams = new FieldMap(params);
execute(handler);
case _:
@:privateAccess parent.pathNotFound(request, response);
}
Expand All @@ -103,6 +143,9 @@ class Server extends SocketServer {
public function update(blocking:Bool = true) {
do {
@:privateAccess MainLoop.tick(); // for timers
#if (js||cs)
var NoWait = 0;
#end
loop.run(NoWait);
} while (running && blocking);
}
Expand Down
Loading