├── examples ├── mvc │ ├── .gitignore │ ├── features │ │ ├── pet │ │ │ ├── .gitignore │ │ │ ├── views │ │ │ │ ├── show.js │ │ │ │ └── edit.js │ │ │ ├── package.json │ │ │ ├── model.js │ │ │ └── index.js │ │ ├── main │ │ │ ├── .gitignore │ │ │ ├── package.json │ │ │ └── index.js │ │ └── user │ │ │ ├── .gitignore │ │ │ ├── views │ │ │ ├── list.js │ │ │ ├── show.js │ │ │ └── edit.js │ │ │ ├── package.json │ │ │ ├── model.js │ │ │ └── index.js │ ├── config │ │ ├── config.json │ │ └── config.production.json │ ├── db │ │ └── readme.md │ ├── Makefile │ ├── readme.md │ ├── static │ │ └── style.css │ ├── views │ │ ├── not-found.js │ │ ├── error-page.js │ │ └── layout.js │ ├── db.js │ └── server.js ├── static-files │ ├── static │ │ ├── hello.txt │ │ ├── js │ │ │ └── app.js │ │ └── css │ │ │ └── style.css │ └── server.js ├── downloads │ ├── files │ │ └── amazing.txt │ └── server.js ├── favicon │ ├── favicon.ico │ ├── test │ │ └── test.js │ └── server.js ├── route-seperation │ ├── routes │ │ ├── user │ │ │ ├── user-db.js │ │ │ ├── index.js │ │ │ ├── load-user.js │ │ │ ├── view.js │ │ │ └── edit.js │ │ ├── home.js │ │ └── posts.js │ ├── templates │ │ ├── users │ │ │ ├── view.js │ │ │ ├── index.js │ │ │ └── edit.js │ │ ├── layout.js │ │ ├── posts │ │ │ └── index.js │ │ └── home.js │ ├── static │ │ └── style.css │ └── server.js ├── error-pages │ ├── templates │ │ ├── 404.js │ │ ├── error.js │ │ ├── layout.js │ │ ├── 500.js │ │ └── index.js │ └── server.js ├── hello-world │ ├── server.js │ └── test │ │ └── test.js ├── cors │ ├── static │ │ └── index.html │ └── server.js ├── search │ ├── client.js │ ├── page.html │ └── server.js ├── route-map │ ├── map.js │ └── server.js ├── cookie-sessions │ └── server.js ├── csrf │ ├── template.js │ └── server.js ├── basic-auth │ ├── server.js │ └── admin.js ├── expose-data-to-client │ ├── template.js │ └── server.js ├── error │ └── server.js ├── vhost │ ├── server.js │ └── vhost.js ├── content-negotiation │ └── server.js ├── online │ └── server.js ├── auth │ ├── template.js │ └── server.js ├── session │ ├── redis.js │ └── memory.js ├── cookies │ └── server.js ├── multipart │ ├── multiparty-form.js │ └── server.js ├── tail │ └── server.js ├── validate-api │ └── server.js ├── web-service │ └── server.js └── auth-helpers │ └── server.js ├── modules ├── st.js ├── pwd.js ├── basic.js ├── cookies.js ├── corsify.js ├── filed.js ├── redsess.js ├── server.js ├── csrf-lite.js ├── form-body.js ├── send-text.js ├── config-chain.js ├── hyperscript.js ├── json-globals.js ├── media-types.js ├── multiparty.js ├── redirecter.js ├── send-html.js ├── send-json.js ├── typed-error.js ├── routes-router.js ├── serve-favicon.js ├── generic-session.js ├── string-template.js ├── memory-store.js └── serve-file-download.js ├── .travis.yml ├── .gitignore ├── documents ├── send-json.md ├── send-html.md ├── filed.md ├── cookies.md ├── form-body.md ├── corsify.md ├── config-chain.md └── csrf-lite.md ├── LICENCE ├── package.json ├── CONTRIBUTING.md └── README.md /examples/mvc/.gitignore: -------------------------------------------------------------------------------- 1 | db/local 2 | -------------------------------------------------------------------------------- /examples/static-files/static/hello.txt: -------------------------------------------------------------------------------- 1 | hello 2 | -------------------------------------------------------------------------------- /modules/st.js: -------------------------------------------------------------------------------- 1 | module.exports = require("st") 2 | -------------------------------------------------------------------------------- /examples/mvc/features/pet/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /modules/pwd.js: -------------------------------------------------------------------------------- 1 | module.exports = require("pwd") 2 | -------------------------------------------------------------------------------- /examples/mvc/features/main/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /examples/mvc/features/user/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /modules/basic.js: -------------------------------------------------------------------------------- 1 | module.exports = require("basic") 2 | -------------------------------------------------------------------------------- /modules/cookies.js: -------------------------------------------------------------------------------- 1 | module.exports = require("cookies") 2 | -------------------------------------------------------------------------------- /modules/corsify.js: -------------------------------------------------------------------------------- 1 | module.exports = require("corsify") 2 | -------------------------------------------------------------------------------- /modules/filed.js: -------------------------------------------------------------------------------- 1 | module.exports = require("filed") 2 | -------------------------------------------------------------------------------- /modules/redsess.js: -------------------------------------------------------------------------------- 1 | module.exports = require("redsess") 2 | -------------------------------------------------------------------------------- /modules/server.js: -------------------------------------------------------------------------------- 1 | module.exports = require("http") 2 | -------------------------------------------------------------------------------- /examples/static-files/static/js/app.js: -------------------------------------------------------------------------------- 1 | console.log("app") 2 | -------------------------------------------------------------------------------- /modules/csrf-lite.js: -------------------------------------------------------------------------------- 1 | module.exports = require("csrf-lite") 2 | -------------------------------------------------------------------------------- /modules/form-body.js: -------------------------------------------------------------------------------- 1 | module.exports = require("body/form") 2 | -------------------------------------------------------------------------------- /modules/send-text.js: -------------------------------------------------------------------------------- 1 | module.exports = require("send-data") 2 | -------------------------------------------------------------------------------- /examples/downloads/files/amazing.txt: -------------------------------------------------------------------------------- 1 | what an amazing download 2 | -------------------------------------------------------------------------------- /examples/static-files/static/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /modules/config-chain.js: -------------------------------------------------------------------------------- 1 | module.exports = require("config-chain") 2 | -------------------------------------------------------------------------------- /modules/hyperscript.js: -------------------------------------------------------------------------------- 1 | module.exports = require("hyperscript") 2 | -------------------------------------------------------------------------------- /modules/json-globals.js: -------------------------------------------------------------------------------- 1 | module.exports = require("json-globals") 2 | -------------------------------------------------------------------------------- /modules/media-types.js: -------------------------------------------------------------------------------- 1 | module.exports = require("media-types") 2 | -------------------------------------------------------------------------------- /modules/multiparty.js: -------------------------------------------------------------------------------- 1 | module.exports = require("multiparty") 2 | -------------------------------------------------------------------------------- /modules/redirecter.js: -------------------------------------------------------------------------------- 1 | module.exports = require("redirecter") 2 | -------------------------------------------------------------------------------- /modules/send-html.js: -------------------------------------------------------------------------------- 1 | module.exports = require("send-data/html") 2 | -------------------------------------------------------------------------------- /modules/send-json.js: -------------------------------------------------------------------------------- 1 | module.exports = require("send-data/json") 2 | -------------------------------------------------------------------------------- /modules/typed-error.js: -------------------------------------------------------------------------------- 1 | module.exports = require("error/typed") 2 | -------------------------------------------------------------------------------- /modules/routes-router.js: -------------------------------------------------------------------------------- 1 | module.exports = require("routes-router") 2 | -------------------------------------------------------------------------------- /modules/serve-favicon.js: -------------------------------------------------------------------------------- 1 | module.exports = require("serve-favicon") 2 | -------------------------------------------------------------------------------- /examples/mvc/config/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "dbPath": "./db/local" 3 | } 4 | -------------------------------------------------------------------------------- /modules/generic-session.js: -------------------------------------------------------------------------------- 1 | module.exports = require("generic-session") 2 | -------------------------------------------------------------------------------- /modules/string-template.js: -------------------------------------------------------------------------------- 1 | module.exports = require("string-template") 2 | -------------------------------------------------------------------------------- /modules/memory-store.js: -------------------------------------------------------------------------------- 1 | module.exports = require("generic-session").MemoryStore 2 | -------------------------------------------------------------------------------- /modules/serve-file-download.js: -------------------------------------------------------------------------------- 1 | module.exports = require("serve-file-download") 2 | -------------------------------------------------------------------------------- /examples/mvc/config/config.production.json: -------------------------------------------------------------------------------- 1 | { 2 | "dbPath": "./db/production" 3 | } 4 | -------------------------------------------------------------------------------- /examples/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raynos/http-framework/HEAD/examples/favicon/favicon.ico -------------------------------------------------------------------------------- /examples/mvc/db/readme.md: -------------------------------------------------------------------------------- 1 | # db folder. 2 | 3 | Checked into git because otherwise levelup will fail to open 4 | the database 5 | -------------------------------------------------------------------------------- /examples/mvc/Makefile: -------------------------------------------------------------------------------- 1 | install: 2 | cd ./features/user && npm install 3 | cd ./features/main && npm install 4 | echo "installed features" 5 | -------------------------------------------------------------------------------- /examples/mvc/readme.md: -------------------------------------------------------------------------------- 1 | # MVC Example 2 | 3 | ## Running instructions 4 | 5 | run `make install` to install the dependencies. 6 | run `node server.js` to start the server 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.8 4 | - "0.10" 5 | before_script: 6 | - npm install 7 | - npm install istanbul coveralls 8 | script: npm run travis-test 9 | -------------------------------------------------------------------------------- /examples/route-seperation/routes/user/user-db.js: -------------------------------------------------------------------------------- 1 | // Fake user database 2 | var users = [ 3 | { name: "TJ", email: "tj@vision-media.ca" }, 4 | { name: "Tobi", email: "tobi@vision-media.ca" } 5 | ] 6 | 7 | module.exports = users 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .monitor 3 | .*.swp 4 | .nodemonignore 5 | releases 6 | *.log 7 | *.err 8 | fleet.json 9 | public/browserify 10 | bin/*.json 11 | .bin 12 | build 13 | compile 14 | .lock-wscript 15 | coverage 16 | node_modules 17 | -------------------------------------------------------------------------------- /examples/error-pages/templates/404.js: -------------------------------------------------------------------------------- 1 | var h = require("hyperscript") 2 | 3 | var error = require("./error.js") 4 | 5 | module.exports = fourofour 6 | 7 | function fourofour(url) { 8 | return error(h("h2", "Cannot find " + url)) 9 | } 10 | -------------------------------------------------------------------------------- /examples/mvc/static/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 16px "Helvetica Neue", Helvetica, Arial; 4 | } 5 | a { 6 | color: #107aff; 7 | text-decoration: none; 8 | } 9 | a:hover { 10 | text-decoration: underline; 11 | } 12 | h1 a { 13 | font-size: 16px; 14 | } 15 | -------------------------------------------------------------------------------- /examples/route-seperation/routes/home.js: -------------------------------------------------------------------------------- 1 | var sendHtml = require("send-data/html") 2 | 3 | var homePage = require("../templates/home.js") 4 | 5 | module.exports = home 6 | 7 | function home(req, res) { 8 | sendHtml(req, res, homePage({ title: "Route seperation example"})) 9 | } 10 | -------------------------------------------------------------------------------- /examples/mvc/views/not-found.js: -------------------------------------------------------------------------------- 1 | var h = require("hyperscript") 2 | 3 | var layout = require("./layout.js") 4 | 5 | module.exports = notFound 6 | 7 | function notFound(url) { 8 | return layout("Not Found", [ 9 | h("h1", "404: Not Found"), 10 | h("p", "Sorry we can't find " + url) 11 | ]) 12 | } 13 | -------------------------------------------------------------------------------- /examples/hello-world/server.js: -------------------------------------------------------------------------------- 1 | // based on https://github.com/visionmedia/express/blob/master/examples/error-pages/index.js 2 | var http = require("http") 3 | 4 | var app = http.createServer(function (req, res) { 5 | res.end("Hello World") 6 | }) 7 | 8 | app.listen(3000) 9 | console.log("hello world server listening on port 3000") 10 | -------------------------------------------------------------------------------- /examples/route-seperation/templates/users/view.js: -------------------------------------------------------------------------------- 1 | var h = require("hyperscript") 2 | 3 | var layout = require("../layout.js") 4 | 5 | module.exports = view 6 | 7 | function view(opts) { 8 | return layout(opts.title, [ 9 | h("h1", opts.name), 10 | h("#user", 11 | h("p", "Email: ", opts.email)) 12 | ]) 13 | } 14 | -------------------------------------------------------------------------------- /examples/mvc/views/error-page.js: -------------------------------------------------------------------------------- 1 | var h = require("hyperscript") 2 | 3 | var layout = require("./layout.js") 4 | 5 | module.exports = errorPage 6 | 7 | function errorPage(err) { 8 | return layout("Internal Server Error", [ 9 | h("h1", "500: Internal Server Error"), 10 | h("p", "Looks like something blew up!") 11 | ]) 12 | } 13 | -------------------------------------------------------------------------------- /examples/route-seperation/routes/user/index.js: -------------------------------------------------------------------------------- 1 | var sendHtml = require("send-data/html") 2 | 3 | var usersPage = require("../../templates/users/index.js") 4 | var users = require("./user-db.js") 5 | 6 | module.exports = usersRoute 7 | 8 | function usersRoute(req, res) { 9 | sendHtml(req, res, usersPage({ title: "Users", users: users })) 10 | } 11 | -------------------------------------------------------------------------------- /examples/error-pages/templates/error.js: -------------------------------------------------------------------------------- 1 | var h = require("hyperscript") 2 | 3 | module.exports = error 4 | 5 | function error(content) { 6 | return "" + h("html", [ 7 | h("head", 8 | h("title", "Error")), 9 | h("body", [ 10 | h("h1", "An error occured!"), 11 | content 12 | ]) 13 | ]).outerHTML 14 | } 15 | -------------------------------------------------------------------------------- /examples/error-pages/templates/layout.js: -------------------------------------------------------------------------------- 1 | var h = require("hyperscript") 2 | 3 | module.exports = layout 4 | 5 | function layout(content) { 6 | return "" + h("html", [ 7 | h("head", 8 | h("title", "Custom Pages Example")), 9 | h("body", [ 10 | h("h1", "My Site"), 11 | content 12 | ]) 13 | ]).outerHTML 14 | } 15 | -------------------------------------------------------------------------------- /examples/mvc/features/main/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "main", 3 | "version": "0.0.0", 4 | "description": "ERROR: No README.md file found!", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": "", 10 | "author": "", 11 | "license": "BSD", 12 | "dependencies": { 13 | "redirecter": "~0.2.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/mvc/features/pet/views/show.js: -------------------------------------------------------------------------------- 1 | var h = require("hyperscript") 2 | 3 | module.exports = show 4 | 5 | function show(pet, opts) { 6 | return opts.layout("Pet page", [ 7 | h("h1", [ 8 | pet.name, 9 | h("a", { 10 | href: opts.urlMap.petEdit({ petId: pet.id }) 11 | }, " edit") 12 | ]), 13 | h("p", "You are viewing " + pet.name) 14 | ]) 15 | } 16 | -------------------------------------------------------------------------------- /examples/mvc/views/layout.js: -------------------------------------------------------------------------------- 1 | var h = require("hyperscript") 2 | 3 | module.exports = layout 4 | 5 | function layout(title, content) { 6 | return "" + 7 | h("html", [ 8 | h("head", [ 9 | h("title", title), 10 | h("link", { href: "/static/style.css", rel: "stylesheet" }) 11 | ]), 12 | h("body", content) 13 | ]).outerHTML 14 | } 15 | -------------------------------------------------------------------------------- /examples/route-seperation/static/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Helvetica Neue", Arial, sans-serif; 4 | } 5 | a { 6 | color: #00AEFF; 7 | text-decoration: none; 8 | } 9 | a.edit { 10 | color: #000; 11 | opacity: .3; 12 | } 13 | a.edit::before { 14 | content: ' ['; 15 | } 16 | a.edit::after { 17 | content: ']'; 18 | } 19 | dt { 20 | font-weight: bold; 21 | } 22 | dd { 23 | margin: 15px; 24 | } 25 | -------------------------------------------------------------------------------- /examples/route-seperation/templates/layout.js: -------------------------------------------------------------------------------- 1 | var h = require("hyperscript") 2 | 3 | module.exports = layout 4 | 5 | function layout(title, content) { 6 | return "" + 7 | h("html", [ 8 | h("head", [ 9 | h("title", title), 10 | h("link", { href: "style.css", rel: "stylesheet" }) 11 | ]), 12 | h("body", content) 13 | ]).outerHTML 14 | } 15 | -------------------------------------------------------------------------------- /examples/cors/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /examples/search/client.js: -------------------------------------------------------------------------------- 1 | var search = document.querySelector("[type=search]") 2 | var code = document.querySelector("pre") 3 | 4 | search.addEventListener("keyup", function(){ 5 | var xhr = new XMLHttpRequest() 6 | xhr.open("GET", "/search/" + search.value, true) 7 | xhr.onreadystatechange = function(){ 8 | if (4 === xhr.readyState) { 9 | code.textContent = xhr.responseText 10 | } 11 | } 12 | xhr.send() 13 | }, false) 14 | -------------------------------------------------------------------------------- /examples/route-seperation/templates/posts/index.js: -------------------------------------------------------------------------------- 1 | var h = require("hyperscript") 2 | 3 | var layout = require("../layout.js") 4 | 5 | module.exports = posts 6 | 7 | function posts(opts) { 8 | return layout(opts.title, [ 9 | h("h1", "Posts"), 10 | h("dl#posts", opts.posts.map(function (post) { 11 | return [ 12 | h("dt", post.title), 13 | h("dd", post.body) 14 | ] 15 | })) 16 | ]) 17 | } 18 | -------------------------------------------------------------------------------- /examples/error-pages/templates/500.js: -------------------------------------------------------------------------------- 1 | var h = require("hyperscript") 2 | 3 | // note that we extend a different layout with 4xx and 5xx 4 | // responses by requiring the `error` default instead 5 | var error = require("./error.js") 6 | 7 | module.exports = fivehundred 8 | 9 | function fivehundred(err, opts) { 10 | return error([ 11 | h("h1", "Error: " + err.message), 12 | opts.verbose ? 13 | h("pre", err.stack) : 14 | h("p", "An error occured!") 15 | ]) 16 | } 17 | -------------------------------------------------------------------------------- /examples/route-seperation/routes/posts.js: -------------------------------------------------------------------------------- 1 | var sendHtml = require("send-data/html") 2 | 3 | var postsPage = require("../templates/posts/index.js") 4 | 5 | // Fake posts database 6 | var posts = [ 7 | { title: "Foo", body: "some foo bar" }, 8 | { title: "Foo bar", body: "more foo bar" }, 9 | { title: "Foo bar baz", body: "more foo bar baz" } 10 | ] 11 | 12 | module.exports = postsRoute 13 | 14 | function postsRoute(req, res) { 15 | sendHtml(req, res, postsPage({ title: "Posts", posts: posts })) 16 | } 17 | -------------------------------------------------------------------------------- /examples/mvc/features/user/views/list.js: -------------------------------------------------------------------------------- 1 | var h = require("hyperscript") 2 | 3 | module.exports = list 4 | 5 | function list(users, opts) { 6 | return opts.layout("Users list", [ 7 | h("h1", "Users"), 8 | h("p", "Click a user below to view their pets."), 9 | h("ul", users.map(function (user) { 10 | return h("li", [ 11 | h("a", { 12 | href: opts.urlMap.user({ userId: user.id }) 13 | }, user.name) 14 | ]) 15 | })) 16 | ]) 17 | } 18 | -------------------------------------------------------------------------------- /examples/search/page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Search examples 5 | 11 | 12 | 13 |

