HTTP Client/B

From CommonJS Spec Wiki
Jump to: navigation, search

STATUS: DISCUSSED, SOME IMPLEMENTATIONS

Implementations
[[Implementations/Wakanda|]]

Proposal B, Draft 1

Status: Proposal

An HTTP Client module which might be called "xhr" exposes an XMLHttpRequest constructor that creates an xhr object.

It is designed to be useable with Object.create()


Introduction

The purpose to this proposal is to provide an interface which would be:

  • compatible with lot of existing JavaScript libraries and scripts
  • very easy to learn because :


"XML" in "XMLHttpRequest" is only used for historical compatibility. An alias could be "HttpRequest". The main difference between XMLHttpRequest and HttpRequest would then be the value returned by the toString() method.

For better user experience while using this API, It would be recommended:

  • to be abble to set default values of the open() method as settings
  • to let the constructor return an instance without the "new" operator
  • to allow chaining


Example of intuitive use:

var HttpRequest = require("xhr").XMLHttpRequest;
var settings = HttpRequest.defaultSettings;
var cookies = {};

// these are some proposed setting properties
settings.async = false;
settings.cookies = cookies;
settings.host = "http://www.foo.com/";

var client = HttpRequest();

// as these requests are synchronous, open() doesn't abort previous send()
var txt = client.open("GET", "license.txt").send().responseText;
var obj = client.open("GET", "data.json").send().responseObject;
var doc = client.open("GET", "doc.html").send().responseXML;
var img = client.open("GET", "img.jpg").send().responseBody;
...
// be aware that obj and doc may be set to null if the requests failed

NOTE: the upload property and the XMLHttpRequestUpload, as primarly used for DOM elements, are not defined in this proposal. It may be defined if an implementation wants to set upload progress properties to a server component (like a folder).

Constructor: XMLHttpRequest(settings)

If called as a simple function, then return a new XMLHttpRequest(settings).

The settings parameter can be an object or undefined

  • The settings parameter can have a "custom" property which is an object.
    • Each implementor may define its own plate-form dependent object to carry its own specific settings
  • Some standard setting properties may be defined later once a consensus is done regarding settings defined in plate-form dependent objects


Some suggested setting properties are:

  • acceptType: String. Default: "*/*"
  • async: Boolean. Default: true
  • authType: String. Default: "Basic"
  • body: String, ByteArray, Document, Object, or null. Default: null
  • contentCharset: String or null. Default: null
  • contentType: String or null. Default: null
  • cookies
  • dom: Object or null. Provide the Document and/or HTMLDocument constructors in an object if available
  • headers: String or null
  • host: String or null. Would be used as the base URL
  • jsonHandler: Function or null. Used by JSON.parse for responseObject
  • keepAlive: String or null. Default: "115"
  • maxRedirects: Number or null
  • proxy: String, Location, or null.
  • timeout: Number.
  • user: String or null
  • password: String or null

These ones would break the standard minimal open() signature:

  • method: String or null.
  • url
  • location: a BOM like Location object


Another constructor named HttpRequest, with the same properties and behaviors could also be available.

Constructor properties

prototype

The constructor has a prototype property described as bellow


Prototype Read Only properties

UNSENT

Read-Only.

Its value is the number 0.

This represent the original value of the #readyState property of XMLHttpRequest instances.

OPENED

Read-Only.

Its value is the number 1.

This represent the value of the #readyState property of XMLHttpRequest instances once its #open() method has been successfully invoked.

When an instance is in this state, the #setRequestHeader() method can be used to fix HTTP header field values.

HEADERS_RECEIVED

Read-Only.

Its value is the number 2.

This represent the value of the #readyState property of XMLHttpRequest instances once all HTTP headers have been received.

When an instance is in this state, the informations returned by these properties and methods are reliable:

Properties

Methods


Some scripts may wait this state when an Expect: 101-continue header were set in the request to send big content or chuncked data.

LOADING

Read-Only.

Its value is the number 3.

This represent the value of the #readyState property of XMLHttpRequest instances once the HTTP body started to be received.

