diff --git a/examples/http_server/main.noja b/examples/http_server/main.noja
index 450c77f..0a49f90 100755
--- a/examples/http_server/main.noja
+++ b/examples/http_server/main.noja
@@ -1,10 +1,9 @@
-server = import("server.noja");
-request = import("request.noja");
-response = import("response.noja");
-cookie = import("cookie.noja");
+newRouter = import("router.noja").new;
+serve = import("server.noja").serve;
+cookie = import("cookie.noja");
+Request = import("request.noja").Request;
+respond = import("response.noja").new;
urlencoded = import("urlencoded.noja");
-Request = request.Request;
-respond = response.new;
fun getCookie(req: Request, name: String) {
src = req.headers["Cookie"];
@@ -32,239 +31,143 @@ fun getUsername(req: Request) {
return username, session_id;
}
-fun loadFile(path: String) {
-
- stream, error = files.openFile(path, files.READ);
- if error != none:
- return none, error;
-
- text = "";
- size = 512;
- do {
- size = 2 * size;
- data = buffer.new(size);
-
- num_bytes, error = files.read(stream, data);
- if error != none:
- return none, error;
-
- text = string.cat(text, buffer.toString(data));
-
- } while num_bytes == size;
-
- #files.close(stream);
- return text;
+fun redirect(endpoint) {
+ fun callback()
+ return respond(307, none, {'Location': endpoint});
+ return callback;
}
user_table = {};
session_table = {};
-fun redirect(endpoint) {
- fun callback()
- return response.new(307, none, {'Location': endpoint});
- return callback;
-}
+router = newRouter();
-route_table = {
- '/': {
- GET: redirect("/login")
- },
- '/home': {
- fun GET(req: Request) {
-
- username, _, error = getUsername(req);
- if error != none:
- return respond(400, error);
- if username == none:
- return respond(401, "You're not logged in");
-
- content = string.cat(
- "",
- "
",
- "Homepage",
- "",
- "",
- "Hello, ", username, "!",
- "",
- ""
- );
- return respond(200, content);
- }
- },
- '/login': {
- GET: 'pages/login.html',
- fun POST(req: Request) {
+router->plug("/", {GET: redirect("/login")});
- username, _, error = getUsername(req);
- if error != none:
- return respond(400, error);
- if username != none:
- return respond(400, "You're already logged in");
+router->plug("/home", {
- fun areValid(params) {
- expect = {user: String, pass: String};
- return istypeof(expect, params)
- and count(params.user) > 0
- and count(params.pass) > 0;
- }
-
- params = urlencoded.parse(req.body);
- if not areValid(params):
- return respond(400, "Invalid parameters");
- username = params.user;
- password = params.pass;
-
- info = user_table[username];
-
- if info == none:
- return respond(200, "No such user");
- if info.pass != password:
- return respond(200, "Wrong password");
-
- # Logged in!
- session_id = generateRandomValue(0, 1000);
- session_table[session_id] = username;
-
- message = cat("Welcome, ", username, "!");
- headers = {
- "Location" : "/home",
- "Set-Cookie": cat("SESSID=", session_id)
- };
- return respond(303, message, headers);
- }
- },
- '/signup': {
+ fun GET(req: Request) {
- GET: 'pages/signup.html',
+ username, _, error = getUsername(req);
+ if error != none:
+ return respond(400, error);
+ if username == none:
+ return respond(401, "You're not logged in");
+
+ content = string.cat(
+ "",
+ "",
+ "Homepage",
+ "",
+ "",
+ "Hello, ", username, "!",
+ "",
+ ""
+ );
+ return respond(200, content);
+ }
+});
- fun POST(req: Request) {
+router->plug("/login", {
+ GET: 'pages/login.html',
+ fun POST(req: Request) {
- username, _, error = getUsername(req);
- if error != none:
- return respond(400, error);
- if username != none:
- return respond(400, "You're already logged in");
+ username, _, error = getUsername(req);
+ if error != none:
+ return respond(400, error);
+ if username != none:
+ return respond(400, "You're already logged in");
- fun areValid(params) {
- expect = {user: String, pass: String};
- return istypeof(expect, params)
- and count(params.user) > 0
- and count(params.pass) > 0;
- }
-
- params = urlencoded.parse(req.body);
- if not areValid(params):
- return respond(400, "Invalid parameters");
- username = params.user;
- password = params.pass;
-
- if user_table[password] != none:
- return respond(200, "User already exists");
-
- # Signed up!
- user_table[username] = {pass: password};
- session_id = random.generate(0, 1000);
- session_table[session_id] = username;
-
- # Respond
- message = string.cat("Welcome, ", username, "!");
- headers = {
- "Location" : "/home",
- "Set-Cookie": string.cat("SESSID=", toString(session_id))
- };
- return respond(303, message, headers);
+ fun areValid(params) {
+ expect = {user: String, pass: String};
+ return istypeof(expect, params)
+ and count(params.user) > 0
+ and count(params.pass) > 0;
}
- },
- '/logout': {
- fun GET(req: Request) {
- username, session_id, error = getUsername(req);
- if error != none:
- return respond(400, error);
- if username == none:
- return respond(401, "You're not logged in");
+ params = urlencoded.parse(req.body);
+ if not areValid(params):
+ return respond(400, "Invalid parameters");
+ username = params.user;
+ password = params.pass;
- session_table[session_id] = none;
+ info = user_table[username];
+
+ if info == none:
+ return respond(200, "No such user");
+ if info.pass != password:
+ return respond(200, "Wrong password");
+
+ # Log-in succeded
- return respond(303, "Goodbye!", {"Location": "/login"});
+ # Create a new session
+ session_id = random.generate(0, 1000);
+ session_table[session_id] = username;
+
+ message = string.cat("Welcome, ", username, "!");
+ headers = {
+ "Location" : "/home",
+ "Set-Cookie": string.cat("SESSID=", toString(session_id))
+ };
+ return respond(303, message, headers);
+ }
+});
+
+router->plug("/signup", {
+ GET: 'pages/signup.html',
+
+ fun POST(req: Request) {
+
+ username, _, error = getUsername(req);
+ if error != none:
+ return respond(400, error);
+ if username != none:
+ return respond(400, "You're already logged in");
+
+ fun areValid(params) {
+ expect = {user: String, pass: String};
+ return istypeof(expect, params)
+ and count(params.user) > 0
+ and count(params.pass) > 0;
}
+
+ params = urlencoded.parse(req.body);
+ if not areValid(params):
+ return respond(400, "Invalid parameters");
+ username = params.user;
+ password = params.pass;
+
+ if user_table[password] != none:
+ return respond(200, "User already exists");
+
+ # Signed up!
+ user_table[username] = {pass: password};
+ session_id = random.generate(0, 1000);
+ session_table[session_id] = username;
+
+ # Respond
+ message = string.cat("Welcome, ", username, "!");
+ headers = {
+ "Location" : "/home",
+ "Set-Cookie": string.cat("SESSID=", toString(session_id))
+ };
+ return respond(303, message, headers);
}
-};
+});
-fun join(list: List, glue="") {
- if count(list) == 0:
- text = "";
- else {
- i = 1;
- text = toString(list[0]);
- while i < count(list): {
- text = string.cat(text, glue, toString(list[i]));
- i = i+1;
- }
+router->plug("/logout", {
+ fun GET(req: Request) {
+
+ username, session_id, error = getUsername(req);
+ if error != none:
+ return respond(400, error);
+ if username == none:
+ return respond(401, "You're not logged in");
+
+ session_table[session_id] = none;
+
+ return respond(303, "Goodbye!", {"Location": "/login"});
}
- return text;
-}
+});
-debug = true;
-
-fun joinPath(a, b) {
- u = a[count(a)-1] == '/';
- v = b[count(b)-1] == '/';
- if u and v:
- return string.cat(string.slice(a, 0, count(a)-1), b);
- if u or v:
- return string.cat(a, b);
- return string.cat(a, '/', b);
-}
-
-fun getRouteAllowedMethods(route) {
-
- method_table = route_table[route];
- if method_table == none:
- return none;
-
- allowed = keysof(method_table);
- if method_table.HEAD == none and method_table.GET != none:
- append(allowed, "HEAD");
- if method_table.OPTIONS == none:
- append(allowed, "OPTIONS");
- return allowed;
-}
-
-fun callback(req: Request) {
-
- endpoint = req.url.path;
- method_table = route_table[endpoint];
- if method_table == none:
- return response.new(404);
-
- method_item = method_table[req.method];
- if method_item == none: {
- allowed = getRouteAllowedMethods(endpoint);
- if req.method == "OPTIONS":
- status = 204;
- else
- status = 405;
- return response.new(status, none, {"Allow": join(allowed, ", ")});
- }
-
- if isCallable(method_item):
- res = method_item(req);
- else {
- file = joinPath(getCurrentWorkingDirectory(), method_item);
- data, error = loadFile(file);
- if error == none:
- res = response.new(200, data);
- else if debug:
- res = response.new(500, error);
- else
- res = response.new(500);
- }
-
- return res;
-}
-
-s, err = server.new(callback, "127.0.0.1", 8080);
-if s == none: error(err);
-s->loop(); # This doesn't return
-s->close();
+error(serve("127.0.0.1", 8080, router));
\ No newline at end of file
diff --git a/examples/http_server/path.noja b/examples/http_server/path.noja
new file mode 100644
index 0000000..163f336
--- /dev/null
+++ b/examples/http_server/path.noja
@@ -0,0 +1,10 @@
+
+fun join(a, b) {
+ u = a[count(a)-1] == '/';
+ v = b[count(b)-1] == '/';
+ if u and v:
+ return string.cat(string.slice(a, 0, count(a)-1), b);
+ if u or v:
+ return string.cat(a, b);
+ return string.cat(a, '/', b);
+}
\ No newline at end of file
diff --git a/examples/http_server/router.noja b/examples/http_server/router.noja
new file mode 100644
index 0000000..f441760
--- /dev/null
+++ b/examples/http_server/router.noja
@@ -0,0 +1,100 @@
+path = import("path.noja");
+Request = import("request.noja").Request;
+respond = import("response.noja").new;
+Router = {table: Map, plug: Callable, solve: Callable};
+
+fun loadFile(path: String) {
+
+ stream, error = files.openFile(path, files.READ);
+ if error != none:
+ return none, error;
+
+ text = "";
+ size = 512;
+ do {
+ size = 2 * size;
+ data = buffer.new(size);
+
+ num_bytes, error = files.read(stream, data);
+ if error != none:
+ return none, error;
+
+ text = string.cat(text, buffer.toString(data));
+
+ } while num_bytes == size;
+
+ #files.close(stream);
+ return text;
+}
+
+fun join(list: List, glue="") {
+ if count(list) == 0:
+ text = "";
+ else {
+ i = 1;
+ text = toString(list[0]);
+ while i < count(list): {
+ text = string.cat(text, glue, toString(list[i]));
+ i = i+1;
+ }
+ }
+ return text;
+}
+
+fun new() {
+ return {
+ table: {},
+
+ fun plug(router: Router, name: String, methods: Map)
+ router.table[name] = methods;
+
+
+ fun getRouteAllowedMethods(route) {
+
+ method_table = route_table[route];
+ if method_table == none:
+ return none;
+
+ allowed = keysof(method_table);
+ if method_table.HEAD == none and method_table.GET != none:
+ append(allowed, "HEAD");
+ if method_table.OPTIONS == none:
+ append(allowed, "OPTIONS");
+ return allowed;
+ }
+
+ fun solve(router: Router, req: Request) {
+
+ endpoint = req.url.path;
+ method_table = router.table[endpoint];
+ if method_table == none:
+ return respond(404);
+
+ method_item = method_table[req.method];
+ if method_item == none: {
+ allowed = router->getRouteAllowedMethods(endpoint);
+ if req.method == "OPTIONS":
+ status = 204;
+ else
+ status = 405;
+ return respond(status, none, {"Allow": join(allowed, ", ")});
+ }
+
+ if isCallable(method_item):
+ res = method_item(req);
+ else {
+ cwd = getCurrentWorkingDirectory();
+ file = path.join(cwd, method_item);
+ data, error = loadFile(file);
+ if error == none:
+ res = respond(200, data);
+ else
+ res = respond(500, error);
+ }
+
+ return res;
+ }
+ };
+}
+
+return {new: new, Router: Router};
\ No newline at end of file
diff --git a/examples/http_server/server.noja b/examples/http_server/server.noja
index 9969c06..8c8100b 100644
--- a/examples/http_server/server.noja
+++ b/examples/http_server/server.noja
@@ -1,80 +1,57 @@
request = import("request.noja");
response = import("response.noja");
+respond = response.new;
+Router = import("router.noja").Router;
-Server = {
- fd: int,
- callback: Callable,
- loop : Callable,
- close: Callable
-};
-
-fun handleClient(fd: int, server: Server) {
+fun handleClient(fd: int, router: Router) {
req, error, error_is_internal = request.fromSocket(fd);
if error != none: {
- if error_is_internal: {
- if debug:
- res = response.new(500, error);
- else
- res = response.new(500);
- } else
- res = response.new(400, error);
+ if error_is_internal:
+ res = respond(500, error);
+ else
+ res = respond(400, error);
} else {
if req.version.major != 1:
- res = response.new(505, "HTTP version not supported");
+ res = respond(505, "HTTP version not supported");
else
- res = server.callback(req);
+ res = router->solve(req);
}
return response.toSocket(fd, res);
}
-fun fallback_callback(request: Request)
- return response.new(500, "No callback provided");
-
return {
-
- Server: Server,
+ fun serve(addr: ?String, port: int = 8080, router: Router, backlog=32) {
- fun new(callback: Callable = fallback_callback,
- addr : String = "127.0.0.1",
- port : int = 8080,
- backlog : int = 32) {
+ server_fd, error = net.socket(net.AF_INET, net.SOCK_STREAM, 0, true);
+ if error != none:
+ return none, error;
- fd, err = net.socket(net.AF_INET, net.SOCK_STREAM, 0, true);
- if err != none:
- return none, err;
-
- if (err = net.bind(fd, net.AF_INET, port, addr)) != none: {
- net.close(fd);
- return none, err;
+ error = net.bind(server_fd, net.AF_INET, port, addr);
+ if error != none: {
+ net.close(server_fd);
+ return none, error;
}
- if (err = net.listen(fd, backlog)) != none: {
- net.close(fd);
- return none, err;
+ error = net.listen(server_fd, backlog);
+ if error != none: {
+ net.close(server_fd);
+ return none, error;
}
- return {
- fd: fd,
- callback: callback,
+ while true: {
- fun loop(server: Server) {
-
- while true: {
- fd, addr_or_err, port = net.accept(server.fd);
- if fd == none:
- error = addr_or_err;
- else {
- error = handleClient(fd, server);
- net.close(fd);
- }
- if error != none:
- print(error);
- }
+ new_fd, addr_or_err, port = net.accept(server_fd);
+ if new_fd == none:
+ error = addr_or_err;
+ else {
+ error = handleClient(new_fd, router);
+ net.close(new_fd);
}
+ if error != none:
+ print(error);
+ }
- fun close(server: Server)
- net.close(server.fd);
- };
+ net.close(server_fd);
}
};
\ No newline at end of file