Search

14 |

Try searching for "ferret" or "cat".

15 | 16 |

17 |         
18 |     
19 | 
20 | 


--------------------------------------------------------------------------------
/examples/favicon/test/test.js:
--------------------------------------------------------------------------------
 1 | var test = require('tape')
 2 |   , http = require('http')
 3 |   , options = {
 4 |       host: 'localhost',
 5 |       port: 3000,
 6 |       path: '/'
 7 |     }
 8 | 
 9 | // test if the favicon was served
10 | test('serve-favicon', function(t) {
11 |   t.plan(2)
12 |   http.get(options, function(res) {
13 |     t.deepEqual(res.statusCode, 200)
14 |     res.on('data', function(d) {
15 |       t.deepEqual(d.toString(), 'favicon example')
16 |     })
17 |   }).on('error', function(e) {
18 |     console.log(e.message)
19 |   })
20 | })
21 | 


--------------------------------------------------------------------------------
/examples/route-seperation/templates/users/index.js:
--------------------------------------------------------------------------------
 1 | var h = require("hyperscript")
 2 | 
 3 | var layout = require("../layout.js")
 4 | 
 5 | module.exports = show
 6 | 
 7 | function show(opts) {
 8 |     return layout(opts.title, [
 9 |         h("h1", "Users"),
10 |         h("#users", opts.users.map(function (user, index) {
11 |             return h("li", [
12 |                 h("a", { href: "/users/" + index }, user.name), " ",
13 |                 h("a.edit", { href: "/users/" + index + "/edit" }, "edit")
14 |             ])
15 |         }))
16 |     ])
17 | }
18 | 


--------------------------------------------------------------------------------
/examples/hello-world/test/test.js:
--------------------------------------------------------------------------------
 1 | var test = require("tape")
 2 |   , http = require("http")
 3 |   , options = {
 4 |       host: "localhost",
 5 |       port: 3000,
 6 |       path: "/"
 7 |     }
 8 | 
 9 | // check the res sent by the hello world server
10 | test("hello-world", function(t) {
11 |   t.plan(2)
12 |   http.get(options, function(res) {
13 |     t.deepEqual(res.statusCode, 200)
14 |     res.on("data", function(d) {
15 |       t.deepEqual(d.toString(), "Hello World")
16 |     })
17 |   }).on("error", function(e) {
18 |     console.log(e.message)
19 |   })
20 | })
21 | 


--------------------------------------------------------------------------------
/examples/mvc/features/pet/package.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "name": "pet",
 3 |   "version": "0.0.0",
 4 |   "description": "",
 5 |   "main": "index.js",
 6 |   "scripts": {
 7 |     "test": "echo \"Error: no test specified\" && exit 1"
 8 |   },
 9 |   "author": "",
10 |   "license": "BSD-2-Clause",
11 |   "dependencies": {
12 |     "routes-router": "~1.5.3",
13 |     "error": "~2.0.4",
14 |     "send-data": "~3.1.2",
15 |     "body": "~4.4.2",
16 |     "redirecter": "~0.2.3",
17 |     "string-template": "~0.1.3",
18 |     "level-sublevel": "~5.1.1",
19 |     "hyperscript": "~1.3.2"
20 |   }
21 | }
22 | 


--------------------------------------------------------------------------------
/examples/route-seperation/routes/user/load-user.js:
--------------------------------------------------------------------------------
 1 | var TypedError = require("error/typed")
 2 | var process = require("process")
 3 | 
 4 | var users = require("./user-db.js")
 5 | 
 6 | var NOT_FOUND = TypedError({
 7 |     statusCode: 404,
 8 |     message: "Cannot find user %s"
 9 | })
10 | 
11 | module.exports = loadUser
12 | 
13 | function loadUser(id, callback) {
14 |     var user = users[id]
15 | 
16 |     process.nextTick(function () {
17 |         if (user) {
18 |             return callback(null, user)
19 |         }
20 | 
21 |         callback(NOT_FOUND(id))
22 |     })
23 | }
24 | 


--------------------------------------------------------------------------------
/examples/mvc/features/pet/views/edit.js:
--------------------------------------------------------------------------------
 1 | var h = require("hyperscript")
 2 | 
 3 | module.exports = edit
 4 | 
 5 | function edit(pet, opts) {
 6 |     return opts.layout("Edit Pet", [
 7 |         h("h1", pet.name),
 8 |         h("form", {
 9 |             action: opts.urlMap.pet({ petId: pet.id }),
10 |             method: "post"
11 |         }, [
12 |             h("label", [
13 |                 "Name: ",
14 |                 h("input", { name: "name", value: pet.name })
15 |             ]),
16 |             h("input", { type: "submit", value: "Update" })
17 |         ])
18 |     ])
19 | }
20 | 


--------------------------------------------------------------------------------
/examples/route-map/map.js:
--------------------------------------------------------------------------------
 1 | var METHODS = ["GET", "DELETE", "POST", "PUT"]
 2 | 
 3 | // this should be a module on npm
 4 | module.exports = map
 5 | 
 6 | function map(router, hash, route) {
 7 |     route = route || ""
 8 |     var keys = Object.keys(hash)
 9 |     keys.forEach(function (routeOrMethod) {
10 |         if (METHODS.indexOf(routeOrMethod) !== -1) {
11 |             return
12 |         }
13 | 
14 |         var value = hash[routeOrMethod]
15 |         var newRoute = route + routeOrMethod
16 |         router.addRoute(newRoute, value)
17 |         map(router, value, newRoute)
18 |     })
19 | }
20 | 


--------------------------------------------------------------------------------
/examples/route-seperation/templates/home.js:
--------------------------------------------------------------------------------
 1 | var h = require("hyperscript")
 2 | 
 3 | var layout = require("./layout.js")
 4 | 
 5 | module.exports = home
 6 | 
 7 | function home(opts) {
 8 |     return layout(opts.title,
 9 |         h("ul", [
10 |             h("li", [
11 |                 "Visit the ",
12 |                 h("a", { href: "/users" }, "users"),
13 |                 " page"
14 |             ]),
15 |             h("li", [
16 |                 "Visit the ",
17 |                 h("a", { href: "/posts" }, "posts"),
18 |                 " page"
19 |             ])
20 |         ]))
21 | }
22 | 


--------------------------------------------------------------------------------
/examples/route-seperation/routes/user/view.js:
--------------------------------------------------------------------------------
 1 | var sendHtml = require("send-data/html")
 2 | 
 3 | var userPage = require("../../templates/users/view.js")
 4 | var loadUser = require("./load-user.js")
 5 | 
 6 | module.exports = userView
 7 | 
 8 | function userView(req, res, opts, cb) {
 9 |     loadUser(opts.id, function (err, user) {
10 |         if (err) {
11 |             return cb(err)
12 |         }
13 | 
14 |         sendHtml(req, res, userPage({
15 |             title: "Viewing user " + user.name,
16 |             name: user.name,
17 |             email: user.email
18 |         }))
19 |     })
20 | }
21 | 


--------------------------------------------------------------------------------
/examples/error-pages/templates/index.js:
--------------------------------------------------------------------------------
 1 | var h = require("hyperscript")
 2 | 
 3 | var layout = require("./layout.js")
 4 | 
 5 | module.exports = home
 6 | 
 7 | function home() {
 8 |     return layout([
 9 |         h("h2", "Pages Example"),
10 |         h("ul", [
11 |             visitLink({ href: "/500", text: "500" }),
12 |             visitLink({ href: "/404", text: "404" }),
13 |             visitLink({ href: "/403", text: "403" })
14 |         ])
15 |     ])
16 | }
17 | 
18 | function visitLink(opts) {
19 |     return h("li", [
20 |         "visit ",
21 |         h("a", { href: opts.href }, opts.text)
22 |     ])
23 | }
24 | 


--------------------------------------------------------------------------------
/examples/mvc/features/user/package.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "name": "user",
 3 |   "version": "0.0.0",
 4 |   "description": "ERROR: No README.md file found!",
 5 |   "main": "index.js",
 6 |   "scripts": {
 7 |     "test": "echo \"Error: no test specified\" && exit 1"
 8 |   },
 9 |   "repository": "",
10 |   "author": "",
11 |   "license": "BSD",
12 |   "dependencies": {
13 |     "send-data": "~3.1.2",
14 |     "routes-router": "~1.5.3",
15 |     "redirecter": "~0.2.3",
16 |     "body": "~4.4.2",
17 |     "error": "~2.0.4",
18 |     "string-template": "~0.1.3",
19 |     "hyperscript": "~1.3.2",
20 |     "level-sublevel": "~5.1.1"
21 |   }
22 | }
23 | 


--------------------------------------------------------------------------------
/examples/cookie-sessions/server.js:
--------------------------------------------------------------------------------
 1 | // based on https://github.com/visionmedia/express/blob/master/examples/cookie-sessions/index.js
 2 | var http = require("http")
 3 | var Router = require("routes-router")
 4 | var Cookies = require("cookies")
 5 | 
 6 | var app = Router()
 7 | 
 8 | app.addRoute("*", function (req, res) {
 9 |     var cookies = new Cookies(req, res)
10 |     var count = Number(cookies.get("count") || "0") + 1
11 |     cookies.set("count", String(count))
12 | 
13 |     res.end("viewed " + count + " times\n")
14 | })
15 | 
16 | var server = http.createServer(app)
17 | server.listen(3000)
18 | console.log("cookie session server listening on port 3000")
19 | 


--------------------------------------------------------------------------------
/examples/favicon/server.js:
--------------------------------------------------------------------------------
 1 | // based on https://github.com/expressjs/serve-favicon#vanilla-http-server
 2 | // middleware for serving a favicon using vanilla http
 3 | var http = require('http')
 4 |   , path = require('path')
 5 |   , favicon = require('serve-favicon')
 6 | 
 7 | var _favicon = favicon(path.join(__dirname,'/favicon.ico'))
 8 | 
 9 | http.createServer(function(req, res) {
10 |   _favicon(req, res, function(err) {
11 |     if (err) {
12 |       res.statusCode = 500
13 |       res.end('error')
14 |       return
15 |     }
16 |     res.end('favicon example')
17 |   })
18 | }).listen(3000, function() {
19 |   console.log('listening on port: 3000')
20 | })
21 | 


--------------------------------------------------------------------------------
/examples/mvc/features/main/index.js:
--------------------------------------------------------------------------------
 1 | var redirecter = require("redirecter")
 2 | 
 3 | // This is our controller.
 4 | // note that our feature has it's own package.json and it's own
 5 | // features dependencies
 6 | module.exports = MainController
 7 | 
 8 | function MainController(config) {
 9 |     // the main feature only works if the user feature is installed
10 |     // if it is installed then the uri where the user feature lives
11 |     // is in the features hash.
12 |     // this allows us to change the uris where features live on
13 |     // our website at the top level
14 |     var userUri = config.features.user
15 | 
16 |     return controller
17 | 
18 |     function controller(req, res) {
19 |         redirecter(req, res, userUri)
20 |     }
21 | }
22 | 


--------------------------------------------------------------------------------
/examples/route-seperation/templates/users/edit.js:
--------------------------------------------------------------------------------
 1 | var h = require("hyperscript")
 2 | 
 3 | var layout = require("../layout.js")
 4 | 
 5 | module.exports = edit
 6 | 
 7 | function edit(opts) {
 8 |     return layout(opts.title, [
 9 |         h("h1", "Editing ", opts.name),
10 |         h("#user",
11 |             h("form", { method: "post" }, [
12 |                 h("p", [
13 |                     "Name: ",
14 |                     h("input", { value: opts.name, name: "name" })
15 |                 ]),
16 |                 h("p", [
17 |                     "Email: ",
18 |                     h("input", { value: opts.email, name: "email" })
19 |                 ]),
20 |                 h("p", h("input", { type: "submit", value: "Save" }))
21 |             ]))
22 |     ])
23 | }
24 | 


--------------------------------------------------------------------------------
/examples/mvc/features/user/views/show.js:
--------------------------------------------------------------------------------
 1 | var h = require("hyperscript")
 2 | 
 3 | module.exports = show
 4 | 
 5 | function show(user, opts) {
 6 |     return opts.layout("User page", [
 7 |         h("h1", [
 8 |             user.name + " ",
 9 |             h("a", {
10 |                 href: opts.urlMap.userEdit({ userId: user.id })
11 |             }, "edit")
12 |         ]),
13 |         // handle messaging somehow
14 |         user.pets.length ? [
15 |             h("p", "View " + user.name + "s pets:"),
16 |             h("ul", user.pets.map(function (pet) {
17 |                 return h("li", [
18 |                     h("a", {
19 |                         href: opts.urlMap.pet({ petId: pet.id })
20 |                     }, pet.name)
21 |                 ])
22 |             }))
23 |         ] : h("p", "No pets!")
24 |     ])
25 | }
26 | 


--------------------------------------------------------------------------------
/examples/static-files/server.js:
--------------------------------------------------------------------------------
 1 | // based on https://github.com/visionmedia/express/blob/master/examples/static-files/index.js
 2 | var http = require("http")
 3 | var st = require("st")
 4 | var Router = require("routes-router")
 5 | 
 6 | var app = Router()
 7 | 
 8 | // Serve all the static assets prefixed at /static
 9 | // so GET /static/js/app.js will work. 
10 | app.addRoute("/static/*", st({
11 |     path: __dirname + "/static",
12 |     url: "/static"
13 | }))
14 | 
15 | // for all routes try serve the files statically
16 | app.addRoute("*", st(__dirname + "/static"))
17 | 
18 | var server = http.createServer(app)
19 | server.listen(3000)
20 | 
21 | console.log("static server listening on port 3000")
22 | console.log("try:")
23 | console.log("  GET /hello.txt")
24 | console.log("  GET /js/app.js")
25 | console.log("  GET /css/style.css")
26 | 


--------------------------------------------------------------------------------
/examples/csrf/template.js:
--------------------------------------------------------------------------------
 1 | var h = require("hyperscript")
 2 | 
 3 | module.exports = template
 4 | 
 5 | function template(opts) {
 6 |     return "" + h("html", [
 7 |         h("head",
 8 |             h("title", "CSRF demo")),
 9 |         h("body", [
10 |             h("form", { action: "/", method: "post" }, [
11 |                 h("input", {
12 |                     type: "hidden",
13 |                     name: "x-csrf-token",
14 |                     value: opts.token
15 |                 }),
16 |                 h("input", {
17 |                     type: "text",
18 |                     name: "name",
19 |                     value: opts.name,
20 |                     placeholder: "Username"
21 |                 }),
22 |                 h("input", { type: "submit", value: "Login" })
23 |             ])
24 |         ])
25 |     ]).outerHTML
26 | }
27 | 


--------------------------------------------------------------------------------
/examples/basic-auth/server.js:
--------------------------------------------------------------------------------
 1 | // based on https://github.com/senchalabs/connect/blob/master/examples/basicAuth.js
 2 | var basic = require("basic")
 3 | var http = require("http")
 4 | 
 5 | function sendBasicError(req, res) {
 6 |     res.statusCode = 401
 7 |     res.setHeader("WWW-Authenticate", "Basic realm=\"Secure Area\"")
 8 |     res.end("Unauthorized")
 9 | }