This state is interesting to read a response while it is being received. It would be useful but requires the support of #responseStream and/or #responseTextStream properties

DONE

Read-Only.

Its value is the number 4.

This represent the value of the #readyState property of XMLHttpRequest instances once the HTTP body is fully received.

defaultSettings

The constructor MAY have a defaultSettings object property which properties would be Read-Write and accept the same settings as the ones defined for the constructor. Any setting fixed in this constructor property is used by all future instances if not overridden by a different value fixed in the constructor parameter.


Prototype Event Handler properties

Quite all Client-Side JavaScript libraries are based on asynchronous requests.

When implementing XMLHttpRequest on server-side, the implementor can either:

  • support asynchronous execution
  • simulated asynchronous execution, blocking the execution and calling the handlers each time the state of the request change


onreadystatechange

A onreadystatechange event handler attribute handling the readystatechange event MUST be supported. The handler is called each time the value of the #readyState constructor property change.


Prototype properties

constructor

Read-Only.

The #prototype property SHOULD have a constructor property which default value is the XMLHttpRequest constructor.

If an HttpRequest constructor is provided as well, its prototype should have a constructor set to HttpRequest.

timeout

Read-Write.

The amount of milliseconds a request can take before being terminated. By default this is 0 and has no observable effect.

On setting the timeout property these steps must be run:

  1. If the state is not #OPENED raise an #INVALID_STATE_ERR exception and terminate these steps.
  2. If the send() flag is true raise an #INVALID_STATE_ERR exception and terminate these steps.
  3. Set request timeout to the given value.

On getting, the timeout property must return the value of the request timeout.

