JSGI/Level0/A/Draft2

From CommonJS Spec Wiki
Jump to: navigation, search

JSGI Level 0/A Draft 2 Proposal (AKA JSGI 0.3)

Contents

Philosophy

Principles

Specification

Application

A JSGI Application is a JavaScript Function which takes exactly one argument, the Request as a JavaScript Object, and returns its Response as a JavaScript Object containing three required attributes: status, headers and body (DL: should we mention `then` here?).

Middleware

JSGI Middleware are JSGI Applications that can call other JSGI Applications. Middleware can be stacked up into a call chain to provide useful services to Requests and Responses.

Server

A JSGI Server is the glue that connects JSGI Applications to HTTP request and response messages. (DL: this definition sucks, but Server should be defined...ideas?)

Request

The request environment MUST be a JavaScript Object representative of an HTTP request. Applications are free to modify the Request.

Required Keys

The Request is required to include these keys:

Optional Keys

The Request MAY contain contain these keys:

(DL: added the above keys (everything spec'd by CGI) to effectively reserve them in the top level request so that we can safely extend the JSGI spec without worrying about conflicts -- is this alright?)

Response

Applications MUST return a JavaScript Object representative of an HTTP response:

status

The status MUST be a three-digit integer (RFC 2616 Section 6.1.1)

headers

MUST be a JavaScript object containing key/value pairs of Strings or Arrays. Servers SHOULD output multiple headers for `header` values supplied as Arrays. All keys MUST be lower-case. The `headers` object MUST NOT contain a `status` key and MUST NOT contain key names that end in `-` or `_`. It MUST contain keys that consist of letters, digits, `_` or `-` and start with a letter. Header values MUST NOT contain characters below 037.

body

MUST be an object which responds to `forEach` and MUST only yield objects which have a toByteString method (including Strings and Binary<ref>Binary, as defined in Binary/D (DL: or whatever binary module is ratified) objects</ref>).

If `body` responds to close, it SHOULD be called after iteration.

Conventions

To facilitate interoperability among JSGI Applications and Servers, Applications SHOULD be added as the `app` key on their module's `exports` object.

Upon initialization Servers MUST provide an object representative of every Request's `jsgi` object as a second argument to the `app` function. (AB: this needs more detail)

Extensions

This specification can be extended by JSGI Extension Proposals. Ratification of extensions occurs by the standard CommonJS process. An Extension Proposal MUST specify a key String and version Array to be placed in `request.jsgi.ext` to be ratified.



The following sections of this specification are non-normative.



Rationale

Multiple header values with the same key should now be provided as an Array value. JSGI 0.2 specified this as a string containing `\n` but working with them as arrays is easier and should a middleware mistakenly concatenate to the header value Array's toString will do the right thing according to RFC 2616 and coerce header value into a comma-joined string.

Header casing is specified as lower-case to aid interoperability amongst middleware. While case is irrelevant according to RFC 2616 Servers MAY correct the case of Response header keys or expose this functionality to Applications.

(DL: notes about potential CGI information loss, particularly with regard to decoded paths, headers actually containing an _ and multiple headers, as well as the security implications (X_FORWARDED_FOR/x-forwarded-for)

(AB: If server on top of CGI and undecoded PATH_INFO available, we should note that servers should use it)

(DL: there should be a note about forEach for sync streaming and a promise's progressCallback for async streaming, which has been the subject of confusion. Any other areas that deserve special explanation?)

Changes from JSGI 0.2

Specification changes

Request changes

Required
env.REQUEST_METHOD => request.method
env.SERVER_PROTOCOL => request.version, parsed "HTTP/1.1" => [1, 1]
env.SERVER_NAME => request.host
env.SERVER_PORT => request.port, parsed
env.SCRIPT_NAME => request.scriptName, MUST NOT be decoded, MUST NOT end in slash
env.PATH_INFO => request.pathInfo, MUST NOT be decoded
env.QUERY_STRING => request.queryString
Optional
env.AUTH_TYPE => request.authType
env.PATH_TRANSLATED => request.pathTranslated DL: this is duplicate data, should we forbid?
env.REMOTE_ADDR => request.remoteAddr DL: remoteAddress?
env.REMOTE_HOST => request.remoteHost
env.REMOTE_IDENT => request.remoteIdent DL: remoteIdentity?
env.REMOTE_USER => request.remoteUser
env.SERVER_SOFTWARE => request.serverSoftware
Forbidden
env.CONTENT_TYPE: exists in request.jsgi.headers["content-type"] if applicable
env.CONTENT_LENGTH: exists in request.jsgi.headers["content-length"] if applicable
env.GATEWAY_INTERFACE: exists in request.jsgi.cgi if applicable

request.jsgi

request.headers

request.env

response

response.status

response.headers

response.body

Conventions

Examples

Request

{
    version: [1, 1], // parsed HTTP version ~ "HTTP/1.1"
    method: "GET",
    headers: { // HTTP headers, lowercased but otherwise unmangled
        host: "jackjs.org",
        "user-agent": "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3",
        accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
        "accept-language": "en-us,en;q=0.5",
        "accept-encoding": "gzip,deflate",
        "accept-charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7",
        "keep-alive": "300",
        connection: "keep-alive",
        "if-modified-since": "Fri, 04 Sep 2009 07:47:22 GMT",
        "cache-control": "max-age=0"
    },
    input: (new InputStream), // request body stream
    scheme: "http",
    host: "jackjs.org",
    port: 80,
    env: { // this is the place where Server and Middleware put keys
        juice: {
            version: [0, 1]
        },
        jack: {
            request: null
        }
    },
    jsgi: {
        version: [0, 3],
        errors: (new OutputStream),
        multithread: false,
        multiprocess: true,
        runOnce: false,
        async: true,
        cgi: false // [1, 1] if CGI/1.1
    }
}

(DL: I'd like to have at least a simple response and Async Extension promise response example)

Acknowledgements

This specification is adapted from the Rack specification written by Christian Neukirchen. Some parts of this specification are adopted from PEP333: Python Web Server Gateway Interface v1.0. Special thanks to the many contributors of the CommonJS group.

Personal tools
Namespaces
Variants
Actions
Navigation
Toolbox