docs: function HttpRequest(uri, config) {
      config = config || {};
      this.status = 400;
      this.responseText = [];
      this._headers = {};
  
      // disable connection pooling
      // disable chunked transfer-encoding which nginx doesn't support
      var opts = url.parse(uri);
      opts.agent = false;
      opts.method = config.type;
      opts.headers = config.headers || {};
  
      // Preprocess data if it is JSON data.
      if (isObject(config.data) && config.processData) {
        config.data = JSON.stringify(config.data);
      }
  
      // Authenticate request if we have authentication information.
      if (config.username || config.password) {
        opts.headers.Authorization = "Basic " + btoa(config.username + ':' + config.password);
      }
  
      // Attach a content-length
      // Prevent node.js from sending transfer-encoding:chunked when there is no body
      config.data = config.data || '';
      if (isArray(config.data)) opts.headers['content-length'] = Buffer.byteLength(config.data);
      else if (isString(config.data)) opts.headers['content-length'] = Buffer.byteLength(config.data);
  
      // Fire request.
      var self = this, cbContext = config.context || this;
      this._textStatus = 'success';
      this._request = (opts.protocol === "http:" ? http : https).request(opts, function(response) {
        response.setEncoding(config.encoding || 'utf8');
  
        response.on('data', function(chunk) {
          self.responseText.push(chunk);
        });
  
        response.on('end', function () {
          self._headers = stringify(response.headers, ': ', '\n', true) || "";
          self.status = response.statusCode;
  
          var data;
          try {
            // Process data if necessary.
            data = self.responseText = self.responseText.join('');
            if (config.dataType == 'json' || (config.dataType != 'text' && response.headers['content-type'].match(/\bapplication\/json\b/i))) {
              data = JSON.parse(data);
            }
          } catch(e)
          {
            self._textStatus = 'parsererror';
          }
  
          if (self._textStatus == 'success' && self.status >= 200 && self.status < 300) {
            if (config.success) config.success.call(cbContext, data, 'success', self);
          } else if (config.error) {
            config.error.call(cbContext, self, 'error', self.responseText);
          }
          if (config.complete) config.complete.call(cbContext, self, self._textStatus);
        });
      });
  
      // Handle request errors.
      this._request.on('error', function(e) {
        self.status = e.status;
        self.responseText = e.message;
        self._textStatus = 'error';
        if (config.error) config.error.call(cbContext, self, 'error', e.message);
        if (config.complete) config.complete.call(cbContext, self, 'error');
      });
  
      // Send data (if present) and fire the request.
      this._request.end(config.data);
    }