Like JSGI, but using streams for the request bodies.
EJSGI Middleware are EJSGI applications that can call other EJSGI applications. Middleware can be stacked up into a call chain to provide useful services to Requests and Responses.
An EJSGI Server is the glue that connects EJSGI Applications to HTTP request and response messages.
The reference implementation is built on nodejs.
A promise is an object that exposes an
addCallback takes a single function argument, which is called with the appropriate arguments when they're ready. For the purpose of EJSGI, this is the only required functionality, but servers MAY add additional features.
(IZS Maybe apps should just take a callback, and then we don't have to talk about promises at all? It'd make post-app middleware a lot simpler.)
Stream Objects are representations of a stream of data in an asynchronous evented paradigm.
Stream Objects have the following methods:
- write - Send data down the stream. If the stream is closed, then throw an Error. This must not actually perform the write until after the current scope of execution is completed. The order of written data MUST be consistent with the order in which
writeis called. Returns
trueif the data could be written to the output stream immediately, or
falseif the data was buffered for writing later. If it returns
false, then the caller can attach a listener to the
drainevent to know when it's ready to receive more data.
- close - Close the stream. Once closed, no more data may be written to the Stream.
- pause - Temporarily prevent the firing of
dataevents. This is useful when a reader needs to throttle a stream of incoming data.
- resume - Resume a paused thread, so that
dataevents will begin firing again.
- addListener - Attach an event handler to an event. The first argument is the event name, and the second is the callback.
Stream Objects emit the following events
- data - All the data that is passed through
writeeventually triggers a
dataevent. The argument is the data that was written.
- end - Emitted when all data has been written, and the stream is closed.
- drain - Emitted when the internal buffer empties after a buffered
- pause - Emitted when the stream is paused with the
- resume - Emitted when the stream is resumed with the
Stream objects MUST implement some sort of "event queue" in order to defer callbacks until after the current execution context has exited. Specifically, the
drain events MUST NOT be fired immediately on the corresponding calls to
All streams SHOULD be both readable and writeable. This allows for middleware to step into the flow and filter the input or output to/from an app. Of course, the underlying input and output streams may be read- or write-only.
The request environment MUST be an Object representative of an HTTP request. Applications are free to modify the Request.
The Request is required to have these keys:
- method - The HTTP request method, such as "GET" or "POST". Request's method cannot be undefined and so MUST be present with a value of an UPPERCASE string.
- url - The URL on the first line of the request. This MUST be exactly the requested URL as it appears on the first line of the HTTP request, without any modifications or corrections. (Note that it generally will not contain the hostname.)
- scriptName - The initial portion of the request URL's "path" that corresponds to the Application object, so that the Application knows its virtual "location". This MAY be an empty string, if the Application corresponds to the "root" of the Server. Restriction: if non-empty
scriptNameMUST start with "/", MUST NOT end with "/" and MUST NOT be decoded.
- pathInfo - The remainder of the request URL's "path", designating the virtual "location" of the Request's target within the Application. This may be an empty string, if the request URL targets the Application root and does not have a trailing slash. Restriction: if non-empty
pathInfoMUST start with "/" and MUST NOT be decoded.
- queryString - The portion of the request URL that follows the first "?", if any. Restriction: MAY be an empty string but
queryStringkey MUST NOT be undefined.
- host - The portion of the request URL that follows the
scheme. Restriction: MUST be non-empty String, MUST NOT contain a colon or slash. Note: when combined with
queryStringthis variable can be used to complete the URL. If not found in the request line, then the
HOSTheader, can be used. If not found in the
- port - Representative of the Request port. If
hostis followed by a colon this is all digits following this colon. If not present in the request URL
portcan be derived from the
scheme. Restriction: MUST NOT be undefined and MUST be an integer.
- scheme - URL scheme (per RFC 1738). "http", "https", etc.
- headers - Variables corresponding to the client-supplied HTTP request headers are stored in the
headersobject. The presence or absence of these variables should correspond with the presence or absence of the appropriate HTTP header in the request. All keys in the
headersobject MUST be the lower-case equivalent of the request's HTTP header keys. See: example requests for more details.
- jsgi - The Request MUST include these JSGI-specific variables in a
- jsgi.version - The Array [0,3], representing this version of JSGI.
- jsgi.errors - Stream for Application errors (anyone have better wording?). Requirement - MUST be an output stream.
- jsgi.multithread - truthy if the Application object may be simultaneously invoked by another thread in the same process, otherwise falsey.
- jsgi.multiprocess - truthy if an equivalent Application object may be simultaneously invoked by another process, otherwise falsey.
- jsgi.runOnce - truthy if Server expects (but does not guarantee) that the Application will only be invoked this one time during the life of its containing process, otherwise falsey. Note - normally, this will only be true for a server based on CGI (or something similar).
- jsgi.cgi - CGI version Array in [major, minor] form if Server is implemented atop CGI, otherwise falsey.
- jsgi.ext - Defined in the Extensions section.
- env - The top level of the Request object SHOULD only consist of the keys specified above. Servers and Middleware MAY add additional information the
envkey. Servers are encouraged to add any additional relevant data relating to the Request in a key in the
envobject. Requirements: MUST be an object.
- input - The input Stream. Requirements: MUST be a Stream Object.
The Request MAY contain contain these keys:
- authType - Corresponds to the CGI key AUTH_TYPE
- pathTranslated - Corresponds to the CGI key PATH_TRANSLATED
- remoteAddr - Corresponds to the CGI key REMOTE_ADDR
- remoteHost - Corresponds to the CGI key REMOTE_HOST
- remoteIdent - Corresponds to the CGI key REMOTE_IDENT
- remoteUser - Corresponds to the CGI key REMOTE_USER
- serverSoftware - Corresponds to the CGI key SERVER_SOFTWARE
The Request body object is required to emit the following events at the appropriate time, in this order:
- data - Fired as each chunk of the body is uploaded. Argument: the data that has been uploaded in this chunk
- end - Fired when the entire body has been uploaded, and the request is completed. Argument: none.
The app may call the following methods on the request body:
- pause - Pause the incoming stream of data.
- resume - Resume the incoming stream.
The Response is required to have these fields:
- status - The status MUST be a three-digit integer (RFC 2616 Section 6.1.1)
headervalues supplied as Arrays. All keys MUST be lower-case. The
headersobject MUST NOT contain a
statuskey and MUST NOT contain key names that end in
_. It MUST contain keys that consist of letters, digits,
-and start with a letter. Header values MUST NOT contain characters below 037. Additionally:
- content-type - There MUST be a
content-typeheader key, except when the Status is 1xx, 204 or 3xx, in which case `content-type MUST NOT be present.
- content-length - There MUST NOT be
content-lengthheader key when the Status is 1xx, 204 or 3xx.
- content-type - There MUST be a
- body - A Stream representing the response body.
The application should call the following methods on the response body stream:
- write - To send data to the client, call the
writemethod, passing the data as an argument. This is optional; in cases where a response body is not appropriate, don't do it.
- close - To complete the request,
closethe output stream. This signals the end of the connection. This event is optional; in cases where it is not appropriate to terminate the connection (such as long-polling comet connections), do not call it.
- Body encoding needs to be sorted out. Right now, it's just sending everything binary, and that's not ideal for UTF-8 text.
- Use a vendor/submodule pattern for the stream lib.
- Figure out how to merge this into JSGI proper.
- Port Jack's middleware (not here, but into a separate project which will be forthcoming)
0.0.4- Add the return value from
write(), clarify the semantics of
0.0.3- More JSGI compliance. At this point, it's ready to be written up as an extension.
0.0.2- Updated to use Streams instead of direct Emitters. (Makes the name make less sense, but the code make more sense.)
0.0.1- Initial pass.