text: var AWS = require('../core');
  var Stream = AWS.util.stream.Stream;
  var TransformStream = AWS.util.stream.Transform;
  var ReadableStream = AWS.util.stream.Readable;
  require('../http');
  var CONNECTION_REUSE_ENV_NAME = 'AWS_NODEJS_CONNECTION_REUSE_ENABLED';
  
  /**
   * @api private
   */
  AWS.NodeHttpClient = AWS.util.inherit({
    handleRequest: function handleRequest(httpRequest, httpOptions, callback, errCallback) {
      var self = this;
      var endpoint = httpRequest.endpoint;
      var pathPrefix = '';
      if (!httpOptions) httpOptions = {};
      if (httpOptions.proxy) {
        pathPrefix = endpoint.protocol + '//' + endpoint.hostname;
        if (endpoint.port !== 80 && endpoint.port !== 443) {
          pathPrefix += ':' + endpoint.port;
        }
        endpoint = new AWS.Endpoint(httpOptions.proxy);
      }
  
      var useSSL = endpoint.protocol === 'https:';
      var http = useSSL ? require('https') : require('http');
      var options = {
        host: endpoint.hostname,
        port: endpoint.port,
        method: httpRequest.method,
        headers: httpRequest.headers,
        path: pathPrefix + httpRequest.path
      };
  
      if (!httpOptions.agent) {
        options.agent = this.getAgent(useSSL, {
          keepAlive: process.env[CONNECTION_REUSE_ENV_NAME] === '1' ? true : false
        });
      }
  
      AWS.util.update(options, httpOptions);
      delete options.proxy; // proxy isn't an HTTP option
      delete options.timeout; // timeout isn't an HTTP option
  
      var stream = http.request(options, function (httpResp) {
        if (stream.didCallback) return;
  
        callback(httpResp);
        httpResp.emit(
          'headers',
          httpResp.statusCode,
          httpResp.headers,
          httpResp.statusMessage
        );
      });
      httpRequest.stream = stream; // attach stream to httpRequest
      stream.didCallback = false;
  
      // connection timeout support
      if (httpOptions.connectTimeout) {
        var connectTimeoutId;
        stream.on('socket', function(socket) {
          if (socket.connecting) {
            connectTimeoutId = setTimeout(function connectTimeout() {
              if (stream.didCallback) return; stream.didCallback = true;
  
              stream.abort();
              errCallback(AWS.util.error(
                new Error('Socket timed out without establishing a connection'),
                {code: 'TimeoutError'}
              ));
            }, httpOptions.connectTimeout);
            socket.on('connect', function() {
              clearTimeout(connectTimeoutId);
              connectTimeoutId = null;
            });
          }
        });
      }
  
      // timeout support
      stream.setTimeout(httpOptions.timeout || 0, function() {
        if (stream.didCallback) return; stream.didCallback = true;
  
        var msg = 'Connection timed out after ' + httpOptions.timeout + 'ms';
        errCallback(AWS.util.error(new Error(msg), {code: 'TimeoutError'}));
        stream.abort();
      });
  
      stream.on('error', function() {
        if (connectTimeoutId) {
          clearTimeout(connectTimeoutId);
          connectTimeoutId = null;
        }
        if (stream.didCallback) return; stream.didCallback = true;
        errCallback.apply(stream, arguments);
      });
  
      var expect = httpRequest.headers.Expect || httpRequest.headers.expect;
      if (expect === '100-continue') {
        stream.on('continue', function() {
          self.writeBody(stream, httpRequest);
        });
      } else {
        this.writeBody(stream, httpRequest);
      }
  
      return stream;
    },
  
    writeBody: function writeBody(stream, httpRequest) {
      var body = httpRequest.body;
      var totalBytes = parseInt(httpRequest.headers['Content-Length'], 10);
  
      if (body instanceof Stream) {
        // For progress support of streaming content -
        // pipe the data through a transform stream to emit 'sendProgress' events
        var progressStream = this.progressStream(stream, totalBytes);
        if (progressStream) {
          body.pipe(progressStream).pipe(stream);
        } else {
          body.pipe(stream);
        }
      } else if (body) {
        // The provided body is a buffer/string and is already fully available in memory -
        // For performance it's best to send it as a whole by calling stream.end(body),
        // Callers expect a 'sendProgress' event which is best emitted once
        // the http request stream has been fully written and all data flushed.
        // The use of totalBytes is important over body.length for strings where
        // length is char length and not byte length.
        stream.once('finish', function() {
          stream.emit('sendProgress', {
            loaded: totalBytes,
            total: totalBytes
          });
        });
        stream.end(body);
      } else {
        // no request body
        stream.end();
      }
    },
  
    /**
     * Create the https.Agent or http.Agent according to the request schema.
     */
    getAgent: function getAgent(useSSL, agentOptions) {
      var http = useSSL ? require('https') : require('http');
      if (useSSL) {
        if (!AWS.NodeHttpClient.sslAgent) {
          AWS.NodeHttpClient.sslAgent = new http.Agent(AWS.util.merge({
            rejectUnauthorized: process.env.NODE_TLS_REJECT_UNAUTHORIZED === '0' ? false : true
          }, agentOptions || {}));
          AWS.NodeHttpClient.sslAgent.setMaxListeners(0);
  
          // delegate maxSockets to globalAgent, set a default limit of 50 if current value is Infinity.
          // Users can bypass this default by supplying their own Agent as part of SDK configuration.
          Object.defineProperty(AWS.NodeHttpClient.sslAgent, 'maxSockets', {
            enumerable: true,
            get: function() {
              var defaultMaxSockets = 50;
              var globalAgent = http.globalAgent;
              if (globalAgent && globalAgent.maxSockets !== Infinity && typeof globalAgent.maxSockets === 'number') {
                return globalAgent.maxSockets;
              }
              return defaultMaxSockets;
            }
          });
        }
        return AWS.NodeHttpClient.sslAgent;
      } else {
        if (!AWS.NodeHttpClient.agent) {
          AWS.NodeHttpClient.agent = new http.Agent(agentOptions);
        }
        return AWS.NodeHttpClient.agent;
      }
    },
  
    progressStream: function progressStream(stream, totalBytes) {
      if (typeof TransformStream === 'undefined') {
        // for node 0.8 there is no streaming progress
        return;
      }
      var loadedBytes = 0;
      var reporter = new TransformStream();
      reporter._transform = function(chunk, encoding, callback) {
        if (chunk) {
          loadedBytes += chunk.length;
          stream.emit('sendProgress', {
            loaded: loadedBytes,
            total: totalBytes
          });
        }
        callback(null, chunk);
      };
      return reporter;
    },
  
    emitter: null
  });
  
  /**
   * @!ignore
   */
  
  /**
   * @api private
   */
  AWS.HttpClient.prototype = AWS.NodeHttpClient.prototype;
  
  /**
   * @api private
   */
  AWS.HttpClient.streamsApiVersion = ReadableStream ? 2 : 1;
  