summaryrefslogtreecommitdiff
path: root/school/node_modules/node-forge/js/xhr.js
diff options
context:
space:
mode:
Diffstat (limited to 'school/node_modules/node-forge/js/xhr.js')
-rw-r--r--school/node_modules/node-forge/js/xhr.js739
1 files changed, 739 insertions, 0 deletions
diff --git a/school/node_modules/node-forge/js/xhr.js b/school/node_modules/node-forge/js/xhr.js
new file mode 100644
index 0000000..96082ad
--- /dev/null
+++ b/school/node_modules/node-forge/js/xhr.js
@@ -0,0 +1,739 @@
+/**
+ * XmlHttpRequest implementation that uses TLS and flash SocketPool.
+ *
+ * @author Dave Longley
+ *
+ * Copyright (c) 2010-2013 Digital Bazaar, Inc.
+ */
+(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;
+
+// local aliases
+if(typeof forge === 'undefined') {
+ forge = {};
+}
+var net = forge.net;
+var http = forge.http;
+
+// define the xhr interface
+var xhrApi = {};
+
+/**
+ * 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;
+};
+
+// expose public api
+forge.xhr = xhrApi;
+
+})(jQuery);