Merge remote-tracking branch 'ry/v0.10' into master
authorisaacs <i@izs.me>
Tue, 25 Jun 2013 18:12:33 +0000 (11:12 -0700)
committerisaacs <i@izs.me>
Tue, 25 Jun 2013 18:12:33 +0000 (11:12 -0700)
Conflicts:
ChangeLog
deps/uv/ChangeLog
deps/uv/src/unix/stream.c
deps/uv/src/version.c
deps/v8/build/common.gypi
deps/v8/src/frames.h
deps/v8/src/runtime.cc
deps/v8/test/mjsunit/debug-set-variable-value.js
lib/http.js
src/node_version.h

13 files changed:
1  2 
AUTHORS
ChangeLog
Makefile
configure
doc/api/fs.markdown
doc/api/http.markdown
lib/_http_client.js
lib/_http_server.js
lib/crypto.js
lib/net.js
lib/readline.js
src/string_bytes.cc
test/simple/test-http-upgrade-server.js

diff --cc AUTHORS
Simple merge
diff --cc ChangeLog
+++ b/ChangeLog
 -2013.06.18, Version 0.10.12 (Stable)
 +2013.05.13, Version 0.11.2 (Unstable)
 +
 +* uv: Upgrade to 0.11.2
 +
 +* V8: Upgrade to 3.19.0
 +
 +* npm: Upgrade to 1.2.21
 +
 +* build: Makefile should respect configure --prefix (Timothy J Fontaine)
 +
 +* cluster: use round-robin load balancing (Ben Noordhuis)
 +
 +* debugger, cluster: each worker has new debug port (Miroslav Bajtoš)
 +
 +* debugger: `restart` with custom debug port (Miroslav Bajtoš)
 +
 +* debugger: breakpoints in scripts not loaded yet (Miroslav Bajtoš)
 +
 +* event: EventEmitter#setMaxListeners() returns this (Sam Roberts)
 +
 +* events: add EventEmitter.defaultMaxListeners (Ben Noordhuis)
 +
 +* install: Support $(PREFIX) install target directory prefix (Olof Johansson)
 +
 +* os: Include netmask in os.networkInterfaces() (Ben Kelly)
 +
 +* path: add path.isAbsolute(path) (Ryan Doenges)
 +
 +* stream: Guarantee ordering of 'finish' event (isaacs)
 +
 +* streams: introduce .cork/.uncork/._writev (Fedor Indutny)
 +
 +* vm: add support for timeout argument (Andrew Paprocki)
 +
 +
 +2013.04.19, Version 0.11.1 (Unstable), 4babd2b46ebf9fbea2c9946af5cfae25a33b2b22
 +
 +* V8: upgrade to 3.18.0
 +
 +* uv: Upgrade to v0.11.1
 +
 +* http: split into multiple separate modules (Timothy J Fontaine)
 +
 +* http: escape unsafe characters in request path (Ben Noordhuis)
 +
 +* url: Escape all unwise characters (isaacs)
 +
 +* build: depend on v8 postmortem-metadata if enabled (Paddy Byers)
 +
 +* etw: update prototypes to match dtrace provider (Timothy J Fontaine)
 +
 +* buffer: change output of Buffer.prototype.toJSON() (David Braun)
 +
 +* dtrace: actually use the _handle.fd value (Timothy J Fontaine)
 +
 +* dtrace: pass more arguments to probes (Dave Pacheco)
 +
 +* build: allow building with dtrace on osx (Dave Pacheco)
 +
 +* zlib: allow passing options to convenience methods (Kyle Robinson Young)
 +
 +
