Routing Functions
How to do routing without a framework. He pushes routes the same way as you would push middleware functions into an Express app.
Same concept as
app.use
or app.get
, but his API is routes.push
.
// server.js
// setup HTTP routes
routes.push(
// always set server name
function serverName(req,res) {
res.setHeader("Server",secret.SERVER_NAME);
}
);
Each route function receives a req
and res
parameters and it decides if it should act on it or not. So each route come one after the other.
Talks about the security headers in server.js.
Another route from server.js:
routes.push(
// await full request
function fullRequest(req,res) {
req.body = "";
return ASQ.react(function listener(next){
req.addListener("data",function(chunk){
req.body += chunk;
});
req.addListener("end",next);
req.resume();
});
}
);
This route is waiting for the entire request to come in, if it's more than one chunk. He sets up a data listener addListener
to compose the chunks. Then a route for favicon (for Chrome). When the browser ask for the favicon he returns a catchable 204 response to stop the browser asking for it. :
routes.push(
// favicon
function favicon(req,res) {
try {
if (req.method === "GET" && req.url === "/favicon.ico") {
fs.statSync(path.join(__dirname,"web","favicon.ico"));
}
return;
}
catch (err) {}
// empty favicon.ico response
res.writeHead(204,{
"Content-Type": "image/x-icon",
"Cache-Control": "public, max-age: 604800"
});
res.end();
return true;
}
)
Then handle static files (static files modules). It's the same static file server that comes with Express:
routes.push(
// static file request?
function staticResources(req,res) {
if (req.method === "GET" &&
/^\/(?:js\/(?=.+)|css\/(?=.+)|images\/(?=.+)|robots\.txt\b|humans\.txt\b|favicon\.ico\b)/
.test(req.url)
) {
req.url = "/web" + req.url;
static_files.serve(req,res);
return true;
}
}
);
The last route if none of the others have taken over is for a full page load:
routes.push(
// a recognized full-page request?
function loadPage(req,res) {
var url;
if (
// NOTE: check if it's a GET request and
// if it's a route that the server can handle
// i.e. it knows how to render that page
req.method === "GET" &&
(url = Pages.recognize(req.url))
) {
return ASQ(function ASQ(done){
View.getPageHTML(url)
.val(function pageHTML(url,html) {
res.writeHead(200,{ "Content-type": "text/html; charset=UTF-8" });
res.end(html);
done(true);
})
.or(done.fail);
});
}
}
);
Finally the default route of 404...
routes.push(
// default route
function defaultRoute(req,res) {
res.writeHead(404);
res.end();
}
);
So how does this routes array work?Express does an internal
for
loop of routes.Kyle created the
router
generator function:
function *router(token) {
var req = token.messages[0], res = token.messages[1], route, error;
for (route of routes) {
try {
route = route(req,res);
if (ASQ.isSequence(route)) {
// wait to resolve the route
route = yield route;
}
if (route === true) { break; }
}
catch (err) { .. }
}
// response error?
if (error) {
throw { .. };
}
}
He can stop/pause the local execution with yield
. Line#7 : if the route function returns a promise (or an asequence object – from his async library), then wait for it to finish (
yield
). Line#11: if one of the routes function can handle that route and returns
true
then it can break out of the loop and not try the remaining routes functions.He can use
try/catch
inside the generator function. Talks about the
runner
helper that runs the generator function to completion. :
// server request handling
ASQ.react(function listen(trigger){
httpserv.on("request",trigger);
})
.runner(router)
.or(responseError);
If an error happens in runner
(
line#5), it will be bubble out to responseError
. :
function responseError(respErr) {
try {
if (respErr.req && respErr.res) {
if (respErr.req.headers &&
respErr.req.headers["accept"] === "application/json"
) {
respErr.reason = JSON.stringify({
error: respErr.reason
});
}
respErr.res.writeHead(500);
respErr.res.end(respErr.reason.toString());
return true;
}
} catch(e) {}
ERROR("responseError",
respErr ? ((respErr.stack + "") || respErr.toString()) : "Unknown response error"
);
}
Basically, if it received a JSON request, it will reply with a JSON, otherwise it will just log the error. All the errors will bubble here, from anywhere in the machinery.