10 | 
11 | var auth = basic(function (user, pass, callback) {
12 |     if (user === "tj" && pass === "tobi") {
13 |         return callback(null)
14 |     }
15 | 
16 |     callback(new Error("Access Denied"))
17 | })
18 | 
19 | var server = http.createServer(function (req, res) {
20 |     auth(req, res, function (err) {
21 |         if (err) {
22 |             return sendBasicError(req, res)
23 |         }
24 | 
25 |         res.end("authorized!")
26 |     })
27 | })
28 | server.listen(3000)
29 | console.log("basic auth server listening on port 3000")
30 | 


--------------------------------------------------------------------------------
/documents/send-json.md:
--------------------------------------------------------------------------------
 1 | # [`send-json`](https://github.com/Raynos/send-data)
 2 | 
 3 | > I want to send a JSON payload to my user
 4 | 
 5 | ```js
 6 | var http = require("http")
 7 | var sendJson = require("send-data/json")
 8 | 
 9 | http.createServer(function (req, res) {
10 |     sendJson(req, res, {
11 |         sending: "json"
12 |     })
13 | }).listen(8080)
14 | ```
15 | 
16 | ## Alternatively by hand:
17 | 
18 | ```js
19 | var http = require("http")
20 | 
21 | http.createServer(function (req, res) {
22 |     // notice creating a buffer from the string
23 |     var payload = new Buffer(JSON.stringify({
24 |         sending: "json"
25 |     }))
26 |     res.statusCode = 200
27 |     res.setHeader("Content-Type", "application/json")
28 |     // ensure Content-Length is number of bytes, not number of characters
29 |     res.setHeader("Content-Length", payload.length)
30 |     res.end(payload)
31 | }).listen(8080)
32 | ```
33 | 


--------------------------------------------------------------------------------
/examples/downloads/server.js:
--------------------------------------------------------------------------------
 1 | // based on https://github.com/visionmedia/express/tree/master/examples/downloads
 2 | var http = require("http")
 3 | var Router = require("routes-router")
 4 | var sendHtml = require("send-data/html")
 5 | 
 6 | var serveFileDownload = require("serve-file-download")
 7 | 
 8 | var app = Router()
 9 | 
10 | app.addRoute("/", function (req, res) {
11 |     sendHtml(req, res, "")
15 | })
16 | 
17 | app.addRoute("/files/*", function (req, res, opts, cb) {
18 |     var file = opts.splats[0]
19 |     var uri = __dirname  + "/files/" + file
20 |     serveFileDownload(req, res, uri, cb)
21 | })
22 | 
23 | var server = http.createServer(app)
24 | server.listen(3000)
25 | console.log("downloads server listening on port 3000")
26 | 


--------------------------------------------------------------------------------
/examples/route-seperation/server.js:
--------------------------------------------------------------------------------
 1 | // based on https://github.com/visionmedia/express/blob/master/examples/route-separation/index.js
 2 | var http = require("http")
 3 | var Router = require("routes-router")
 4 | 
 5 | var home = require("./routes/home.js")
 6 | var posts = require("./routes/posts.js")
 7 | var users = require("./routes/user/index.js")
 8 | var userView = require("./routes/user/view.js")
 9 | var userEdit = require("./routes/user/edit.js")
10 | 
11 | var app = Router()
12 | 
13 | // general routes
14 | app.addRoute("/", home)
15 | 
16 | // user routes
17 | app.addRoute("/users", users)
18 | app.addRoute("/users/:id", userView)
19 | app.addRoute("/users/:id/view", userView)
20 | app.addRoute("/users/:id/edit", userEdit)
21 | 
22 | // post routes
23 | app.addRoute("/posts", posts)
24 | 
25 | var server = http.createServer(app)
26 | server.listen(3000)
27 | console.log("route seperated server listening on port 3000")
28 | 
29 | 


--------------------------------------------------------------------------------
/examples/expose-data-to-client/template.js:
--------------------------------------------------------------------------------
 1 | var h = require("hyperscript")
 2 | var JSONGlobals = require("json-globals")
 3 | 
 4 | module.exports = template
 5 | 
 6 | function template(clientData) {
 7 |     return "" + h("html", [
 8 |         h("head", [
 9 |             h("title", "Express"),
10 |             // to expose state to the client
11 |             // we create a script tag with JSONGlobals version
12 |             // of the data. This will just expose a single
13 |             // global variable called `__JSON_GLOBALS_`
14 |             h("script", JSONGlobals(clientData))
15 |         ]),
16 |         h("body", [
17 |             h("h1", "Expose client data"),
18 |             h("p", "The following was exposed to the client:"),
19 |             h("pre",
20 |                 h("script", [
21 |                     "document.write(JSON.stringify(__JSON_GLOBALS_, null, 2))"
22 |                 ]))
23 |         ])
24 |     ]).outerHTML
25 | }
26 | 


--------------------------------------------------------------------------------
/documents/send-html.md:
--------------------------------------------------------------------------------
 1 | # [`send-html`](https://github.com/Raynos/send-data)
 2 | 
 3 | > I want to send a HTML payload to my user
 4 | 
 5 | ```js
 6 | var http = require("http")
 7 | var fs = require("fs")
 8 | var sendHtml = require("send-data/html")
 9 | 
10 | var page = fs.readFileSync(__dirname + "/static/index.html")
11 | 
12 | http.createServer(function (req, res) {
13 |     sendHtml(req, res, page)
14 | }).listen(8080)
15 | ```
16 | 
17 | ## Alternatively by hand:
18 | 
19 | ```js
20 | var http = require("http")
21 | var fs = require("fs")
22 | 
23 | // note that fs.readFileSync returns a buffer
24 | var page = fs.readFileSync(__dirname + "/static/index.html")
25 | 
26 | http.createServer(function (req, res) {
27 |     res.statusCode = 200
28 |     res.setHeader("Content-Type", "text/html")
29 |     // ensure Content-Length is number of bytes, not number of characters
30 |     res.setHeader("Content-Length", page.length)
31 |     res.end(page)
32 | }).listen(8080)
33 | ```
34 | 


--------------------------------------------------------------------------------
/examples/mvc/features/user/views/edit.js:
--------------------------------------------------------------------------------
 1 | var h = require("hyperscript")
 2 | 
 3 | module.exports = edit
 4 | 
 5 | function edit(user, options) {
 6 |     return options.layout("Edit User", [
 7 |         h("h1", user.name),
 8 |         h("form", {
 9 |             action: options.urlMap.user({ userId: user.id }),
10 |             method: "post"
11 |         }, [
12 |             h("label", [
13 |                 "Name: ",
14 |                 h("input", { type: "text", name: "name", value: user.name })
15 |             ]),
16 |             h("input", { type: "submit", value: "Update" })
17 |         ]),
18 |         h("form", {
19 |             action: options.urlMap.userPet({ userId: user.id }),
20 |             method: "post"
21 |         }, [
22 |             h("label", [
23 |                 "Pet: ",
24 |                 h("input", { type: "text", name: "name", placeholder: "name" })
25 |             ]),
26 |             h("input", { type: "submit", value: "Add" })
27 |         ])
28 |     ])
29 | }
30 | 


--------------------------------------------------------------------------------
/examples/error/server.js:
--------------------------------------------------------------------------------
 1 | // based on https://github.com/visionmedia/express/blob/master/examples/error/index.js
 2 | var http = require("http")
 3 | var Router = require("routes-router")
 4 | 
 5 | var app = Router({
 6 |     // you can define your own error handler. If you call the cb
 7 |     // in (req, res, opts, cb) in your routes then this error
 8 |     // handler gets called
 9 |     errorHandler: function (req, res, err) {
10 |         res.statusCode = 500
11 |         res.end(err.message || "Internal server error")
12 |     }
13 | })
14 | 
15 | app.addRoute("/", function (req, res, opts, cb) {
16 |     // try catch is slow. do not throw. pass them to cb
17 |     cb(new Error("something broke!"))
18 | })
19 | 
20 | app.addRoute("/next", function (req, res, opts, cb) {
21 |     // you can pass asynchronous exceptions to the cb
22 |     process.nextTick(function () {
23 |         cb(new Error("oh no!"))
24 |     })
25 | })
26 | 
27 | var server = http.createServer(app)
28 | server.listen(3000)
29 | console.log("error server listening on port 3000")
30 | 


--------------------------------------------------------------------------------
/LICENCE:
--------------------------------------------------------------------------------
 1 | Copyright (c) 2013 Raynos.
 2 | 
 3 | Permission is hereby granted, free of charge, to any person obtaining a copy
 4 | of this software and associated documentation files (the "Software"), to deal
 5 | in the Software without restriction, including without limitation the rights
 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 7 | copies of the Software, and to permit persons to whom the Software is
 8 | furnished to do so, subject to the following conditions:
 9 | 
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 | 
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 | 


--------------------------------------------------------------------------------
/examples/basic-auth/admin.js:
--------------------------------------------------------------------------------
 1 | // based on https://github.com/senchalabs/connect/blob/master/examples/basicAuth.js
 2 | var basic = require("basic")
 3 | var Router = require("routes-router")
 4 | var http = require("http")
 5 | 
 6 | function sendBasicError(req, res) {
 7 |     res.statusCode = 401
 8 |     res.setHeader("WWW-Authenticate", "Basic realm=\"Secure Area\"")
 9 |     res.end("Unauthorized")
10 | }
11 | 
12 | var auth = basic(function (user, pass, callback) {
13 |     if (user === "tj" && pass === "tobi") {
14 |         return callback(null)
15 |     }
16 | 
17 |     callback(new Error("Access Denied"))
18 | })
19 | 
20 | var app = Router()
21 | 
22 | app.addRoute("/admin", function (req, res) {
23 |     auth(req, res, function (err) {
24 |         if (err) {
25 |             return sendBasicError(req, res)
26 |         }
27 | 
28 |         res.end("authorized!")
29 |     })
30 | })
31 | 
32 | 
33 | app.addRoute("*", function (req, res) {
34 |     res.end("hello! try /admin")
35 | })
36 | 
37 | var adminServer = http.createServer(app)
38 | adminServer.listen(3000)
39 | console.log("basic authed admin server listening on port 3001")
40 | 


--------------------------------------------------------------------------------
/examples/cors/server.js:
--------------------------------------------------------------------------------
 1 | // based on https://github.com/visionmedia/express/tree/master/examples/cors
 2 | var http = require("http")
 3 | var Router = require("routes-router")
 4 | var filed = require("filed")
 5 | var Corsify = require("corsify")
 6 | var jsonBody = require("body/json")
 7 | var sendJson = require("send-data/json")
 8 | 
 9 | var app = Router()
10 | var api = Router()
11 | 
12 | app.addRoute("/", function (req, res) {
13 |     filed(__dirname + "/static/index.html").pipe(res)
14 | })
15 | 
16 | var cors = Corsify({
17 |     "Access-Control-Allow-Origin": "http://localhost:3000"
18 | })
19 | 
20 | api.addRoute("/user", cors(function (req, res, opts, cb) {
21 |     jsonBody(req, res, function (err, body) {
22 |         if (err) {
23 |             return cb(err)
24 |         }
25 | 
26 |         console.log("body", body)
27 |         sendJson(req, res, body)
28 |     })
29 | }))
30 | 
31 | var appServer = http.createServer(app)
32 | appServer.listen(3000)
33 | console.log("cors app server listening on port 3000")
34 | 
35 | var apiServer = http.createServer(api)
36 | apiServer.listen(3001)
37 | console.log("cors api server listening on port 3001")
38 | 


--------------------------------------------------------------------------------
/examples/vhost/server.js:
--------------------------------------------------------------------------------
 1 | // inspired by https://github.com/visionmedia/express/blob/master/examples/vhost/index.js
 2 | 
 3 | /*
 4 | edit /etc/hosts:
 5 | 
 6 | 127.0.0.1       foo.example.com
 7 | 127.0.0.1       bar.example.com
 8 | 127.0.0.1       example.com
 9 | */
10 | 
11 | var http = require("http")
12 | var redirecter = require("redirecter")
13 | var Router = require("routes-router")
14 | 
15 | var vhost = require("./vhost.js")
16 | 
17 | var main = Router()
18 | 
19 | main.addRoute("/", function (req, res) {
20 |     res.end("Hello from main app!")
21 | })
22 | main.addRoute("/:sub", function (req, res, opts) {
23 |     res.end("you requested " + opts.sub)
24 | })
25 | 
26 | // redirect router
27 | 
28 | var redirect = Router()
29 | 
30 | redirect.addRoute("*", function (req, res) {
31 |     var subdomains = (req.headers.host || "").split(".")
32 | 
33 |     redirecter(req, res, "http://example.com:3000/" + subdomains[0])
34 | })
35 | 
36 | var app = Router()
37 | app.addRoute("*", vhost({
38 |     "*.example.com": redirect,
39 |     "example.com": main
40 | }))
41 | 
42 | var server = http.createServer(app)
43 | server.listen(3000)
44 | console.log("vhost server started on port 3000")
45 | 


--------------------------------------------------------------------------------
/examples/content-negotiation/server.js:
--------------------------------------------------------------------------------
 1 | // https://github.com/visionmedia/express/blob/master/examples/content-negotiation/index.js
 2 | var http = require("http")
 3 | var Router = require("routes-router")
 4 | var mediaTypes = require("media-types")
 5 | var sendText = require("send-data")
 6 | var sendJson = require("send-data/json")
 7 | var sendHtml = require("send-data/html")
 8 | 
 9 | var users = [
10 |     { name: "Tobi" },
11 |     { name: "Loki" },
12 |     { name: "Jane" }
13 | ]
14 | 
15 | var app = Router()
16 | 
17 | app.addRoute("/", mediaTypes({
18 |     "text/html": function (req, res) {
19 |         sendHtml(req, res, "")
22 |     },
23 |     "application/json": function (req, res) {
24 |         sendJson(req, res, users)
25 |     },
26 |     "default": function (req, res) {
27 |         sendText(req, res, users.map(function (user) {
28 |             return " - " + user.name + "\n"
29 |         }).join(""))
30 |     }
31 | }))
32 | 
33 | var server = http.createServer(app)
34 | server.listen(3000)
35 | console.log("content negotiation server listening on port 3000")
36 | 


--------------------------------------------------------------------------------
/examples/route-seperation/routes/user/edit.js:
--------------------------------------------------------------------------------
 1 | var sendHtml = require("send-data/html")
 2 | var formBody = require("body/form")
 3 | var redirecter = require("redirecter")
 4 | 
 5 | var userPage = require("../../templates/users/edit.js")
 6 | var loadUser = require("./load-user.js")
 7 | var users = require("./user-db.js")
 8 | 
 9 | module.exports = {
10 |     GET: renderPage,
11 |     POST: updateUser
12 | }
13 | 
14 | function renderPage(req, res, opts, callback) {
15 |     loadUser(opts.id, function (err, user) {
16 |         if (err) {
17 |             return callback(err)
18 |         }
19 | 
20 |         sendHtml(req, res, userPage({
21 |             title: "Editing user " + user.name,
22 |             name: user.name,
23 |             email: user.email
24 |         }))
25 |     })
26 | }
27 | 
28 | // normally you would handle validation logic and asynchronously
29 | // save back to the database
30 | function updateUser(req, res, opts, callback) {
31 |     var userId = opts.id
32 |     formBody(req, res, function (err, body) {
33 |         if (err) {
34 |             return callback(err)
35 |         }
36 | 
37 |         users[userId] = body
38 | 
39 |         redirecter(req, res, "/users/" + userId + "/edit")
40 |     })
41 | }
42 | 


--------------------------------------------------------------------------------
/examples/vhost/vhost.js:
--------------------------------------------------------------------------------
 1 | // heavily based on https://github.com/senchalabs/connect/blob/master/lib/middleware/vhost.js
 2 | module.exports = vhost
 3 | 
 4 | // this should be a module on npm!
 5 | function vhost(hosts) {
 6 |     var pairs = Object.keys(hosts).map(function (hostName) {
 7 |         var handler = hosts[hostName]
 8 |         var regexp = new RegExp("^" + hostName
 9 |             .replace(/[^*\w]/g, "\\$&")
10 |             .replace(/[*]/g, "(?:.*?)") + "$", "i")
11 | 
12 |         return [regexp, handler]
13 |     })
14 | 
15 |     return function (req, res, opts, cb) {
16 |         if (!req.headers.host) {
17 |             return cb(new Error("no host header found"))
18 |         }
19 | 
20 |         var host = req.headers.host.split(":")[0]
21 |         var handled = pairs.some(function (pair) {
22 |             var regexp = pair[0]
23 |             var handler = pair[1]
24 | 
25 |             var match = regexp.test(host)
26 |             if (!match) {
27 |                 return false
28 |             }
29 | 
30 |             handler(req, res)
31 |             return true
32 |         })
33 | 
34 |         if (!handled) {
35 |             return cb(new Error("could not find a vhost redirect match"))
36 |         }
37 |     }
38 | }
39 | 


--------------------------------------------------------------------------------
/examples/online/server.js:
--------------------------------------------------------------------------------
 1 | // https://github.com/visionmedia/express/blob/master/examples/online/index.js
 2 | var http = require("http")
 3 | var Router = require("routes-router")
 4 | var redis = require("redis")
 5 | var Online = require("online")
 6 | var sendHtml = require("send-data/html")
 7 | 
 8 | var db = redis.createClient()
 9 | var online = Online(db)
