Skip to content

Instantly share code, notes, and snippets.

@creationix
Last active November 10, 2017 06:28
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save creationix/7778005 to your computer and use it in GitHub Desktop.
Save creationix/7778005 to your computer and use it in GitHub Desktop.
This is a science experiment showing how the new process.addAsyncListener API in node 0.12.x could be (ab)used to create a very easy to use web-framework.
module.exports = function () {
// If there is an uncaught exception anywhre in your app, it will result in a proper 500 page.
if (Math.random() < 0.3) throw new Error("Oops, my random is low");
// They don't have to happen in the first tick either
if (Math.random() < 0.2) return setTimeout(function () {
throw new Error("Delayed random bites");
});
// If you throw an object, it will send a JSON document to the client
if (Math.random() > 0.6) throw {Hello: request.url};
// You can throw a stream to stream data to the client.
if (Math.random() > 0.6) {
response.setHeader("Content-Type", "application/javascript");
throw require('fs').createReadStream(__filename);
}
// And simple strings work, they get written to the client.
setTimeout(function () {
// It doesn't matter what tick this happens in, it's all tracked by magic.
throw "Slow coming from " + request.url + "\n";
}, 200);
};
// polyfill process.addAsyncListener for older nodes.
if (!process.addAsyncListener) require('async-listener');
var http = require('http');
var app = require('./app.js');
var ignore = false;
var current = null;
var stack = [];
process.addAsyncListener(setup, {
before: onBefore,
after: onAfter,
error: onError
});
http.createServer(function (req, res) {
run({
request: req,
response: res
}, app);
}).listen(8080, function () {
console.log("Server listening at http://localhost:8080/");
});
function onThrow(value) {
// If we're not in app code yet then crash.
if (!global.response) throw value;
// If it was truly an error, send a 500 page.
if (value instanceof Error) {
response.statusCode = 500;
response.setHeader("Content-Type", "text/plain");
value = new Buffer(value.stack + "\n");
response.setHeader("Content-Length", value.length);
response.end(value);
}
// If it's a string, send it!
else if (typeof value === "string") {
value = new Buffer(value);
response.setHeader("Content-Length", value.length);
response.end(value);
}
// If it's a stream, then stream it!
else if (value.pipe) {
value.pipe(response);
}
// If it's an object, send a JSON document.
else if (typeof value === "object") {
var json;
json = new Buffer(JSON.stringify(value) + "\n");
response.setHeader("Content-Type", "application/json");
response.setHeader("Content-Length", json.length);
response.end(json);
}
else {
throw new Error("Unknown throw value: " + value);
}
}
// When a new event is setup record the current context.
function setup() {
return current;
}
// When starting a new event, set the globals from the context and make storage current.
function onBefore(context, storage) {
for (var key in storage) {
global[key] = storage[key];
}
if (current) stack.push(current);
current = storage;
}
// When done, remove the globals and remove current storage
function onAfter(context, storage) {
for (var key in storage) {
delete global[key];
}
current = stack.pop();
}
function onError(storage, error) {
onThrow(error);
onAfter(this, storage);
return true;
}
function run(scope, fn) {
onBefore(null, scope);
try { fn(); }
catch (err) { onError(scope, err); }
finally { onAfter(null, scope); }
}
tim@linux-l7qc:~/Code/wrk> ./wrk -c 100 -t 8 -d 10 http://localhost:8080/foo
Running 10s test @ http://localhost:8080/foo
8 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 48.48ms 79.36ms 209.45ms 79.38%
Req/Sec 250.56 45.10 384.00 72.56%
20165 requests in 10.00s, 9.17MB read
Non-2xx or 3xx responses: 8941
Requests/sec: 2016.49
Transfer/sec: 0.92MB
@DTrejo
Copy link

DTrejo commented Dec 4, 2013

hot hot hot

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment