From 5a8d1d3e95ecf65f3d4dcc777581a2a1c4b0c753 Mon Sep 17 00:00:00 2001 From: fardog Date: Fri, 14 Aug 2015 14:17:37 -0700 Subject: [PATCH] Prototype --- .eslintrc | 3 ++ .gitignore | 29 +++++++++++++ bin/cli.js | 5 +++ package.json | 31 ++++++++++++++ src/index.js | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 180 insertions(+) create mode 100644 .eslintrc create mode 100644 .gitignore create mode 100755 bin/cli.js create mode 100644 package.json create mode 100644 src/index.js diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..a755cdb --- /dev/null +++ b/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": ["standard"] +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1a68402 --- /dev/null +++ b/.gitignore @@ -0,0 +1,29 @@ +# Logs +logs +*.log + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directory +# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git +node_modules + +lib/ diff --git a/bin/cli.js b/bin/cli.js new file mode 100755 index 0000000..5157358 --- /dev/null +++ b/bin/cli.js @@ -0,0 +1,5 @@ +#!/usr/bin/env node + +var path = require('path') + +require(path.join(process.cwd(), 'frockfile.js')) diff --git a/package.json b/package.json new file mode 100644 index 0000000..df03f9d --- /dev/null +++ b/package.json @@ -0,0 +1,31 @@ +{ + "name": "frock", + "version": "0.0.0", + "description": "A configurable mock HTTP server", + "main": "lib/index.js", + "bin": { + "frock": "./bin/cli.js" + }, + "scripts": { + "test": "npm run lint && npm run compile && babel test/index.js | tape", + "prepublish": "npm run compile", + "compile": "babel src --out-dir lib", + "lint": "standard src/**/*.js test/**/*.js" + }, + "author": "Urban Airship", + "license": "Apache-2.0", + "devDependencies": { + "babel": "^5.6.14", + "eslint": "^1.1.0", + "eslint-config-standard": "^4.1.0", + "eslint-config-standard-react": "^1.0.4", + "eslint-plugin-react": "^3.2.2", + "eslint-plugin-standard": "^1.2.0", + "standard": "^5.1.0", + "tape": "^4.0.0" + }, + "dependencies": { + "arrify": "^1.0.0", + "commuter": "^1.0.2" + } +} diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..da439b6 --- /dev/null +++ b/src/index.js @@ -0,0 +1,112 @@ +import http from 'http' + +import commuter from 'commuter' +import arrayify from 'arrify' + +export default createFrockInstance + +function createFrockInstance (config = {}) { + const frock = {} + const handlers = new Map() + const servers = [] + + frock.run = run + frock.stop = stop + frock.reload = reload + frock.registerHandler = registerHandler + + return frock + + function run (ready = noop) { + let count = 0 + + config.servers.forEach(s => { + const router = commuter(defaultRoute, s.baseUrl) + const server = http.createServer(router) + const boundHandlers = [] + + s.routes.forEach(r => { + const methods = arrayify(r.methods).map(m => m.toLowerCase()) + + methods.forEach(m => { + const handler = handlers.get(r.handler)( + frock, + logger.bind(null, r.handler), + r.options + ) + + router[m](r.path, handler) + boundHandlers.push(handler) + }) + }) + + servers.push({server, handlers: boundHandlers}) + server.listen(s.port, done) + }) + + function done () { + ++count + + if (count >= config.servers.length) { + ready() + } + } + } + + function registerHandler (name, handler) { + handlers.set(name, handler) + } + + function reload (ready = noop) { + stop(() => run(ready)) + } + + function stop (ready = noop) { + servers.forEach(s => { + s.handlers.forEach(h => { + h.end(innerDone) + }) + + function innerDone (handler) { + const idx = s.handlers.indexOf(handler) + + if (idx) { + s.handlers.splice(idx, 1) + } else { + throw new Error('No handler to remove, throwing to avoid infinite loop') + } + + if (!s.handlers.length) { + s.server.close(() => done(s)) + } + } + }) + + function done (server) { + const idx = servers.indexOf(server) + + if (idx) { + servers.splice(idx, 1) + } else { + throw new Error('No server to remove, throwing to avoid infinite loop') + } + + if (!servers.length) { + ready() + } + } + } +} + +function defaultRoute (req, res) { + res.statusCode = 404 + res.end('not found') +} + +function logger (handler, level, msg, extra) { + console.log(`${handler}: [${level.toUpperCase()}] ${msg}`) +} + +function noop () { + // nooperations +}