Skip to content

Instantly share code, notes, and snippets.

@creationix
Last active November 10, 2017 06:28
Show Gist options
  • Select an option

  • Save creationix/7778005 to your computer and use it in GitHub Desktop.

Select an option

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
Copy Markdown

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