Rob Kinyon - HTTP.Request-0.03
NAME
HTTP.Request
DESCRIPTION
This provides a way to make a HTTP request in a completely browser-independent fashion, if the browser can support this kind of activity.
DEPENDENCIES
None.
CLASSES
HTTP.Request
This is the basic AJAX communicator. It does all of its work within the constructor, using callbacks as provided (if in asynchronous mode).
The constructor takes a hash of options. All option names are case-insensitive. These options are:
- method (post|get|head)
- asychronous (true|false)
- uri
- parameters
- postbody
- transport
- onUninitialized
- onLoading
- onLoaded
- onInteractive
- onComplete
- on[status] / onSuccess / onFailure
This defaults to 'post'. If any value other than 'post', 'get', or 'head' is passed, this will be set to 'post'.
This defaults to true. This determines whether or not the request is blocking.
this is the URL you wish to call. If this is set, then request() will be called at the end of the constructor. If this is not set, then you can call request() with your URI.
This is a string containing the URL parameters you wish to pass. Depending on the method, they will either be appended to the URL or sent as if they were in a form.
This is a string containing the body of the HTTP request, if the method is set to 'post'. This will default to the parameters, if not set.
This is the class that is used for the transport. It defaults to HTTP.Request.Transport and is expected to conform to its API. (q.v. below for more information.) If this is not a function, then it will be set to HTTP.Request.Transport.
This is a callback that will be called when the Uninitialized event occurs. It will only be called if the request is asynchronous.
This is a callback that will be called when the Loading event occurs. It will only be called if the request is asynchronous.
This is a callback that will be called when the Loaded event occurs. It will only be called if the request is asynchronous.
This is a callback that will be called when the Interactive event occurs. It will only be called if the request is asynchronous.
This is a callback that will be called when the Complete event occurs. It will only be called if the request is asynchronous.
These are callbacks that will be called when the Complete event occurs. If [status] is found (numeric), then that will be called. Otherwise, onSuccess will be called if this is a success (as defined by isSuccess()) or onFailure will be called.
The callback functions must be defined as so:
onComplete: function (trans) {
...
}
where 'trans' is the transport being used. This will be an object of HTTP.Request.Transport, as detailed below.
The following public methods are defined:
- getOption( name )
- setOption( name, value )
- request( [uri] )
- setRequestHeaders()
- isSuccess()
Given an option name, it will return the value that has been set, if any.
This will set the option specified by name
to value
. It will also gatekeeper certain options to make sure that their values are sane.
This will actually make the HTTP request. If you specified a uri in the constructor, this will be called at the end of the constructor. If you do not specify a URI in the function call and the URI option isn't set, then this will return without doing anything.
Note: Currently, this will append an additional parameter of '_' set to nothing to the parameter list.
This will set the request headers.
This will return back true or false depending on if the request was successful.
HTTP.Request.Transport
This is the class that actually does the tranport. In Gecko-based browsers, this is equivalent to the XMLHttpRequest class. In IE-based browsers, this is equivalent to the correct ActiveX object. In other browsers, this is an object that abstracts away the layers/iframe method that is used.
TODO
- Add support for DIV/Layers/IFRAME methods of transport
- Better error-handling
- Add user-defined headers
- Remove need for try/catch (degrade to JS 1.2 or lower, if possible)
- Tests, tests, and more tests
HTTP.Request.Transport
This is a class that is the default transport mechanism. It is set, at compile-time, to the best XMLHttpRequest option the browser supports.
If the browser does not support XMLHttpRequest or any of the ActiveXObject() variants, then an HTTP.Request.Transport class is created to transparently do IFRAME/DIV support.
If you wish to write your own transport class, you can do so and pass it in as the 'transport' option to new() or set it using setOption( "transport", My.Transport.Class )
.
Any class you provide must support the following API:
- void abort()
- string getAllResponseHeaders()
- string getResponseHeader(string headerName)
- functionRef onerror
- functionRef onload
- void open (string HTTP_Method, string URL, [boolean async, [string userName, [string password]]])
- int readyState
- UNINITIALIZED - open() has not been called yet.
- 1
- 2
- 3
- 4
- string responseText
- DOMDocument responseXML
- void send(variant body)
- void setRequestHeader (string headerName, string headerValue)
- string status
Stops the request if it is still running.
Returns all response headers as one string.
Returns the value of the specified header.
If set, this function will be called whenever an error occurs during the request.
If set, the references function will be called when the request completes successfully and the response has been received. Use when an asynchronous request is used.
Initializes the request for the specified URL, using either GET or POST as the HTTP method. To send the request, call the send() method after initialization. If async is false, the request is synchronous, else it defaults to asynchronous. Optionally, you can specify a username and password for the given URL needed.
State of the request. Possible values:
LOADING - send() has not been called yet.
LOADED - send() has been called, headers and status are available.
INTERACTIVE - Downloading, responseText holds partial data.
COMPLETED - Finished with all operations.
String containing the response.
DOM Document containing the response.
Initiates the request. If body is defined, it is sent as the body of the POST request. body can be an XML document or a string serialized XML document.
Sets an HTTP request header for use in the HTTP request. Has to be called after open() is called.
The status code of the HTTP response.
(This was taken from http://www-128.ibm.com/developerworks/web/library/wa-ie2mozgd/)
SUPPORT
Currently, there is no mailing list or IRC channel. Please send bug reports and patches to the author.
AUTHOR
Rob Kinyon (rob.kinyon@iinteractive.com)
Code taken from several sources, including those written by Sam Stephenson, Adam Kennedy, and Eric Andreychek. Many thanks to their generous help.
My time is generously donated by Infinity Interactive, Inc. http://www.iinteractive.com
POD ERRORS
Hey! The above document had some coding errors, which are explained below:
- Around line 107:
- You forgot a '=back' before '=head2'
/* */ if ( typeof( Method ) == "undefined" ) { Method = {}; } if ( typeof( Method["bind"] ) == "undefined" ) { Method.bind = function ( method, object ) { return function() { method.apply(object, arguments); } }; } /* */ if ( typeof( HTTP ) == "undefined" ) { HTTP = {}; } if ( typeof( HTTP.Request ) == "undefined" ) { HTTP.Request = function ( options ) { if ( !options ) options = {}; this.options = {}; for ( var i in options ) { this.setOption( i, options[i] ); } if ( this.getOption( "method" ) == undefined ) { this.setOption( "method", "post" ); } if ( this.getOption( "asynchronous" ) == undefined ) { this.setOption( "asynchronous", true ); } if ( this.getOption( "parameters" ) == undefined ) { this.setOption( "parameters", "" ); } if ( this.getOption( "transport" ) == undefined ) { this.setOption( "transport", HTTP.Request.Transport ); } if ( this.getOption( "uri" ) ) this.request(); }; HTTP.Request.EventNames = [ "uninitialized" ,"loading" ,"loaded" ,"interactive" ,"complete" ]; /* */ HTTP.Request.prototype.getOption = function( name ) { if ( typeof( name ) != "string" ) { return; } return this.options[name.toLowerCase()]; }; /* */ HTTP.Request.prototype.setOption = function( name, value ) { if ( typeof( name ) != "string" ) { return; } name = name.toLowerCase(); this.options[name] = value; if ( name == "method" ) { if ( ! ( this.options.method == "get" || this.options.method == "post" || this.options.method == "head" ) ) { this.options.method = "post"; } } if ( name == "transport" ) { if ( typeof( value ) != "function" ) { this.options.transport = HTTP.Request.Transport; } } }; /* */ HTTP.Request.prototype.request = function ( uri ) { if ( ! uri ) uri = this.getOption( "uri" ); if ( ! uri ) return; var parameters = this.getOption( "parameters" ); // XXX Why? if (parameters.length > 0) parameters += "&_="; var method = this.getOption( "method" ); if ( method == "get" ) { uri += "?" + parameters; } this.transport = new (this.getOption( "transport" ))(); var async = this.getOption( "asynchronous" ); this.transport.open( method ,uri ,async ); if ( async ) { this.transport.onreadystatechange = Method.bind( this.onStateChange, this ); setTimeout( Method.bind( function() { this.respondToReadyState(1) } ,this ) ,10 ); } this.setRequestHeaders(); if ( method == "post" ) { var body = this.getOption( "postbody" ); if ( ! body ) body = parameters; this.transport.send( body ); } else { this.transport.send( null ); } }; /* */ HTTP.Request.prototype.setRequestHeaders = function() { this.transport.setRequestHeader( "X-Requested-With", "HTTP.Request" ); this.transport.setRequestHeader( "X-HTTP-Request-Version", HTTP.Request.VERSION ); if (this.getOption( "method" ) == "post") { this.transport.setRequestHeader( "Content-type", "application/x-www-form-urlencoded" ); /* Force "Connection: close" for Mozilla browsers to work around * a bug where XMLHttpReqeuest sends an incorrect Content-length * header. See Mozilla Bugzilla #246651. */ if (this.transport.overrideMimeType) { this.transport.setRequestHeader( "Connection", "close" ); } } /* TODO Add support for this back in later if (this.options.requestHeaders) requestHeaders.push.apply(requestHeaders, this.options.requestHeaders); */ }; /* */ // XXX This confuses me a little ... how are undefined and 0 considered a success? HTTP.Request.prototype.isSuccess = function () { return this.transport.status == undefined || this.transport.status == 0 || (this.transport.status >= 200 && this.transport.status < 300); }; HTTP.Request.prototype.onStateChange = function() { var readyState = this.transport.readyState; if (readyState != 1) { this.respondToReadyState( this.transport.readyState ); } }; HTTP.Request.prototype.respondToReadyState = function( readyState ) { var event = HTTP.Request.EventNames[readyState]; if (event == "complete") { var func = this.getOption( "on" + this.transport.status ); if ( ! func ) { if ( this.isSuccess() ) { func = this.getOption( "onsuccess" ); } else { func = this.getOption( "onfailure" ); } } if ( func ) { ( func )( this.transport ); } } if ( this.getOption( "on" + event ) ) ( this.getOption( "on" + event ) )( this.transport ); /* Avoid memory leak in MSIE: clean up the oncomplete event handler */ if (event == "complete") { this.transport.onreadystatechange = function (){}; } }; HTTP.Request.VERSION = 0.03; } /* */ if ( typeof( HTTP.Request.Transport ) == "undefined" ) { if ( window.XMLHttpRequest ) { HTTP.Request.Transport = window.XMLHttpRequest; } // This tests for ActiveXObject in IE5+ else if ( window.ActiveXObject && window.clipboardData ) { var msxmls = new Array( "Msxml2.XMLHTTP.5.0" ,"Msxml2.XMLHTTP.4.0" ,"Msxml2.XMLHTTP.3.0" ,"Msxml2.XMLHTTP" ,"Microsoft.XMLHTTP" ); for ( var i = 0; i < msxmls.length; i++ ) { try { new ActiveXObject(msxmls[i]); HTTP.Request.Transport = function () { return new ActiveXObject(msxmls[i]); }; break; } catch(e) { } } } if ( typeof( HTTP.Request.Transport ) == "undefined" ) { // This is where we add DIV/IFRAME support masquerading as an XMLHttpRequest object } if ( typeof( HTTP.Request.Transport ) == "undefined" ) { throw new Error("Unable to locate XMLHttpRequest or other HTTP transport mechanism"); } } /* */