├── 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, "" + users.map(function (user) {
20 | return "" + user.name + " "
21 | }).join("") + " ")
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 "" + ids.map(function (id) {
14 | return "" + id + " "
15 | }).join("") + " "
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, "")
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 | "")
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("");
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("");
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 |
--------------------------------------------------------------------------------