(from XMLHttpRequest Level 2 W3C Editor's Working Draft)


readyState

Read-Only.

Its default value is 0 (#UNSENT).

This instance property value is changed automatically to 1 (#OPENED), 2 (#HEADERS_RECEIVED), 3 (#LOADING), and 4 (#DONE).

These changes can be handled by function set to the #onreadystatechange property.


withCredentials

Read-Write.

Boolean. The default value is false.

The withCredentials property controls the credentials flag which indicates whether a cross-origin request will include cookie and HTTP authentication data and whether cookies can be set.

It should be supported/simulated for compatibility with client libraries

On getting, the withCredentials attribute must return the value of the credentials flag.

This property can be set once #open() has been called, until #send() is called.

When the withCredentials property is set, the implementation MUST run these steps:

  1. If the state is not #OPENED raise an #INVALID_STATE_ERR exception and terminate these steps.
  2. If the send() flag is true raise an #INVALID_STATE_ERR exception and terminate these steps.
  3. If the given value is true, set the credentials flag to true. For any other value, set the credentials flag to false.

status

Read-Only.

Number.

The status property must return the result of running these steps:

  1. If the state is #UNSENT or #OPENED raise an #INVALID_STATE_ERR exception and terminate these steps.
  2. If the error flag is true return 0 and terminate these steps.
  3. Return the HTTP status code.


statusText

Read-Only.

String.

The statusText property must return the result of running these steps:

  1. If the state is #UNSENT or #OPENED raise an #INVALID_STATE_ERR exception and terminate these steps.
  2. If the error flag is true return the empty string and terminate these steps.
  3. Return the HTTP status text.


responseBody

Read-Only.

ByteArray | null.

The responseBody property, on getting, must return the result of running the following steps:

  1. If the state is not #LOADING or #DONE return null and terminate these steps.
  2. Return a ByteArray object representing the response entity body or return null if the response entity body is null.


responseText

Read-Only.

String. It's default value is the empty string.

The responseText property, on getting, must return the result of running the following steps:

  1. If the state is not #LOADING or #DONE, return the empty string and terminate these steps.
  2. Return the text response entity body.

responseXML

Read-Only.

Document | null. It's default value is null.

The responseXML property has XML in its name for historical reasons. It also returns documents created using an HTML parser.

The responseXML property, on getting, must return the result of running the following steps:

  1. If DOM is not implemented and no Document nor HTMLDocument constructors have been provided, return null and terminate these steps.
  2. If the state is not #DONE, return null and terminate these steps.
  3. If the response entity body is null, return null and terminate these steps.
  4. If final MIME type is not null, text/html, text/xml, application/xml, and does not end in +xml, return null and terminate these steps.
  5. If final MIME type is text/html or application/xhtml+xml, let document be an object implementing the HTMLDocument interface that represents the response entity body parsed following the rules set forth in the HTML specification for an HTML parser with scripting disabled and then terminate this algorithm.
  6. Otherwise, let document be an object implementing the Document interface that represents the result of parsing the response entity body into a document tree following the rules from the XML specifications. If this fails (unsupported character encoding, namespace well-formedness error et cetera), return null and terminate these steps.
  7. Return document.


responseObject

Read-Only.

Object | null. It's default value is null.

The responseObject property, on getting, must return the result of running the following steps:

  1. If the state is not #DONE, return null and terminate these steps.
  2. If the response entity body is null, return null and terminate these steps.
  3. If final MIME type is not null, text/json, application/json, application/json-rpc, application/jsonrequest and does not end in +json, return null and terminate these steps.
  4. Otherwise, let object be the result of a JSON parse on the response entity body following the rules from the JSON specification. If this fails, return null and terminate these steps.
  5. Return the resulting Object.


responseStream

Read-Only.

Reserved for a binary Stream object


responseTextStream

Read-Only.

Reserved for a text Stream object


upload

Read-Only

This property should return null if XMLHttpRequestUpload is not supported

Prototype methods

open()

open(method:String, url:String[, async:Boolean[, user:String[, password:String]]])

Parameters description:

  • method: The method used in the request (ex: "POST", "GET", "PUT", ...)
  • url: The URL used in the request (ex: "http://wiki.commonjs.org/")
  • async: A flag that is either true or false that indicates whether the request is done asynchronously (default: true)
  • user: The username used in the request (default: null)
  • password: The password used in the request (default: null)


When the open(method, url, async, user, password) method is invoked, the implementation must run these steps (unless otherwise indicated):

  1. If method is not a valid token as defined in RFC 2616 section 5.1.1, raise a #SYNTAX_ERR exception and terminate these steps.
  2. If method is an ASCII case-insensitive match for CONNECT, DELETE, GET, HEAD, OPTIONS, POST, PUT, TRACE, or TRACK, convert method to ASCII uppercase.
    If it does not match any of the above, it is passed through literally, including in the final request.
  3. Let url be a URL and its character encoding be UTF-8.
  4. If the implementation is an HTTP server, it MAY resolve relative URLs to its host (or virtual host) hostname setting.
    If the algorithm returns an error, raise a #SYNTAX_ERR exception and terminate these steps.
  5. Drop <fragment> from url (servers don't expect to receive a fragment like "#here" in URLs)
  6. If url contains an unsupported <scheme> (like "ftp://") raise a #NOT_SUPPORTED_ERR and terminate these steps.
  7. If the "user:password" format is not supported for the relevant scheme and url contains this format raise a #SYNTAX_ERR and terminate these steps.
  8. If url contains the "user:password" format let temp user be the user part and temp password be the password part.
  9. If url just contains the "user" format let temp user be the user part.
  10. Let async be the value of the async argument
  11. If the user argument was not omitted follow these sub steps:
    These steps override anything that may have been set by the url argument (user parameter of the #open() method override the potential one defined in the URL)
    1. If the syntax of user does not match the syntax specified by the relevant authentication scheme, raise a #SYNTAX_ERR exception and terminate the overall set of steps.
    2. If user is null let temp user be null.
    3. Otherwise let temp user be user.
  12. If the password argument was not omitted follow these sub steps:
    These steps override anything that may have been set by the url argument (password parameter of the #open() method override the potential one defined in the URL)
    1. If the syntax of password does not match the syntax specified by the relevant authentication scheme, raise a #SYNTAX_ERR exception and terminate the overall set of steps.
    2. If password is null let temp password be null.
    3. Otherwise let temp password be password.
  13. Abort the #send() algorithm (if #send() was called before this #open() invocation)
  14. The implementation should cancel any network activity for which the object is responsible.
  15. Set variables associated with the object as follows:
    1. Set the send() flag to false.
    2. Set response entity body to null.
    3. Empty the list of author request headers.
    4. Set the request method to method.
    5. Set the request URL to url.
    6. Set the request username to temp user.
    7. Set the request password to temp password.
    8. Set the asynchronous flag to true if async is true. Otherwise set it to false.
  16. Switch the the state to #OPENED
  17. Dispatch a readystatechange event

setRequestHeader()

setRequestHeader(header:String, value:String)

As indicated in the algorithm below certain headers cannot be set and are left up to the implementation. In addition there are certain other headers the implementation will take control of if they are not set by the author as indicated at the end of the #send() method section.

The setRequestHeader() method appends a value if the HTTP header given as argument is already part of the author request headers list.

When the setRequestHeader(header, value) method is invoked, the implementation must run these steps (unless otherwise indicated):

  1. If the state is not #OPENED raise an #INVALID_STATE_ERR exception and terminate these steps.
  2. If the send() flag is true raise an #INVALID_STATE_ERR exception and terminate these steps.
  3. If the header argument does not match a valid field-name syntax (section 4.2 of RFC 2616) raise a #SYNTAX_ERR exception and terminate these steps.
  4. If the value argument does not match a valid field-value syntax (section 4.2 of RFC 2616) raise a #SYNTAX_ERR exception and terminate these steps. (The empty string is legal and represents the empty header value)
  5. The following headers are not allowed to be set as they are better controlled by the implementation as it knows best what value they should have. Header names starting with Sec- are not allowed to be set to allow new headers to be minted in the future that are guaranteed not to come from XMLHttpRequest. If header is an ASCII case-insensitive match for one of the following headers, or if the start of header is an ASCII case-insensitive match for Sec- (including when header is just Sec-), raise an #INVALID_STATE_ERR exception and terminate these steps.
    1. Accept-Encoding (it is implementation specific)
    2. Connection (it is generated by the #send() method, should be customizable via a keepAlive setting)
    3. Content-Length (it is fixed by from the length of the data parameter of the #send() method)
    4. Content-Transfer-Encoding (it is generated by the #send() method)
    5. Host (it is fixed by from URL parameter of the #open() method)
    6. Keep-Alive (it is generated by the #send() method, should be customizable via a keepAlive setting)
    7. TE (it is generated by the #send() method)
    8. Transfer-Encoding (it is generated by the #send() method)
    9. Upgrade
  6. If header is not in the author request headers list append header with its associated value to the list and return the XMLHttpRequest instance.
  7. If header is in the author request headers list either use multiple headers, combine the values or use a combination of those (section 4.2, RFC 2616)
  8. Return the XMLHttpRequest instance to provide chaining

See also the #send() method regarding implementation header handling for caching, authentication, proxies, and cookies.

The following script:

var client = new XMLHttpRequest();
client.open('GET', 'demo.cgi');
client.setRequestHeader('X-Test', 'one');
client.setRequestHeader('X-Test', 'two');
client.send();

Would result in the following header being sent:

...
X-Test: one, two
...

send()

send([data:(Document|String|ByteArray|Object)[, complete:Boolean]])

The send(data, complete) method initiates the request

Arguments description:

  • data: entity body for the request (default: null)
  • complete: If false, the request's body is chunked and will be complete only once #send() is called with complete set to true (default: true)


Authors are encouraged to ensure that they have specified the Content-Type header via #setRequestHeader() before invoking #send() with a non-null data argument.

When invoked, the implementation must run these steps (unless otherwise noted). This algorithm might get aborted if the #open() or #abort() method is invoked. When the #send() algorithm is aborted the user agent must terminate the algorithm after finishing the step it is on.

The #send() algorithm can only be aborted when async is true (i.e., the request is done asynchronously) and only after the method call has returned.

  1. If the state is not #OPENED raise an #INVALID_STATE_ERR exception and terminate these steps.
  2. If the send() flag is true raise an #INVALID_STATE_ERR exception and terminate these steps.
  3. If stored method is GET, HEAD, or TRACE, act as if the data argument is null.
  4. If the data argument has not been omitted and is not null use it for the entity body observing the following rules:
    1. data is a ByteArray: This will be done in terms of the File API in due course. Use data literally for transmission.
    2. data is a String:
      1. if a charset is defined in the Content-Type header field: Encode data using it for transmission, otherwise, encode data using UTF-8 for transmission.
    3. data is a Document (meaning DOM is supported):
      1. Let tempdata be the result of getting the innerHTML attribute on the data object and encode it using data.inputEncoding or UTF-8 if data.inputEncoding is null. Re-raise any exception this raises.
      2. If the document cannot be serialized an #INVALID_STATE_ERR exception is raised.
      3. Let data be tempdata.
      4. If no Content-Type header has been set using #setRequestHeader() set a Content-Type request header with a value of application/xml;charset=charset where charset is the encoding used to encode the document.
      5. Subsequent changes to the document have no effect on what is submitted.
    4. data is an Object (neither ByteArray or Document):
      1. Let tempdata be the result of computing a JSON stringify on the data object and encode it with the charset defined in the Content-Type or UTF-8 if not defined. Re-raise any exception this raises.
      2. If the object cannot be serialized an #INVALID_STATE_ERR exception is raised.
      3. Let data be tempdata.
      4. If no Content-Type header has been set using #setRequestHeader() set a Content-Type request header with a value of application/json;charset=UTF-8
      5. Subsequent changes to the object have no effect on what is submitted.
    5. If the data argument has been omitted, or is null, no entity body is used in the request.
  5. If the asynchronous flag is false release the storage mutex.
  6. Set the error flag to false.
  7. If the asynchronous flag is true run these substeps:
    1. Set the send() flag to true.
    2. Dispatch a readystatechange event. (The state does not change. The event is dispatched for historical reasons.)
    3. Return the #send() method call, but continue running the steps in this algorithm.


If request timeout is not 0 and since the request started the amount of milliseconds specified by request timeout has passed before the response is fully received, raise a #TIMEOUT_ERR exception and terminate these steps.

If the implementation allows the end user to configure a proxy it should modify the request appropriately; i.e., connect to the proxy host instead of the origin server, modify the Request-Line and send Proxy-Authorization headers as specified.


If the user agent supports HTTP Authentication and Authorization is not in the list of author request headers, it should consider requests originating from the XMLHttpRequest object to be part of the protection space that includes the accessed URIs and send Authorization headers and handle 401 Unauthorized response appropriately. If authentication fails the implementation must raise an #UNAUTHORIZED_ERR exception.


If the implementation supports HTTP State Management it should persist, discard and send cookies (as received in the Set-Cookie and Set-Cookie2 response headers, and sent in the Cookie header) as applicable. For compatibility with Client-side JavaScript libraries, it is recommended to provide a document object with a cookie property as available in browsers.

If the implementation implements a HTTP client cache it should respect Cache-Control request headers set by the #setRequestHeader() (e.g., Cache-Control: no-cache bypasses the cache). It must not send Cache-Control or Pragma request headers automatically unless the end user explicitly requests such behavior.


For 304 Not Modified responses that are a result of an implementation generated conditional request the user agent must act as if the server gave a 200 OK response with the appropriate content. The implementation must allow #setRequestHeader() to override automatic cache validation by setting request headers (e.g.,If-None-Match, If-Modified-Since), in which case 304 Not Modified responses must be passed through.


If the implementations support server-driven content-negotiation it should set Accept-Encoding and Accept-Charset headers as appropriate. Unless set through #setRequestHeader() implementations should set the Accept and Accept-Language headers as well. Responses must have the content-encodings automatically decoded.


Besides the author request headers implementation should not include additional request headers other than those mentioned above or other than those authors are not allowed to set using #setRequestHeader(). This ensures that authors have a reasonably predictable API.

abort()

When the abort() method is invoked, the implementation must run these steps (unless otherwise noted):

  1. Abort the send() algorithm
  2. The implementation SHOULD cancel any network activity for which the object is responsible.
  3. Set the response entity body to null.
  4. Set the error flag to true.
  5. Empty the list of author request headers.
  6. If the state is #UNSENT, #OPENED with the send() flag being false, or #DONE go to the next step. Otherwise run these substeps:
    1. Switch the state to #DONE.
    2. Set the send() flag to false.
    3. Dispatch a readystatechange event.
  7. Switch the state to #UNSENT.

No readystatechange event is dispatched.

getResponseHeader()

getResponseHeader(header:String)

When the getResponseHeader(header) is invoked, the implementation must run these steps:

  1. If the state is #UNSENT or #OPENED raise an #INVALID_STATE_ERR exception and terminate these steps.
  2. If the header argument does not match a valid field-name syntax (section 4.2 of RFC 2616), return null and terminate these steps.
  3. If the error flag is true return null and terminate these steps.
  4. If header is an ASCII case-insensitive match for multiple HTTP response headers, return the values of these headers as a single concatenated string separated from each other by a U+002C COMMA U+0020 SPACE character pair and terminate these steps.
  5. If header is an ASCII case-insensitive match for a single HTTP response header, return the value of that header and terminate these steps.
  6. Return null.

// The following script:

var client = new XMLHttpRequest();
client.open("GET", "test.txt", true);
client.send();
client.onreadystatechange = function() {
    if (this.readyState == XMLHttpRequest.HEADERS_RECEIVED) {
        print(client.getResponseHeader("Content-Type"));
    }
}

// ...should output something similar to the following text:

Content-Type: text/plain; charset=utf-8

getAllResponseHeaders()

When the getAllResponseHeaders() method is invoked, the implementation must run the following steps:

  1. If the state is #UNSENT or #OPENED raise an #INVALID_STATE_ERR exception and terminate these steps.
  2. If the error flag is true return the empty string and terminate these steps.
  3. Return all the HTTP headers as a single string, with each header line separated by a U+000D CR U+000A LF pair, and with each header name and header value separated by a U+003A COLON U+0020 SPACE pair.


// The following script:

var client = new XMLHttpRequest();
client.open("GET", "test.txt", true);
client.send();
client.onreadystatechange = function() {
 if(this.readyState == XMLHttpRequest.HEADERS_RECEIVED) {
  print(this.getAllResponseHeaders());
 }
}

// ...should output something similar to the following text:

Date: Sun, 24 Oct 2004 04:58:38 GMT
Server: Apache/1.3.31 (Unix)
Keep-Alive: timeout=15, max=99
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/plain; charset=utf-8

overrideMimeType()

overrideMimeType(mime:String)

When the overrideMimeType(mime) method is invoked, the user implementation run the following steps:

  1. If parsing mime analogously to the value of the Content-Type headers fails raise a #SYNTAX_ERR exception and abort this algorithm.
  2. If a MIME type (without any parameters) is successfully parsed set override MIME type to that MIME type.
  3. If a charset parameter is successfully parsed set override charset to its value.

toString()

If the constructor is XMLHttpRequest, the returned value must be "[object XMLHttpRequest]".

If the constructor is HttpRequest, the returned value must be "[object HttpRequest]".


Exceptions

This API requires to send Exceptions which error code can be defined in DOM. Here are the description of these required Exceptions and their error code value:

Exceptions of DOM Level 2 Core

This API requires new Exceptions defined in the W3C Editor's Draft of XMLHttpRequest Level 2

Exceptions of the W3C Editor's Draft of XMLHttpRequest Level 2

NOT_SUPPORTED_ERR

Code value: 9

This is a DOM level 1 error meaning:

"The implementation does not support the requested type of object or operation."

INVALID_STATE_ERR

Code value: 11

This is a DOM level 2 error meaning:

"An attempt is made to use an object that is not, or is no longer, usable."

SYNTAX_ERR

Code value: 12

This is a DOM level 2 error meaning:

"An invalid or illegal string is specified."

TIMEOUT_ERR

Code value: 23

This is a XMLHttpRequest Level 2 error meaning:

"The author specified timeout has passed before the request could complete in synchronous requests."

This exception will be folded into an update of DOM Level 3 Core in due course, as they are appropriate for other API specifications as well.