10 | var app = Router()
11 | 
12 | function lists(ids) {
13 |     return ""
16 | }
17 | 
18 | // GET users online
19 | app.addRoute("/", function (req, res, opts, cb) {
20 |     online.last(5, function (err, ids) {
21 |         if (err) {
22 |             return cb(err)
23 |         }
24 | 
25 |         sendHtml(req, res, "

Users online: " + ids.length + 26 | "

" + lists(ids)) 27 | }) 28 | }) 29 | 30 | // if you want to do something globally you should put it in 31 | // your server request response handler 32 | var server = http.createServer(function (req, res) { 33 | // activity tracking, in this case the UA string 34 | // normally you would get it from the session 35 | online.add(req.headers["user-agent"]) 36 | app(req, res) 37 | }) 38 | server.listen(3000) 39 | console.log("online server listening on port 3000") 40 | -------------------------------------------------------------------------------- /examples/search/server.js: -------------------------------------------------------------------------------- 1 | // based on https://github.com/visionmedia/express/blob/master/examples/search/index.js 2 | var http = require("http") 3 | var Router = require("routes-router") 4 | var redis = require("redis") 5 | var filed = require("filed") 6 | var sendJson = require("send-data/json") 7 | 8 | // create and instantiate a redis db connection 9 | var db = redis.createClient() 10 | var app = Router() 11 | 12 | // initialize db 13 | db.sadd("ferret", "tobi") 14 | db.sadd("ferret", "loki") 15 | db.sadd("ferret", "jane") 16 | db.sadd("cat", "manny") 17 | db.sadd("cat", "luna") 18 | 19 | // GET the search page 20 | app.addRoute("/", function (req, res) { 21 | filed(__dirname + "/page.html").pipe(res) 22 | }) 23 | 24 | // GET search for :query 25 | app.addRoute("/search/:query?", function (req, res, opts, cb) { 26 | var query = opts.query 27 | db.smembers(query, function (err, vals) { 28 | if (err) { 29 | return cb(err) 30 | } 31 | 32 | sendJson(req, res, vals) 33 | }) 34 | }) 35 | 36 | // serve the javascript client seperately. 37 | app.addRoute("/client.js", function (req, res) { 38 | filed(__dirname + "/client.js").pipe(res) 39 | }) 40 | 41 | var server = http.createServer(app) 42 | server.listen(3000) 43 | console.log("search app listening on port 3000") 44 | -------------------------------------------------------------------------------- /examples/route-map/server.js: -------------------------------------------------------------------------------- 1 | // based on https://github.com/visionmedia/express/blob/master/examples/route-map/index.js 2 | var http = require("http") 3 | var Router = require("routes-router") 4 | var sendText = require("send-data") 5 | 6 | var map = require("./map.js") 7 | 8 | var app = Router() 9 | 10 | var users = { 11 | list: function (req, res) { 12 | sendText(req, res, "user list") 13 | }, 14 | get: function (req, res, opts) { 15 | sendText(req, res, "user " + opts.uid) 16 | }, 17 | del: function (req, res) { 18 | sendText(req, res, "delete users") 19 | } 20 | } 21 | var pets = { 22 | list: function (req, res, opts) { 23 | sendText(req, res, "user " + opts.uid + "'s pets") 24 | }, 25 | del: function (req, res, opts) { 26 | sendText(req, res, "delete " + opts.uid + "'s pet " + opts.pid) 27 | } 28 | } 29 | 30 | map(app, { 31 | "/users": { 32 | GET: users.list, 33 | DELETE: users.del, 34 | "/:uid": { 35 | GET: users.get, 36 | "/pets": { 37 | GET: pets.list, 38 | "/:pid": { 39 | DELETE: pets.del 40 | } 41 | } 42 | } 43 | } 44 | }) 45 | 46 | var server = http.createServer(app) 47 | server.listen(3000) 48 | console.log("map example server is listening on port 3000") 49 | -------------------------------------------------------------------------------- /examples/mvc/db.js: -------------------------------------------------------------------------------- 1 | var level = require("level") 2 | var SubLevel = require("level-sublevel") 3 | 4 | module.exports = Db 5 | 6 | // Create a database based on our configuration 7 | // we keep the production & local database seperate 8 | // also set encoding to json so we can just store objects 9 | function Db(config) { 10 | var db = level(config.dbPath, { 11 | valueEncoding: "json", 12 | keyEncoding: "json" 13 | }) 14 | 15 | initializeDb(db) 16 | 17 | return db 18 | } 19 | 20 | // put fake data in the database! 21 | function initializeDb(db) { 22 | var pets = [] 23 | 24 | pets.push({ name: "Tobi", id: "0" }) 25 | pets.push({ name: "Loki", id: "1" }) 26 | pets.push({ name: "Jane", id: "2" }) 27 | pets.push({ name: "Raul", id: "3" }) 28 | 29 | var users = [] 30 | 31 | users.push({ name: "TJ", pets: [pets[0], pets[1], pets[2]], id: "0" }) 32 | users.push({ name: "Guillermo", pets: [pets[3]], id: "1" }) 33 | users.push({ name: "Nathan", pets: [], id: "2" }) 34 | 35 | db = SubLevel(db) 36 | 37 | var usersDb = db.sublevel("users") 38 | var petsDb = db.sublevel("pets") 39 | 40 | petsDb.put(pets[0].id, pets[0]) 41 | petsDb.put(pets[1].id, pets[1]) 42 | petsDb.put(pets[2].id, pets[2]) 43 | petsDb.put(pets[3].id, pets[3]) 44 | 45 | usersDb.put(users[0].id, users[0]) 46 | usersDb.put(users[1].id, users[1]) 47 | usersDb.put(users[2].id, users[2]) 48 | } 49 | -------------------------------------------------------------------------------- /examples/auth/template.js: -------------------------------------------------------------------------------- 1 | var h = require("hyperscript") 2 | 3 | var styleContent = [ 4 | "body {", 5 | " padding: 50px;", 6 | " font: 13px Helvetica, Arial, sans-serif;", 7 | "}", 8 | ".error {", 9 | " color: red;", 10 | "}", 11 | ".success {", 12 | " color: green;", 13 | "}" 14 | ].join("\n") 15 | 16 | module.exports = template 17 | 18 | function template(opts) { 19 | return layout("Authentication Example", [ 20 | h("h1", "Login"), 21 | h("div", { innerHTML: opts.message }), 22 | "Try accessing ", 23 | h("a", { href: "/restricted" }, "/restricted"), 24 | ", then authenticate with tj and foobar", 25 | h("form", { method: "post", action: "/login" }, [ 26 | h("p", [ 27 | h("label", "Username:"), 28 | h("input", { type: "text", name: "username" }) 29 | ]), 30 | h("p", [ 31 | h("label", "Password:"), 32 | h("input", { type: "text", name: "password" }) 33 | ]), 34 | h("p", 35 | h("input", { type: "submit", value: "Login" })) 36 | ]) 37 | ]) 38 | } 39 | 40 | function layout(title, content) { 41 | return "" + 42 | h("html", 43 | h("head", [ 44 | h("title", title), 45 | h("style", styleContent) 46 | ]), 47 | h("body", content) 48 | ).outerHTML 49 | } 50 | -------------------------------------------------------------------------------- /examples/session/redis.js: -------------------------------------------------------------------------------- 1 | // based on https://github.com/visionmedia/express/blob/master/examples/session/redis.js 2 | var http = require("http") 3 | var Router = require("routes-router") 4 | var Session = require("redsess") 5 | var Redis = require("redis") 6 | var sendHtml = require("send-data/html") 7 | 8 | // create a single redis connection. This is shared among all 9 | // req/res pairs in your server 10 | var client = Redis.createClient() 11 | 12 | var app = Router() 13 | 14 | app.addRoute("/", function (req, res, opts, cb) { 15 | var session = Session(req, res, { client: client }) 16 | 17 | // get a value of the session is a database read is async 18 | session.get("views", function (err, views) { 19 | if (err) { 20 | return cb(err) 21 | } 22 | 23 | views = views || 0 24 | views++ 25 | 26 | // setting a value in the session is a database write and 27 | // is async 28 | session.set("views", views, function (err) { 29 | if (err) { 30 | return cb(err) 31 | } 32 | 33 | var html = "" 34 | 35 | if (views === 1) { 36 | html += "

First time visiting?" + 37 | " view this page in several browsers :)

" 38 | } 39 | 40 | sendHtml(req, res, html + "

viewed " + 41 | views + " times.

") 42 | }) 43 | }) 44 | }) 45 | 46 | var server = http.createServer(app) 47 | server.listen(3000) 48 | console.log("redis session server on port 3000") 49 | 50 | -------------------------------------------------------------------------------- /documents/filed.md: -------------------------------------------------------------------------------- 1 | # [`filed`](https://github.com/mikeal/filed) 2 | 3 | > I want to send a file down a response 4 | 5 | Using `filed` you will stream a file down the `HttpResponse`. The usage 6 | of streaming the payload is nice for efficiency and memory usage. 7 | 8 | ```js 9 | var http = require("http") 10 | var filed = require("filed") 11 | 12 | http.createServer(function (req, res) { 13 | filed(__dirname + "/static/index.html").pipe(res) 14 | }).listen(8080) 15 | ``` 16 | 17 | ## Alternatively by hand: 18 | 19 | ```js 20 | var http = require("http") 21 | var fs = require("fs") 22 | 23 | http.createServer(function (req, res) { 24 | var filePath = __dirname + "/static/index.html" 25 | // optionally stat the file your streaming. This allows you 26 | // to set the Content-Length header. 27 | fs.stat(filePath, function (err, stat) { 28 | if (err) { 29 | // the ENOENT code means no such file or directory 30 | if (err.code === "ENOENT") { 31 | res.statusCode = 404 32 | return res.end("Not Found") 33 | } 34 | 35 | res.statusCode = 500 36 | return res.end(err.message) 37 | } 38 | 39 | // you can skip the fs.stat call and just stream the content directly 40 | var stream = fs.createReadStream(__dirname + "/static/index.html") 41 | res.statusCode = 200 42 | res.setHeader("Content-Type", "text/html") 43 | res.setHeader("Content-Length", stat.size) 44 | stream.pipe(res) 45 | }) 46 | }).listen(8080) 47 | ``` 48 | -------------------------------------------------------------------------------- /examples/session/memory.js: -------------------------------------------------------------------------------- 1 | // based on https://github.com/visionmedia/express/blob/master/examples/session/redis.js 2 | var http = require("http") 3 | var Router = require("routes-router") 4 | var Session = require("generic-session") 5 | var MemoryStore = require("generic-session").MemoryStore 6 | var sendHtml = require("send-data/html") 7 | 8 | // create a single memory store for session data is used by 9 | // all req/res pairs in the http server 10 | var store = MemoryStore() 11 | 12 | var app = Router() 13 | 14 | app.addRoute("/", function (req, res, opts, cb) { 15 | var session = Session(req, res, store) 16 | 17 | // get a value of the session is a database read is async 18 | session.get("views", function (err, views) { 19 | if (err) { 20 | return cb(err) 21 | } 22 | 23 | views = views || 0 24 | views++ 25 | 26 | // setting a value in the session is a database write and 27 | // is async 28 | session.set("views", views, function (err) { 29 | if (err) { 30 | return cb(err) 31 | } 32 | 33 | var html = "" 34 | 35 | if (views === 1) { 36 | html += "

First time visiting?" + 37 | " view this page in several browsers :)

" 38 | } 39 | 40 | sendHtml(req, res, html + "

viewed " + 41 | views + " times.

") 42 | }) 43 | }) 44 | }) 45 | 46 | var server = http.createServer(app) 47 | server.listen(3000) 48 | console.log("memory session server on port 3000") 49 | 50 | -------------------------------------------------------------------------------- /examples/expose-data-to-client/server.js: -------------------------------------------------------------------------------- 1 | // based on https://github.com/visionmedia/express/blob/master/examples/expose-data-to-client/index.js 2 | var http = require("http") 3 | var Router = require("routes-router") 4 | var redirect = require("redirecter") 5 | var sendHtml = require("send-data/html") 6 | 7 | var template = require("./template.js") 8 | 9 | function User(name) { 10 | this.private = "heyyyy" 11 | this.secret = "something" 12 | this.name = name 13 | this.id = 123 14 | } 15 | 16 | // you want to serialize the user without 'secret' stuff 17 | User.prototype.toJSON = function () { 18 | return { id: this.id, name: this.name } 19 | } 20 | 21 | // dummy user loading function 22 | function getUser(req, res, callback) { 23 | process.nextTick(function () { 24 | callback(null, new User("Tobi")) 25 | }) 26 | } 27 | 28 | var app = Router() 29 | 30 | app.addRoute("/", function (req, res) { 31 | redirect(req, res, "/user") 32 | }) 33 | 34 | app.addRoute("/user", function (req, res, opts, cb) { 35 | getUser(req, res, function (err, user) { 36 | if (err) { 37 | return cb(err) 38 | } 39 | 40 | var clientData = { user: user } 41 | // we expose the clientData by passing it to the template 42 | // the template will expose this properly. 43 | // you can pass the data to your layout instead to avoid 44 | // passing it to every template 45 | sendHtml(req, res, template(clientData)) 46 | }) 47 | }) 48 | 49 | var server = http.createServer(app) 50 | server.listen(3000) 51 | console.log("expose data to client server listening on port 3000") 52 | -------------------------------------------------------------------------------- /documents/cookies.md: -------------------------------------------------------------------------------- 1 | # [`cookies`](https://github.com/mikeal/cookies) 2 | 3 | > I want to persist data between req/res pairs from the same client 4 | 5 | Using `cookies` you cam `get()` and `set()` HTTP cookies on a req / res 6 | pair. `cookies` also allows you to handle options like signed cookies 7 | and setting the domain / http only flag. Cookies will stick in the 8 | http client and will be send again with the next request by that 9 | client. 10 | 11 | ```js 12 | var http = require("http") 13 | var cookies = require("cookies") 14 | 15 | http.createServer(function (req, res) { 16 | var cookies = Cookies(req, res) 17 | 18 | // fetch value out of cookie 19 | var count = Number(cookies.get("count")) || 0 20 | count++ 21 | // store value in cookie 22 | cookies.set("count", String(count)) 23 | 24 | res.end("you have watched this page " + count + " times") 25 | }).listen(8080) 26 | ``` 27 | 28 | ## Alternatively by hand: 29 | 30 | ```js 31 | var http = require("http") 32 | 33 | http.createServer(function (req, res) { 34 | var cookie = req.headers.cookie || "" 35 | 36 | // parse cookie out 37 | var pairs = cookie.split(";") 38 | var obj = {} 39 | pairs.forEach(function (pair) { 40 | var parts = pair.split("=") 41 | obj[parts[0].trim()] = parts[1].trim() 42 | }) 43 | 44 | var count = Number(obj.count) || 0 45 | count++ 46 | 47 | // serialize cookie 48 | res.setHeader("Set-Cookie", "count=" + count) 49 | 50 | res.end("you have watched this page " + count + " times") 51 | }).listen(8080) 52 | ``` 53 | 54 | Note that using `cookies` gives you a more forgiving parser & 55 | serializer as well as it handling all options & signed 56 | cookies. 57 | -------------------------------------------------------------------------------- /examples/cookies/server.js: -------------------------------------------------------------------------------- 1 | // based on https://github.com/visionmedia/express/tree/master/examples/cookies 2 | var http = require("http") 3 | var Router = require("routes-router") 4 | var redirect = require("redirecter") 5 | var sendHtml = require("send-data/html") 6 | var formBody = require("body/form") 7 | var Cookies = require("cookies") 8 | 9 | var SECOND = 1000 10 | var MINUTE = 60 * SECOND 11 | 12 | var app = Router() 13 | 14 | app.addRoute("/", { 15 | GET: function (req, res) { 16 | var cookies = new Cookies(req, res) 17 | 18 | if (cookies.get("remember")) { 19 | sendHtml(req, res, "Remembered :). Click to " + 20 | "forget") 21 | } else { 22 | sendHtml(req, res, "

Check to " + 25 | ".

") 26 | } 27 | }, 28 | POST: function (req, res, opts, cb) { 29 | formBody(req, res, function (err, body) { 30 | if (err) { 31 | return cb(err) 32 | } 33 | 34 | var cookies = new Cookies(req, res) 35 | if (body.remember) { 36 | cookies.set("remember", 1, { maxage: MINUTE }) 37 | } 38 | 39 | redirect(req, res, "back") 40 | }) 41 | } 42 | }) 43 | 44 | app.addRoute("/forget", function (req, res) { 45 | var cookies = new Cookies(req, res) 46 | cookies.set("remember", "", { expires: new Date(0) }) 47 | redirect(req, res, "back") 48 | }) 49 | 50 | var server = http.createServer(app) 51 | server.listen(3000) 52 | console.log("cookies server is listening on port 3000") 53 | -------------------------------------------------------------------------------- /examples/mvc/features/pet/model.js: -------------------------------------------------------------------------------- 1 | var SubLevel = require("level-sublevel") 2 | 3 | module.exports = PetModel 4 | 5 | function PetModel(db) { 6 | db = SubLevel(db) 7 | 8 | var users = db.sublevel("users") 9 | var pets = db.sublevel("pets") 10 | 11 | return { 12 | get: get, 13 | create: create, 14 | getAll: getAll, 15 | setName: setName 16 | } 17 | 18 | function get(id, callback) { 19 | pets.get(id, function (err, result) { 20 | if (err && err.notFound) { 21 | return callback(null, null) 22 | } 23 | 24 | callback(err, result) 25 | }) 26 | } 27 | 28 | function getAll(callback) { 29 | var list = [] 30 | var stream = pets.createReadStream() 31 | 32 | stream 33 | .on("data", onData) 34 | .once("error", callback) 35 | .once("end", function onEnd() { 36 | stream.removeListener("data", onData) 37 | 38 | callback(null, list) 39 | }) 40 | 41 | function onData(chunk) { 42 | list.push(chunk.value) 43 | } 44 | } 45 | 46 | function setName(id, name, callback) { 47 | pets.get(id, function (err, pet) { 48 | if (err) { 49 | return callback(err) 50 | } 51 | 52 | pet.name = name 53 | 54 | pets.put(id, pet, function (err) { 55 | if (err) { 56 | return callback(err) 57 | } 58 | 59 | //TODO. Fix the user records we denormalized 60 | callback(null, pet) 61 | }) 62 | }) 63 | } 64 | 65 | function create(pet, callback) { 66 | pets.put(pet.id, pet, callback) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "http-framework", 3 | "version": "1.2.0", 4 | "description": "A web framework based purely on `require('http')`", 5 | "keywords": [], 6 | "author": "Raynos ", 7 | "repository": "git://github.com/Raynos/http-framework.git", 8 | "main": "index", 9 | "homepage": "https://github.com/Raynos/http-framework", 10 | "contributors": [ 11 | { 12 | "name": "Raynos" 13 | }, 14 | { 15 | "name": "Matt-Esch" 16 | }, 17 | { 18 | "name": "maxogden" 19 | } 20 | ], 21 | "bugs": { 22 | "url": "https://github.com/Raynos/http-framework/issues", 23 | "email": "raynos2@gmail.com" 24 | }, 25 | "dependencies": { 26 | "redirecter": "~0.2.1", 27 | "routes-router": "~1.5.3", 28 | "error": "~2.0.3", 29 | "send-data": "~3.1.2", 30 | "st": "~0.2.3", 31 | "generic-session": "0.0.2", 32 | "redsess": "~0.3.2", 33 | "filed": "~0.1.0", 34 | "hyperscript": "~1.3.2", 35 | "process": "~0.5.1", 36 | "body": "~2.0.1", 37 | "pwd": "0.0.2", 38 | "media-types": "~1.0.3", 39 | "cookies": "~0.3.8", 40 | "corsify": "~1.0.2", 41 | "json-globals": "~0.2.1", 42 | "multiparty": "~3.1.0", 43 | "config-chain": "~1.1.8", 44 | "string-template": "~0.1.3", 45 | "basic": "~0.0.1", 46 | "csrf-lite": "~0.1.0", 47 | "serve-favicon":"~2.0.0", 48 | "serve-file-download":"~0.0.1" 49 | }, 50 | "devDependencies": { 51 | "redis": "~0.9.0", 52 | "online": "0.0.1", 53 | "level": "~0.18.0", 54 | "level-sublevel": "~5.1.1", 55 | "uuid": "~1.4.1", 56 | "joi": "~2.0.3", 57 | "tape":"~2.13.0" 58 | }, 59 | "licenses": [ 60 | { 61 | "type": "MIT", 62 | "url": "http://github.com/Raynos/http-framework/raw/master/LICENSE" 63 | } 64 | ], 65 | "scripts": { 66 | "test": "echo 'no tests'" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /examples/multipart/multiparty-form.js: -------------------------------------------------------------------------------- 1 | var Form = require("multiparty").Form 2 | 3 | module.exports = MultipartyForm 4 | 5 | // This should be a module on npm 6 | function MultipartyForm(req, res, opts, cb) { 7 | var form = new Form() 8 | var fields = {} 9 | var files = {} 10 | var result = { fields: fields, files: files } 11 | var counter = 1 12 | form.parse(req) 13 | 14 | // any normal non file form input is considered a field 15 | // if you had then 16 | // key would be 'title' and value would be 'foo' 17 | form.on("field", function (key, value) { 18 | // BUG: do not handle case where name appears multiple 19 | // times in a form. 20 | fields[key] = value 21 | }) 22 | 23 | // a part is any piece of data in a multipart upload. 24 | // here we only handle the file ones. `part` in this case 25 | // is a readablestream 26 | form.on("part", function (part) { 27 | // if filename is null then this is a 'field' 28 | // we already handle it in the 'field' listener 29 | if (part.filename === null) { 30 | return 31 | } 32 | 33 | // increment counter. This means we wait for all parts 34 | counter++ 35 | 36 | opts.handlePart.call(result, part, function (err, result) { 37 | if (err) { 38 | return cb(err) 39 | } 40 | 41 | // BUG: do not handle case where name appears multiple 42 | // times in a form 43 | files[part.name] = result 44 | finish() 45 | }) 46 | }) 47 | 48 | form.once("error", cb) 49 | form.once("close", finish) 50 | 51 | function finish() { 52 | // wait for all parts to finish 53 | if (--counter === 0) { 54 | // pass a hash of fields and a hash of files 55 | // fields are strings and files are readable streams 56 | cb(null, { fields: fields, files: files }) 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /documents/form-body.md: -------------------------------------------------------------------------------- 1 | # [`form-body`](https://github.com/Raynos/body) 2 | 3 | > I want to read the body of a HTML <form> submission. 4 | 5 | ```js 6 | var http = require("http") 7 | var formBody = require("body/form") 8 | 9 | http.createServer(function (req, res) { 10 | formBody(req, res, function (err, body) { 11 | if (err) { 12 | res.statusCode = 500 13 | res.end(err.message) 14 | } 15 | 16 | res.end("you submitted " + JSON.stringify(body)) 17 | }) 18 | }).listen(8080) 19 | ``` 20 | 21 | ## Alternatively by hand: 22 | 23 | ```js 24 | var http = require("http") 25 | var querystring = require("qs") 26 | var StringDecoder = require("string_decoder").StringDecoder 27 | 28 | http.createServer(function (req, res) { 29 | var requestBody = "" 30 | // use a StringDecoder to parse two byte UTF8 characters. 31 | // if you use string concatenation you might get half of 32 | // a two byte character and your body will be wrong 33 | var stringDecoder = new StringDecoder() 34 | 35 | req.on("data", onData) 36 | req.once("end", onEnd) 37 | req.once("error", onError) 38 | 39 | function onData(chunk) { 40 | requestBody += stringDecoder.write(chunk) 41 | } 42 | 43 | function onEnd() { 44 | // remove listeners to avoid leaks 45 | req.removeListener("data", onData) 46 | req.removeListener("error", onError) 47 | 48 | requestBody += stringDecoder.end() 49 | 50 | var body = querystring.parse(requestBody) 51 | 52 | res.end("you submitted " + JSON.stringify(body)) 53 | } 54 | 55 | function onError(err) { 56 | // // remove listeners to avoid leaks 57 | req.removeListener("data", onData) 58 | req.removeListener("end", onEnd) 59 | 60 | res.statusCode = 500 61 | res.end(err.message) 62 | } 63 | }).listen(8080) 64 | ``` 65 | 66 | Note that this hand written alternative has a buffer overflow 67 | attack in it. It never keeps appending to `requestBody` and 68 | will crash the process if someone sends you a chunked encoded 69 | body of 100s of GBs. 70 | -------------------------------------------------------------------------------- /examples/tail/server.js: -------------------------------------------------------------------------------- 1 | // based on https://github.com/spumko/hapi/blob/master/examples/tails.js 2 | var http = require("http") 3 | var Router = require("routes-router") 4 | var sendText = require("send-data") 5 | var setTimeout = require("timers").setTimeout 6 | 7 | // configure the Tail so that it does your own error handling 8 | // in this case we log things 9 | var tail = Tail(function errorHandler(err, meta) { 10 | console.log("error happened %s on uri %s for event %s with key %s", 11 | err.message, meta.req.url, meta.name, meta.key) 12 | }, function succcesHandler(result, meta) { 13 | console.log("operation succeeded %s", meta.name) 14 | }) 15 | var app = Router() 16 | 17 | // fakeCache. We can update something in the cache. 18 | // The cache will asynchronously break 19 | var fakeCache = { 20 | update: function (key, value, cb) { 21 | setTimeout(function () { 22 | cb(new Error("cache oops")) 23 | }, 200) 24 | } 25 | } 26 | 27 | // our app will update a cache for a key and a value 28 | // we do not want our application to wait to hear back from 29 | // the database so we end the response immediately. 30 | // however we do want to configure error handling in one place 31 | // and have the error from the async thing be reported properly 32 | app.addRoute("/:key/:value", function (req, res, opts) { 33 | var callback = tail("fakeCache", { 34 | req: req, 35 | res: res, 36 | key: opts.key 37 | }) 38 | fakeCache.update(opts.key, opts.value, callback) 39 | 40 | sendText(req, res, "successfully saved thing") 41 | }) 42 | 43 | var server = http.createServer(app) 44 | server.listen(3000) 45 | console.log("tail server listening on port 3000") 46 | 47 | function Tail(errorHandler, succcesHandler) { 48 | function tail(name, opts) { 49 | opts.name = name 50 | 51 | return callback 52 | 53 | function callback(err, result) { 54 | if (err) { 55 | errorHandler(err, opts) 56 | } else if (succcesHandler) { 57 | succcesHandler(result, opts) 58 | } 59 | } 60 | } 61 | 62 | return tail 63 | } 64 | -------------------------------------------------------------------------------- /examples/csrf/server.js: -------------------------------------------------------------------------------- 1 | // https://github.com/senchalabs/connect/blob/master/examples/csrf.js 2 | 3 | var http = require("http") 4 | var Router = require("routes-router") 5 | var formBody = require("body/form") 6 | var sendHtml = require("send-data/html") 7 | var redirect = require("redirecter") 8 | var Session = require("generic-session") 9 | var MemoryStore = require("generic-session").MemoryStore 10 | var csrf = require("csrf-lite") 11 | var Cookies = require("cookies") 12 | 13 | var template = require("./template.js") 14 | 15 | var store = MemoryStore() 16 | var app = Router() 17 | 18 | app.addRoute("/", { 19 | "GET": function (req, res, opts, cb) { 20 | var session = Session(req, res, store) 21 | var token = getToken(req, res) 22 | 23 | session.get("user", function (err, user) { 24 | if (err) { 25 | return cb(err) 26 | } 27 | 28 | sendHtml(req, res, template({ 29 | token: token, name: user && user.name 30 | })) 31 | }) 32 | }, 33 | "POST": function (req, res, opts, cb) { 34 | var token = getToken(req, res) 35 | var session = Session(req, res, store) 36 | 37 | formBody(req, res, function (err, body) { 38 | if (err) { 39 | return cb(err) 40 | } 41 | 42 | var isValid = csrf.validate(body, token) 43 | if (!isValid) { 44 | return cb(new Error("INVALID CSRF")) 45 | } 46 | 47 | session.set("user", { name: body.name }, function (err) { 48 | if (err) { 49 | return cb(err) 50 | } 51 | 52 | redirect(req, res, "/") 53 | }) 54 | }) 55 | } 56 | }) 57 | 58 | var server = http.createServer(app) 59 | server.listen(3000) 60 | console.log("CSRF server listening on port 3000") 61 | 62 | function getToken(req, res) { 63 | var cookies = Cookies(req, res) 64 | 65 | // use the sessionId as the token 66 | var token = cookies.get("ssid") 67 | 68 | if (!token) { 69 | token = csrf() 70 | cookies.set("ssid", token) 71 | } 72 | 73 | return token 74 | } 75 | -------------------------------------------------------------------------------- /documents/corsify.md: -------------------------------------------------------------------------------- 1 | # [`corsify`](https://github.com/raynos/corsify) 2 | 3 | > I want to be able to make cross domain ajax requests to my web server 4 | 5 | You can set CORS headers on your `res` that allow browsers to make 6 | http ajax requests from other domains bypassing the same 7 | domain restriction. 8 | 9 | This is useful if you have a public HTTP API server that other 10 | developers & clients use or if your API server is on a different 11 | domain / port then your web server. 12 | 13 | ```js 14 | var http = require("http") 15 | var Corsify = require("corsify") 16 | 17 | http.createServer(Corsify({ 18 | // you should not allow all origins! That's a security risk 19 | // you can use getOrigin to whitelist the origin. 20 | // whatever you return becomes the Access-Control-Allow-Origin 21 | // HTTP header. 22 | getOrigin: function (req, res) { 23 | var origin = req.headers.origin 24 | 25 | // manually white list allowed CORS servers 26 | if (origin === "http://web.my-server.com") { 27 | return "http://web.my-server-.com" 28 | } 29 | } 30 | }, function () { 31 | res.end("API server! CORS me.") 32 | })).listen(8080) 33 | ``` 34 | 35 | ## Alternatively by hand: 36 | 37 | ```js 38 | var http = require("http") 39 | 40 | http.createServer(function (req, res) { 41 | var origin = req.headers.origin 42 | 43 | if (origin === "http://web.my-server.com") { 44 | res.setHeader("Access-Control-Allow-Origin", origin) 45 | } 46 | 47 | res.setHeader("Access-Control-Allow-Methods", 48 | "POST, GET, PUT, DELETE, OPTIONS") 49 | res.setHeader("Access-Control-Allow-Credentials", "true") 50 | res.setHeader("Access-Control-Max-Age", "86400") 51 | res.setHeader("Access-Control-Allow-Headers", 52 | "X-Requested-With, Content-Type, Accept") 53 | 54 | // CORS in browsers send a preflight OPTIONS HTTP request 55 | // for POSTs to see whether making a HTTP request to this 56 | // end point is allowed. You should handle `OPTIONS` seperately 57 | // from `GET` / `POST` / other headers. 58 | if (req.method === "OPTIONS") { 59 | return res.end() 60 | } 61 | 62 | res.end("API Server! CORS me.") 63 | }).listen(8080) 64 | ``` 65 | -------------------------------------------------------------------------------- /examples/mvc/features/pet/index.js: -------------------------------------------------------------------------------- 1 | var Router = require("routes-router/child") 2 | var TypedError = require("error/typed") 3 | var sendHtml = require("send-data/html") 4 | var formBody = require("body/form") 5 | var redirect = require("redirecter") 6 | var format = require("string-template") 7 | 8 | var petPage = require("./views/show.js") 9 | var editPage = require("./views/edit.js") 10 | var PetModel = require("./model.js") 11 | 12 | var PetNotFound = TypedError({ 13 | message: "Pet %s not found", 14 | type: "pet.not.found", 15 | statusCode: 404 16 | }) 17 | 18 | module.exports = PetController 19 | 20 | function PetController(config) { 21 | var prefix = config.features.pet 22 | 23 | var controller = Router({ prefix: prefix }) 24 | var model = PetModel(config.db) 25 | 26 | var viewsConfig = { layout: config.layout, urlMap: { 27 | "petEdit": format.bind(null, prefix + "/{petId}/edit"), 28 | "pet": format.bind(null, prefix + "/{petId}") 29 | } } 30 | 31 | controller.addRoute("/:petId", { 32 | GET: function (req, res, opts, cb) { 33 | model.get(opts.petId, function (err, pet) { 34 | if (err) { 35 | return cb(err) 36 | } 37 | 38 | if (!pet) { 39 | return cb(PetNotFound(opts.petId)) 40 | } 41 | 42 | sendHtml(req, res, petPage(pet, viewsConfig)) 43 | }) 44 | }, 45 | POST: function (req, res, opts, cb) { 46 | formBody(req, res, function (err, body) { 47 | if (err) { 48 | return cb(err) 49 | } 50 | 51 | model.setName(opts.petId, body.name, function (err, user) { 52 | if (err) { 53 | return cb(err) 54 | } 55 | 56 | // setMessage('Information updated!') 57 | redirect(req, res, prefix + "/" + opts.petId) 58 | }) 59 | }) 60 | } 61 | }) 62 | 63 | controller.addRoute("/:petId/edit", function (req, res, opts, cb) { 64 | model.get(opts.petId, function (err, pet) { 65 | if (err) { 66 | return cb(err) 67 | } 68 | 69 | if (!pet) { 70 | return cb(PetNotFound(opts.petId)) 71 | } 72 | 73 | sendHtml(req, res, editPage(pet, viewsConfig)) 74 | }) 75 | }) 76 | 77 | controller 78 | 79 | return controller 80 | } 81 | -------------------------------------------------------------------------------- /examples/mvc/features/user/model.js: -------------------------------------------------------------------------------- 1 | var SubLevel = require("level-sublevel") 2 | var uuid = require("uuid") 3 | 4 | module.exports = UserModel 5 | 6 | function UserModel(db) { 7 | db = SubLevel(db) 8 | 9 | var users = db.sublevel("users") 10 | // naughty. two models talk to one database section 11 | // really should refactor with dependencies but that requires 12 | // managing with git or npm 13 | var pets = db.sublevel("pets") 14 | 15 | return { 16 | get: get, 17 | create: create, 18 | getAll: getAll, 19 | addPet: addPet, 20 | setName: setName 21 | } 22 | 23 | function get(id, callback) { 24 | users.get(id, function (err, result) { 25 | if (err && err.notFound) { 26 | return callback(null, null) 27 | } 28 | 29 | callback(err, result) 30 | }) 31 | } 32 | 33 | function getAll(callback) { 34 | var list = [] 35 | var stream = users.createReadStream() 36 | 37 | stream 38 | .on("data", onData) 39 | .once("error", callback) 40 | .once("end", function onEnd() { 41 | stream.removeListener("data", onData) 42 | 43 | callback(null, list) 44 | }) 45 | 46 | function onData(chunk) { 47 | list.push(chunk.value) 48 | } 49 | } 50 | 51 | function setName(id, name, callback) { 52 | users.get(id, function (err, user) { 53 | if (err) { 54 | return callback(err) 55 | } 56 | 57 | user.name = name 58 | 59 | users.put(id, user, function (err) { 60 | if (err) { 61 | return callback(err) 62 | } 63 | 64 | callback(null, user) 65 | }) 66 | }) 67 | } 68 | 69 | function create(user, callback) { 70 | users.put(user.id, user, callback) 71 | } 72 | 73 | function addPet(userId, petName, callback) { 74 | users.get(userId, function (err, user) { 75 | if (err) { 76 | return callback(err) 77 | } 78 | 79 | var pet = { name: petName, id: uuid() } 80 | pets.put(pet.id, pet, function (err) { 81 | if (err) { 82 | return callback(err) 83 | } 84 | 85 | user.pets = user.pets || [] 86 | user.pets.push(pet) 87 | 88 | users.put(user.id, user, callback) 89 | }) 90 | }) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # http-framework is an OPEN Open Source Project 2 | 3 | ----------------------------------------- 4 | 5 | ## What? 6 | 7 | Individuals making significant and valuable contributions are given commit-access to the project to contribute as they see fit. This project is more like an open wiki than a standard guarded open source project. 8 | 9 | ## The structure of http-framework 10 | 11 | `http-framework` consists of three pieces 12 | 13 | ### modules 14 | 15 | All small modules that can be used for building web services can 16 | be added to the wiki page for modules. 17 | 18 | If a module is used in an example it should be added to the 19 | modules directory and the `package.json` 20 | 21 | ### examples 22 | 23 | The main value of `http-framework` comes from a rich set of 24 | examples. 25 | 26 | Every example should use modules where they are useful and 27 | write production level code so that the quality is high 28 | even if they are copy pasted into production level apps 29 | 30 | The examples should use a subset of the modules from the wiki, 31 | namely they should only use one module for one thing and not 32 | use two competing modules in two different examples. 33 | 34 | To write an example feel free to copy / rewrite examples from 35 | other frameworks like `connect` and `hapi` 36 | 37 | ### Documentation 38 | 39 | The second set of value of `http-framework` comes in the form of 40 | documentation. 41 | 42 | This documents how to use the modules and how to solve the 43 | problem of the module from scratch. 44 | 45 | For each module used in the examples we should have a single 46 | markdown document, documenting that module. 47 | 48 | The format for documentation is to state what problem the module 49 | solves and then implement a small server that uses the module 50 | and only the module to do that. 51 | 52 | Next write a 'Do it by hand' example that uses zero modules and 53 | only uses node core to demonstrate how you would solve this 54 | from scratch so that users can see what benefit the module 55 | brings. 56 | 57 | ## Rules 58 | 59 | There are a few basic ground-rules for contributors: 60 | 61 | 1. **No `--force` pushes** or modifying the Git history in any way. 62 | 1. **Non-master branches** ought to be used for ongoing work. 63 | 1. **External API changes and significant modifications** ought to be subject to an **internal pull-request** to solicit feedback from other contributors. 64 | 1. Internal pull-requests to solicit feedback are *encouraged* for any other non-trivial contribution but left to the discretion of the contributor. 65 | 1. Contributors should attempt to adhere to the prevailing code-style. 66 | 67 | ## Releases 68 | 69 | Declaring formal releases remains the prerogative of the project maintainer. 70 | 71 | ## Changes to this arrangement 72 | 73 | This is an experiment and feedback is welcome! This document may also be subject to pull-requests or changes by contributors where you believe you have something valuable to add or change. 74 | 75 | ----------------------------------------- 76 | -------------------------------------------------------------------------------- /examples/mvc/server.js: -------------------------------------------------------------------------------- 1 | // based on https://github.com/visionmedia/express/tree/master/examples/mvc 2 | 3 | // REMEMBER 4 | // 5 | // run `make install` in the root to install nested dependencies!!! 6 | // 7 | 8 | var http = require("http") 9 | var path = require("path") 10 | var console = require("console") 11 | var process = require("process") 12 | var Router = require("routes-router") 13 | var cc = require("config-chain") 14 | var sendHtml = require("send-data/html") 15 | var st = require("st") 16 | 17 | // global views live in the top level views folder 18 | var errorPage = require("./views/error-page.js") 19 | var notFoundPage = require("./views/not-found.js") 20 | var layout = require("./views/layout.js") 21 | var Db = require("./db.js") 22 | 23 | // NODE_ENV is used to turn a server from local to production 24 | // `$ NODE_ENV=production node server.js` 25 | var NODE_ENV = process.env.NODE_ENV 26 | 27 | // We use config chain to load config files. This grabs the default 28 | // config and overwrites it with the NODE_ENV config for 29 | // config.production.json or config.developer.json if it exists 30 | var config = cc( 31 | path.join(__dirname, "config", "config." + NODE_ENV + ".json"), 32 | path.join(__dirname, "config", "config.json") 33 | ).store 34 | 35 | // make the database path local to entry point instead of CWD 36 | config.dbPath = path.join(__dirname, config.dbPath) 37 | 38 | // store the layout on the config. This way other features 39 | // can use the same layout! 40 | config.layout = layout 41 | // store the database on the config. This way other features 42 | // can all share the same database 43 | config.db = Db(config) 44 | 45 | // here we set what features are installed on our server 46 | // in the config. We store the uri locations where the 47 | // features live in this hash 48 | config.features = { 49 | "user": "/user", 50 | "pet": "/pet", 51 | "main": "/" 52 | } 53 | 54 | var app = Router({ 55 | errorHandler: function (req, res, err) { 56 | if (err.statusCode === 404) { 57 | return this.notFound(req, res) 58 | } 59 | 60 | // log the stack 61 | console.log(err.stack) 62 | 63 | // send error page 64 | sendHtml(req, res, errorPage(err)) 65 | }, 66 | notFound: function (req, res) { 67 | sendHtml(req, res, notFoundPage(req.url)) 68 | } 69 | }) 70 | 71 | // configure static file server globally. Note that features 72 | // don't reference /static in their views. Only the global 73 | // layout does. 74 | app.addRoute("/static/*", st({ 75 | path: path.join(__dirname, "static"), 76 | url: "/static" 77 | })) 78 | 79 | var MainFeature = require("./features/main") 80 | var UserFeature = require("./features/user") 81 | var PetFeature = require("./features/pet") 82 | 83 | // Load the main feature into our server 84 | app.addRoute("/", MainFeature(config)) 85 | 86 | // Load the user feature into our server 87 | app.addRoute("/user*?", UserFeature(config)) 88 | 89 | // Load the pet feature into our server 90 | app.addRoute("/pet*?", PetFeature(config)) 91 | 92 | var server = http.createServer(app) 93 | server.listen(3000) 94 | console.log("MVC server listening on port 3000") 95 | -------------------------------------------------------------------------------- /examples/validate-api/server.js: -------------------------------------------------------------------------------- 1 | // based on https://github.com/spumko/hapi/blob/master/examples/validation.js 2 | var http = require("http") 3 | var console = require("console") 4 | var url = require("url") 5 | var Router = require("routes-router") 6 | var jsonBody = require("body/json") 7 | var sendText = require("send-data") 8 | var ValidationError = require("error/validation") 9 | // the validation example uses the joi validation library 10 | // since that's what hapi uses. There are alternatives, check out: 11 | // https://github.com/Raynos/http-framework/wiki/Modules#wiki-validators 12 | var Joi = require("joi") 13 | 14 | var app = Router() 15 | 16 | var usersSchema = { 17 | title: Joi.string(), 18 | status: Joi.string().valid("open", "pending", "close"), 19 | participants: Joi.array().includes(Joi.string(), Joi.number()) 20 | } 21 | 22 | // we use validateQuery to pre-emptively validate the query paramaters 23 | // and only run our route handler if the schema passes 24 | app.addRoute("/", validateQuery({ username: Joi.string() }, sendSuccess)) 25 | 26 | app.addRoute("/admin", validateQuery({ 27 | username: Joi.string().required().with("password"), 28 | password: Joi.string() 29 | }, sendSuccess)) 30 | 31 | app.addRoute("/users", validateQuery({ 32 | email: Joi.string().email().required().min(18) 33 | }, sendSuccess)) 34 | 35 | app.addRoute("/config", validateQuery({ 36 | choices: Joi.array().required() 37 | }, sendSuccess)) 38 | 39 | app.addRoute("/test", validateQuery({ 40 | num: Joi.number().min(0) 41 | }, sendSuccess)) 42 | 43 | app.addRoute("/test2", validateQuery({ 44 | p1: Joi.string().required().rename("itemId") 45 | }, sendSuccess)) 46 | 47 | app.addRoute("/simple", validateQuery({ 48 | input: Joi.string().min(3) 49 | }, sendSuccess)) 50 | 51 | app.addRoute("/users/{id}", { "POST": userId }) 52 | 53 | var server = http.createServer(app) 54 | server.listen(3000) 55 | console.log("Listening on port 3000") 56 | 57 | function validateQuery(schema, routeHandler) { 58 | return function (req, res, opts, cb) { 59 | var queryObject = url.parse(req.url, true).query 60 | var err = Joi.validate(queryObject, schema) 61 | 62 | if (err){ 63 | // you can't cb(errors) an array of errors 64 | // you have to cb(Error instance) so we wrap 65 | // the array in the ValidationError wrapper 66 | return cb(ValidationError(err._errors)) 67 | } 68 | 69 | // We can choose to pass the query object along so that we don't 70 | // have to compute it again 71 | opts.query = queryObject 72 | 73 | routeHandler(req, res, opts, cb) 74 | } 75 | } 76 | 77 | function userId(req, res, opts, cb) { 78 | jsonBody(req, res, function (err, body) { 79 | if (err) { 80 | return cb(err) 81 | } 82 | 83 | // you can also manually validate any object you want 84 | // for example here we validateion the HTTP body 85 | var errors = Joi.validate(body, usersSchema) 86 | if (errors) { 87 | return cb(ValidationError(errors._errors)) 88 | } 89 | 90 | sendSuccess(req, res) 91 | }) 92 | } 93 | 94 | function sendSuccess(req, res) { 95 | sendText(req, res, "Success!") 96 | } 97 | -------------------------------------------------------------------------------- /examples/error-pages/server.js: -------------------------------------------------------------------------------- 1 | // https://github.com/visionmedia/express/blob/master/examples/error-pages/index.js 2 | var http = require("http") 3 | var Router = require("routes-router") 4 | var mediaTypes = require("media-types") 5 | var sendHtml = require("send-data/html") 6 | var sendJson = require("send-data/json") 7 | var sendText = require("send-data") 8 | var TypedError = require("error/typed") 9 | 10 | var homePage = require("./templates/index.js") 11 | var fourofourPage = require("./templates/404.js") 12 | var errorPage = require("./templates/500.js") 13 | 14 | // enable verbose errors in non production 15 | // start app with NODE_ENV=production to disable them 16 | var verboseErrors = process.env.NODE_ENV !== "production" 17 | 18 | var app = Router({ 19 | // add a notFound 404 handler to our router. This will 20 | // be called by 21 | // 22 | // $ curl http://localhost:3000/notfound 23 | // $ curl http://localhost:3000/notfound -H "Accept: application/json" 24 | // $ curl http://localhost:3000/notfound -H "Accept: text/plain" 25 | notFound: function (req, res) { 26 | res.statusCode = 404 27 | 28 | // for each different media type serve the 404 differently 29 | mediaTypes({ 30 | "text/html": function (req, res) { 31 | // our template is just a function that returns a string 32 | sendHtml(req, res, fourofourPage(req.url)) 33 | }, 34 | "application/json": function (req, res) { 35 | sendJson(req, res, { error: "Not Found" }) 36 | }, 37 | "default": function (req, res) { 38 | sendText(req, res, "Not Found") 39 | } 40 | })(req, res) 41 | }, 42 | // we can specify an error handler for our router. Every call to 43 | // the cb in (req, res, opts, cb) will call this error handler 44 | errorHandler: function (req, res, err) { 45 | // make sure to redirect 404 errors back to the notFound handler 46 | if (err.statusCode === 404) { 47 | return this.notFound(req, res) 48 | } 49 | 50 | res.statusCode = err.statusCode || 500 51 | 52 | sendHtml(req, res, errorPage(err, { verbose: verboseErrors })) 53 | } 54 | }) 55 | 56 | var ERROR_403 = TypedError({ 57 | message: "Not allowed", 58 | type: "not.allowed", 59 | statusCode: 403 60 | }) 61 | var ERROR_404 = TypedError({ 62 | message: "Not Found", 63 | type: "not.found", 64 | statusCode: 404 65 | }) 66 | 67 | app.addRoute("/", function (req, res) { 68 | sendHtml(req, res, homePage()) 69 | }) 70 | 71 | app.addRoute("/404", function (req, res, opts, cb) { 72 | // to trigger a 404 just send an error with statusCode === 404 73 | // to the cb. This is a way to delegate responsibility back 74 | // to the notFound handler defined on the Router 75 | cb(ERROR_404()) 76 | }) 77 | 78 | app.addRoute("/403", function (req, res, opts, cb) { 79 | // trigger a 403 error 80 | cb(ERROR_403()) 81 | /* can also be done by hand 82 | var err = new Error("Not Allowed") 83 | err.statusCode = 403 84 | cb(err) 85 | */ 86 | }) 87 | 88 | app.addRoute("/500", function (req, res, opts, cb) { 89 | cb(new Error("Keyboard cat")) 90 | }) 91 | 92 | var server = http.createServer(app) 93 | server.listen(3000) 94 | console.log("error pages server listening on port 3000") 95 | -------------------------------------------------------------------------------- /documents/config-chain.md: -------------------------------------------------------------------------------- 1 | # [`config-chain`](https://github.com/dominictarr/config-chain) 2 | 3 | > I want to load multiple cascading configuration files 4 | 5 | Using `config-chain` you can load the configuration for your web 6 | application from multiple locations. It also cascades the 7 | loading so you can load general configuration and overwrite 8 | it with more specific configuration. 9 | 10 | ```js 11 | var path = require("path") 12 | var http = require("http") 13 | var cc = require("config-chain") 14 | var optimist = require("optimist") 15 | var NODE_ENV = process.env.NODE_ENV 16 | 17 | // optimist is a useful module to parse command line arguments 18 | // if you run `node example.js --port 8000 --host localhost` then 19 | // argv is { port: 8000, host: 'localhost' } 20 | var argv = optimist.argv 21 | 22 | // config loads host & port from your config.json file in the 23 | // config folder. The host & port are overwritten by NODE_ENV 24 | // specific config. This is useful for running a test & local 25 | // server without conflicting ports. 26 | // You can also overwrite the configuration with a hardcoded 27 | // path. You can then just have secret configuration files on 28 | // the production box. Lastly we pass in the argv which allows 29 | // us to overwrite configuration values by passing command line 30 | // arguments to our process! 31 | var config = cc( 32 | argv, 33 | '/usr/config/my-app/config.json', 34 | path.join(__dirname, "config", "config." + NODE_ENV + ".json"), 35 | path.join(__dirname, "config", "config.json") 36 | ).store 37 | 38 | // port & host are pretty flexibly defined. 39 | http.createServer(function (req, res) { 40 | res.end("hello world") 41 | }).listen(config.port, config.host) 42 | ``` 43 | 44 | ## Alternatively by hand: 45 | 46 | ```js 47 | var path = require("path") 48 | var http = require("http") 49 | var fs = require("fs") 50 | var NODE_ENV = process.env.NODE_ENV 51 | 52 | var configOpts = [ 53 | '/usr/config/my-app/config.json', 54 | path.join(__dirname, "config", "config." + NODE_ENV + ".json"), 55 | path.join(__dirname, "config", "config.json") 56 | ] 57 | 58 | var config = {} 59 | // loop trick. Asynchronous for loop. 60 | (function loop(callback) { 61 | // if we have read all files then done 62 | if (configOpts.length === 0) { 63 | return callback() 64 | } 65 | 66 | // do a thing 67 | var uri = configOpts.pop() 68 | fs.readFile(uri, function (err, file) { 69 | if (err) { 70 | return callback(err) 71 | } 72 | 73 | var value = JSON.parse(String(file)) 74 | extend(config, value) 75 | // when we done the thing get the next value of array 76 | loop(callback) 77 | }) 78 | }(startServer)) 79 | 80 | function startServer(err) { 81 | extend(config, parseArguments(process.argv)) 82 | 83 | http.createServer(function (req, res) { 84 | res.end("hello world") 85 | }).listen(config.port, config.host) 86 | } 87 | 88 | 89 | function extend(dest, src) { 90 | for (var k in src) { 91 | dest[k] = src[k] 92 | } 93 | } 94 | 95 | function parseArguments(args) { 96 | var opts = {}, key 97 | args = args.slice(2) 98 | 99 | args.forEach(function (arg) { 100 | if (arg.substr(0, 2) === '--') { 101 | key = arg 102 | } else { 103 | opts[key] = arg 104 | } 105 | }) 106 | 107 | return opts 108 | } 109 | ``` 110 | -------------------------------------------------------------------------------- /examples/multipart/server.js: -------------------------------------------------------------------------------- 1 | // based on https://github.com/visionmedia/express/blob/master/examples/multipart/index.js 2 | var http = require("http") 3 | var os = require("os") 4 | var fs = require("fs") 5 | var path = require("path") 6 | var util = require("util") 7 | var Router = require("routes-router") 8 | var sendHtml = require("send-data/html") 9 | var MultipartyForm = require("./multiparty-form.js") 10 | 11 | var app = Router() 12 | 13 | app.addRoute("/", { 14 | GET: function (req, res) { 15 | sendHtml(req, res, 16 | "
" + 17 | "

Title:

" + 18 | "

Image:

" + 19 | "

" + 20 | "
") 21 | }, 22 | POST: function (req, res, opts, cb) { 23 | // we tell the multi part form parser what to do with 24 | // each part it comes across. In this case we are storing 25 | // them to disk using `storeFile` 26 | MultipartyForm(req, res, { 27 | handlePart: storeFile 28 | }, function (err, values) { 29 | if (err) { 30 | return cb(err) 31 | } 32 | 33 | // fields are all the non file inputs in the form 34 | var title = values.fields.title 35 | // image is a { location, filename } object returned 36 | // by storeFile 37 | var image = values.files.image 38 | 39 | // stat the file to figure out how large it is 40 | fs.stat(image.location, function (err, stat) { 41 | if (err) { 42 | return cb(err) 43 | } 44 | 45 | var message = util.format("uploaded %s (%d Kb) to %s as %s", 46 | image.filename, 47 | Math.round(stat.size / 1024), 48 | image.location, 49 | title) 50 | 51 | // cool now lets get rid of that file or 52 | // we accumulate garbage forever 53 | // in production you should upload to s3 or something 54 | fs.unlink(image.location, function (err) { 55 | if (err) { 56 | return cb(err) 57 | } 58 | 59 | sendHtml(req, res, message) 60 | }) 61 | }) 62 | }) 63 | } 64 | }) 65 | 66 | function storeFile(part, callback) { 67 | // we have an file stream lets save it to disk!! 68 | // multiparty-form does not save to disk for you. 69 | // if it does so then that's a disk overload attack vector 70 | 71 | var filename = part.filename 72 | var uploadsDir = path.join(os.tmpdir(), Math.random().toString(32)) 73 | var loc = path.join(uploadsDir, path.basename(filename)) 74 | 75 | // WAIT up. we need to create the uploadsDir 76 | // the uploadsDir is a random directory which allows concurrent 77 | // uploads of files with the same filename 78 | fs.mkdir(uploadsDir, function (err) { 79 | if (err) { 80 | return callback(err) 81 | } 82 | 83 | // ok we're good let's store the image 84 | var dest = fs.createWriteStream(loc) 85 | part.pipe(dest) 86 | 87 | // once we have finished uploading 88 | dest.once("finish", function () { 89 | // lets callback with the location 90 | callback(null, { 91 | location: loc, 92 | filename: filename 93 | }) 94 | }) 95 | }) 96 | } 97 | 98 | var server = http.createServer(app) 99 | server.listen(3000) 100 | console.log("multipart server listening on port 3000") 101 | -------------------------------------------------------------------------------- /documents/csrf-lite.md: -------------------------------------------------------------------------------- 1 | # [`csrf-lite`](https://github.com/isaacs/csrf-lite) 2 | 3 | > I want to secure a HTML form post against cross site attack vectors 4 | 5 | If you do not protect your HTML forms then other websites can submit 6 | your forms on other users behalf and thus hijack their sessions. 7 | 8 | This is based on the fact they can just guess the field names and 9 | values. To circumvent this we can CSRF to put an effectively 10 | random field in the form that can't be guessed by malicious 11 | third party websites 12 | 13 | ```js 14 | var http = require("http") 15 | var querystring = require("querystring") 16 | var csrf = require("csrf-lite") 17 | var Cookies = require("cookies") 18 | 19 | http.createServer(function (req, res) { 20 | var token = getToken(req, res) 21 | 22 | // render a HTML form. put the csrf token in there as 23 | // a html hidden input field 24 | if (req.method === "GET") { 25 | res.setHeader("Content-Type", "text/html") 26 | res.end("
" + 27 | "" + 28 | csrf.html(token) + 29 | "" + 30 | "
"); 31 | } else if (req.method === "POST") { 32 | var body = "" 33 | req.on("data", function (chunk) { 34 | body += chunk 35 | }) 36 | req.on("end", function () { 37 | var payload = querystring.parse(body) 38 | 39 | var valid = csrf.validate(payload, token) 40 | if (!valid) { 41 | res.statusCode = 403 42 | return res.end("invalid csrf") 43 | } 44 | 45 | res.end("ok! " + payload.name) 46 | }) 47 | } 48 | }).listen(8080) 49 | 50 | function getToken(req, res) { 51 | // use the sessionId as the token 52 | var token = cookies.get("ssid") 53 | 54 | if (!token) { 55 | token = crypto.randomBytes(24).toString("base64") 56 | res.setHeader("Set-Cookie", "ssid=" + token) 57 | } 58 | 59 | return token 60 | } 61 | ``` 62 | 63 | ## Alternatively by hand: 64 | 65 | ```js 66 | var http = require("http") 67 | var querystring = require("querystring") 68 | var crypto = require("crypto") 69 | 70 | http.createServer(function (req, res) { 71 | var token = getToken(req, res) 72 | 73 | // render a HTML form. put the csrf token in there as 74 | // a html hidden input field 75 | if (req.method === "GET") { 76 | // HTML encode the token so it can be inserted into a form 77 | token = token.replace(/"/g, """) 78 | res.setHeader("Content-Type", "text/html") 79 | res.end("
" + 80 | "" + 81 | "" + 82 | "" + 83 | "
"); 84 | } else if (req.method === "POST") { 85 | var body = "" 86 | req.on("data", function (chunk) { 87 | body += chunk 88 | }) 89 | req.on("end", function () { 90 | var payload = querystring.parse(body) 91 | 92 | var csrfToken = payload["x-csrf-token"] 93 | if (csrfToken !== token) { 94 | res.statusCode = 403 95 | return res.end("invalid csrf") 96 | } 97 | 98 | res.end("ok! " + payload.name) 99 | }) 100 | } 101 | }).listen(8080) 102 | 103 | function getToken(req, res) { 104 | var cookie = req.headers.cookie || "" 105 | 106 | // parse cookie out 107 | var pairs = cookie.split(";") 108 | var obj = {} 109 | pairs.forEach(function (pair) { 110 | var parts = pair.split("=") 111 | obj[parts[0].trim()] = parts[1].trim() 112 | }) 113 | 114 | // use the sessionId as the token 115 | var token = obj.ssid 116 | 117 | if (!token) { 118 | token = csrf() 119 | cookies.set("ssid", token) 120 | } 121 | 122 | return token 123 | } 124 | ``` 125 | 126 | -------------------------------------------------------------------------------- /examples/mvc/features/user/index.js: -------------------------------------------------------------------------------- 1 | var Router = require("routes-router/child") 2 | var sendHtml = require("send-data/html") 3 | var redirect = require("redirecter") 4 | var formBody = require("body/form") 5 | var TypedError = require("error/typed") 6 | var format = require("string-template") 7 | 8 | var UserModel = require("./model.js") 9 | var usersPage = require("./views/list.js") 10 | var editPage = require("./views/edit.js") 11 | var userPage = require("./views/show.js") 12 | 13 | var UserNotFound = TypedError({ 14 | message: "User %s not found", 15 | type: "user.not.found", 16 | statusCode: 404 17 | }) 18 | 19 | // This is our UserController 20 | module.exports = UserController 21 | 22 | function UserController(config) { 23 | // the prefix uri for this feature (the user feature) is stored 24 | // on the features hash in config 25 | // we could also check for other features like the pet feature 26 | // and throw an error if the pet feature is not installed 27 | var prefix = config.features.user 28 | 29 | // to enable using sub routes in our controller we create 30 | // a new router. This router is a ChildRouter and will delegate 31 | // all error and 404 handler to it's parent. i.e. the global 32 | // application 33 | var controller = Router({ prefix: prefix }) 34 | 35 | // We instantiate our model here with the database configuration 36 | // the model is local to this user feature 37 | var model = UserModel(config.db) 38 | 39 | // we set up configuration to pass to our views. 40 | // we also set up an urlMap so that views are not hard coded 41 | // to specific paths 42 | var viewsConfig = { layout: config.layout, urlMap: { 43 | "user": format.bind(null, prefix + "/{userId}"), 44 | "userEdit": format.bind(null, prefix + "/{userId}/edit"), 45 | "userPet": format.bind(null, prefix + "/{userId}/pet"), 46 | "pet": format.bind(null, config.features.pet + "/{petId}") 47 | } } 48 | 49 | // optionally /. so its /user & /user/ 50 | controller.addRoute("/?", function (req, res, opts, cb) { 51 | model.getAll(function (err, users) { 52 | if (err) { 53 | return cb(err) 54 | } 55 | 56 | sendHtml(req, res, usersPage(users, viewsConfig)) 57 | }) 58 | }) 59 | 60 | controller.addRoute("/:userId/edit", function (req, res, opts, cb) { 61 | model.get(opts.userId, function (err, user) { 62 | if (err) { 63 | return cb(err) 64 | } 65 | 66 | if (!user) { 67 | return cb(UserNotFound(opts.userId)) 68 | } 69 | 70 | sendHtml(req, res, editPage(user, viewsConfig)) 71 | }) 72 | }) 73 | 74 | controller.addRoute("/:userId", { 75 | GET: function (req, res, opts, cb) { 76 | model.get(opts.userId, function (err, user) { 77 | if (err) { 78 | return cb(err) 79 | } 80 | 81 | if (!user) { 82 | return cb(UserNotFound(opts.userId)) 83 | } 84 | 85 | sendHtml(req, res, userPage(user, viewsConfig)) 86 | }) 87 | }, 88 | POST: function (req, res, opts, cb) { 89 | formBody(req, res, function (err, body) { 90 | if (err) { 91 | return cb(err) 92 | } 93 | 94 | model.setName(opts.userId, body.name, function (err, user) { 95 | if (err) { 96 | return cb(err) 97 | } 98 | 99 | // setMessage('Information updated!') 100 | redirect(req, res, prefix + "/" + opts.userId) 101 | }) 102 | }) 103 | } 104 | }) 105 | 106 | controller.addRoute("/:userId/pet", { 107 | POST: function (req, res, opts, cb) { 108 | formBody(req, res, function (err, body) { 109 | if (err) { 110 | return cb(err) 111 | } 112 | 113 | model.addPet(opts.userId, body.name, function (err, user) { 114 | if (err) { 115 | return cb(err) 116 | } 117 | 118 | // setMesssage("Added pet" + body.name) 119 | redirect(req, res, prefix + "/" + opts.userId) 120 | }) 121 | }) 122 | } 123 | }) 124 | 125 | return controller 126 | } 127 | -------------------------------------------------------------------------------- /examples/web-service/server.js: -------------------------------------------------------------------------------- 1 | // based on https://github.com/visionmedia/express/blob/master/examples/web-service/index.js 2 | var http = require("http") 3 | var url = require("url") 4 | var Router = require("routes-router") 5 | var TypedError = require("error/typed") 6 | var sendJson = require("send-data/json") 7 | 8 | // TypedError is a an error creation blueprint. 9 | // It will create errors with the message and a statusCode property 10 | // The statusCode property can be used in the error handler 11 | // routes-router also respects this by default. 12 | 13 | var KEY_REQUIRED = TypedError({ 14 | message: "Api key required", 15 | statusCode: 400 16 | }) 17 | var INVALID_KEY = TypedError({ 18 | message: "Invalid api key", 19 | statusCode: 401 20 | }) 21 | var NOT_FOUND_ERROR = TypedError({ 22 | message: "Couldn't find user %s", 23 | statusCode: 404 24 | }) 25 | 26 | // map of valid api keys, typically mapped to 27 | // account info with some sort of database like redis. 28 | // api keys do _not_ serve as authentication, merely to 29 | // track API usage or help prevent malicious behavior etc. 30 | 31 | var apiKeys = ["foo", "bar", "baz"]; 32 | 33 | // these two objects will serve as our faux database 34 | 35 | var repos = [ 36 | { name: "express", url: "http://github.com/visionmedia/express" }, 37 | { name: "stylus", url: "http://github.com/learnboost/stylus" }, 38 | { name: "cluster", url: "http://github.com/learnboost/cluster" } 39 | ] 40 | 41 | var users = [ 42 | { name: "tobi" }, 43 | { name: "loki" }, 44 | { name: "jane" } 45 | ] 46 | 47 | var userRepos = { 48 | tobi: [repos[0], repos[1]], 49 | loki: [repos[1]], 50 | jane: [repos[2]] 51 | } 52 | 53 | // this is our validation logic 54 | // our validator is a function that looks like a route handler 55 | // but returns either an error or the key. This should be called 56 | // in your other route handlers 57 | 58 | function validate(req, res, opts, callback) { 59 | var key = url.parse(req.url, true).query["api-key"] 60 | 61 | if (!key) { 62 | return callback(KEY_REQUIRED()) 63 | } 64 | 65 | if (apiKeys.indexOf(key) === -1) { 66 | return callback(INVALID_KEY()) 67 | } 68 | 69 | callback(null, key) 70 | } 71 | 72 | // you can configure the router with a custom error handler 73 | // by default it uses `send-data/error` 74 | // you can also configure it with a custom notFound handler 75 | var app = Router({ 76 | notFound: function notFound(req, res) { 77 | sendJson(req, res, { 78 | statusCode: 404, 79 | body: { error: "Lame, can't find that" } 80 | }) 81 | }, 82 | errorHandler: function (req, res, err) { 83 | sendJson(req, res, { 84 | statusCode: err.statusCode || 500, 85 | body: { error: err.message } 86 | }) 87 | } 88 | }) 89 | 90 | 91 | // we call the validate function in our route handlers to 92 | // ensure that each route handles validation 93 | 94 | /* this code looks verbose, but that's just the fault of callbacks. 95 | If we were to use generators for flow control it would look cleaner 96 | 97 | app.addRoute("/api/users", function* (req, res) { 98 | yield validate.bind(null, req, res) 99 | 100 | sendJson(req, res, users) 101 | }) 102 | 103 | app.addRoute("/api/repos", function* (req, res) { 104 | yield validate.bind(null, req, res) 105 | 106 | sendJson(req, res, repos) 107 | }) 108 | */ 109 | 110 | app.addRoute("/api/users", function(req, res, opts, callback) { 111 | validate(req, res, opts, function (err) { 112 | if (err) { 113 | return callback(err) 114 | } 115 | 116 | sendJson(req, res, users) 117 | }) 118 | }) 119 | 120 | app.addRoute("/api/repos", function (req, res, opts, callback) { 121 | validate(req, res, opts, function (err) { 122 | if (err) { 123 | return callback(err) 124 | } 125 | 126 | sendJson(req, res, repos) 127 | }) 128 | }) 129 | 130 | app.addRoute("/api/user/:name/repos", function (req, res, opts, cb) { 131 | validate(req, res, opts, function (err) { 132 | if (err) { 133 | return cb(err) 134 | } 135 | 136 | var name = opts.name 137 | var user = userRepos[name] 138 | 139 | if (user) { 140 | return sendJson(req, res, user) 141 | } 142 | 143 | // you can pass an argument to NOT_FOUND_ERROR and that value 144 | // will be placed in %s. Notice that we are handling the 404 case here 145 | cb(NOT_FOUND_ERROR(name)) 146 | }) 147 | }) 148 | 149 | var server = http.createServer(app) 150 | server.listen(3000) 151 | console.log("web service server listening on port 3000") 152 | -------------------------------------------------------------------------------- /examples/auth-helpers/server.js: -------------------------------------------------------------------------------- 1 | // based on https://github.com/visionmedia/express/blob/master/examples/route-middleware/index.js 2 | var http = require("http") // really should use https for security 3 | var Router = require("routes-router") 4 | var Session = require("generic-session") 5 | var MemoryStore = require("generic-session").MemoryStore 6 | var sendText = require("send-data") 7 | var redirect = require("redirecter") 8 | var TypedError = require("error/typed") 9 | 10 | var store = MemoryStore() 11 | var app = Router() 12 | 13 | // Example requests: 14 | // curl http://localhost:3000/login 15 | // curl http://localhost:3000/user/0 16 | // curl http://localhost:3000/user/0/edit 17 | // curl http://localhost:3000/user/1 18 | // curl http://localhost:3000/user/1/edit 19 | // (unauthorized since this is not you) 20 | // curl -X DELETE http://localhost:3000/user/0 21 | // (unauthorized since you are not an admin) 22 | 23 | // Dummy users 24 | var users = [ 25 | { id: 0, name: "tj", email: "tj@vision-media.ca", role: "member" }, 26 | { id: 1, name: "ciaran", email: "ciaranj@gmail.com", role: "member" }, 27 | { 28 | id: 2, 29 | name: "aaron", 30 | email: "aaron.heckmann+github@gmail.com", 31 | role: "admin" 32 | } 33 | ] 34 | 35 | var USER_NOT_FOUND = TypedError({ 36 | statusCode: 404, 37 | message: "Failed to load user %s" 38 | }) 39 | 40 | var UNAUTHORIZED = TypedError({ 41 | statusCode: 403, 42 | message: "Unauthorized" 43 | }) 44 | 45 | // you would fetch your user from the db here 46 | function loadUser(id, callback) { 47 | var user = users[id] 48 | if (!user) { 49 | return callback(USER_NOT_FOUND(id)) 50 | } 51 | 52 | callback(null, user) 53 | } 54 | 55 | function restrictToSelf(req, res, user, callback) { 56 | var session = Session(req, res, store) 57 | // fetch the current user from the session 58 | session.get("user", function (err, authedUser) { 59 | if (err) { 60 | return callback(err) 61 | } 62 | 63 | // if the session user is the user we are viewing then 64 | // there is no problem. 65 | if (user.id === (authedUser && authedUser.id)) { 66 | return callback() 67 | } 68 | 69 | // if it's not we can pass up an unauthorizederorr 70 | // which can be handled specially in your error handler 71 | callback(UNAUTHORIZED()) 72 | }) 73 | } 74 | 75 | function restrictTo(req, res, role, callback) { 76 | var session = Session(req, res, store) 77 | session.get("user", function (err, authedUser) { 78 | if (err) { 79 | return callback(err) 80 | } 81 | 82 | if (authedUser.role !== role) { 83 | return callback(UNAUTHORIZED()) 84 | } 85 | 86 | callback() 87 | }) 88 | } 89 | 90 | // faux login route. If you hit it we are going to store your 91 | // user in the session. 92 | app.addRoute("/login", function (req, res, opts, cb) { 93 | var session = Session(req, res, store) 94 | session.set("user", users["0"], function (err) { 95 | if (err) { 96 | return cb(err) 97 | } 98 | 99 | sendText(req, res, "Login ok") 100 | }) 101 | }) 102 | 103 | app.addRoute("/", function (req, res) { 104 | redirect(req, res, "/user/0") 105 | }) 106 | 107 | // this route is verbose because of callbacks. It's cleaner with flow control 108 | /* 109 | app.addRoute("/user/:id", { 110 | "GET": function (req, res, opts) { 111 | var user = yield loadUser.bind(null, opts.id) 112 | sendText(req, res, "Viewing user " + user.name) 113 | }, 114 | "DELETE": function (req, res, opts) { 115 | yield restrictTo.bind(null, req, res, "admin") 116 | var user = yield loadUser.bind(null, opts.id) 117 | sendText("Deleted user " + user.name) 118 | } 119 | }) 120 | */ 121 | app.addRoute("/user/:id", { 122 | "GET": function (req, res, opts, cb) { 123 | loadUser(opts.id, function (err, user) { 124 | if (err) { 125 | return cb(err) 126 | } 127 | 128 | sendText(req, res, "Viewing user " + user.name) 129 | }) 130 | }, 131 | "DELETE": function (req, res, opts, cb) { 132 | restrictTo(req, res, "admin", function (err) { 133 | if (err) { 134 | return cb(err) 135 | } 136 | 137 | loadUser(opts.id, function (err, user) { 138 | if (err) { 139 | return cb(err) 140 | } 141 | 142 | sendText("Deleted user " + user.name) 143 | }) 144 | }) 145 | } 146 | }) 147 | 148 | app.addRoute("/user/:id/edit", function (req, res, opts, cb) { 149 | loadUser(opts.id, function (err, user) { 150 | if (err) { 151 | return cb(err) 152 | } 153 | 154 | restrictToSelf(req, res, user, function (err) { 155 | if (err) { 156 | return cb(err) 157 | } 158 | 159 | sendText(req, res, "Editing user " + user.name) 160 | }) 161 | }) 162 | }) 163 | 164 | // really should use https for security 165 | var server = http.createServer(app) 166 | server.listen(3000) 167 | console.log("auth helpers server listen on port 3000") 168 | -------------------------------------------------------------------------------- /examples/auth/server.js: -------------------------------------------------------------------------------- 1 | // based on https://github.com/visionmedia/express/blob/master/examples/auth/app.js 2 | var http = require("http") // really should use https for security 3 | var Router = require("routes-router") 4 | var pwd = require("pwd") 5 | var redirect = require("redirecter") 6 | var Session = require("generic-session") 7 | var MemoryStore = require("generic-session").MemoryStore 8 | var sendHtml = require("send-data/html") 9 | var formBody = require("body/form") 10 | 11 | var template = require("./template.js") 12 | 13 | var store = MemoryStore() 14 | var app = Router() 15 | 16 | // dummy users database 17 | var users = { 18 | tj: { name: "tj" } 19 | } 20 | 21 | // hash a user password. This generatrs a salt & hash 22 | pwd.hash("foobar", function (err, salt, hash) { 23 | if (err) { 24 | throw err 25 | } 26 | 27 | users.tj.salt = salt 28 | users.tj.hash = String(hash) 29 | }) 30 | 31 | // authentication logic based on our mock database 32 | function authenticate(name, password, callback) { 33 | var user = users[name] 34 | // check whether user eists 35 | if (!user) { 36 | return callback(new Error("cannot find user")) 37 | } 38 | 39 | // hash the password with the users salt. If the hash 40 | // matches what is on the user then it's good 41 | pwd.hash(password, user.salt, function (err, hash) { 42 | if (err) { 43 | return callback(err) 44 | } 45 | 46 | // hashes returned by pwd.hash are buffers so cast them to strings 47 | if (String(hash) === user.hash) { 48 | return callback(null, user) 49 | } 50 | 51 | callback(new Error("invalid password")) 52 | }) 53 | } 54 | 55 | // restrict intercepts your route handler and runs some session logic 56 | // before it. It will then just take your route handler over completely 57 | // if the user doesnt exist otherwise it will call out to your route handler 58 | function restrict(handler) { 59 | return function (req, res, opts, callback) { 60 | var session = Session(req, res, store) 61 | session.get("user", function (err, user) { 62 | if (err) { 63 | return callback(err) 64 | } 65 | 66 | if (!user) { 67 | return session.set("message", { 68 | text: "Access denied!", 69 | type: "error" 70 | }, function (err) { 71 | if (err) { 72 | return callback(err) 73 | } 74 | 75 | redirect(req, res, "/login") 76 | }) 77 | } 78 | 79 | handler(req, res, opts, callback) 80 | }) 81 | } 82 | } 83 | 84 | app.addRoute("/", function (req, res) { 85 | redirect(req, res, "/login") 86 | }) 87 | 88 | app.addRoute("/restricted", restrict(function (req, res) { 89 | sendHtml(req, res, "Wahoo! restricted area, click to " + 90 | "logout") 91 | })) 92 | 93 | app.addRoute("/logout", function (req, res, opts, callback) { 94 | var session = Session(req, res, store) 95 | // destroy(cb) will delete the entire session for this user. 96 | // this means a new one will be created next time 97 | session.destroy(function (err) { 98 | if (err) { 99 | return callback(err) 100 | } 101 | 102 | redirect(req, res, "/") 103 | }) 104 | }) 105 | 106 | app.addRoute("/login", { 107 | GET: function (req, res, opts, callback) { 108 | var session = Session(req, res, store) 109 | // fetch the message out of the session. if a previous 110 | // POST failed or succeeded we will generate a message 111 | session.get("message", function (err, doc) { 112 | if (err) { 113 | return callback(err) 114 | } 115 | 116 | var message = "" 117 | if (doc && doc.type === "error") { 118 | message = "

" + doc.text + "

" 119 | } 120 | if (doc && doc.type === "success") { 121 | message = "

" + doc.text + "

" 122 | } 123 | 124 | // we are rendering the message so make sure to remove it 125 | // from the session otherwise it will be seen twice 126 | session.del("message", function (err) { 127 | if (err) { 128 | return callback(err) 129 | } 130 | 131 | sendHtml(req, res, template({ message: message })) 132 | }) 133 | }) 134 | }, 135 | POST: function (req, res, opts, callback) { 136 | formBody(req, res, function (err, body) { 137 | if (err) { 138 | return callback(err) 139 | } 140 | 141 | authenticate(body.username, body.password, function (err, user) { 142 | var session = Session(req, res, store) 143 | 144 | // if no user matches authentication then set error message 145 | // for next GET to login 146 | if (err || !user) { 147 | return session.set("message", { 148 | type: "error", 149 | text: "Authentication failed, pleace check your " + 150 | " username and password." + 151 | " (user 'tj' and 'foobar')" 152 | }, function (err) { 153 | if (err) { 154 | return callback(err) 155 | } 156 | 157 | redirect(req, res, "/login") 158 | }) 159 | } 160 | 161 | // succesfull user authentication. we should delete any 162 | // existing session 163 | session.del(function (err) { 164 | if (err) { 165 | return callback(err) 166 | } 167 | 168 | // then set the user 169 | session.set("user", user, function (err) { 170 | if (err) { 171 | return callback(err) 172 | } 173 | 174 | // and a success message 175 | session.set("message", { 176 | type: "success", 177 | text: "Authenticated as " + user.name + 178 | " click to logout. " + 179 | " You may now access " + 180 | "/restricted" 181 | }, function (err) { 182 | if (err) { 183 | return callback(err) 184 | } 185 | 186 | redirect(req, res, "/login") 187 | }) 188 | }) 189 | }) 190 | }) 191 | }) 192 | } 193 | }) 194 | 195 | // really should use https for security 196 | var server = http.createServer(app) 197 | server.listen(3000) 198 | console.log("example auth server listening on port 3000") 199 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # http-framework 2 | 3 | A web framework based purely on `require('http')` 4 | 5 | 6 | 7 | **Table of Contents** *generated with [DocToc](http://doctoc.herokuapp.com/)* 8 | 9 | - [http-framework](#http-framework) 10 | - [Motivation](#motivation) 11 | - [[Modules][20]](#modules20) 12 | - [For a complete list of [Modules check out the wiki][20]](#for-a-complete-list-of-modules-check-out-the-wiki20) 13 | - [[Documentation][26]](#documentation26) 14 | - [[HTTP workshop][27]](#http-workshop27) 15 | - [module docs](#module-docs) 16 | - [rest of the modules](#rest-of-the-modules) 17 | - [[Examples][17]](#examples17) 18 | - [[`express`][18] inspired examples](#express18-inspired-examples) 19 | - [[`hapi`][24] inspired examples](#hapi24-inspired-examples) 20 | - [[`connect`][25] inspired examples](#connect25-inspired-examples) 21 | - [Todo](#todo) 22 | - [Installation](#installation) 23 | - [Contributors](#contributors) 24 | - [MIT Licenced](#mit-licenced) 25 | 26 | 27 | 28 | 29 | ## Motivation 30 | 31 | `require('http')` is a web framework. All you need are a few 32 | small modules that do one thing well when you are building your 33 | web application route handlers. 34 | 35 | This module aggregates and documents (with examples) a selection 36 | of small modules that can be used to achieve this goal. 37 | 38 | It's recommended you check out the: 39 | 40 | - [the wiki of modules][23] 41 | - [the documentation][22] 42 | - [the examples][21] 43 | 44 | I do not recommend you use this "framework". You should check 45 | out the small modules and use them directly. Use the list of 46 | examples here for inspiration. 47 | 48 | http-framework is an OPEN Open Source Project, see the Contributing 49 | section to find out what this means. 50 | 51 | ## [Modules][20] 52 | 53 | Check out the [modules folder](modules) for an example of small 54 | modules you can combine to build your own custom "framework" 55 | 56 | See the [`package.json`][19] dependencies hash for an example of 57 | many small modules used in the examples folder of this project. 58 | 59 | ### For a complete list of [Modules check out the wiki][20] 60 | 61 | ## [Documentation][26] 62 | 63 | ### [HTTP workshop][27] 64 | 65 | For a tutorial / workshop see [http-works][27] 66 | 67 | ### module docs 68 | 69 | - [config-chain](documents/config-chain.md) 70 | - [cookies](documents/cookies.md) 71 | - [corsify](documents/corsify.md) 72 | - [csrf-lite](documents/csrf-lite.md) 73 | - [filed](documents/filed.md) 74 | - [form-body](documents/form-body.md) 75 | - [send-json](documents/send-json.md) 76 | - [send-html](documents/send-html.md) 77 | 78 | ### rest of the modules 79 | 80 | ```js 81 | // TODO 82 | ``` 83 | 84 | For now see the [examples folder][17] 85 | 86 | ## [Examples][17] 87 | 88 | ### [`express`][18] inspired examples 89 | 90 | These examples are clones of [`express`][18] examples demonstrating 91 | how to author web apps without frameworks. 92 | 93 | - [**auth**](examples/auth) 94 | An example demonstrating how to login and authenticate users 95 | - [**auth-helpers**](examples/auth-helpers) 96 | An example demonstrating how you can restrict certain routes in your 97 | apps to only be accessed by certain types of users 98 | - [**content-negotiation**](examples/content-negotiation) 99 | An example demonstrating how you can return different types of 100 | content based on what the user asks for in his Accept header. 101 | - [**cookie-sessions**](examples/cookie-sessions) 102 | An example of storing session information in a users cookie 103 | - [**cookies**](examples/cookies) 104 | An example of setting a cookie to track a user 105 | - [**cors**](examples/cors) 106 | An example of adding cors support to your HTTP server 107 | - [**downloads**](examples/downloads) 108 | An example of allowing users to download files from your server 109 | - [**error**](examples/error) 110 | An example of handling errors in your HTTP server 111 | - [**error-pages**](examples/error-pages) 112 | An example of rendering custom 500 and 404 pages in your web 113 | server 114 | - [**expose-data-to-client**](examples/expose-data-to-client) 115 | An example of putting server side state into your template 116 | so that it can be accessed from browser javascript 117 | - [**hello-world**](examples/hello-world) 118 | A simple hello world example 119 | - [**multipart**](examples/multipart) 120 | An example demonstrating file uploads and saving them to 121 | disk temporarily. 122 | - [**mvc**](examples/mvc) 123 | An over engineered example of how to structure a slightly 124 | larger web application. Includes configuration, modularity 125 | and databases. Take ideas from it, please do not copy paste it 126 | - [**online**](examples/online) 127 | An example of using redis and the `online` module to track 128 | user presence 129 | - [**route-map**](examples/route-map) 130 | An example of a `map` utility function that can be used to 131 | structure your routes differently. This demonstrates you can 132 | do whatever you want, if you like it, do it. 133 | - [**route-seperation**](examples/route-seperation) 134 | An example of spreading your route handlers over multiple 135 | files. 136 | - [**search**](examples/search) 137 | An example of doing a database query over XHR with a web 138 | server backed by redis 139 | - [**session**](examples/session) 140 | An example of storing information in a session. The session 141 | is either backed by redis or memory. 142 | - [**static-files**](examples/static-files) 143 | An example of serving static files for your web server 144 | - [**vhost**](examples/vhost) 145 | An example of handling multiple sub domains as seperate 146 | applications in a singlue web server 147 | - [**web-service**](examples/web-service) 148 | An example JSON REST api web server. Contains API key 149 | authentication and error handling 150 | 151 | Credit for the applications goes to 152 | 153 | - @tj & express maintainers 154 | 155 | ### [`hapi`][24] inspired examples 156 | 157 | These examples are clones of [`hapi`][24] examples demonstrating 158 | how to author web apps without frameworks. 159 | 160 | - [**tail**](examples/tail) 161 | An example of handling async errors in your applications 162 | after you have ended the HTTP response 163 | - [**validate-api**](examples/validate-api) 164 | An example of how to add validation logic to your HTTP 165 | route handlers. Both validating query params & the HTTP body 166 | 167 | Credit for the application's goes to 168 | 169 | - @hapijs & hapi maintainers 170 | 171 | ### [`connect`][25] inspired examples 172 | 173 | These examples are clones of [`connect`][25] examples demonstrating 174 | how to author web apps without frameworks. 175 | 176 | - [**basic-auth**](examples/basic-auth) 177 | An example of handling basic authorization style authentication 178 | of a web server. 179 | - [**csrf**](examples/csrf) 180 | An example of adding csrf security to a form in a web application 181 | 182 | Credit for the applications goes to 183 | 184 | - @tj & connect maintainers 185 | 186 | ## Todo 187 | 188 | - [x] Finish porting express examples 189 | - [ ] continue porting hapi examples 190 | - [ ] continue connect examples 191 | - [ ] Port koajs examples 192 | - [ ] Port restify examples 193 | - [ ] Port partial.js examples 194 | - [ ] Finish documentation 195 | - [ ] Get community feedback 196 | - [ ] Author new fresh examples 197 | 198 | 199 | ## Installation 200 | 201 | `npm install http-framework` 202 | 203 | ## Contributors 204 | 205 | - Raynos 206 | - Matt-Esch 207 | - maxogden 208 | 209 | ## MIT Licenced 210 | 211 | [17]: https://github.com/Raynos/http-framework/tree/master/examples 212 | [18]: https://github.com/visionmedia/express 213 | [19]: https://github.com/Raynos/http-framework/blob/master/package.json#L19 214 | [20]: https://github.com/Raynos/http-framework/wiki/Modules 215 | [21]: https://github.com/Raynos/http-framework#examples 216 | [22]: https://github.com/Raynos/http-framework#documentation 217 | [23]: https://github.com/Raynos/http-framework#modules 218 | [24]: https://github.com/spumko/hapi 219 | [25]: https://github.com/senchalabs/connect 220 | [26]: https://github.com/Raynos/http-framework/tree/master/documents 221 | [27]: https://github.com/Raynos/http-works 222 | --------------------------------------------------------------------------------