| @@ -1,3 +1,95 @@ | |||
| # webframe | |||
| Web server. | |||
| Webframe is a small and dependency free web application framework. | |||
| ## Usage | |||
| Install: | |||
| npm install --save webframe | |||
| Use: | |||
| var webframe = require("webframe"); | |||
| var app = new webframe.App(); | |||
| The file example.js contains an example of a small web application. | |||
| ## Why a new web framework? | |||
| A new installation of express is 2.3 megabytes. You end up with 42 dependencies | |||
| in your node\_modules directory. The Koa framework is 2.5 megabytes, with 36 | |||
| dependencies. This isn't bad for a big web application, but I write a lot of | |||
| tiny web applications where megabytes of dependencies just feels wrong. As a | |||
| result, I have in the past often just used the http module directly. This | |||
| framework is a nice middle ground; it provides some of the utility of big web | |||
| frameworks, but without adding a ton of extra dependencies to the application. | |||
| ## Documentation | |||
| ### webframe.App([options]) | |||
| Creates a new instance, with an optional options argument. | |||
| Options: | |||
| * `server`: Your own HTTP (like the one created by http.createHttpServer). | |||
| * `port`: The port to listen on. Defaults to the PORT environment variable, or | |||
| 8080. | |||
| * `host`: The host to listen on. Defaults to "127.0.0.1". For the server to be | |||
| accessible to the outside world, listen on "0.0.0.0". | |||
| * `client_utils`: Whether to add some utility functions to /webframe.js. | |||
| Defaults to false. | |||
| * `res404`: The string to return for a 404 error. "{{pathname}}" will be | |||
| replaced with the pathname. | |||
| * `res403`: The string to return for a 403 error. "{{pathname}}" will be | |||
| replaced with the pathname. | |||
| var app = webframe.App({ client_utils: true }); | |||
| ### app.route(method, path [, middleware], func) | |||
| Add a route. | |||
| * `method`: What HTTP method the route should be available at. | |||
| Valid options: "GET", "POST", "PUT", "DELETE", or "ALL". | |||
| * `path`: The path. If it starts with a "^", it will be interpreted as a | |||
| regular expression (with `new RegExp(path)`), and the route will be ran on | |||
| all matching paths. Routes without a pattern are prioritized over those with | |||
| a pattern. | |||
| * `middleware`: Optional. An array of middleware functions which will be ran | |||
| before the route's function. | |||
| * `func`: function(request, response). The function which handles the request. | |||
| ### app.get, app.post, app.put, app.delete, app.all | |||
| Utility functions which just call app.route() with a predefined method value. | |||
| E.g `app.get("/foo", handler)` is the same as | |||
| `app.route("GET", "/foo", handler)`. | |||
| ### app.info(str), app.notice(str), app.warning(str), app.panic(str) | |||
| Logging functions. They all print a message. `app.panic` also exits, and is | |||
| intended for errors so bad you have to crash. | |||
| In the future, it will be possible to control the logging level. | |||
| ### Middleware | |||
| `webframe.middleware` contains middleware for the route functions. | |||
| * `webframe.middleware.cookies`: Adds `request.cookies`, parsed COOKIE header. | |||
| * `webframe.middleware.params`: Adds `request.params`, parsed URL parameters. | |||
| In the future, there will be middleware for POST request too. | |||
| ### webframe.static(root[, before]) | |||
| Serve static files. | |||
| * `app.get("^.*", webframe.static("web"))` will serve all the files in the | |||
| `web` directory, so `/index.html` will serve `web/index.html`. | |||
| * `app.get("^/static/.*", webframe.static("web", "/static"))` will serve all | |||
| files in the `web` directory under `/static`, so `/static/script.js` will | |||
| serve `web/script.js`. | |||
| @@ -0,0 +1,17 @@ | |||
| // Replace with require("webframe") | |||
| var webframe = require("./index.js"); | |||
| var mw = webframe.middleware; | |||
| var app = new webframe.App(); | |||
| // Endpoint which uses the "params" middleware to parse URL parameters | |||
| app.get("/endpoint", [ mw.params ], (req, res) => { | |||
| res.end("Hello, "+req.params.name); | |||
| }); | |||
| // When a an endpoint path starts with a ^, it's interpreted as a | |||
| // regular expression pattern. "^.*" matches everything (though endpoints | |||
| // with explicit paths, like the "/endpoint" above, are prioritized). | |||
| // | |||
| // webframe.staic("web") returns a function which serves the file in "web". | |||
| app.get("^.*", webframe.static("web")); | |||
| @@ -84,20 +84,20 @@ class App { | |||
| return; | |||
| } | |||
| // Run all the before stuff if applicable | |||
| // Run all the middleware stuff if applicable | |||
| var self = this; | |||
| if (route.before) { | |||
| var cbs = route.before.length; | |||
| if (route.middleware) { | |||
| var cbs = route.middleware.length; | |||
| function cb() { | |||
| if (--cbs === 0) | |||
| route.func(req, res, self); | |||
| } | |||
| for (var i in route.before) { | |||
| route.before[i](req, res, cb); | |||
| for (var i in route.middleware) { | |||
| route.middleware[i](req, res, cb); | |||
| } | |||
| // Just run the function if there's no before | |||
| // Just run the function if there's no middleware | |||
| } else { | |||
| route.func(req, res, this); | |||
| } | |||
| @@ -111,7 +111,7 @@ class App { | |||
| * path: path, | |||
| * func: function | |||
| */ | |||
| route(method, path, before, func) { | |||
| route(method, path, middleware, func) { | |||
| if (method !== "GET" && method !== "POST" && method !== "PUT" && | |||
| method !== "DELETE" && method !== "ALL") { | |||
| throw new Error("Invalid method."); | |||
| @@ -119,8 +119,8 @@ class App { | |||
| // Before is optional | |||
| if (func === undefined) { | |||
| func = before; | |||
| before = undefined; | |||
| func = middleware; | |||
| middleware = undefined; | |||
| } | |||
| // All necessary arguments must exist | |||
| @@ -136,13 +136,13 @@ class App { | |||
| method: method, | |||
| func: func, | |||
| pattern: pat, | |||
| before: before | |||
| middleware: middleware | |||
| }); | |||
| } else { | |||
| this._routeMap[path] = { | |||
| method: method, | |||
| func: func, | |||
| before: before | |||
| middleware: middleware | |||
| }; | |||
| } | |||
| } | |||
| @@ -163,11 +163,11 @@ class App { | |||
| */ | |||
| template(tmpl, args) { return template(tmpl, args); } | |||
| get(path, before, func) { this.route("GET", path, before, func); } | |||
| post(path, before, func) { this.route("POST", path, before, func); } | |||
| put(path, before, func) { this.route("PUT", path, before, func); } | |||
| delete(path, before, func) { this.route("DELETE", path, before, func); } | |||
| all(path, before, func) { this.route("ALL", path, before, func); } | |||
| get(path, middleware, func) { this.route("GET", path, middleware, func); } | |||
| post(path, middleware, func) { this.route("POST", path, middleware, func); } | |||
| put(path, middleware, func) { this.route("PUT", path, middleware, func); } | |||
| delete(path, middleware, func) { this.route("DELETE", path, middleware, func); } | |||
| all(path, middleware, func) { this.route("ALL", path, middleware, func); } | |||
| info(str) { | |||
| console.log("Info: "+str); | |||
| @@ -12,11 +12,21 @@ var mimes = { | |||
| zip: "application/zip", | |||
| pdf: "application/pdf", | |||
| gif: "image/gif", | |||
| png: "image/png", | |||
| jpeg: "image/jpeg", | |||
| jpg: "image/jpeg", | |||
| gif: "image/gif", | |||
| svg: "image/svg", | |||
| svg: "image/svg+xml", | |||
| bmp: "image/bmp", | |||
| webp: "image/webp", | |||
| midi: "audio/midi", | |||
| mp3: "audio/mpeg", | |||
| ogg: "audio/ogg", | |||
| webm: "video/webm", | |||
| mkv: "video/mkv", | |||
| ogv: "video/ogg", | |||
| } | |||
| function mimetype(path) { | |||
| @@ -52,7 +62,6 @@ function sendfile(path, app, pathname, res) { | |||
| } | |||
| module.exports = function(root, before) { | |||
| return function(req, res, app) { | |||
| var pn = req.urlobj.pathname; | |||
| @@ -1,6 +1,6 @@ | |||
| { | |||
| "name": "webframe", | |||
| "version": "0.1.0", | |||
| "version": "0.1.1", | |||
| "description": "Web server.", | |||
| "main": "index.js", | |||
| "scripts": { | |||
| @@ -0,0 +1,14 @@ | |||
| <!DOCTYPE html> | |||
| <html> | |||
| <head> | |||
| <meta charset="utf-8"> | |||
| <title>Hello World</title> | |||
| </head> | |||
| <body> | |||
| <p> Hello World! </p> | |||
| <form action="/endpoint" method="get"> | |||
| <label>What's your name? <input type="text" name="name"></label> | |||
| </form> | |||
| </body> | |||
| </html> | |||