++2013.03.28, Version 0.11.0 (Unstable), bce38b3d74e64fcb7d04a2dd551151da6168cdc5
++
++* V8: update to 3.17.13
++
++* os: use %SystemRoot% or %windir% in os.tmpdir() (Suwon Chae)
++
++* util: fix util.inspect() line width calculation (Marcin Kostrzewa)
++
++* buffer: remove _charsWritten (Trevor Norris)
++
++* fs: uv_[fl]stat now reports subsecond resolution (Timothy J Fontaine)
++
++* fs: Throw if error raised and missing callback (bnoordhuis)
++
++* tls: expose SSL_CTX_set_timeout via tls.createServer (Manav Rathi)
++
++* tls: remove harmful unnecessary bounds checking (Marcel Laverdet)
++
++* buffer: write ascii strings using WriteOneByte (Trevor Norris)
++
++* dtrace: fix generation of v8 constants on freebsd (Fedor Indutny)
++
++* dtrace: x64 ustack helper (Fedor Indutny)
++
++* readline: handle wide characters properly (Nao Iizuka)
++
++* repl: Use a domain to catch async errors safely (isaacs)
++
++* repl: emit 'reset' event when context is reset (Sami Samhuri)
++
++* util: custom `inspect()` method may return an Object (Nathan Rajlich)
++
++* console: `console.dir()` bypasses inspect() methods (Nathan Rajlich)
++
++
++2013.06.18, Version 0.10.12 (Stable), a088cf4f930d3928c97d239adf950ab43e7794aa
+ * npm: Upgrade to 1.2.32
+ * readline: make `ctrl + L` clear the screen (Yuan Chuan)
+ * v8: add setVariableValue debugger command (Ben Noordhuis)
+ * net: Do not destroy socket mid-write (isaacs)
+ * v8: fix build for mips32r2 architecture (Andrei Sedoi)
+ * configure: fix cross-compilation host_arch_cc() (Andrei Sedoi)
+ 2013.06.13, Version 0.10.11 (Stable), d9d5bc465450ae5d60da32e9ffcf71c2767f1fad
+ * uv: upgrade to 0.10.11
+ * npm: Upgrade to 1.2.30
+ * openssl: add missing configuration pieces for MIPS (Andrei Sedoi)
+ * Revert "http: remove bodyHead from 'upgrade' events" (isaacs)
+ * v8: fix pointer arithmetic undefined behavior (Trevor Norris)
+ * crypto: fix utf8/utf-8 encoding check (Ben Noordhuis)
+ * net: Fix busy loop on POLLERR|POLLHUP on older linux kernels (Ben Noordhuis, isaacs)
 -
  2013.06.04, Version 0.10.10 (Stable), 25e51c396aa23018603baae2b1d9390f5d9db496
  
  * uv: Upgrade to 0.10.10
  * src: tie process.versions.uv to uv_version_string() (Ben Noordhuis)
  
  
- 2013.03.28, Version 0.11.0 (Unstable), bce38b3d74e64fcb7d04a2dd551151da6168cdc5
- * V8: update to 3.17.13
- * os: use %SystemRoot% or %windir% in os.tmpdir() (Suwon Chae)
- * util: fix util.inspect() line width calculation (Marcin Kostrzewa)
- * buffer: remove _charsWritten (Trevor Norris)
- * fs: uv_[fl]stat now reports subsecond resolution (Timothy J Fontaine)
- * fs: Throw if error raised and missing callback (bnoordhuis)
- * tls: expose SSL_CTX_set_timeout via tls.createServer (Manav Rathi)
- * tls: remove harmful unnecessary bounds checking (Marcel Laverdet)
- * buffer: write ascii strings using WriteOneByte (Trevor Norris)
- * dtrace: fix generation of v8 constants on freebsd (Fedor Indutny)
- * dtrace: x64 ustack helper (Fedor Indutny)
- * readline: handle wide characters properly (Nao Iizuka)
- * repl: Use a domain to catch async errors safely (isaacs)
- * repl: emit 'reset' event when context is reset (Sami Samhuri)
- * util: custom `inspect()` method may return an Object (Nathan Rajlich)
- * console: `console.dir()` bypasses inspect() methods (Nathan Rajlich)
 -2013.03.28, Version 0.10.2 (Stable)
 +2013.03.28, Version 0.10.2 (Stable), 1e0de9c426e07a260bbec2d2196c2d2db8eb8886
  
  * npm: Upgrade to 1.2.15
  
diff --cc Makefile
Simple merge
diff --cc configure
Simple merge
Simple merge
Simple merge
index 6825fd2,0000000..1e0b94f
mode 100644,000000..100644
--- /dev/null
@@@ -1,501 -1,0 +1,496 @@@
- // an empty buffer for UPGRADE/CONNECT bodyHead compatibility
- var emptyBuffer = new Buffer(0);
 +// Copyright Joyent, Inc. and other Node contributors.
 +//
 +// 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.
 +
 +var util = require('util');
 +var net = require('net');
 +var EventEmitter = require('events').EventEmitter;
 +var HTTPParser = process.binding('http_parser').HTTPParser;
 +var assert = require('assert').ok;
 +
