text: /*
   * Copyright 2013 Apigee Corporation.
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
   * SOFTWARE.
   */
  /*
   * This is our implementation of the "http" module. Normally, it just delegates to Node's normal
   * JavaScript-built HTTP module. However, it can also delegate to an HTTP adaptor, which means that it
   * gives up control of networking and HTTP to a third party Java library instead, although it
   * implements the same basic interface.
   *
   * This module is normally initialized by Noderunner to load when the "http" module is included.
   */
  
  var HttpWrap = process.binding('http_wrap');
  var NodeHttp = require('node_http');
  
  var debug, debugOn;
  if (process.env.NODE_DEBUG && /http/.test(process.env.NODE_DEBUG)) {
    debugOn = true;
    debug = function(x) { console.error('HTTP: %s', x); };
  } else {
    debugOn = false;
    debug = function() { };
  }
  
  function toNumber(x) { return (x = Number(x)) >= 0 ? x : false; }
  
  var STATUS_CODES = exports.STATUS_CODES = NodeHttp.STATUS_CODES;
  
  exports.IncomingMessage = NodeHttp.IncomingMessage;
  exports.OutgoingMessage = NodeHttp.OutgoingMessage;
  
  exports.Agent = NodeHttp.Agent;
  exports.globalAgent = NodeHttp.globalAgent;
  
  exports.ClientRequest = NodeHttp.ClientRequest;
  
  exports.request = NodeHttp.request;
  exports.get = NodeHttp.get;
  
  exports.Client = NodeHttp.Client;
  exports.createClient = NodeHttp.createClient;
  
  /*
   * Allow the tool embedding Trireme to configure the default
   * http.globalAgent.maxSockets.
   */
  var customMaxSockets = toNumber(process.env.NODE_HTTP_MAX_SOCKETS);
  
  if (customMaxSockets) {
    exports.globalAgent.maxSockets = customMaxSockets;
  }
  
  if (HttpWrap.hasServerAdapter()) {
    var util = require('util');
    var net = require('net');
    var events = require('events');
    var stream = require('stream');
    var timers = require('timers');
    var domain = require('domain');
    var assert = require('assert');
    var tcp_wrap = process.binding('tcp_wrap');
  
    debug('Using server adapter');
  
    var END_OF_FILE = {};
  
    /*
     * This object represents the "http.ServerResponse" object from Node with code that wraps our
     * adapter. Frameworks like Express like to add to this object, take things away, and even
     * add and remove functions from the prototype. So for that reason we do NOT add any functions
     * to this object's prototype that are not documented in the Node docs.
     */
  
    function ServerResponse(adapter, conn) {
      if (!(this instanceof ServerResponse)) return new ServerResponse(adapter, conn);
      stream.Writable.call(this, {decodeStrings: true});
  
      this.statusCode = 200;
      this.headersSent = false;
      this.ended = false;
      this.sendDate = true;
      this._headers = {};
      this.connection = conn;
      this.socket = conn;
      this.finished = false;
      this.closeDelivered = false;
  
      Object.defineProperty(this, '_adapter', {
        value: adapter,
        enumerable: false
      });
      Object.defineProperty(this, 'attachment', {
        value: adapter.attachment,
        enumerable: false
      });
    }
  
    util.inherits(ServerResponse, stream.Writable);
    exports.ServerResponse = ServerResponse;
  
    ServerResponse.prototype.statusCode = 200;
  
    ServerResponse.prototype.writeContinue = function() {
      throw Error('writeContinue not implemented');
    };
  
    ServerResponse.prototype._implicitHeader = function() {
      this.writeHead(this.statusCode);
    };
  
    ServerResponse.prototype.writeHeader = function() {
      this.writeHead.apply(this, arguments);
    };
  
    ServerResponse.prototype.writeHead = function(statusCode) {
      debug('writeHead');
      // Argument and header parsing code from http.js
      if (typeof arguments[1] == 'string') {
        reasonPhrase = arguments[1];
        headerIndex = 2;
      } else {
        reasonPhrase = STATUS_CODES[statusCode] || 'unknown';
        headerIndex = 1;
      }
      this.statusCode = statusCode;
  
      var obj = arguments[headerIndex];
      headers = this._renderHeaders();
  
      if (obj) {
        headers = this._renderHeaders();
        if (Array.isArray(obj)) {
          // handle array case
          // TODO: remove when array is no longer accepted
          var field;
          for (var i = 0, len = obj.length; i < len; ++i) {
            field = obj[i][0];
            if (field in headers) {
              obj.push([field, headers[field]]);
            }
          }
          headers = obj;
  
        } else {
          // handle object case
          var keys = Object.keys(obj);
          for (var i = 0; i < keys.length; i++) {
            var k = keys[i];
            if (k) headers[k] = obj[k];
          }
        }
      }
  
      this._saveHeaders(headers);
    };
  
    function responseWriteComplete(self, err, cb) {
      if (debugOn) {
        debug('Response write complete: err ' + err);
      }
      if (err) {
        if (!self.ended && !self.closeDelivered) {
          self.closeDelivered = true;
          self.emit('close');
        }
      } else {
        cb();
      }
    }
  
    ServerResponse.prototype._write = function(data, encoding, cb) {
      if (!this._savedHeaders) {
        this.writeHead(this.statusCode);
      }
  
      var self = this;
      timers.active(this.connection);
      if (!this.headersSent) {
        // Just send one additional chunk of data
        if (debugOn) {
          debug('Sending http headers status = ' + this.statusCode +
               ' data = ' + (data ? data.length : 0));
        }
  
        // We should always get a buffer with no encoding in this case
        this._adapter.send(this.statusCode, this.sendDate, this._savedHeaders,
                           data, encoding, undefined, false, function(err) {
          responseWriteComplete(self, err, cb);
        });
        this.headersSent = true;
  
      } else {
        if (debugOn) {
          debug('Sending data = ' + (data ? data.length : 0));
        }
        this._adapter.sendChunk(data, encoding, undefined, false, function(err) {
          responseWriteComplete(self, err, cb);
        });
      }
    };
  
    ServerResponse.prototype._send = function() {
      return this.write(data, encoding);
    };
  
    function responseEndComplete(self) {
      debug('Response end completed');
      self.connection.emit('finish');
    }
  
    ServerResponse.prototype.end = function(data, encoding) {
      debug('end');
      if (!this._savedHeaders) {
        this.writeHead(this.statusCode);
      }
  
      timers.active(this.connection);
      var self = this;
      stream.Writable.prototype.end.call(this, data, encoding, function() {
        // The connection is a dummy connection, but it still must be ended to clean up state
        self.connection.end();
        // That does not "unref" it in all cases -- must do that so we don't hold server open.
        self.connection.unref();
  
        self.ended = true;
        self.finished = true;
  
        if (self.headersSent) {
          debug('Sending end of response');
          self._adapter.sendChunk(null, null, self._trailers, true, function() {
            responseEndComplete(self);
          });
        } else {
          // We will only get here if we are sending an empty response
          self._adapter.send(self.statusCode, self.sendDate, self._savedHeaders,
                             null, null, self._trailers, true, function() {
            responseEndComplete(self);
          });
        }
      });
    };
  
    ServerResponse.prototype._saveHeaders = function(headers) {
      this._savedHeaders = [];
      if (headers) {
        var keys = Object.keys(headers);
        var isArray = (Array.isArray(headers));
        var field, value;
  
        for (var i = 0, l = keys.length; i < l; i++) {
          var key = keys[i];
          if (isArray) {
            field = headers[key][0];
            value = headers[key][1];
          } else {
            field = key;
            value = headers[key];
          }
  
          if (Array.isArray(value)) {
            for (var j = 0; j < value.length; j++) {
              this._savedHeaders.push(field);
              this._savedHeaders.push(value[j]);
            }
          } else {
            this._savedHeaders.push(field);
            this._savedHeaders.push(value);
          }
        }
      }
    };
  
    ServerResponse.prototype.addTrailers = function(headers) {
      if (!this._trailers) {
        this._trailers = [];
      }
      var keys = Object.keys(headers);
      var isArray = (Array.isArray(headers));
      var field, value;
      for (var i = 0, l = keys.length; i < l; i++) {
        var key = keys[i];
        if (isArray) {
          this._trailers.push(headers[key][0]);
          this._trailers.push( headers[key][1]);
        } else {
          this._trailers.push(key);
          this._trailers.push(headers[key]);
        }
      }
    };
  
    ServerResponse.prototype.destroy = function() {
      debug('Destroy');
      if (this._adapter) {
        this._adapter.destroy();
      }
      this.connection.destroy();
    };
  
    ServerResponse.prototype.setTimeout = function(timeout, cb) {
      this.connection.setTimeout(timeout, cb);
    };
  
    ServerResponse.prototype.clearTimeout = function(cb) {
      this.connection.clearTimeout(cb);
    };
  
    ServerResponse.prototype.setHeader = NodeHttp.OutgoingMessage.prototype.setHeader;
    ServerResponse.prototype.getHeader = NodeHttp.OutgoingMessage.prototype.getHeader;
    ServerResponse.prototype.removeHeader = NodeHttp.OutgoingMessage.prototype.removeHeader;
    ServerResponse.prototype._renderHeaders = NodeHttp.OutgoingMessage.prototype._renderHeaders;
  
    /*
     * Like ServerResponse, frameworks like to mess with the prototype to this object, so add no
     * functions there unless they are publicly-documented in the Node docs.
     */
  
    function ServerRequest(adapter, conn) {
      if (!(this instanceof ServerRequest)) return new ServerRequest(adapter, conn);
      NodeHttp.IncomingMessage.call(this, conn);
  
      this.httpVersionMajor = adapter.requestMajorVersion;
      this.httpVersionMinor = adapter.requestMinorVersion;
      this.httpVersion = adapter.requestMajorVersion + '.' + adapter.requestMinorVersion;
      this.url = adapter.requestUrl;
      this.complete = false;
      this.reading = false;
  
      Object.defineProperty(this, '_adapter', {
        value: adapter,
        enumerable: false
      });
      Object.defineProperty(this, 'attachment', {
        value: adapter.attachment,
        enumerable: false
      });
    }
  
    util.inherits(ServerRequest, NodeHttp.IncomingMessage);
  
    function addPending(self, chunk) {
      // We are always readable in this implementation, so whenever we get incoming data,
      // add it to the read queue. We don't care if _read was already called.
      timers.active(self.connection);
      if (debugOn) {
        debug('Pushing ' + chunk.length + ' bytes to the request stream');
      }
      var ret;
      if (chunk === END_OF_FILE) {
        ret = self.push(null);
      } else {
        ret = self.push(chunk);
      }
      if (debugOn) {
        debug('Push result = ' + ret);
      }
      return ret;
    }
  
    ServerRequest.prototype.setTimeout = function(timeout, cb) {
      this.connection.setTimeout(timeout, cb);
    };
  
    ServerRequest.prototype.clearTimeout = function(cb) {
      this.connection.clearTimeout(cb);
    };
  
    ServerRequest.prototype._read = function(size) {
      if (!this.reading) {
        debug('Resuming request stream read');
        this.reading = true;
        this._adapter.resume();
      }
    };
  
    function Server(requestListener) {
      if (!(this instanceof Server)) return new Server(requestListener);
      debug('New HTTP server');
      events.EventEmitter.call(this);
  
      if (requestListener) {
        this.addListener('request', requestListener);
      }
  
      // Code from http.js
      this.httpAllowHalfOpen = false;
      this.addListener('clientError', function(err, conn) {
        conn.destroy(err);
      });
    }
  
    util.inherits(Server, events.EventEmitter);
  
    exports.Server = Server;
  
    exports.createServer = function(requestListener) {
      return new Server(requestListener);
    };
  
    function getErrorMessage(err) {
      var msg;
      if (typeof err === 'string') {
        return msg;
      } else if ((typeof err === 'object') && (err instanceof Error)) {
        return err.message;
      } else {
        return '';
      }
    }
  
    /**
     * Error callback for domain. This gets invoked if the user's code throws an exception while processing
     * a request.
     */
    function handleError(err, response) {
      debug('Handling server error and sending to adapter');
      if (err.stack && debugOn) {
        debug(err.message);
        debug(err.stack);
      }
      if (response.headersSent) {
        debug('Response already sent -- closing');
        response.destroy();
  
      } else {
        var msg = getErrorMessage(err);
        var stack = err.stack ? err.stack : undefined;
        response._adapter.fatalError(msg, stack);
      }
    }
  
    Server.prototype._makeSocket = function(handle) {
      var self = this;
  
      // The handle is just a Java object -- make it an actual socket handle
      var sockHandle = new tcp_wrap.TCP(handle);
  
      // And make that handle an actual socket
      var conn = new net.Socket({
        handle: sockHandle
      });
      if (this.sslContext) {
        conn.encrypted = true;
      }
      if (this.timeout && (this.timeout > 0)) {
        conn.setTimeout(this.timeout, function() {
          self.timeoutCallback(conn);
        });
      }
      conn.readable = conn.writable = true;
      timers.active(conn);
      this.emit('connection', conn);
      return conn;
    };
  
    // An adapter can call this method at any time to create the ServerRequest object,
    // backed by the supplied request adapter
    Server.prototype._makeRequest = function(reqAdapter, conn) {
      debug('_makeRequest');
      var headers = reqAdapter.getRequestHeaders();
  
      var ret = new ServerRequest(reqAdapter, conn);
  
      var n = headers.length;
  
      // If parser.maxHeaderPairs <= 0 - assume that there're no limit
      if (this.maxHeaderPairs > 0) {
        // TODO but "parser" is never set?
        n = Math.min(n, parser.maxHeaderPairs);
      }
  
      for (var i = 0; i < n; i += 2) {
        var k = headers[i];
        var v = headers[i + 1];
        ret._addHeaderLine(k, v);
      }
  
      ret.method = reqAdapter.requestMethod;
      return ret;
    };
  
    // Make the JavaScript "response" object based on the same adapter
    Server.prototype._makeResponse = function(respAdapter, conn, timeoutOpts) {
      debug('_makeResponse');
      var ret = new ServerResponse(respAdapter, conn);
      conn._outgoing = ret;
  
      if (timeoutOpts && (timeoutOpts.timeout > 0)) {
        ret.setTimeout(timeoutOpts.timeout, function() {
          var timeoutCode = (timeoutOpts.statusCode ? timeoutOpts.statusCode : 503);
          if (debugOn) {
            debug('Sending automatic response timeout of ' + timeoutCode);
          }
          if (ret.headersSent) {
            ret.end();
          } else {
            ret.writeHead(timeoutCode, {
              'content-type': (timeoutOpts.contentType ? timeoutOpts.contentType : 'text/plain')
            });
            ret.end(timeoutOpts.message ? timeoutOpts.message : undefined);
          }
        });
      }
  
      return ret;
    };
  
    /*
     * Called directly by the adapter when a message arrives and all the headers have been received.
     */
    Server.prototype._onHeaders = function(request, response) {
      debug('onHeadersComplete');
      var self = this;
  
      response._adapter.onchannelclosed = function() {
        debug('Server channel closed');
        // We decided to emit the close event elsewhere here.
      };
  
      response._adapter.onwritecomplete = function(err) {
        if (debugOn) {
          debug('write complete: ' + err + ' outstanding: ' + response._outstanding);
        }
        // We decided to emit the write error elsewhere.
      };
  
      request._adapter.domain = domain.create();
      request._adapter.domain.on('error', function(err) {
        handleError(err, response);
      });
      request._adapter.domain.run(function() {
        self.emit('request', request, response);
      });
    };
  
    /**
     * Called when the client sent a message containing an upgrade header.
     */
    var EMPTY_BUFFER = new Buffer(0);
  
    Server.prototype._onUpgrade = function(request, socket) {
      debug('onUpgrade');
  
      if (events.EventEmitter.listenerCount(this, 'upgrade') === 0) {
        debug('No listeners for upgrade event');
        socket.unref();
        return false;
      }
  
      this.emit('upgrade', request, socket, EMPTY_BUFFER);
      return true;
    };
  
    /*
     * This is called directly by the adapter when data is received for the message body.
     */
    function onBody(request, b) {
      debug('onBody');
      timers.active(request.connection);
      var keepReading;
      request._adapter.domain.run(function() {
        keepReading = addPending(request, b);
      });
      if (!keepReading && request.reading) {
        debug('Pausing further reads');
        request.reading = false;
        request._adapter.pause();
      }
    }
  
    /*
     * This is called directly by the adapter when the complete message has been received.
     */
    function onMessageComplete(request) {
      debug('onMessageComplete');
      timers.active(request.connection);
      request.complete = true;
      if (!request.upgrade) {
        request._adapter.domain.run(function() {
          addPending(request, END_OF_FILE);
        });
      }
    }
  
    function onClose(request, response) {
      debug('onClose');
      request.connection.destroy();
      if (!request.ended) {
        request._adapter.domain.run(function() {
          request.ended = true;
          request.emit('close');
        });
      }
    }
  
    function listen(self, address, port, addressType, backlog, fd) {
      if (debugOn) {
        debug('listen address = ' + address + ' port = ' + port);
      }
      var fam;
      if (addressType === 4) {
        fam = 'IPv4';
      } else if (addressType === 6) {
        fam = 'IPv6';
      } else {
        fam = 'IPv4';
      }
  
      self._adapter = HttpWrap.createServerAdapter();
      if (self.sslContext) {
        self._adapter.setSslContext(self.sslContext, self.rejectUnauthorized, self.requestCert);
      }
      var r = self._adapter.listen(address, port, backlog);
  
      if (r) {
        var ex = errnoException(r, 'listen');
        self._adapter.close();
        self._adapter = null;
        process.nextTick(function() {
          self.emit('error', ex);
        });
        return;
      }
  
      self._adapter.makeRequest = function(req, conn) {
        return self._makeRequest(req, conn);
      };
      self._adapter.makeResponse = function(resp, conn, timeoutOpts) {
        return self._makeResponse(resp, conn, timeoutOpts);
      };
      self._adapter.makeSocket = function(handle) {
        return self._makeSocket(handle);
      };
      self._adapter.onheaders = function(request, response) {
        self._onHeaders(request, response);
      };
      self._adapter.ondata = onBody;
      self._adapter.oncomplete = onMessageComplete;
      self._adapter.onupgrade = function(request, handle) {
        return self._onUpgrade(request, handle);
      };
      self._adapter.onclose = onClose;
  
      // Prior to 0.9.0, uncaught exceptions thrown outside of a domain-wrapped request handler were handled by the HTTP
      // adaptor.  This was not ideal but for some consumers, altering this functionality resulted in breaking backward
      // compatibility because unmodified code was all the sudden exiting.  While this bug deserves to be fixed, and is by
      // default, consumers can tell Trireme that its HTTP adaptor will handle uncaught exceptions to provide backward
      // compatibility.
      if ((process.env.HTTP_ADAPTOR_HANDLES_UNCAUGHT_EXCEPTIONS || 'false').toLowerCase() === 'true') {
        process.on('uncaughtException', function(err) {
          if ((self._adapter !== null) && !self.exiting) {
            self.exiting = true;
            var msg = getErrorMessage(err);
            var stack = err.stack ? err.stack : undefined;
            self._adapter.fatalError(msg, stack);
          }
        });
      }
      // Same as above.
      if ((process.env.HTTP_ADAPTOR_HANDLES_EXIT || 'false').toLowerCase() === 'true') {
        process.on('exit', function() {
          if ((self._adapter !== null) && !self.exiting) {
            self.exiting = true;
            self._adapter.fatalError('Premature script exit');
          }
        });
      }
  
      process.nextTick(function() {
        self.emit('listening');
      });
    }
  
    Server.prototype.address = function() {
      return this._adapter.localAddress();
    };
  
    Server.prototype.listen = function() {
      debug('server listen');
      // Run through the various arguments to "listen." This code is taken from "net.js".
      var self = this;
  
      var lastArg = arguments[arguments.length - 1];
      if (typeof lastArg == 'function') {
        self.once('listening', lastArg);
      }
  
      var port = toNumber(arguments[0]);
  
      // The third optional argument is the backlog size.
      // When the ip is omitted it can be the second argument.
      var backlog = toNumber(arguments[1]) || toNumber(arguments[2]);
  
      if (arguments.length == 0 || typeof arguments[0] == 'function') {
        // Bind to a random port.
        listen(self, '0.0.0.0', 0, null, backlog);
  
      } else if (typeof arguments[1] == 'undefined' ||
                 typeof arguments[1] == 'function' ||
                 typeof arguments[1] == 'number') {
        // The first argument is the port, no IP given.
        listen(self, '0.0.0.0', port, 4, backlog);
  
      } else {
        // The first argument is the port, the second an IP.
        require('dns').lookup(arguments[1], function(err, ip, addressType) {
          if (err) {
            self.emit('error', err);
          } else {
            listen(self, ip || '0.0.0.0', port, ip ? addressType : 4, backlog);
          }
        });
      }
      return self;
    };
  
    Server.prototype.close = function(cb) {
      if (!this._adapter) {
        // Throw error. Follows net_legacy behaviour.
        throw new Error('Not running');
      }
  
      if (cb) {
        this.once('close', cb);
      }
      this._adapter.close();
      this._adapter = null;
  
      var self = this;
      process.nextTick(function() {
        self.emit('close');
      });
  
      return this;
    };
  
    Server.prototype.setTimeout = function(timeout, cb) {
      this.timeout = timeout;
      this.timeoutCallback = cb;
    };
  
  } else {
    debug('Using standard Node HTTP module');
    exports.Server = NodeHttp.Server;
    exports.ServerResponse = NodeHttp.ServerResponse;
    exports.createServer = function(requestListener) {
      return new NodeHttp.Server(requestListener);
    };
    exports._connectionListener = NodeHttp._connectionListener;
  }
  