HTTP Client/B
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()
Contents
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 :
- mostly already known
- already described in lot of JavaScript, Ajax, and Web books and Web sites
- already documented as W3C recommendations (XMLHttpRequest & XMLHttpRequest Level 2)
"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:
- If the state is not #OPENED raise an #INVALID_STATE_ERR exception and terminate these steps.
- If the
send()
flag is true raise an #INVALID_STATE_ERR exception and terminate these steps. - 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:
- If the state is not #OPENED raise an #INVALID_STATE_ERR exception and terminate these steps.
- If the send() flag is true raise an #INVALID_STATE_ERR exception and terminate these steps.
- 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:
- If the state is #UNSENT or #OPENED raise an #INVALID_STATE_ERR exception and terminate these steps.
- If the error flag is true return 0 and terminate these steps.
- Return the HTTP status code.
statusText
Read-Only.
String.
The statusText
property must return the result of running these steps:
- If the state is #UNSENT or #OPENED raise an #INVALID_STATE_ERR exception and terminate these steps.
- If the error flag is true return the empty string and terminate these steps.
- Return the HTTP status text.
responseBody
Read-Only.
ByteArray | null.
The responseBody
property, on getting, must return the result of running the following steps:
- If the state is not #LOADING or #DONE return
null
and terminate these steps. - Return a
ByteArray
object representing the response entity body or returnnull
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:
- If the state is not #LOADING or #DONE, return the empty string and terminate these steps.
- 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:
- If DOM is not implemented and no Document nor HTMLDocument constructors have been provided, return
null
and terminate these steps. - If the state is not #DONE, return
null
and terminate these steps. - If the response entity body is null, return
null
and terminate these steps. - If final MIME type is not null,
text/html
,text/xml
,application/xml
, and does not end in+xml
, returnnull
and terminate these steps. - If final MIME type is
text/html
orapplication/xhtml+xml
, let document be an object implementing theHTMLDocument
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. - 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), returnnull
and terminate these steps. - 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:
- If the state is not #DONE, return
null
and terminate these steps. - If the response entity body is null, return
null
and terminate these steps. - If final MIME type is not null,
text/json
,application/json
,application/json-rpc
,application/jsonrequest
and does not end in+json
, returnnull
and terminate these steps. - 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. - 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):
- 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.
- If method is an ASCII case-insensitive match for
CONNECT
,DELETE
,GET
,HEAD
,OPTIONS
,POST
,PUT
,TRACE
, orTRACK
, convert method to ASCII uppercase.If it does not match any of the above, it is passed through literally, including in the final request. - Let url be a URL and its character encoding be UTF-8.
- 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.
- Drop <fragment> from url (servers don't expect to receive a fragment like "#here" in URLs)
- If url contains an unsupported <scheme> (like "ftp://") raise a #NOT_SUPPORTED_ERR and terminate these steps.
- 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.
- If url contains the "user:password" format let temp user be the user part and temp password be the password part.
- If url just contains the "user" format let temp user be the user part.
- Let async be the value of the async argument
- 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)- 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.
- If user is null let temp user be null.
- Otherwise let temp user be user.
- 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)- 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.
- If password is null let temp password be null.
- Otherwise let temp password be password.
- Abort the
#send()
algorithm (if#send()
was called before this#open()
invocation) - The implementation should cancel any network activity for which the object is responsible.
- Set variables associated with the object as follows:
- Set the send() flag to false.
- Set response entity body to null.
- Empty the list of author request headers.
- Set the request method to method.
- Set the request URL to url.
- Set the request username to temp user.
- Set the request password to temp password.
- Set the asynchronous flag to true if async is true. Otherwise set it to false.
- Switch the the state to #OPENED
- 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):
- If the state is not #OPENED raise an #INVALID_STATE_ERR exception and terminate these steps.
- If the send() flag is true raise an #INVALID_STATE_ERR exception and terminate these steps.
- 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.
- 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)
- 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 fromXMLHttpRequest
. 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 forSec-
(including when header is justSec-
), raise an #INVALID_STATE_ERR exception and terminate these steps.-
Accept-Encoding
(it is implementation specific) -
Connection
(it is generated by the#send()
method, should be customizable via a keepAlive setting) -
Content-Length
(it is fixed by from the length of the data parameter of the#send()
method) -
Content-Transfer-Encoding
(it is generated by the#send()
method) -
Host
(it is fixed by from URL parameter of the#open()
method) -
Keep-Alive
(it is generated by the#send()
method, should be customizable via a keepAlive setting) -
TE
(it is generated by the#send()
method) -
Transfer-Encoding
(it is generated by the#send()
method) -
Upgrade
-
- If header is not in the author request headers list append header with its associated value to the list and return the XMLHttpRequest instance.
- 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)
- 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.
- If the state is not #OPENED raise an #INVALID_STATE_ERR exception and terminate these steps.
- If the send() flag is true raise an #INVALID_STATE_ERR exception and terminate these steps.
- If stored method is
GET
,HEAD
, orTRACE
, act as if the data argument is null. - If the data argument has not been omitted and is not null use it for the entity body observing the following rules:
- data is a
ByteArray
: This will be done in terms of the File API in due course. Use data literally for transmission. - data is a String:
- 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.
- if a charset is defined in the
- data is a
Document
(meaning DOM is supported):- Let tempdata be the result of getting the innerHTML attribute on the data object and encode it using
data.inputEncoding
or UTF-8 ifdata.inputEncoding
is null. Re-raise any exception this raises. - If the document cannot be serialized an #INVALID_STATE_ERR exception is raised.
- Let data be tempdata.
- If no
Content-Type
header has been set using#setRequestHeader()
set aContent-Type
request header with a value ofapplication/xml;charset=charset
wherecharset
is the encoding used to encode the document. - Subsequent changes to the document have no effect on what is submitted.
- Let tempdata be the result of getting the innerHTML attribute on the data object and encode it using
- data is an Object (neither
ByteArray
orDocument
):- 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. - If the object cannot be serialized an #INVALID_STATE_ERR exception is raised.
- Let data be tempdata.
- If no
Content-Type
header has been set using#setRequestHeader()
set aContent-Type
request header with a value ofapplication/json;charset=UTF-8
- Subsequent changes to the object have no effect on what is submitted.
- Let tempdata be the result of computing a JSON stringify on the data object and encode it with the charset defined in the
- If the data argument has been omitted, or is null, no entity body is used in the request.
- data is a
- If the asynchronous flag is false release the storage mutex.
- Set the error flag to false.
- If the asynchronous flag is true run these substeps:
- Set the send() flag to true.
- Dispatch a
readystatechange
event. (The state does not change. The event is dispatched for historical reasons.) - 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):
- Abort the send() algorithm
- The implementation SHOULD cancel any network activity for which the object is responsible.
- Set the response entity body to null.
- Set the error flag to true.
- Empty the list of author request headers.
- If the state is #UNSENT, #OPENED with the
send()
flag being false, or #DONE go to the next step. Otherwise run these substeps:- Switch the state to #DONE.
- Set the send() flag to false.
- Dispatch a
readystatechange
event.
- 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:
- If the state is #UNSENT or #OPENED raise an #INVALID_STATE_ERR exception and terminate these steps.
- If the header argument does not match a valid field-name syntax (section 4.2 of RFC 2616), return null and terminate these steps.
- If the error flag is true return null and terminate these steps.
- 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.
- If header is an ASCII case-insensitive match for a single HTTP response header, return the value of that header and terminate these steps.
- 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:
- If the state is #UNSENT or #OPENED raise an #INVALID_STATE_ERR exception and terminate these steps.
- If the error flag is true return the empty string and terminate these steps.
- 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:
- If parsing mime analogously to the value of the
Content-Type
headers fails raise a #SYNTAX_ERR exception and abort this algorithm. - If a MIME type (without any parameters) is successfully parsed set override MIME type to that MIME type.
- 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.