Files
Noja/examples/http_server/main.noja
T

271 lines
6.1 KiB
Plaintext
Executable File

server = import("server.noja");
request = import("request.noja");
response = import("response.noja");
cookie = import("cookie.noja");
urlencoded = import("urlencoded.noja");
Request = request.Request;
respond = response.new;
fun getCookie(req: Request, name: String) {
src = req.headers["Cookie"];
if src == none:
return none;
cookies, error = cookie.parseCookieHeader(src);
if error != none:
return none, error;
return cookies[name];
}
fun getSessionID(req: Request) {
session_id, error = getCookie(req, "SESSID");
if error != none:
return none, error;
session_id = string.toInt(session_id);
return session_id;
}
fun getUsername(req: Request) {
session_id, error = getSessionID(req);
if error != none:
return none, none, error;
username = session_table[session_id];
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;
}
user_table = {};
session_table = {};
fun redirect(endpoint) {
fun callback()
return response.new(307, none, {'Location': endpoint});
return callback;
}
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(
"<html>",
"<head>",
"<title>Homepage</title>",
"</head>",
"<body>",
"<a>Hello, ", username, "!</a>",
"</body>",
"</html>"
);
return respond(200, content);
}
},
'/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");
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': {
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);
}
},
'/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"});
}
}
};
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;
}
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();