-       socket.unshift(bodyHead);
-       req.emit(eventName, res, socket, emptyBuffer);
 +var common = require('_http_common');
 +
 +var httpSocketSetup = common.httpSocketSetup;
 +var parsers = common.parsers;
 +var freeParser = common.freeParser;
 +var debug = common.debug;
 +
 +var IncomingMessage = require('_http_incoming').IncomingMessage;
 +var OutgoingMessage = require('_http_outgoing').OutgoingMessage;
 +
 +var agent = require('_http_agent');
 +var Agent = agent.Agent;
 +var globalAgent = agent.globalAgent;
 +
 +
 +function ClientRequest(options, cb) {
 +  var self = this;
 +  OutgoingMessage.call(self);
 +
 +  self.agent = options.agent === undefined ? globalAgent : options.agent;
 +
 +  var defaultPort = options.defaultPort || 80;
 +
 +  var port = options.port || defaultPort;
 +  var host = options.hostname || options.host || 'localhost';
 +
 +  if (options.setHost === undefined) {
 +    var setHost = true;
 +  }
 +
 +  self.socketPath = options.socketPath;
 +
 +  var method = self.method = (options.method || 'GET').toUpperCase();
 +  self.path = options.path || '/';
 +  if (cb) {
 +    self.once('response', cb);
 +  }
 +
 +  if (!Array.isArray(options.headers)) {
 +    if (options.headers) {
 +      var keys = Object.keys(options.headers);
 +      for (var i = 0, l = keys.length; i < l; i++) {
 +        var key = keys[i];
 +        self.setHeader(key, options.headers[key]);
 +      }
 +    }
 +    if (host && !this.getHeader('host') && setHost) {
 +      var hostHeader = host;
 +      if (port && +port !== defaultPort) {
 +        hostHeader += ':' + port;
 +      }
 +      this.setHeader('Host', hostHeader);
 +    }
 +  }
 +
 +  if (options.auth && !this.getHeader('Authorization')) {
 +    //basic auth
 +    this.setHeader('Authorization', 'Basic ' +
 +                   new Buffer(options.auth).toString('base64'));
 +  }
 +
 +  if (method === 'GET' || method === 'HEAD' || method === 'CONNECT') {
 +    self.useChunkedEncodingByDefault = false;
 +  } else {
 +    self.useChunkedEncodingByDefault = true;
 +  }
 +
 +  if (Array.isArray(options.headers)) {
 +    self._storeHeader(self.method + ' ' + self.path + ' HTTP/1.1\r\n',
 +                      options.headers);
 +  } else if (self.getHeader('expect')) {
 +    self._storeHeader(self.method + ' ' + self.path + ' HTTP/1.1\r\n',
 +                      self._renderHeaders());
 +  }
 +  if (self.socketPath) {
 +    self._last = true;
 +    self.shouldKeepAlive = false;
 +    if (options.createConnection) {
 +      self.onSocket(options.createConnection(self.socketPath));
 +    } else {
 +      self.onSocket(net.createConnection(self.socketPath));
 +    }
 +  } else if (self.agent) {
 +    // If there is an agent we should default to Connection:keep-alive.
 +    self._last = false;
 +    self.shouldKeepAlive = true;
 +    self.agent.addRequest(self, host, port, options.localAddress);
 +  } else {
 +    // No agent, default to Connection:close.
 +    self._last = true;
 +    self.shouldKeepAlive = false;
 +    if (options.createConnection) {
 +      options.port = port;
 +      options.host = host;
 +      var conn = options.createConnection(options);
 +    } else {
 +      var conn = net.createConnection({
 +        port: port,
 +        host: host,
 +        localAddress: options.localAddress
 +      });
 +    }
 +    self.onSocket(conn);
 +  }
 +
 +  self._deferToConnect(null, null, function() {
 +    self._flush();
 +    self = null;
 +  });
 +
 +}
 +util.inherits(ClientRequest, OutgoingMessage);
 +
 +exports.ClientRequest = ClientRequest;
 +
 +ClientRequest.prototype._implicitHeader = function() {
 +  this._storeHeader(this.method + ' ' + this.path + ' HTTP/1.1\r\n',
 +                    this._renderHeaders());
 +};
 +
 +ClientRequest.prototype.abort = function() {
 +  if (this.socket) {
 +    // in-progress
 +    this.socket.destroy();
 +  } else {
 +    // haven't been assigned a socket yet.
 +    // this could be more efficient, it could
 +    // remove itself from the pending requests
 +    this._deferToConnect('destroy', []);
 +  }
 +};
 +
 +
 +function createHangUpError() {
 +  var error = new Error('socket hang up');
 +  error.code = 'ECONNRESET';
 +  return error;
 +}
 +
 +
 +function socketCloseListener() {
 +  var socket = this;
 +  var parser = socket.parser;
 +  var req = socket._httpMessage;
 +  debug('HTTP socket close');
 +  req.emit('close');
 +  if (req.res && req.res.readable) {
 +    // Socket closed before we emitted 'end' below.
 +    req.res.emit('aborted');
 +    var res = req.res;
 +    res.on('end', function() {
 +      res.emit('close');
 +    });
 +    res.push(null);
 +  } else if (!req.res && !req.socket._hadError) {
 +    // This socket error fired before we started to
 +    // receive a response. The error needs to
 +    // fire on the request.
 +    req.emit('error', createHangUpError());
 +    req.socket._hadError = true;
 +  }
 +
 +  // Too bad.  That output wasn't getting written.
 +  // This is pretty terrible that it doesn't raise an error.
 +  // Fixed better in v0.10
 +  if (req.output)
 +    req.output.length = 0;
 +  if (req.outputEncodings)
 +    req.outputEncodings.length = 0;
 +
 +  if (parser) {
 +    parser.finish();
 +    freeParser(parser, req);
 +  }
 +}
 +
 +function socketErrorListener(err) {
 +  var socket = this;
 +  var parser = socket.parser;
 +  var req = socket._httpMessage;
 +  debug('SOCKET ERROR:', err.message, err.stack);
 +
 +  if (req) {
 +    req.emit('error', err);
 +    // For Safety. Some additional errors might fire later on
 +    // and we need to make sure we don't double-fire the error event.
 +    req.socket._hadError = true;
 +  }
 +
 +  if (parser) {
 +    parser.finish();
 +    freeParser(parser, req);
 +  }
 +  socket.destroy();
 +}
 +
 +function socketOnEnd() {
 +  var socket = this;
 +  var req = this._httpMessage;
 +  var parser = this.parser;
 +
 +  if (!req.res && !req.socket._hadError) {
 +    // If we don't have a response then we know that the socket
 +    // ended prematurely and we need to emit an error on the request.
 +    req.emit('error', createHangUpError());
 +    req.socket._hadError = true;
 +  }
 +  if (parser) {
 +    parser.finish();
 +    freeParser(parser, req);
 +  }
 +  socket.destroy();
 +}
 +
 +function socketOnData(d, start, end) {
 +  var socket = this;
 +  var req = this._httpMessage;
 +  var parser = this.parser;
 +
 +  var ret = parser.execute(d, start, end - start);
 +  if (ret instanceof Error) {
 +    debug('parse error');
 +    freeParser(parser, req);
 +    socket.destroy();
 +    req.emit('error', ret);
 +    req.socket._hadError = true;
 +  } else if (parser.incoming && parser.incoming.upgrade) {
 +    // Upgrade or CONNECT
 +    var bytesParsed = ret;
 +    var res = parser.incoming;
 +    req.res = res;
 +
 +    socket.ondata = null;
 +    socket.onend = null;
 +    parser.finish();
 +
 +    // This is start + byteParsed
 +    var bodyHead = d.slice(start + bytesParsed, end);
 +
 +    var eventName = req.method === 'CONNECT' ? 'connect' : 'upgrade';
 +    if (EventEmitter.listenerCount(req, eventName) > 0) {
 +      req.upgradeOrConnect = true;
 +
 +      // detach the socket
 +      socket.emit('agentRemove');
 +      socket.removeListener('close', socketCloseListener);
 +      socket.removeListener('error', socketErrorListener);
 +
++      req.emit(eventName, res, socket, bodyHead);
 +      req.emit('close');
 +    } else {
 +      // Got Upgrade header or CONNECT method, but have no handler.
 +      socket.destroy();
 +    }
 +    freeParser(parser, req);
 +  } else if (parser.incoming && parser.incoming.complete &&
 +             // When the status code is 100 (Continue), the server will
 +             // send a final response after this client sends a request
 +             // body. So, we must not free the parser.
 +             parser.incoming.statusCode !== 100) {
 +    freeParser(parser, req);
 +  }
 +}
 +
 +
 +// client
 +function parserOnIncomingClient(res, shouldKeepAlive) {
 +  var parser = this;
 +  var socket = this.socket;
 +  var req = socket._httpMessage;
 +
 +
 +  // propogate "domain" setting...
 +  if (req.domain && !res.domain) {
 +    debug('setting "res.domain"');
 +    res.domain = req.domain;
 +  }
 +
 +  debug('AGENT incoming response!');
 +
 +  if (req.res) {
 +    // We already have a response object, this means the server
 +    // sent a double response.
 +    socket.destroy();
 +    return;
 +  }
 +  req.res = res;
 +
 +  // Responses to CONNECT request is handled as Upgrade.
 +  if (req.method === 'CONNECT') {
 +    res.upgrade = true;
 +    return true; // skip body
 +  }
 +
 +  // Responses to HEAD requests are crazy.
 +  // HEAD responses aren't allowed to have an entity-body
 +  // but *can* have a content-length which actually corresponds
 +  // to the content-length of the entity-body had the request
 +  // been a GET.
 +  var isHeadResponse = req.method == 'HEAD';
 +  debug('AGENT isHeadResponse', isHeadResponse);
 +
 +  if (res.statusCode == 100) {
 +    // restart the parser, as this is a continue message.
 +    delete req.res; // Clear res so that we don't hit double-responses.
 +    req.emit('continue');
 +    return true;
 +  }
 +
 +  if (req.shouldKeepAlive && !shouldKeepAlive && !req.upgradeOrConnect) {
 +    // Server MUST respond with Connection:keep-alive for us to enable it.
 +    // If we've been upgraded (via WebSockets) we also shouldn't try to
 +    // keep the connection open.
 +    req.shouldKeepAlive = false;
 +  }
 +
 +
 +  DTRACE_HTTP_CLIENT_RESPONSE(socket, req);
 +  COUNTER_HTTP_CLIENT_RESPONSE();
 +  req.res = res;
 +  res.req = req;
 +
 +  // add our listener first, so that we guarantee socket cleanup
 +  res.on('end', responseOnEnd);
 +  var handled = req.emit('response', res);
 +
 +  // If the user did not listen for the 'response' event, then they
 +  // can't possibly read the data, so we ._dump() it into the void
 +  // so that the socket doesn't hang there in a paused state.
 +  if (!handled)
 +    res._dump();
 +
 +  return isHeadResponse;
 +}
 +
 +// client
 +function responseOnEnd() {
 +  var res = this;
 +  var req = res.req;
 +  var socket = req.socket;
 +
 +  if (!req.shouldKeepAlive) {
 +    if (socket.writable) {
 +      debug('AGENT socket.destroySoon()');
 +      socket.destroySoon();
 +    }
 +    assert(!socket.writable);
 +  } else {
 +    debug('AGENT socket keep-alive');
 +    if (req.timeoutCb) {
 +      socket.setTimeout(0, req.timeoutCb);
 +      req.timeoutCb = null;
 +    }
 +    socket.removeListener('close', socketCloseListener);
 +    socket.removeListener('error', socketErrorListener);
 +    // Mark this socket as available, AFTER user-added end
 +    // handlers have a chance to run.
 +    process.nextTick(function() {
 +      socket.emit('free');
 +    });
 +  }
 +}
 +
 +ClientRequest.prototype.onSocket = function(socket) {
 +  var req = this;
 +
 +  process.nextTick(function() {
 +    var parser = parsers.alloc();
 +    req.socket = socket;
 +    req.connection = socket;
 +    parser.reinitialize(HTTPParser.RESPONSE);
 +    parser.socket = socket;
 +    parser.incoming = null;
 +    req.parser = parser;
 +
 +    socket.parser = parser;
 +    socket._httpMessage = req;
 +
 +    // Setup "drain" propogation.
 +    httpSocketSetup(socket);
 +
 +    // Propagate headers limit from request object to parser
 +    if (typeof req.maxHeadersCount === 'number') {
 +      parser.maxHeaderPairs = req.maxHeadersCount << 1;
 +    } else {
 +      // Set default value because parser may be reused from FreeList
 +      parser.maxHeaderPairs = 2000;
 +    }
 +
 +    socket.on('error', socketErrorListener);
 +    socket.ondata = socketOnData;
 +    socket.onend = socketOnEnd;
 +    socket.on('close', socketCloseListener);
 +    parser.onIncoming = parserOnIncomingClient;
 +    req.emit('socket', socket);
 +  });
 +
 +};
 +
 +ClientRequest.prototype._deferToConnect = function(method, arguments_, cb) {
 +  // This function is for calls that need to happen once the socket is
 +  // connected and writable. It's an important promisy thing for all the socket
 +  // calls that happen either now (when a socket is assigned) or
 +  // in the future (when a socket gets assigned out of the pool and is
 +  // eventually writable).
 +  var self = this;
 +  var onSocket = function() {
 +    if (self.socket.writable) {
 +      if (method) {
 +        self.socket[method].apply(self.socket, arguments_);
 +      }
 +      if (cb) { cb(); }
 +    } else {
 +      self.socket.once('connect', function() {
 +        if (method) {
 +          self.socket[method].apply(self.socket, arguments_);
 +        }
 +        if (cb) { cb(); }
 +      });
 +    }
 +  }
 +  if (!self.socket) {
 +    self.once('socket', onSocket);
 +  } else {
 +    onSocket();
 +  }
 +};
 +
 +ClientRequest.prototype.setTimeout = function(msecs, callback) {
 +  if (callback) this.once('timeout', callback);
 +
 +  var self = this;
 +  function emitTimeout() {
 +    self.emit('timeout');
 +  }
 +
 +  if (this.socket && this.socket.writable) {
 +    if (this.timeoutCb)
 +      this.socket.setTimeout(0, this.timeoutCb);
 +    this.timeoutCb = emitTimeout;
 +    this.socket.setTimeout(msecs, emitTimeout);
 +    return;
 +  }
 +
 +  // Set timeoutCb so that it'll get cleaned up on request end
 +  this.timeoutCb = emitTimeout;
 +  if (this.socket) {
 +    var sock = this.socket;
 +    this.socket.once('connect', function() {
 +      sock.setTimeout(msecs, emitTimeout);
 +    });
 +    return;
 +  }
 +
 +  this.once('socket', function(sock) {
 +    sock.setTimeout(msecs, emitTimeout);
 +  });
 +};
 +
 +ClientRequest.prototype.setNoDelay = function() {
 +  this._deferToConnect('setNoDelay', arguments);
 +};
 +ClientRequest.prototype.setSocketKeepAlive = function() {
 +  this._deferToConnect('setKeepAlive', arguments);
 +};
 +
 +ClientRequest.prototype.clearTimeout = function(cb) {
 +  this.setTimeout(0, cb);
 +};
