Last active
November 10, 2017 06:28
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); } | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
hot hot hot