| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736 | 
							- /**
 
-  * XmlHttpRequest implementation that uses TLS and flash SocketPool.
 
-  *
 
-  * @author Dave Longley
 
-  *
 
-  * Copyright (c) 2010-2013 Digital Bazaar, Inc.
 
-  */
 
- var forge = require('./forge');
 
- require('./socket');
 
- require('./http');
 
- /* XHR API */
 
- var xhrApi = module.exports = forge.xhr = forge.xhr || {};
 
- (function($) {
 
- // logging category
 
- var cat = 'forge.xhr';
 
- /*
 
- XMLHttpRequest interface definition from:
 
- http://www.w3.org/TR/XMLHttpRequest
 
- interface XMLHttpRequest {
 
-   // event handler
 
-   attribute EventListener onreadystatechange;
 
-   // state
 
-   const unsigned short UNSENT = 0;
 
-   const unsigned short OPENED = 1;
 
-   const unsigned short HEADERS_RECEIVED = 2;
 
-   const unsigned short LOADING = 3;
 
-   const unsigned short DONE = 4;
 
-   readonly attribute unsigned short readyState;
 
-   // request
 
-   void open(in DOMString method, in DOMString url);
 
-   void open(in DOMString method, in DOMString url, in boolean async);
 
-   void open(in DOMString method, in DOMString url,
 
-             in boolean async, in DOMString user);
 
-   void open(in DOMString method, in DOMString url,
 
-             in boolean async, in DOMString user, in DOMString password);
 
-   void setRequestHeader(in DOMString header, in DOMString value);
 
-   void send();
 
-   void send(in DOMString data);
 
-   void send(in Document data);
 
-   void abort();
 
-   // response
 
-   DOMString getAllResponseHeaders();
 
-   DOMString getResponseHeader(in DOMString header);
 
-   readonly attribute DOMString responseText;
 
-   readonly attribute Document responseXML;
 
-   readonly attribute unsigned short status;
 
-   readonly attribute DOMString statusText;
 
- };
 
- */
 
- // readyStates
 
- var UNSENT = 0;
 
- var OPENED = 1;
 
- var HEADERS_RECEIVED = 2;
 
- var LOADING = 3;
 
- var DONE = 4;
 
- // exceptions
 
- var INVALID_STATE_ERR = 11;
 
- var SYNTAX_ERR = 12;
 
- var SECURITY_ERR = 18;
 
- var NETWORK_ERR = 19;
 
- var ABORT_ERR = 20;
 
- // private flash socket pool vars
 
- var _sp = null;
 
- var _policyPort = 0;
 
- var _policyUrl = null;
 
- // default client (used if no special URL provided when creating an XHR)
 
- var _client = null;
 
- // all clients including the default, key'd by full base url
 
- // (multiple cross-domain http clients are permitted so there may be more
 
- // than one client in this map)
 
- // TODO: provide optional clean up API for non-default clients
 
- var _clients = {};
 
- // the default maximum number of concurrents connections per client
 
- var _maxConnections = 10;
 
- var net = forge.net;
 
- var http = forge.http;
 
- /**
 
-  * Initializes flash XHR support.
 
-  *
 
-  * @param options:
 
-  *   url: the default base URL to connect to if xhr URLs are relative,
 
-  *     ie: https://myserver.com.
 
-  *   flashId: the dom ID of the flash SocketPool.
 
-  *   policyPort: the port that provides the server's flash policy, 0 to use
 
-  *     the flash default.
 
-  *   policyUrl: the policy file URL to use instead of a policy port.
 
-  *   msie: true if browser is internet explorer, false if not.
 
-  *   connections: the maximum number of concurrent connections.
 
-  *   caCerts: a list of PEM-formatted certificates to trust.
 
-  *   cipherSuites: an optional array of cipher suites to use,
 
-  *     see forge.tls.CipherSuites.
 
-  *   verify: optional TLS certificate verify callback to use (see forge.tls
 
-  *     for details).
 
-  *   getCertificate: an optional callback used to get a client-side
 
-  *     certificate (see forge.tls for details).
 
-  *   getPrivateKey: an optional callback used to get a client-side private
 
-  *     key (see forge.tls for details).
 
-  *   getSignature: an optional callback used to get a client-side signature
 
-  *     (see forge.tls for details).
 
-  *   persistCookies: true to use persistent cookies via flash local storage,
 
-  *     false to only keep cookies in javascript.
 
-  *   primeTlsSockets: true to immediately connect TLS sockets on their
 
-  *     creation so that they will cache TLS sessions for reuse.
 
-  */
 
- xhrApi.init = function(options) {
 
-   forge.log.debug(cat, 'initializing', options);
 
-   // update default policy port and max connections
 
-   _policyPort = options.policyPort || _policyPort;
 
-   _policyUrl = options.policyUrl || _policyUrl;
 
-   _maxConnections = options.connections || _maxConnections;
 
-   // create the flash socket pool
 
-   _sp = net.createSocketPool({
 
-     flashId: options.flashId,
 
-     policyPort: _policyPort,
 
-     policyUrl: _policyUrl,
 
-     msie: options.msie || false
 
-   });
 
-   // create default http client
 
-   _client = http.createClient({
 
-     url: options.url || (
 
-       window.location.protocol + '//' + window.location.host),
 
-     socketPool: _sp,
 
-     policyPort: _policyPort,
 
-     policyUrl: _policyUrl,
 
-     connections: options.connections || _maxConnections,
 
-     caCerts: options.caCerts,
 
-     cipherSuites: options.cipherSuites,
 
-     persistCookies: options.persistCookies || true,
 
-     primeTlsSockets: options.primeTlsSockets || false,
 
-     verify: options.verify,
 
-     getCertificate: options.getCertificate,
 
-     getPrivateKey: options.getPrivateKey,
 
-     getSignature: options.getSignature
 
-   });
 
-   _clients[_client.url.full] = _client;
 
-   forge.log.debug(cat, 'ready');
 
- };
 
- /**
 
-  * Called to clean up the clients and socket pool.
 
-  */
 
- xhrApi.cleanup = function() {
 
-   // destroy all clients
 
-   for(var key in _clients) {
 
-     _clients[key].destroy();
 
-   }
 
-   _clients = {};
 
-   _client = null;
 
-   // destroy socket pool
 
-   _sp.destroy();
 
-   _sp = null;
 
- };
 
- /**
 
-  * Sets a cookie.
 
-  *
 
-  * @param cookie the cookie with parameters:
 
-  *   name: the name of the cookie.
 
-  *   value: the value of the cookie.
 
-  *   comment: an optional comment string.
 
-  *   maxAge: the age of the cookie in seconds relative to created time.
 
-  *   secure: true if the cookie must be sent over a secure protocol.
 
-  *   httpOnly: true to restrict access to the cookie from javascript
 
-  *     (inaffective since the cookies are stored in javascript).
 
-  *   path: the path for the cookie.
 
-  *   domain: optional domain the cookie belongs to (must start with dot).
 
-  *   version: optional version of the cookie.
 
-  *   created: creation time, in UTC seconds, of the cookie.
 
-  */
 
- xhrApi.setCookie = function(cookie) {
 
-   // default cookie expiration to never
 
-   cookie.maxAge = cookie.maxAge || -1;
 
-   // if the cookie's domain is set, use the appropriate client
 
-   if(cookie.domain) {
 
-     // add the cookies to the applicable domains
 
-     for(var key in _clients) {
 
-       var client = _clients[key];
 
-       if(http.withinCookieDomain(client.url, cookie) &&
 
-         client.secure === cookie.secure) {
 
-         client.setCookie(cookie);
 
-       }
 
-     }
 
-   } else {
 
-     // use the default domain
 
-     // FIXME: should a null domain cookie be added to all clients? should
 
-     // this be an option?
 
-     _client.setCookie(cookie);
 
-   }
 
- };
 
- /**
 
-  * Gets a cookie.
 
-  *
 
-  * @param name the name of the cookie.
 
-  * @param path an optional path for the cookie (if there are multiple cookies
 
-  *          with the same name but different paths).
 
-  * @param domain an optional domain for the cookie (if not using the default
 
-  *          domain).
 
-  *
 
-  * @return the cookie, cookies (if multiple matches), or null if not found.
 
-  */
 
- xhrApi.getCookie = function(name, path, domain) {
 
-   var rval = null;
 
-   if(domain) {
 
-     // get the cookies from the applicable domains
 
-     for(var key in _clients) {
 
-       var client = _clients[key];
 
-       if(http.withinCookieDomain(client.url, domain)) {
 
-         var cookie = client.getCookie(name, path);
 
-         if(cookie !== null) {
 
-           if(rval === null) {
 
-             rval = cookie;
 
-           } else if(!forge.util.isArray(rval)) {
 
-             rval = [rval, cookie];
 
-           } else {
 
-             rval.push(cookie);
 
-           }
 
-         }
 
-       }
 
-     }
 
-   } else {
 
-     // get cookie from default domain
 
-     rval = _client.getCookie(name, path);
 
-   }
 
-   return rval;
 
- };
 
- /**
 
-  * Removes a cookie.
 
-  *
 
-  * @param name the name of the cookie.
 
-  * @param path an optional path for the cookie (if there are multiple cookies
 
-  *          with the same name but different paths).
 
-  * @param domain an optional domain for the cookie (if not using the default
 
-  *          domain).
 
-  *
 
-  * @return true if a cookie was removed, false if not.
 
-  */
 
- xhrApi.removeCookie = function(name, path, domain) {
 
-   var rval = false;
 
-   if(domain) {
 
-     // remove the cookies from the applicable domains
 
-     for(var key in _clients) {
 
-       var client = _clients[key];
 
-       if(http.withinCookieDomain(client.url, domain)) {
 
-         if(client.removeCookie(name, path)) {
 
-            rval = true;
 
-         }
 
-       }
 
-     }
 
-   } else {
 
-     // remove cookie from default domain
 
-     rval = _client.removeCookie(name, path);
 
-   }
 
-   return rval;
 
- };
 
- /**
 
-  * Creates a new XmlHttpRequest. By default the base URL, flash policy port,
 
-  * etc, will be used. However, an XHR can be created to point at another
 
-  * cross-domain URL.
 
-  *
 
-  * @param options:
 
-  *   logWarningOnError: If true and an HTTP error status code is received then
 
-  *     log a warning, otherwise log a verbose message.
 
-  *   verbose: If true be very verbose in the output including the response
 
-  *     event and response body, otherwise only include status, timing, and
 
-  *     data size.
 
-  *   logError: a multi-var log function for warnings that takes the log
 
-  *     category as the first var.
 
-  *   logWarning: a multi-var log function for warnings that takes the log
 
-  *     category as the first var.
 
-  *   logDebug: a multi-var log function for warnings that takes the log
 
-  *     category as the first var.
 
-  *   logVerbose: a multi-var log function for warnings that takes the log
 
-  *     category as the first var.
 
-  *   url: the default base URL to connect to if xhr URLs are relative,
 
-  *     eg: https://myserver.com, and note that the following options will be
 
-  *     ignored if the URL is absent or the same as the default base URL.
 
-  *   policyPort: the port that provides the server's flash policy, 0 to use
 
-  *     the flash default.
 
-  *   policyUrl: the policy file URL to use instead of a policy port.
 
-  *   connections: the maximum number of concurrent connections.
 
-  *   caCerts: a list of PEM-formatted certificates to trust.
 
-  *   cipherSuites: an optional array of cipher suites to use, see
 
-  *     forge.tls.CipherSuites.
 
-  *   verify: optional TLS certificate verify callback to use (see forge.tls
 
-  *     for details).
 
-  *   getCertificate: an optional callback used to get a client-side
 
-  *     certificate.
 
-  *   getPrivateKey: an optional callback used to get a client-side private key.
 
-  *   getSignature: an optional callback used to get a client-side signature.
 
-  *   persistCookies: true to use persistent cookies via flash local storage,
 
-  *     false to only keep cookies in javascript.
 
-  *   primeTlsSockets: true to immediately connect TLS sockets on their
 
-  *     creation so that they will cache TLS sessions for reuse.
 
-  *
 
-  * @return the XmlHttpRequest.
 
-  */
 
- xhrApi.create = function(options) {
 
-   // set option defaults
 
-   options = $.extend({
 
-     logWarningOnError: true,
 
-     verbose: false,
 
-     logError: function() {},
 
-     logWarning: function() {},
 
-     logDebug: function() {},
 
-     logVerbose: function() {},
 
-     url: null
 
-   }, options || {});
 
-   // private xhr state
 
-   var _state = {
 
-     // the http client to use
 
-     client: null,
 
-     // request storage
 
-     request: null,
 
-     // response storage
 
-     response: null,
 
-     // asynchronous, true if doing asynchronous communication
 
-     asynchronous: true,
 
-     // sendFlag, true if send has been called
 
-     sendFlag: false,
 
-     // errorFlag, true if a network error occurred
 
-     errorFlag: false
 
-   };
 
-   // private log functions
 
-   var _log = {
 
-     error: options.logError || forge.log.error,
 
-     warning: options.logWarning || forge.log.warning,
 
-     debug: options.logDebug || forge.log.debug,
 
-     verbose: options.logVerbose || forge.log.verbose
 
-   };
 
-   // create public xhr interface
 
-   var xhr = {
 
-     // an EventListener
 
-     onreadystatechange: null,
 
-     // readonly, the current readyState
 
-     readyState: UNSENT,
 
-     // a string with the response entity-body
 
-     responseText: '',
 
-     // a Document for response entity-bodies that are XML
 
-     responseXML: null,
 
-     // readonly, returns the HTTP status code (i.e. 404)
 
-     status: 0,
 
-     // readonly, returns the HTTP status message (i.e. 'Not Found')
 
-     statusText: ''
 
-   };
 
-   // determine which http client to use
 
-   if(options.url === null) {
 
-     // use default
 
-     _state.client = _client;
 
-   } else {
 
-     var url = http.parseUrl(options.url);
 
-     if(!url) {
 
-       var error = new Error('Invalid url.');
 
-       error.details = {
 
-         url: options.url
 
-       };
 
-     }
 
-     // find client
 
-     if(url.full in _clients) {
 
-       // client found
 
-       _state.client = _clients[url.full];
 
-     } else {
 
-       // create client
 
-       _state.client = http.createClient({
 
-         url: options.url,
 
-         socketPool: _sp,
 
-         policyPort: options.policyPort || _policyPort,
 
-         policyUrl: options.policyUrl || _policyUrl,
 
-         connections: options.connections || _maxConnections,
 
-         caCerts: options.caCerts,
 
-         cipherSuites: options.cipherSuites,
 
-         persistCookies: options.persistCookies || true,
 
-         primeTlsSockets: options.primeTlsSockets || false,
 
-         verify: options.verify,
 
-         getCertificate: options.getCertificate,
 
-         getPrivateKey: options.getPrivateKey,
 
-         getSignature: options.getSignature
 
-       });
 
-       _clients[url.full] = _state.client;
 
-     }
 
-   }
 
-   /**
 
-    * Opens the request. This method will create the HTTP request to send.
 
-    *
 
-    * @param method the HTTP method (i.e. 'GET').
 
-    * @param url the relative url (the HTTP request path).
 
-    * @param async always true, ignored.
 
-    * @param user always null, ignored.
 
-    * @param password always null, ignored.
 
-    */
 
-   xhr.open = function(method, url, async, user, password) {
 
-     // 1. validate Document if one is associated
 
-     // TODO: not implemented (not used yet)
 
-     // 2. validate method token
 
-     // 3. change method to uppercase if it matches a known
 
-     // method (here we just require it to be uppercase, and
 
-     // we do not allow the standard methods)
 
-     // 4. disallow CONNECT, TRACE, or TRACK with a security error
 
-     switch(method) {
 
-     case 'DELETE':
 
-     case 'GET':
 
-     case 'HEAD':
 
-     case 'OPTIONS':
 
-     case 'PATCH':
 
-     case 'POST':
 
-     case 'PUT':
 
-       // valid method
 
-       break;
 
-     case 'CONNECT':
 
-     case 'TRACE':
 
-     case 'TRACK':
 
-       throw new Error('CONNECT, TRACE and TRACK methods are disallowed');
 
-     default:
 
-       throw new Error('Invalid method: ' + method);
 
-     }
 
-     // TODO: other validation steps in algorithm are not implemented
 
-     // 19. set send flag to false
 
-     // set response body to null
 
-     // empty list of request headers
 
-     // set request method to given method
 
-     // set request URL
 
-     // set username, password
 
-     // set asychronous flag
 
-     _state.sendFlag = false;
 
-     xhr.responseText = '';
 
-     xhr.responseXML = null;
 
-     // custom: reset status and statusText
 
-     xhr.status = 0;
 
-     xhr.statusText = '';
 
-     // create the HTTP request
 
-     _state.request = http.createRequest({
 
-       method: method,
 
-       path: url
 
-     });
 
-     // 20. set state to OPENED
 
-     xhr.readyState = OPENED;
 
-     // 21. dispatch onreadystatechange
 
-     if(xhr.onreadystatechange) {
 
-        xhr.onreadystatechange();
 
-     }
 
-   };
 
-   /**
 
-    * Adds an HTTP header field to the request.
 
-    *
 
-    * @param header the name of the header field.
 
-    * @param value the value of the header field.
 
-    */
 
-   xhr.setRequestHeader = function(header, value) {
 
-     // 1. if state is not OPENED or send flag is true, raise exception
 
-     if(xhr.readyState != OPENED || _state.sendFlag) {
 
-       throw new Error('XHR not open or sending');
 
-     }
 
-     // TODO: other validation steps in spec aren't implemented
 
-     // set header
 
-     _state.request.setField(header, value);
 
-   };
 
-   /**
 
-    * Sends the request and any associated data.
 
-    *
 
-    * @param data a string or Document object to send, null to send no data.
 
-    */
 
-   xhr.send = function(data) {
 
-     // 1. if state is not OPENED or 2. send flag is true, raise
 
-     // an invalid state exception
 
-     if(xhr.readyState != OPENED || _state.sendFlag) {
 
-       throw new Error('XHR not open or sending');
 
-     }
 
-     // 3. ignore data if method is GET or HEAD
 
-     if(data &&
 
-       _state.request.method !== 'GET' &&
 
-       _state.request.method !== 'HEAD') {
 
-       // handle non-IE case
 
-       if(typeof(XMLSerializer) !== 'undefined') {
 
-         if(data instanceof Document) {
 
-           var xs = new XMLSerializer();
 
-           _state.request.body = xs.serializeToString(data);
 
-         } else {
 
-           _state.request.body = data;
 
-         }
 
-       } else {
 
-         // poorly implemented IE case
 
-         if(typeof(data.xml) !== 'undefined') {
 
-           _state.request.body = data.xml;
 
-         } else {
 
-           _state.request.body = data;
 
-         }
 
-       }
 
-     }
 
-     // 4. release storage mutex (not used)
 
-     // 5. set error flag to false
 
-     _state.errorFlag = false;
 
-     // 6. if asynchronous is true (must be in this implementation)
 
-     // 6.1 set send flag to true
 
-     _state.sendFlag = true;
 
-     // 6.2 dispatch onreadystatechange
 
-     if(xhr.onreadystatechange) {
 
-       xhr.onreadystatechange();
 
-     }
 
-     // create send options
 
-     var options = {};
 
-     options.request = _state.request;
 
-     options.headerReady = function(e) {
 
-       // make cookies available for ease of use/iteration
 
-       xhr.cookies = _state.client.cookies;
 
-       // TODO: update document.cookie with any cookies where the
 
-       // script's domain matches
 
-       // headers received
 
-       xhr.readyState = HEADERS_RECEIVED;
 
-       xhr.status = e.response.code;
 
-       xhr.statusText = e.response.message;
 
-       _state.response = e.response;
 
-       if(xhr.onreadystatechange) {
 
-         xhr.onreadystatechange();
 
-       }
 
-       if(!_state.response.aborted) {
 
-         // now loading body
 
-         xhr.readyState = LOADING;
 
-         if(xhr.onreadystatechange) {
 
-            xhr.onreadystatechange();
 
-         }
 
-       }
 
-     };
 
-     options.bodyReady = function(e) {
 
-       xhr.readyState = DONE;
 
-       var ct = e.response.getField('Content-Type');
 
-       // Note: this null/undefined check is done outside because IE
 
-       // dies otherwise on a "'null' is null" error
 
-       if(ct) {
 
-         if(ct.indexOf('text/xml') === 0 ||
 
-           ct.indexOf('application/xml') === 0 ||
 
-           ct.indexOf('+xml') !== -1) {
 
-           try {
 
-             var doc = new ActiveXObject('MicrosoftXMLDOM');
 
-             doc.async = false;
 
-             doc.loadXML(e.response.body);
 
-             xhr.responseXML = doc;
 
-           } catch(ex) {
 
-             var parser = new DOMParser();
 
-             xhr.responseXML = parser.parseFromString(ex.body, 'text/xml');
 
-           }
 
-         }
 
-       }
 
-       var length = 0;
 
-       if(e.response.body !== null) {
 
-         xhr.responseText = e.response.body;
 
-         length = e.response.body.length;
 
-       }
 
-       // build logging output
 
-       var req = _state.request;
 
-       var output =
 
-         req.method + ' ' + req.path + ' ' +
 
-         xhr.status + ' ' + xhr.statusText + ' ' +
 
-         length + 'B ' +
 
-         (e.request.connectTime + e.request.time + e.response.time) +
 
-         'ms';
 
-       var lFunc;
 
-       if(options.verbose) {
 
-         lFunc = (xhr.status >= 400 && options.logWarningOnError) ?
 
-           _log.warning : _log.verbose;
 
-         lFunc(cat, output,
 
-           e, e.response.body ? '\n' + e.response.body : '\nNo content');
 
-       } else {
 
-         lFunc = (xhr.status >= 400 && options.logWarningOnError) ?
 
-           _log.warning : _log.debug;
 
-         lFunc(cat, output);
 
-       }
 
-       if(xhr.onreadystatechange) {
 
-         xhr.onreadystatechange();
 
-       }
 
-     };
 
-     options.error = function(e) {
 
-       var req = _state.request;
 
-       _log.error(cat, req.method + ' ' + req.path, e);
 
-       // 1. set response body to null
 
-       xhr.responseText = '';
 
-       xhr.responseXML = null;
 
-       // 2. set error flag to true (and reset status)
 
-       _state.errorFlag = true;
 
-       xhr.status = 0;
 
-       xhr.statusText = '';
 
-       // 3. set state to done
 
-       xhr.readyState = DONE;
 
-       // 4. asyc flag is always true, so dispatch onreadystatechange
 
-       if(xhr.onreadystatechange) {
 
-         xhr.onreadystatechange();
 
-       }
 
-     };
 
-     // 7. send request
 
-     _state.client.send(options);
 
-   };
 
-   /**
 
-    * Aborts the request.
 
-    */
 
-   xhr.abort = function() {
 
-     // 1. abort send
 
-     // 2. stop network activity
 
-     _state.request.abort();
 
-     // 3. set response to null
 
-     xhr.responseText = '';
 
-     xhr.responseXML = null;
 
-     // 4. set error flag to true (and reset status)
 
-     _state.errorFlag = true;
 
-     xhr.status = 0;
 
-     xhr.statusText = '';
 
-     // 5. clear user headers
 
-     _state.request = null;
 
-     _state.response = null;
 
-     // 6. if state is DONE or UNSENT, or if OPENED and send flag is false
 
-     if(xhr.readyState === DONE || xhr.readyState === UNSENT ||
 
-      (xhr.readyState === OPENED && !_state.sendFlag)) {
 
-       // 7. set ready state to unsent
 
-       xhr.readyState = UNSENT;
 
-     } else {
 
-       // 6.1 set state to DONE
 
-       xhr.readyState = DONE;
 
-       // 6.2 set send flag to false
 
-       _state.sendFlag = false;
 
-       // 6.3 dispatch onreadystatechange
 
-       if(xhr.onreadystatechange) {
 
-         xhr.onreadystatechange();
 
-       }
 
-       // 7. set state to UNSENT
 
-       xhr.readyState = UNSENT;
 
-     }
 
-   };
 
-   /**
 
-    * Gets all response headers as a string.
 
-    *
 
-    * @return the HTTP-encoded response header fields.
 
-    */
 
-   xhr.getAllResponseHeaders = function() {
 
-     var rval = '';
 
-     if(_state.response !== null) {
 
-       var fields = _state.response.fields;
 
-       $.each(fields, function(name, array) {
 
-         $.each(array, function(i, value) {
 
-           rval += name + ': ' + value + '\r\n';
 
-         });
 
-       });
 
-     }
 
-     return rval;
 
-   };
 
-   /**
 
-    * Gets a single header field value or, if there are multiple
 
-    * fields with the same name, a comma-separated list of header
 
-    * values.
 
-    *
 
-    * @return the header field value(s) or null.
 
-    */
 
-   xhr.getResponseHeader = function(header) {
 
-     var rval = null;
 
-     if(_state.response !== null) {
 
-       if(header in _state.response.fields) {
 
-         rval = _state.response.fields[header];
 
-         if(forge.util.isArray(rval)) {
 
-           rval = rval.join();
 
-         }
 
-       }
 
-     }
 
-     return rval;
 
-   };
 
-   return xhr;
 
- };
 
- })(jQuery);
 
 
  |