text: var ALY = require('../core');
  var EventEmitter = require('events').EventEmitter;
  require('../http');
  
  /**
   * @api private
   */
  ALY.XHRClient = ALY.util.inherit({
    handleRequest: function handleRequest(httpRequest, httpOptions, callback, errCallback) {
      var self = this;
      var endpoint = httpRequest.endpoint;
      var emitter = new EventEmitter();
      var href = endpoint.protocol + '//' + endpoint.hostname;
      if (endpoint.port != 80 && endpoint.port != 443) {
        href += ':' + endpoint.port;
      }
      href += httpRequest.path;
  
      var xhr = new XMLHttpRequest();
      httpRequest.stream = xhr;
  
      if (httpOptions.timeout) {
        xhr.timeout = httpOptions.timeout;
      }
  
      xhr.addEventListener('readystatechange', function() {
        if (xhr.status === 0) return; // 0 code is invalid
        if (this.readyState === this.HEADERS_RECEIVED) {
          try { xhr.responseType = 'arraybuffer'; } catch (e) {}
          emitter.statusCode = xhr.status;
          emitter.headers = self.parseHeaders(xhr.getAllResponseHeaders());
          emitter.emit('headers', emitter.statusCode, emitter.headers);
        } else if (this.readyState === this.DONE) {
          self.finishRequest(xhr, emitter);
        }
      }, false);
      xhr.upload.addEventListener('progress', function (evt) {
        emitter.emit('sendProgress', evt);
      });
      xhr.addEventListener('progress', function (evt) {
        emitter.emit('receiveProgress', evt);
      }, false);
      xhr.addEventListener('timeout', function () {
        errCallback(ALY.util.error(new Error('Timeout'), {code: 'TimeoutError'}));
      }, false);
      xhr.addEventListener('error', function () {
        errCallback(ALY.util.error(new Error('Network Failure'), {
          code: 'NetworkingError'
        }));
      }, false);
  
      callback(emitter);
      xhr.open(httpRequest.method, href, true);
      ALY.util.each(httpRequest.headers, function (key, value) {
        if (key !== 'Content-Length' && key !== 'User-Agent' && key !== 'Host') {
          xhr.setRequestHeader(key, value);
        }
      });
  
      if (httpRequest.body && typeof httpRequest.body.buffer === 'object') {
        xhr.send(httpRequest.body.buffer); // typed arrays sent as ArrayBuffer
      } else {
        xhr.send(httpRequest.body);
      }
  
      return emitter;
    },
  
    parseHeaders: function parseHeaders(rawHeaders) {
      var headers = {};
      ALY.util.arrayEach(rawHeaders.split(/\r?\n/), function (line) {
        var key = line.split(':', 1)[0];
        var value = line.substring(key.length + 2);
        if (key.length > 0) headers[key] = value;
      });
      return headers;
    },
  
    finishRequest: function finishRequest(xhr, emitter) {
      var buffer;
      if (xhr.responseType === 'arraybuffer' && xhr.response) {
        var ab = xhr.response;
        buffer = new ALY.util.Buffer(ab.byteLength);
        var view = new Uint8Array(ab);
        for (var i = 0; i < buffer.length; ++i) {
          buffer[i] = view[i];
        }
      }
  
      try {
        if (!buffer && typeof xhr.responseText === 'string') {
          buffer = new ALY.util.Buffer(xhr.responseText);
        }
      } catch (e) {}
  
      if (buffer) emitter.emit('data', buffer);
      emitter.emit('end');
    }
  });
  
  /**
   * @api private
   */
  ALY.HttpClient.prototype = ALY.XHRClient.prototype;
  
  /**
   * @api private
   */
  ALY.HttpClient.streamsApiVersion = 1;
  