You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					739 lines
				
				22 KiB
			
		
		
			
		
	
	
					739 lines
				
				22 KiB
			| 
								 
											2 years ago
										 
									 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * 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.origin] = _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;
							 | 
						||
| 
								 | 
							
								    try {
							 | 
						||
| 
								 | 
							
								      url = new URL(options.url);
							 | 
						||
| 
								 | 
							
								    } catch(e) {
							 | 
						||
| 
								 | 
							
								      var error = new Error('Invalid url.');
							 | 
						||
| 
								 | 
							
								      error.details = {
							 | 
						||
| 
								 | 
							
								        url: options.url
							 | 
						||
| 
								 | 
							
								      };
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // find client
							 | 
						||
| 
								 | 
							
								    if(url.origin in _clients) {
							 | 
						||
| 
								 | 
							
								      // client found
							 | 
						||
| 
								 | 
							
								      _state.client = _clients[url.origin];
							 | 
						||
| 
								 | 
							
								    } 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.origin] = _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);
							 |