Skip to content

Instantly share code, notes, and snippets.

@timraymond
Created May 18, 2015 16:12

Revisions

  1. timraymond created this gist May 18, 2015.
    140 changes: 140 additions & 0 deletions ie9compatiblexhradapter.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,140 @@
    /**
    * Connection class to use with the elasticsearch.js client for limited compatibility with
    * IE 9 (also IE 8 is you include an es5 shim like https://github.com/es-shims/es5-shim).
    *
    * All connections from the browser, directly to elasticsearch, are assumed to be cross-domain
    * and therefore suffer from a lack of support in legacy browsers. To prevent the client from
    * behaving differently based on the browser version, the following limitations have been imposed
    * on all requests:
    *
    * - only GET and POST are supported
    * - Authentication can't be used
    * - Custom headers can't be set
    * - Requests can't be made to localhost unless the page is running on localhost
    * - Protocols of the parent window and the request must match.
    *
    * Also because of restrictions put in place by IE8/9, responses will be different
    * in the following ways:
    * - Status codes are not available for the responses
    * - Any non-20x status code is considered a generic error. No response body will be available
    *
    * @class Ie9CompatibleXhrConnector
    */
    window.Ie9CompatibleXhrConnector = (function () {

    var EsXhrConnection = elasticsearch.ConnectionPool.connectionClasses.xhr;
    var ConnectionFault = elasticsearch.errors.ConnectionFault;

    // perminent references
    var ModernXhr = window.XMLHttpRequest;
    var LegacyXhr = window.XDomainRequest;
    var setImmediate = window.setImmediate || function (cb) { setTimeout(cb, 1); };

    var currentProtocol = window.location.protocol.substr(0, window.location.protocol.length - 1);
    var localhostRE = /:\/\/(localhost|127\.0\.0\.1)[:\/]/;

    function Ie9CompatibleXhrConnector(host, config) {
    EsXhrConnection.call(this, host, config);

    this._XhrConstructor = (function () {
    if (typeof ModernXhr !== 'undefined') {
    var req = new ModernXhr();
    if ('withCredentials' in req) {
    return ModernXhr;
    }
    }

    if (typeof LegacyXhr !== 'undefined') {
    return LegacyXhr;
    }

    throw new Error('Unable to find a suitable Cross-Domain AJAX handler.');
    }());
    }
    // inherit EsXhrConnection's prototype
    Ie9CompatibleXhrConnector.prototype = Object.create(EsXhrConnection.prototype, { 'constructor': Ie9CompatibleXhrConnector });

    Ie9CompatibleXhrConnector.prototype.request = function (params, cb) {
    var xhr = new this._XhrConstructor();

    // This is a patch to this.host by Tim Raymond to get results working
    // on search. It isn't really necessary for that page.
    this.host.getHeaders = function() { return false };

    var hasHeaders = !!this.host.getHeaders(params.headers);
    var url = this.host.makeUrl(params);
    var log = this.log;
    var hasAuth = this.host.auth;
    var method = (params.method || 'GET').toUpperCase();

    var err = function (msg) {
    // always callback async
    setImmediate(function () {
    cb(new TypeError('Cross-domain AJAX requests ' + msg + ' are not supported'));
    });
    };

    /**
    * To prevent some browsers from behaving differently from others, implement some checks here
    */

    if (method !== 'GET' && method !== 'POST') {
    return err('using HTTP method ' + method);
    }

    if (localhostRE.test(url) && !localhostRE.test(window.location.href)) {
    return err('to localhost from other domains');
    }

    if (hasAuth) {
    return err('with authentication');
    }

    if (hasHeaders) {
    return err('with custom headers');
    }

    if (this.host.protocol !== currentProtocol) {
    return err('across protocols');
    }

    /**
    * Setup listeners for the response
    */
    if (this._XhrConstructor === LegacyXhr) {
    // XDomainRequest does not support onreadystatechange
    xhr.onload = function () {
    log.trace(params.method, url, params.body, xhr.responseText, xhr.status);
    cb(void 0, xhr.responseText, 200);
    };

    // XDomainRequest also sends errors elsewhere
    xhr.onerror = function (err) {
    log.trace(params.method, url, params.body, xhr.responseText, xhr.status);
    cb(new ConnectionFault(err.message), xhr.responseText, void 0);
    };
    }

    if (this._XhrConstructor === ModernXhr) {
    xhr.onreadystatechange = function () {
    if (xhr.readyState === 4) {
    log.trace(params.method, url, params.body, xhr.responseText, xhr.status);
    var err = xhr.status ? void 0 : new ConnectionFault(xhr.statusText || 'Request failed to complete.');
    cb(err, xhr.responseText, xhr.status);
    }
    };
    }

    /**
    * Send the request
    */
    xhr.open(method, url);
    xhr.send(params.body || void 0);

    return function () {
    xhr.abort();
    };
    };

    return Ie9CompatibleXhrConnector;
    }());