Permalink
Browse files

Initial commit

  • Loading branch information...
0 parents commit b9fd401fc765b3b5275c5378b41cca7df6a26d3a @mjackson committed Jul 30, 2011
@@ -0,0 +1,2 @@
+.DS_Store
+node_modules
58 README
@@ -0,0 +1,58 @@
+Link is a modular web server interface for Node.js. Using Link, web developers
+can build highly performant servers in a powerful, modular style that encourages
+readability and clean separation of responsibility. The core Link distribution
+consists of three things:
+
+ - A specification (see SPEC) for building applications and middleware
+ - A library (see lib) with many useful utilities and middleware to aid
+ developers in the common tasks of building applications that conform to the
+ specification
+ - An executable (see bin/linkup) for running Link applications from the
+ command line
+
+= Installation
+
+Using npm:
+
+ $ npm install link
+
+= Usage
+
+Link reduces the complex task of building a web application to the simplest
+terms possible. A Link application is simply a JavaScript function that takes
+two arguments: the environment and a callback. The environment is a JavaScript
+object that contains information about the incoming request such as the request
+method that was used, any parameters that were sent, etc. When the application
+is ready to send the response to the client, it simply calls the callback with
+the response status code, the headers, and the response body.
+
+ function (env, callback) {
+ callback(200, {
+ "Content-Type": "text/plain",
+ "Content-Length": "11"
+ }, "hello world");
+ }
+
+= Tests
+
+To run the tests, first install vows:
+
+ $ npm install vows
+
+Run all tests with:
+
+ $ ./node_modules/.bin/vows test/*_test.js
+
+Otherwise, run the tests for a specific module with:
+
+ $ ./node_modules/.bin/vows test/utils_test.js
+
+= Credits
+
+Link was inspired by similar efforts in the Python and Ruby communities, namely
+WSGI and Rack. It borrows many code patterns from these libraries, as well as
+the JSGI project. Link's multipart parser is based on the fast parser in the
+node-formidable project.
+
+My sincere thanks to the authors of each of these libraries for the excellent
+work they've done and graciously shared.
114 SPEC
@@ -0,0 +1,114 @@
+This specification aims to formalize the Link protocol for building web
+applications using the Node.js JavaScript platform. You can (and should) use
+the lint middleware to enforce it. When you develop middleware, be sure to add
+a lint before and after to catch all mistakes.
+
+= Link Applications
+
+A Link application is a JavaScript function that takes exactly two arguments:
+the *environment* and a *callback*.
+
+== The Environment
+
+The environment is a JavaScript object that includes CGI-like properties. It
+must include the following properties except when they would be empty, but see
+below.
+
+ - protocol The protocol used in the request (i.e. "http:" or
+ "https:"). This variable may never be an empty string and
+ is always required.
+ - protocolVersion The version of the protocol used in the request. This
+ variable may never be an empty string and is always
+ required.
+ - requestMethod The request method (e.g. "GET" or "POST"). This cannot
+ ever be an empty string, and is always required.
+ - serverName, When combined with scriptName and pathInfo these
+ serverPort variables may be used to reconstruct the original
+ request URL. Note, however, that if httpHost is present,
+ it should be used in preference to serverName. These
+ variables can never be empty strings, and are always
+ required.
+ - scriptName The initial portion of the request URL's "path" that
+ corresponds to the application, so that it knows its
+ virtual "location". This may be an empty string, if the
+ application corresponds to the "root" of the server.
+ - pathInfo The remainder of the request URL's "path", designating
+ the virtual "location" of the target resource within the
+ application. This may be an empty string if the request
+ URL targets the root of the application and does not
+ have a trailing slash. This value may be percent-encoded
+ when originating from a URL.
+ - queryString The portion of the request URL that follows the "?", if
+ any. May be an empty string, but is always required.
+ - http* Variables corresponding to the client-supplied HTTP
+ request headers (i.e. variables whose names begin with
+ http). The presence or absence of these variables should
+ correspond with the presence or absence of the
+ appropriate HTTP header in the request. The remainder of
+ the property name will be the camel-cased version of the
+ original header name (e.g. "httpAccept" and
+ "httpUserAgent").
+
+The environment must not contain the properties httpContentType or
+httpContentLength (use contentType and contentLength instead).
+
+In addition to these, the environment must include the following Link-specific
+variables:
+
+ - link.version The current version of the Link library.
+ - link.input An EventEmitter of data contained in the request body.
+ - link.error A writable Stream for error output.
+ - link.session A JavaScript object containing session data.
+
+There are the following restrictions:
+
+ - protocol must be either "http:" or "https:".
+ - requestMethod must be a valid token.
+ - scriptName, if not empty, should start with a "/".
+ - pathInfo, if not empty, should start with a "/".
+ - Both scriptName and pathInfo must be set. pathInfo should be "/" if
+ scriptName is empty. scriptName should never be "/" but instead be empty.
+ - contentLength, if given, must consist of digits only.
+ - link.version must be an array of integers [major, minor, patch].
+
+The application is free to modify the environment. Property names must contain
+at least one dot and should be prefixed uniquely. The prefix "link" is reserved
+for use within the Link core distribution and other accepted specifications and
+is not available for use elsewhere.
+
+== The Callback
+
+The callback is used to issue a response to the client and must be called with
+exactly three arguments: the response *status*, HTTP *headers*, and *body*.
+
+=== The Status
+
+The status must be an HTTP status code as a Number.
+
+=== The Headers
+
+The headers must be a JavaScript object whose properties are the names of HTTP
+headers in their canonical form (i.e. "Content-Type" instead of "content-type").
+Header names may contain only letters, digits, "-", and "_" and must start with
+a letter and must not end with a "-" or "_". If more than one value for a header
+is required, the value for that property must be an array.
+
+=== The Content-Type
+
+There must be a Content-Type header, except for when the status is 1xx, 204, or
+304, in which case there must be none given.
+
+=== The Content-Length
+
+There must not be a Content-Length header when the status is 1xx, 204, or 304.
+
+=== The Body
+
+The body must be either a string or a readable Stream. If it is a Stream, the
+response will be pumped through to the client.
+
+= Credits
+
+Some parts of this specification are adopted from PEP333: Python Web Server
+Gateway Interface v1.0 (http://www.python.org/dev/peps/pep-0333/) and the Rack
+specification (http://rack.rubyforge.org/doc/files/SPEC.html).
@@ -0,0 +1,3 @@
+#!/usr/bin/env node
+
+
@@ -0,0 +1,163 @@
+var util = require("util"),
+ http = require("http"),
+ https = require("https"),
+ url = require("url"),
+ EventEmitter = require("events").EventEmitter,
+ utils = require("./link/utils"),
+ Input = require("./link/input");
+
+/**
+ * The current version of Link as [major, minor, patch].
+ */
+exports.version = [0, 1, 0];
+
+exports.createServer = createServer;
+exports.envFor = envFor;
+
+/**
+ * Creates an HTTP server for the given +app+. If +opts+ are supplied, they are
+ * used as the options for an HTTPS server. The +app+ should be a valid Link app
+ * (see the SPEC), or an object that has a #toApp function that returns an app.
+ */
+function createServer(app, opts) {
+ var server;
+
+ if (typeof opts === "object") {
+ server = https.createServer(opts);
+ } else {
+ server = http.createServer();
+ }
+
+ // The app may be any object that has a toApp method (e.g. a Builder).
+ if (typeof app.toApp === "function") {
+ app = app.toApp();
+ }
+
+ if (typeof app !== "function") {
+ throw new Error("App must be a function");
+ }
+
+ server.on("request", function handleRequest(req, res) {
+ var addr = server.address();
+ var uri = url.parse(req.url);
+
+ uri.protocol = (server instanceof https.Server) ? "https:" : "http:";
+ uri.hostname = process.env.SERVER_NAME || addr.address;
+ uri.port = (parseInt(process.env.SERVER_PORT, 10) || addr.port).toString(10);
+
+ var env = envFor(uri, {
+ protocolVersion: req.httpVersion,
+ method: req.method,
+ headers: req.headers,
+ input: req,
+ error: process.stderr
+ });
+
+ // Call the app.
+ app(env, function handleResponse(status, headers, body) {
+ res.writeHead(status, headers);
+
+ if (body instanceof EventEmitter) {
+ util.pump(body, res);
+ } else {
+ res.end(body);
+ }
+ });
+ });
+
+ return server;
+}
+
+/**
+ * Creates an environment object for the given +uri+, which should be an object
+ * with any of the following properties:
+ *
+ * - protocol
+ * - hostname
+ * - port
+ * - pathname
+ * - query
+ *
+ * The +opts+ parameter should be an object with any of the following
+ * properties:
+ *
+ * - protocolVersion The HTTP protocol version
+ * - method The request method (e.g. "GET" or "POST")
+ * - headers An object of HTTP headers
+ * - input The input stream of the request body
+ * - error The error stream
+ */
+function envFor(uri, opts) {
+ uri = uri || {};
+ opts = opts || {};
+
+ if (!(opts.input instanceof EventEmitter)) {
+ throw new Error("Input must be an EventEmitter");
+ }
+
+ var env = {};
+
+ env.protocolVersion = opts.protocolVersion || "1.0";
+ env.requestMethod = (opts.method || "GET").toUpperCase();
+
+ env.protocol = uri.protocol || "http:";
+ env.serverName = uri.hostname || "";
+ env.serverPort = uri.port || (env.protocol == "https:" ? "443": "80");
+ env.scriptName = "";
+ env.pathInfo = uri.pathname || "";
+ env.queryString = uri.query || "";
+
+ if (opts.headers) {
+ // Add http* properties for HTTP headers.
+ var propName;
+ for (var headerName in opts.headers) {
+ propName = utils.httpPropertyName(headerName);
+ env[propName] = opts.headers[headerName];
+ }
+ }
+
+ // Set contentType and contentLength.
+ env.contentType = env.httpContentType || "";
+ delete env.httpContentType;
+ env.contentLength = (parseInt(env.httpContentLength, 10) || 0).toString(10);
+ delete env.httpContentLength;
+
+ env["link.version"] = exports.version;
+ env["link.input"] = new Input(opts.input);
+ env["link.error"] = opts.error || process.stderr;
+
+ return env;
+}
+
+// Setup dynamic loading of package contents, accessible by property name.
+
+var propPaths = {
+ // Constructors
+ "Builder": "link/builder",
+ "Input": "link/input",
+ "Request": "link/request",
+ "Response": "link/response",
+ "UploadedFile": "link/uploadedfile",
+ // Other
+ "commonLogger": "link/commonlogger",
+ "contentLength": "link/contentlength",
+ "contentType": "link/contenttype",
+ "lint": "link/lint",
+ "methodOverride": "link/methodoverride",
+ "mime": "link/mime",
+ "mock": "link/mock",
+ "multipart": "link/multipart",
+ "querystring": "link/querystring",
+ "sessionCookie": "link/session/cookie",
+ "static": "link/static",
+ "urlMap": "link/urlmap",
+ "utils": "link/utils"
+};
+
+for (var propertyName in propPaths) {
+ exports.__defineGetter__(propertyName, (function (path) {
+ return function () {
+ return require("./" + path);
+ }
+ })(propPaths[propertyName]));
+}
Oops, something went wrong.

0 comments on commit b9fd401

Please sign in to comment.