index 9d1adc6,0000000..700cdea
mode 100644,000000..100644
--- /dev/null
@@@ -1,459 -1,0 +1,455 @@@
- // an empty buffer for UPGRADE/CONNECT bodyHead compatibility
- var emptyBuffer = new Buffer(0);
 +// Copyright Joyent, Inc. and other Node contributors.
 +//
 +// 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.
 +
 +var util = require('util');
 +var net = require('net');
 +var EventEmitter = require('events').EventEmitter;
 +var HTTPParser = process.binding('http_parser').HTTPParser;
 +var assert = require('assert').ok;
 +
-         socket.unshift(bodyHead);
-         self.emit(eventName, req, req.socket, emptyBuffer);
 +var common = require('_http_common');
 +var parsers = common.parsers;
 +var freeParser = common.freeParser;
 +var debug = common.debug;
 +var CRLF = common.CRLF;
 +var continueExpression = common.continueExpression;
 +var chunkExpression = common.chunkExpression;
 +var httpSocketSetup = common.httpSocketSetup;
 +
 +var OutgoingMessage = require('_http_outgoing').OutgoingMessage;
 +
 +
 +var STATUS_CODES = exports.STATUS_CODES = {
 +  100 : 'Continue',
 +  101 : 'Switching Protocols',
 +  102 : 'Processing',                 // RFC 2518, obsoleted by RFC 4918
 +  200 : 'OK',
 +  201 : 'Created',
 +  202 : 'Accepted',
 +  203 : 'Non-Authoritative Information',
 +  204 : 'No Content',
 +  205 : 'Reset Content',
 +  206 : 'Partial Content',
 +  207 : 'Multi-Status',               // RFC 4918
 +  300 : 'Multiple Choices',
 +  301 : 'Moved Permanently',
 +  302 : 'Moved Temporarily',
 +  303 : 'See Other',
 +  304 : 'Not Modified',
 +  305 : 'Use Proxy',
 +  307 : 'Temporary Redirect',
 +  400 : 'Bad Request',
 +  401 : 'Unauthorized',
 +  402 : 'Payment Required',
 +  403 : 'Forbidden',
 +  404 : 'Not Found',
 +  405 : 'Method Not Allowed',
 +  406 : 'Not Acceptable',
 +  407 : 'Proxy Authentication Required',
 +  408 : 'Request Time-out',
 +  409 : 'Conflict',
 +  410 : 'Gone',
 +  411 : 'Length Required',
 +  412 : 'Precondition Failed',
 +  413 : 'Request Entity Too Large',
 +  414 : 'Request-URI Too Large',
 +  415 : 'Unsupported Media Type',
 +  416 : 'Requested Range Not Satisfiable',
 +  417 : 'Expectation Failed',
 +  418 : 'I\'m a teapot',              // RFC 2324
 +  422 : 'Unprocessable Entity',       // RFC 4918
 +  423 : 'Locked',                     // RFC 4918
 +  424 : 'Failed Dependency',          // RFC 4918
 +  425 : 'Unordered Collection',       // RFC 4918
 +  426 : 'Upgrade Required',           // RFC 2817
 +  428 : 'Precondition Required',      // RFC 6585
 +  429 : 'Too Many Requests',          // RFC 6585
 +  431 : 'Request Header Fields Too Large',// RFC 6585
 +  500 : 'Internal Server Error',
 +  501 : 'Not Implemented',
 +  502 : 'Bad Gateway',
 +  503 : 'Service Unavailable',
 +  504 : 'Gateway Time-out',
 +  505 : 'HTTP Version Not Supported',
 +  506 : 'Variant Also Negotiates',    // RFC 2295
 +  507 : 'Insufficient Storage',       // RFC 4918
 +  509 : 'Bandwidth Limit Exceeded',
 +  510 : 'Not Extended',               // RFC 2774
 +  511 : 'Network Authentication Required' // RFC 6585
 +};
 +
 +
 +function ServerResponse(req) {
 +  OutgoingMessage.call(this);
 +
 +  if (req.method === 'HEAD') this._hasBody = false;
 +
 +  this.sendDate = true;
 +
 +  if (req.httpVersionMajor < 1 || req.httpVersionMinor < 1) {
 +    this.useChunkedEncodingByDefault = chunkExpression.test(req.headers.te);
 +    this.shouldKeepAlive = false;
 +  }
 +}
 +util.inherits(ServerResponse, OutgoingMessage);
 +
 +
 +exports.ServerResponse = ServerResponse;
 +
 +ServerResponse.prototype.statusCode = 200;
 +
 +function onServerResponseClose() {
 +  // EventEmitter.emit makes a copy of the 'close' listeners array before
 +  // calling the listeners. detachSocket() unregisters onServerResponseClose
 +  // but if detachSocket() is called, directly or indirectly, by a 'close'
 +  // listener, onServerResponseClose is still in that copy of the listeners
 +  // array. That is, in the example below, b still gets called even though
 +  // it's been removed by a:
 +  //
 +  //   var obj = new events.EventEmitter;
 +  //   obj.on('event', a);
 +  //   obj.on('event', b);
 +  //   function a() { obj.removeListener('event', b) }
 +  //   function b() { throw "BAM!" }
 +  //   obj.emit('event');  // throws
 +  //
 +  // Ergo, we need to deal with stale 'close' events and handle the case
 +  // where the ServerResponse object has already been deconstructed.
 +  // Fortunately, that requires only a single if check. :-)
 +  if (this._httpMessage) this._httpMessage.emit('close');
 +}
 +
 +ServerResponse.prototype.assignSocket = function(socket) {
 +  assert(!socket._httpMessage);
 +  socket._httpMessage = this;
 +  socket.on('close', onServerResponseClose);
 +  this.socket = socket;
 +  this.connection = socket;
 +  this.emit('socket', socket);
 +  this._flush();
 +};
 +
 +ServerResponse.prototype.detachSocket = function(socket) {
 +  assert(socket._httpMessage == this);
 +  socket.removeListener('close', onServerResponseClose);
 +  socket._httpMessage = null;
 +  this.socket = this.connection = null;
 +};
 +
 +ServerResponse.prototype.writeContinue = function() {
 +  this._writeRaw('HTTP/1.1 100 Continue' + CRLF + CRLF, 'ascii');
 +  this._sent100 = true;
 +};
 +
 +ServerResponse.prototype._implicitHeader = function() {
 +  this.writeHead(this.statusCode);
 +};
 +
 +ServerResponse.prototype.writeHead = function(statusCode) {
 +  var reasonPhrase, headers, headerIndex;
 +
 +  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];
 +
 +  if (obj && this._headers) {
 +    // Slow-case: when progressive API and header fields are passed.
 +    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 (headers[field] !== undefined) {
 +          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];
 +      }
 +    }
 +  } else if (this._headers) {
 +    // only progressive api is used
 +    headers = this._renderHeaders();
 +  } else {
 +    // only writeHead() called
 +    headers = obj;
 +  }
 +
 +  var statusLine = 'HTTP/1.1 ' + statusCode.toString() + ' ' +
 +                   reasonPhrase + CRLF;
 +
 +  if (statusCode === 204 || statusCode === 304 ||
 +      (100 <= statusCode && statusCode <= 199)) {
 +    // RFC 2616, 10.2.5:
 +    // The 204 response MUST NOT include a message-body, and thus is always
 +    // terminated by the first empty line after the header fields.
 +    // RFC 2616, 10.3.5:
 +    // The 304 response MUST NOT contain a message-body, and thus is always
 +    // terminated by the first empty line after the header fields.
 +    // RFC 2616, 10.1 Informational 1xx:
 +    // This class of status code indicates a provisional response,
 +    // consisting only of the Status-Line and optional headers, and is
 +    // terminated by an empty line.
 +    this._hasBody = false;
 +  }
 +
 +  // don't keep alive connections where the client expects 100 Continue
 +  // but we sent a final status; they may put extra bytes on the wire.
 +  if (this._expect_continue && !this._sent100) {
 +    this.shouldKeepAlive = false;
 +  }
 +
 +  this._storeHeader(statusLine, headers);
 +};
 +
 +ServerResponse.prototype.writeHeader = function() {
 +  this.writeHead.apply(this, arguments);
 +};
 +
 +
 +function Server(requestListener) {
 +  if (!(this instanceof Server)) return new Server(requestListener);
 +  net.Server.call(this, { allowHalfOpen: true });
 +
 +  if (requestListener) {
 +    this.addListener('request', requestListener);
 +  }
 +
 +  // Similar option to this. Too lazy to write my own docs.
 +  // http://www.squid-cache.org/Doc/config/half_closed_clients/
 +  // http://wiki.squid-cache.org/SquidFaq/InnerWorkings#What_is_a_half-closed_filedescriptor.3F
 +  this.httpAllowHalfOpen = false;
 +
 +  this.addListener('connection', connectionListener);
 +
 +  this.addListener('clientError', function(err, conn) {
 +    conn.destroy(err);
 +  });
 +
 +  this.timeout = 2 * 60 * 1000;
 +}
 +util.inherits(Server, net.Server);
 +
 +
 +Server.prototype.setTimeout = function(msecs, callback) {
 +  this.timeout = msecs;
 +  if (callback)
 +    this.on('timeout', callback);
 +};
 +
 +
 +exports.Server = Server;
 +
 +
 +function connectionListener(socket) {
 +  var self = this;
 +  var outgoing = [];
 +  var incoming = [];
 +
 +  function abortIncoming() {
 +    while (incoming.length) {
 +      var req = incoming.shift();
 +      req.emit('aborted');
 +      req.emit('close');
 +    }
 +    // abort socket._httpMessage ?
 +  }
 +
 +  function serverSocketCloseListener() {
 +    debug('server socket close');
 +    // mark this parser as reusable
 +    if (this.parser)
 +      freeParser(this.parser);
 +
 +    abortIncoming();
 +  }
 +
 +  debug('SERVER new http connection');
 +
 +  httpSocketSetup(socket);
 +
 +  // If the user has added a listener to the server,
 +  // request, or response, then it's their responsibility.
 +  // otherwise, destroy on timeout by default
 +  if (self.timeout)
 +    socket.setTimeout(self.timeout);
 +  socket.on('timeout', function() {
 +    var req = socket.parser && socket.parser.incoming;
 +    var reqTimeout = req && !req.complete && req.emit('timeout', socket);
 +    var res = socket._httpMessage;
 +    var resTimeout = res && res.emit('timeout', socket);
 +    var serverTimeout = self.emit('timeout', socket);
 +
 +    if (!reqTimeout && !resTimeout && !serverTimeout)
 +      socket.destroy();
 +  });
 +
 +  var parser = parsers.alloc();
 +  parser.reinitialize(HTTPParser.REQUEST);
 +  parser.socket = socket;
 +  socket.parser = parser;
 +  parser.incoming = null;
 +
 +  // Propagate headers limit from server instance to parser
 +  if (typeof this.maxHeadersCount === 'number') {
 +    parser.maxHeaderPairs = this.maxHeadersCount << 1;
 +  } else {
 +    // Set default value because parser may be reused from FreeList
 +    parser.maxHeaderPairs = 2000;
 +  }
 +
 +  socket.addListener('error', function(e) {
 +    self.emit('clientError', e, this);
 +  });
 +
 +  socket.ondata = function(d, start, end) {
 +    var ret = parser.execute(d, start, end - start);
 +    if (ret instanceof Error) {
 +      debug('parse error');
 +      socket.destroy(ret);
 +    } else if (parser.incoming && parser.incoming.upgrade) {
 +      // Upgrade or CONNECT
 +      var bytesParsed = ret;
 +      var req = parser.incoming;
 +
 +      socket.ondata = null;
 +      socket.onend = null;
 +      socket.removeListener('close', serverSocketCloseListener);
 +      parser.finish();
 +      freeParser(parser, req);
 +
 +      var eventName = req.method === 'CONNECT' ? 'connect' : 'upgrade';
 +      if (EventEmitter.listenerCount(self, eventName) > 0) {
 +        // This is start + byteParsed
 +        var bodyHead = d.slice(start + bytesParsed, end);
 +
++        self.emit(eventName, req, req.socket, bodyHead);
 +      } else {
 +        // Got upgrade header or CONNECT method, but have no handler.
 +        socket.destroy();
 +      }
 +    }
 +  };
 +
 +  socket.onend = function() {
 +    var ret = parser.finish();
 +
 +    if (ret instanceof Error) {
 +      debug('parse error');
 +      socket.destroy(ret);
 +      return;
 +    }
 +
 +    if (!self.httpAllowHalfOpen) {
 +      abortIncoming();
 +      if (socket.writable) socket.end();
 +    } else if (outgoing.length) {
 +      outgoing[outgoing.length - 1]._last = true;
 +    } else if (socket._httpMessage) {
 +      socket._httpMessage._last = true;
 +    } else {
 +      if (socket.writable) socket.end();
 +    }
 +  };
 +
 +  socket.addListener('close', serverSocketCloseListener);
 +
 +  // The following callback is issued after the headers have been read on a
 +  // new message. In this callback we setup the response object and pass it
 +  // to the user.
 +  parser.onIncoming = function(req, shouldKeepAlive) {
 +    incoming.push(req);
 +
 +    var res = new ServerResponse(req);
 +
 +    res.shouldKeepAlive = shouldKeepAlive;
 +    DTRACE_HTTP_SERVER_REQUEST(req, socket);
 +    COUNTER_HTTP_SERVER_REQUEST();
 +
 +    if (socket._httpMessage) {
 +      // There are already pending outgoing res, append.
 +      outgoing.push(res);
 +    } else {
 +      res.assignSocket(socket);
 +    }
 +
 +    // When we're finished writing the response, check if this is the last
 +    // respose, if so destroy the socket.
 +    res.on('finish', function() {
 +      // Usually the first incoming element should be our request.  it may
 +      // be that in the case abortIncoming() was called that the incoming
 +      // array will be empty.
 +      assert(incoming.length == 0 || incoming[0] === req);
 +
 +      incoming.shift();
 +
 +      // if the user never called req.read(), and didn't pipe() or
 +      // .resume() or .on('data'), then we call req._dump() so that the
 +      // bytes will be pulled off the wire.
 +      if (!req._consuming)
 +        req._dump();
 +
 +      res.detachSocket(socket);
 +
 +      if (res._last) {
 +        socket.destroySoon();
 +      } else {
 +        // start sending the next message
 +        var m = outgoing.shift();
 +        if (m) {
 +          m.assignSocket(socket);
 +        }
 +      }
 +    });
 +
 +    if (req.headers.expect !== undefined &&
 +        (req.httpVersionMajor == 1 && req.httpVersionMinor == 1) &&
 +        continueExpression.test(req.headers['expect'])) {
 +      res._expect_continue = true;
 +      if (EventEmitter.listenerCount(self, 'checkContinue') > 0) {
 +        self.emit('checkContinue', req, res);
 +      } else {
 +        res.writeContinue();
 +        self.emit('request', req, res);
 +      }
 +    } else {
 +      self.emit('request', req, res);
 +    }
 +    return false; // Not a HEAD response. (Not even a response!)
 +  };
 +}
 +exports._connectionListener = connectionListener;
diff --cc lib/crypto.js
Simple merge
diff --cc lib/net.js
Simple merge
diff --cc lib/readline.js
Simple merge
Simple merge