advanced the web server example

This commit is contained in:
Francesco Cozzuto
2023-01-15 19:52:42 +01:00
parent 0f40181e58
commit 78fadb2a6a
4 changed files with 266 additions and 276 deletions
+29 -126
View File
@@ -1,10 +1,9 @@
server = import("server.noja"); newRouter = import("router.noja").new;
request = import("request.noja"); serve = import("server.noja").serve;
response = import("response.noja");
cookie = import("cookie.noja"); cookie = import("cookie.noja");
Request = import("request.noja").Request;
respond = import("response.noja").new;
urlencoded = import("urlencoded.noja"); urlencoded = import("urlencoded.noja");
Request = request.Request;
respond = response.new;
fun getCookie(req: Request, name: String) { fun getCookie(req: Request, name: String) {
src = req.headers["Cookie"]; src = req.headers["Cookie"];
@@ -32,44 +31,21 @@ fun getUsername(req: Request) {
return username, session_id; return username, session_id;
} }
fun loadFile(path: String) { fun redirect(endpoint) {
fun callback()
stream, error = files.openFile(path, files.READ); return respond(307, none, {'Location': endpoint});
if error != none: return callback;
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;
} }
user_table = {}; user_table = {};
session_table = {}; session_table = {};
fun redirect(endpoint) { router = newRouter();
fun callback()
return response.new(307, none, {'Location': endpoint}); router->plug("/", {GET: redirect("/login")});
return callback;
} router->plug("/home", {
route_table = {
'/': {
GET: redirect("/login")
},
'/home': {
fun GET(req: Request) { fun GET(req: Request) {
username, _, error = getUsername(req); username, _, error = getUsername(req);
@@ -90,8 +66,9 @@ route_table = {
); );
return respond(200, content); return respond(200, content);
} }
}, });
'/login': {
router->plug("/login", {
GET: 'pages/login.html', GET: 'pages/login.html',
fun POST(req: Request) { fun POST(req: Request) {
@@ -121,20 +98,22 @@ route_table = {
if info.pass != password: if info.pass != password:
return respond(200, "Wrong password"); return respond(200, "Wrong password");
# Logged in! # Log-in succeded
session_id = generateRandomValue(0, 1000);
# Create a new session
session_id = random.generate(0, 1000);
session_table[session_id] = username; session_table[session_id] = username;
message = cat("Welcome, ", username, "!"); message = string.cat("Welcome, ", username, "!");
headers = { headers = {
"Location" : "/home", "Location" : "/home",
"Set-Cookie": cat("SESSID=", session_id) "Set-Cookie": string.cat("SESSID=", toString(session_id))
}; };
return respond(303, message, headers); return respond(303, message, headers);
} }
}, });
'/signup': {
router->plug("/signup", {
GET: 'pages/signup.html', GET: 'pages/signup.html',
fun POST(req: Request) { fun POST(req: Request) {
@@ -174,8 +153,9 @@ route_table = {
}; };
return respond(303, message, headers); return respond(303, message, headers);
} }
}, });
'/logout': {
router->plug("/logout", {
fun GET(req: Request) { fun GET(req: Request) {
username, session_id, error = getUsername(req); username, session_id, error = getUsername(req);
@@ -188,83 +168,6 @@ route_table = {
return respond(303, "Goodbye!", {"Location": "/login"}); return respond(303, "Goodbye!", {"Location": "/login"});
} }
} });
};
fun join(list: List, glue="") { error(serve("127.0.0.1", 8080, router));
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;
}
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();
+10
View File
@@ -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);
}
+100
View File
@@ -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};
+26 -49
View File
@@ -1,80 +1,57 @@
request = import("request.noja"); request = import("request.noja");
response = import("response.noja"); response = import("response.noja");
respond = response.new;
Router = import("router.noja").Router;
Server = { fun handleClient(fd: int, router: Router) {
fd: int,
callback: Callable,
loop : Callable,
close: Callable
};
fun handleClient(fd: int, server: Server) {
req, error, error_is_internal = request.fromSocket(fd); req, error, error_is_internal = request.fromSocket(fd);
if error != none: { if error != none: {
if error_is_internal: { if error_is_internal:
if debug: res = respond(500, error);
res = response.new(500, error);
else else
res = response.new(500); res = respond(400, error);
} else
res = response.new(400, error);
} else { } else {
if req.version.major != 1: if req.version.major != 1:
res = response.new(505, "HTTP version not supported"); res = respond(505, "HTTP version not supported");
else else
res = server.callback(req); res = router->solve(req);
} }
return response.toSocket(fd, res); return response.toSocket(fd, res);
} }
fun fallback_callback(request: Request)
return response.new(500, "No callback provided");
return { return {
fun serve(addr: ?String, port: int = 8080, router: Router, backlog=32) {
Server: Server, server_fd, error = net.socket(net.AF_INET, net.SOCK_STREAM, 0, true);
if error != none:
return none, error;
fun new(callback: Callable = fallback_callback, error = net.bind(server_fd, net.AF_INET, port, addr);
addr : String = "127.0.0.1", if error != none: {
port : int = 8080, net.close(server_fd);
backlog : int = 32) { 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;
} }
if (err = net.listen(fd, backlog)) != none: { error = net.listen(server_fd, backlog);
net.close(fd); if error != none: {
return none, err; net.close(server_fd);
return none, error;
} }
return {
fd: fd,
callback: callback,
fun loop(server: Server) {
while true: { while true: {
fd, addr_or_err, port = net.accept(server.fd);
if fd == none: new_fd, addr_or_err, port = net.accept(server_fd);
if new_fd == none:
error = addr_or_err; error = addr_or_err;
else { else {
error = handleClient(fd, server); error = handleClient(new_fd, router);
net.close(fd); net.close(new_fd);
} }
if error != none: if error != none:
print(error); print(error);
} }
}
fun close(server: Server) net.close(server_fd);
net.close(server.fd);
};
} }
}; };