Merge branch 'v0.4'
authorisaacs <i@izs.me>
Sun, 8 May 2011 03:38:32 +0000 (20:38 -0700)
committerisaacs <i@izs.me>
Sun, 8 May 2011 03:38:32 +0000 (20:38 -0700)
Conflicts:
lib/tls.js
lib/url.js
src/node_version.h
test/simple/test-buffer.js
test/simple/test-url.js

13 files changed:
1  2 
doc/api/http.markdown
lib/buffer.js
lib/http.js
lib/repl.js
lib/tls.js
src/node.cc
src/node.h
src/platform_sunos.cc
test/message/stack_overflow.out
test/message/throw_custom_error.out
test/message/throw_non_error.out
test/simple/test-buffer.js
wscript

diff --combined doc/api/http.markdown
@@@ -88,7 -88,7 +88,7 @@@ sent to the server on that socket
  
  If a client connection emits an 'error' event - it will forwarded here.
  
- ### http.createServer(requestListener)
+ ### http.createServer([requestListener])
  
  Returns a new web server object.
  
@@@ -368,7 -368,6 +368,7 @@@ Options
  
  - `host`: A domain name or IP address of the server to issue the request to.
  - `port`: Port of remote server.
 +- `socketPath`: Unix Domain Socket (use one of host:port or socketPath)
  - `method`: A string specifying the HTTP request method. Possible values:
    `'GET'` (default), `'POST'`, `'PUT'`, and `'DELETE'`.
  - `path`: Request path. Should include query string and fragments if any.
@@@ -397,6 -396,10 +397,10 @@@ Example
        });
      });
  
+     req.on('error', function(e) {
+       console.log('problem with request: ' + e.message);
+     });
      // write data to request body
      req.write('data\n');
      req.write('data\n');
@@@ -444,19 -447,13 +448,19 @@@ Example
  
  
  ## http.Agent
 -## http.getAgent(host, port)
 +## http.getAgent(options)
  
  `http.request()` uses a special `Agent` for managing multiple connections to
  an HTTP server. Normally `Agent` instances should not be exposed to user
  code, however in certain situations it's useful to check the status of the
  agent. The `http.getAgent()` function allows you to access the agents.
  
 +Options:
 +
 +- `host`: A domain name or IP address of the server to issue the request to.
 +- `port`: Port of remote server.
 +- `socketPath`: Unix Domain Socket (use one of host:port or socketPath)
 +
  ### Event: 'upgrade'
  
  `function (request, socket, head)`
diff --combined lib/buffer.js
@@@ -20,7 -20,6 +20,7 @@@
  // USE OR OTHER DEALINGS IN THE SOFTWARE.
  
  var SlowBuffer = process.binding('buffer').SlowBuffer;
 +var assert = require('assert');
  
  
  function toHex(n) {
@@@ -39,21 -38,6 +39,21 @@@ SlowBuffer.prototype.inspect = function
  };
  
  
 +SlowBuffer.prototype.hexSlice = function(start, end) {
 +  var len = this.length;
 +
 +  if (!start || start < 0) start = 0;
 +  if (!end || end < 0 || end > len) end = len;
 +
 +  var out = '';
 +  for (var i = start; i < end; i++) {
 +    out += toHex(this[i]);
 +  }
 +  return out;
 +};
 +
 +
 +
  SlowBuffer.prototype.toString = function(encoding, start, end) {
    encoding = String(encoding || 'utf8').toLowerCase();
    start = +start || 0;
@@@ -65,9 -49,6 +65,9 @@@
    }
  
    switch (encoding) {
 +    case 'hex':
 +      return this.hexSlice(start, end);
 +
      case 'utf8':
      case 'utf-8':
        return this.utf8Slice(start, end);
  };
  
  
 +SlowBuffer.prototype.hexWrite = function(string, offset) {
 +  var len = string.length;
 +  offset = +offset || 0;
 +
 +  // must be an even number of digits
 +  if (len % 2) {
 +    throw new Error('Invalid hex string');
 +  }
 +  for (var i = 0; i < len / 2; i++) {
 +    var byte = parseInt(string.substr(i * 2, 2), 16);
 +    if (isNaN(byte)) throw new Error('Invalid hex string');
 +    this[offset + i] = byte;
 +  }
 +  return i;
 +};
 +
 +
  SlowBuffer.prototype.write = function(string, offset, encoding) {
    // Support both (string, offset, encoding)
    // and the legacy (string, encoding, offset)
    encoding = String(encoding || 'utf8').toLowerCase();
  
    switch (encoding) {
 +    case 'hex':
 +      return this.hexWrite(string, offset);
 +
      case 'utf8':
      case 'utf-8':
        return this.utf8Write(string, offset);
  
      case 'ucs2':
      case 'ucs-2':
-       return this.ucs2Write(start, end);
+       return this.ucs2Write(string, offset);
  
      default:
        throw new Error('Unknown encoding');
@@@ -286,10 -247,6 +286,10 @@@ Buffer.prototype.write = function(strin
  
    var ret;
    switch (encoding) {
 +    case 'hex':
 +      ret = this.parent.hexWrite(string, this.offset + offset, maxLength);
 +      break;
 +
      case 'utf8':
      case 'utf-8':
        ret = this.parent.utf8Write(string, this.offset + offset, maxLength);
@@@ -343,9 -300,6 +343,9 @@@ Buffer.prototype.toString = function(en
    end = end + this.offset;
  
    switch (encoding) {
 +    case 'hex':
 +      return this.parent.hexSlice(start, end);
 +
      case 'utf8':
      case 'utf-8':
        return this.parent.utf8Slice(start, end);
  Buffer.byteLength = SlowBuffer.byteLength;
  
  
 +// fill(value, start=0, end=buffer.length)
 +Buffer.prototype.fill = function fill (value, start, end) {
 +  value || (value = 0);
 +  start || (start = 0);
 +  end   || (end   = this.length);
 +
 +  if (typeof value === "string") {
 +    value = value.charCodeAt(0);
 +  }
 +  if (!(typeof value === "number") || isNaN(value)) {
 +    throw new Error("value is not a number");
 +  }
 +
 +  if (end < start) throw new Error("end < start");
 +
 +  // Fill 0 bytes; we're done
 +  if (end === start) return 0;
 +  if (this.length == 0) return 0;
 +
 +  if (start < 0 || start >= this.length) {
 +    throw new Error("start out of bounds");
 +  }
 +
 +  if (end < 0 || end > this.length) {
 +    throw new Error("end out of bounds");
 +  }
 +
 +  return this.parent.fill(value,
 +                          start + this.offset,
 +                          end + this.offset);
 +};
 +
 +
  // copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)
  Buffer.prototype.copy = function(target, target_start, start, end) {
    var source = this;
@@@ -483,444 -404,3 +483,444 @@@ Buffer.prototype.asciiWrite = function(
    return this.write(string, offset, 'ascii');
  };
  
 +Buffer.prototype.readUInt8 = function(offset, endian) {
 +  var buffer = this;
 +
 +  assert.ok(endian !== undefined && endian !== null,
 +    'missing endian');
 +
 +  assert.ok(endian == 'big' || endian == 'little',
 +    'bad endian value');
 +
 +  assert.ok(offset !== undefined && offset !== null,
 +    'missing offset');
 +
 +  assert.ok(offset < buffer.length,
 +    'Trying to read beyond buffer length');
 +
 +  return buffer[offset];
 +};
 +
 +
 +Buffer.prototype.readUInt16 = function(offset, endian) {
 +  var val = 0;
 +  var buffer = this;
 +
 +  assert.ok(endian !== undefined && endian !== null,
 +    'missing endian');
 +
 +  assert.ok(endian == 'big' || endian == 'little',
 +    'bad endian value');
 +
 +  assert.ok(offset !== undefined && offset !== null,
 +    'missing offset');
 +
 +  assert.ok(offset + 1 < buffer.length,
 +    'Trying to read beyond buffer length');
 +
 +  if (endian == 'big') {
 +    val = buffer[offset] << 8;
 +    val |= buffer[offset + 1];
 +  } else {
 +    val = buffer[offset];
 +    val |= buffer[offset + 1] << 8;
 +  }
 +
 +  return val;
 +};
 +
 +
 +Buffer.prototype.readUInt32 = function(offset, endian) {
 +  var val = 0;
 +  var buffer = this;
 +
 +  assert.ok(endian !== undefined && endian !== null,
 +    'missing endian');
 +
 +  assert.ok(endian == 'big' || endian == 'little',
 +    'bad endian value');
 +
 +  assert.ok(offset !== undefined && offset !== null,
 +    'missing offset');
 +
 +  assert.ok(offset + 3 < buffer.length,
 +    'Trying to read beyond buffer length');
 +
 +  if (endian == 'big') {
 +    val = buffer[offset + 1] << 16;
 +    val |= buffer[offset + 2] << 8;
 +    val |= buffer[offset + 3];
 +    val = val + (buffer[offset] << 24 >>> 0);
 +  } else {
 +    val = buffer[offset + 2] << 16;
 +    val |= buffer[offset + 1] << 8;
 +    val |= buffer[offset];
 +    val = val + (buffer[offset + 3] << 24 >>> 0);
 +  }
 +
 +  return val;
 +};
 +
 +
 +/*
 + * Signed integer types, yay team! A reminder on how two's complement actually
 + * works. The first bit is the signed bit, i.e. tells us whether or not the
 + * number should be positive or negative. If the two's complement value is
 + * positive, then we're done, as it's equivalent to the unsigned representation.
 + *
 + * Now if the number is positive, you're pretty much done, you can just leverage
 + * the unsigned translations and return those. Unfortunately, negative numbers
 + * aren't quite that straightforward.
 + *
 + * At first glance, one might be inclined to use the traditional formula to
 + * translate binary numbers between the positive and negative values in two's
 + * complement. (Though it doesn't quite work for the most negative value)
 + * Mainly:
 + *  - invert all the bits
 + *  - add one to the result
 + *
 + * Of course, this doesn't quite work in Javascript. Take for example the value
 + * of -128. This could be represented in 16 bits (big-endian) as 0xff80. But of
 + * course, Javascript will do the following:
 + *
 + * > ~0xff80
 + * -65409
 + *
 + * Whoh there, Javascript, that's not quite right. But wait, according to
 + * Javascript that's perfectly correct. When Javascript ends up seeing the
 + * constant 0xff80, it has no notion that it is actually a signed number. It
 + * assumes that we've input the unsigned value 0xff80. Thus, when it does the
 + * binary negation, it casts it into a signed value, (positive 0xff80). Then
 + * when you perform binary negation on that, it turns it into a negative number.
 + *
 + * Instead, we're going to have to use the following general formula, that works
 + * in a rather Javascript friendly way. I'm glad we don't support this kind of
 + * weird numbering scheme in the kernel.
 + *
 + * (BIT-MAX - (unsigned)val + 1) * -1
 + *
 + * The astute observer, may think that this doesn't make sense for 8-bit numbers
 + * (really it isn't necessary for them). However, when you get 16-bit numbers,
 + * you do. Let's go back to our prior example and see how this will look:
 + *
 + * (0xffff - 0xff80 + 1) * -1
 + * (0x007f + 1) * -1
 + * (0x0080) * -1
 + */
 +Buffer.prototype.readInt8 = function(offset, endian) {
 +  var buffer = this;
 +  var neg;
 +
 +  assert.ok(endian !== undefined && endian !== null,
 +    'missing endian');
 +
 +  assert.ok(endian == 'big' || endian == 'little',
 +    'bad endian value');
 +
 +  assert.ok(offset !== undefined && offset !== null,
 +    'missing offset');
 +
 +  assert.ok(offset < buffer.length,
 +    'Trying to read beyond buffer length');
 +
 +  neg = buffer[offset] & 0x80;
 +  if (!neg) {
 +    return (buffer[offset]);
 +  }
 +
 +  return ((0xff - buffer[offset] + 1) * -1);
 +};
 +
 +
 +Buffer.prototype.readInt16 = function(offset, endian) {
 +  var buffer = this;
 +  var neg;
 +
 +  assert.ok(endian !== undefined && endian !== null,
 +    'missing endian');
 +
 +  assert.ok(endian == 'big' || endian == 'little',
 +    'bad endian value');
 +
 +  assert.ok(offset !== undefined && offset !== null,
 +    'missing offset');
 +
 +  assert.ok(offset + 1 < buffer.length,
 +    'Trying to read beyond buffer length');
 +
 +  val = buffer.readUInt16(offset, endian);
 +  neg = val & 0x8000;
 +  if (!neg) {
 +    return val;
 +  }
 +
 +  return (0xffff - val + 1) * -1;
 +};
 +
 +
 +Buffer.prototype.readInt32 = function(offset, endian) {
 +  var buffer = this;
 +  var neg;
 +
 +  assert.ok(endian !== undefined && endian !== null,
 +    'missing endian');
 +
 +  assert.ok(endian == 'big' || endian == 'little',
 +    'bad endian value');
 +
 +  assert.ok(offset !== undefined && offset !== null,
 +    'missing offset');
 +
 +  assert.ok(offset + 3 < buffer.length,
 +    'Trying to read beyond buffer length');
 +
 +  val = buffer.readUInt32(offset, endian);
 +  neg = val & 0x80000000;
 +  if (!neg) {
 +    return (val);
 +  }
 +
 +  return (0xffffffff - val + 1) * -1;
 +};
 +
 +
 +/*
 + * We have to make sure that the value is a valid integer. This means that it is
 + * non-negative. It has no fractional component and that it does not exceed the
 + * maximum allowed value.
 + *
 + *      value           The number to check for validity
 + *
 + *      max             The maximum value
 + */
 +function verifuint(value, max) {
 +  assert.ok(typeof (value) == 'number',
 +    'cannot write a non-number as a number');
 +
 +  assert.ok(value >= 0,
 +    'specified a negative value for writing an unsigned value');
 +
 +  assert.ok(value <= max, 'value is larger than maximum value for type');
 +
 +  assert.ok(Math.floor(value) === value, 'value has a fractional component');
 +}
 +
 +
 +Buffer.prototype.writeUInt8 = function(value, offset, endian) {
 +  var buffer = this;
 +
 +  assert.ok(value !== undefined && value !== null,
 +    'missing value');
 +
 +  assert.ok(endian !== undefined && endian !== null,
 +    'missing endian');
 +
 +  assert.ok(endian == 'big' || endian == 'little',
 +    'bad endian value');
 +
 +  assert.ok(offset !== undefined && offset !== null,
 +    'missing offset');
 +
 +  assert.ok(offset < buffer.length,
 +    'trying to read beyond buffer length');
 +
 +  verifuint(value, 0xff);
 +  buffer[offset] = value;
 +};
 +
 +
 +Buffer.prototype.writeUInt16 = function(value, offset, endian) {
 +  var buffer = this;
 +
 +  assert.ok(value !== undefined && value !== null,
 +    'missing value');
 +
 +  assert.ok(endian !== undefined && endian !== null,
 +    'missing endian');
 +
 +  assert.ok(endian == 'big' || endian == 'little',
 +    'bad endian value');
 +
 +  assert.ok(offset !== undefined && offset !== null,
 +    'missing offset');
 +
 +  assert.ok(offset + 1 < buffer.length,
 +    'trying to read beyond buffer length');
 +
 +  verifuint(value, 0xffff);
 +
 +  if (endian == 'big') {
 +    buffer[offset] = (value & 0xff00) >>> 8;
 +    buffer[offset + 1] = value & 0x00ff;
 +  } else {
 +    buffer[offset + 1] = (value & 0xff00) >>> 8;
 +    buffer[offset] = value & 0x00ff;
 +  }
 +};
 +
 +
 +Buffer.prototype.writeUInt32 = function(value, offset, endian) {
 +  var buffer = this;
 +
 +  assert.ok(value !== undefined && value !== null,
 +    'missing value');
 +
 +  assert.ok(endian !== undefined && endian !== null,
 +    'missing endian');
 +
 +  assert.ok(endian == 'big' || endian == 'little',
 +    'bad endian value');
 +
 +  assert.ok(offset !== undefined && offset !== null,
 +    'missing offset');
 +
 +  assert.ok(offset + 3 < buffer.length,
 +    'trying to read beyond buffer length');
 +
 +  verifuint(value, 0xffffffff);
 +  if (endian == 'big') {
 +    buffer[offset] = (value >>> 24) & 0xff;
 +    buffer[offset + 1] = (value >>> 16) & 0xff;
 +    buffer[offset + 2] = (value >>> 8) & 0xff;
 +    buffer[offset + 3] = value & 0xff;
 +  } else {
 +    buffer[offset + 3] = (value >>> 24) & 0xff;
 +    buffer[offset + 2] = (value >>> 16) & 0xff;
 +    buffer[offset + 1] = (value >>> 8) & 0xff;
 +    buffer[offset] = value & 0xff;
 +  }
 +};
 +
 +
 +/*
 + * We now move onto our friends in the signed number category. Unlike unsigned
 + * numbers, we're going to have to worry a bit more about how we put values into
 + * arrays. Since we are only worrying about signed 32-bit values, we're in
 + * slightly better shape. Unfortunately, we really can't do our favorite binary
 + * & in this system. It really seems to do the wrong thing. For example:
 + *
 + * > -32 & 0xff
 + * 224
 + *
 + * What's happening above is really: 0xe0 & 0xff = 0xe0. However, the results of
 + * this aren't treated as a signed number. Ultimately a bad thing.
 + *
 + * What we're going to want to do is basically create the unsigned equivalent of
 + * our representation and pass that off to the wuint* functions. To do that
 + * we're going to do the following:
 + *
 + *  - if the value is positive
 + *      we can pass it directly off to the equivalent wuint
 + *  - if the value is negative
 + *      we do the following computation:
 + *         mb + val + 1, where
 + *         mb   is the maximum unsigned value in that byte size
 + *         val  is the Javascript negative integer
 + *
 + *
 + * As a concrete value, take -128. In signed 16 bits this would be 0xff80. If
 + * you do out the computations:
 + *
 + * 0xffff - 128 + 1
 + * 0xffff - 127
 + * 0xff80
 + *
 + * You can then encode this value as the signed version. This is really rather
 + * hacky, but it should work and get the job done which is our goal here.
 + */
 +
 +/*
 + * A series of checks to make sure we actually have a signed 32-bit number
 + */
 +function verifsint(value, max, min) {
 +  assert.ok(typeof (value) == 'number',
 +    'cannot write a non-number as a number');
 +
 +  assert.ok(value <= max, 'value larger than maximum allowed value');
 +
 +  assert.ok(value >= min, 'value smaller than minimum allowed value');
 +
 +  assert.ok(Math.floor(value) === value, 'value has a fractional component');
 +}
 +
 +Buffer.prototype.writeInt8 = function(value, offset, endian) {
 +  var buffer = this;
 +
 +  assert.ok(value !== undefined && value !== null,
 +    'missing value');
 +
 +  assert.ok(endian !== undefined && endian !== null,
 +    'missing endian');
 +
 +  assert.ok(endian == 'big' || endian == 'little',
 +    'bad endian value');
 +
 +  assert.ok(offset !== undefined && offset !== null,
 +    'missing offset');
 +
 +  assert.ok(offset < buffer.length,
 +    'Trying to read beyond buffer length');
 +
 +  verifsint(value, 0x7f, -0xf0);
 +
 +  if (value >= 0) {
 +    buffer.writeUInt8(value, offset, endian);
 +  } else {
 +    buffer.writeUInt8(0xff + value + 1, offset, endian);
 +  }
 +};
 +
 +
 +Buffer.prototype.writeInt16 = function(value, offset, endian) {
 +  var buffer = this;
 +
 +  assert.ok(value !== undefined && value !== null,
 +    'missing value');
 +
 +  assert.ok(endian !== undefined && endian !== null,
 +    'missing endian');
 +
 +  assert.ok(endian == 'big' || endian == 'little',
 +    'bad endian value');
 +
 +  assert.ok(offset !== undefined && offset !== null,
 +    'missing offset');
 +
 +  assert.ok(offset + 1 < buffer.length,
 +    'Trying to read beyond buffer length');
 +
 +  verifsint(value, 0x7fff, -0xf000);
 +
 +  if (value >= 0) {
 +    buffer.writeUInt16(value, offset, endian);
 +  } else {
 +    buffer.writeUInt16(0xffff + value + 1, offset, endian);
 +  }
 +};
 +
 +
 +Buffer.prototype.writeInt32 = function(value, offset, endian) {
 +  var buffer = this;
 +
 +  assert.ok(value !== undefined && value !== null,
 +    'missing value');
 +
 +  assert.ok(endian !== undefined && endian !== null,
 +    'missing endian');
 +
 +  assert.ok(endian == 'big' || endian == 'little',
 +    'bad endian value');
 +
 +  assert.ok(offset !== undefined && offset !== null,
 +    'missing offset');
 +
 +  assert.ok(offset + 3 < buffer.length,
 +    'Trying to read beyond buffer length');
 +
 +  verifsint(value, 0x7fffffff, -0xf0000000);
 +  if (value >= 0) {
 +    buffer.writeUInt32(value, offset, endian);
 +  } else {
 +    buffer.writeUInt32(0xffffffff + value + 1, offset, endian);
 +  }
 +};
diff --combined lib/http.js
@@@ -1118,12 -1118,10 +1118,12 @@@ function Agent(options) 
    this.options = options;
    this.host = options.host;
    this.port = options.port || this.defaultPort;
 +  this.socketPath = options.socketPath;
  
    this.queue = [];
    this.sockets = [];
    this.maxSockets = Agent.defaultMaxSockets;
 +
  }
  util.inherits(Agent, EventEmitter);
  exports.Agent = Agent;
@@@ -1163,7 -1161,7 +1163,7 @@@ Agent.prototype._establishNewConnectio
  
    // Grab a new "socket". Depending on the implementation of _getConnection
    // this could either be a raw TCP socket or a TLS stream.
 -  var socket = this._getConnection(this.host, this.port, function() {
 +  var socket = this._getConnection(self, function() {
      socket._httpConnecting = false;
      self.emit('connect'); // mostly for the shim.
      debug('Agent _getConnection callback');
        req = self.queue.shift();
        assert(req._queue === self.queue);
        req._queue = null;
-     } else {
-       // No requests on queue? Where is the request
-       assert(0);
      }
  
-     req.emit('error', err);
-     req._hadError = true; // hacky
+     if (req) {
+       req.emit('error', err);
+       req._hadError = true; // hacky
+     }
  
      // clean up so that agent can handle new requests
      parser.finish();
  
  // Sub-classes can overwrite this method with e.g. something that supplies
  // TLS streams.
 -Agent.prototype._getConnection = function(host, port, cb) {
 +Agent.prototype._getConnection = function(options, cb) {
    debug('Agent connected!');
 -  var c = net.createConnection(port, host);
 +
 +  var c;
 +
 +  if (options.host) {
 +    c = net.createConnection(options.port, options.host);
 +  } else if (options.socketPath) {
 +    c = net.createConnection(options.socketPath);
 +  } else {
 +    c = net.createConnection(options.port);
 +  }
    c.on('connect', cb);
    return c;
  };
@@@ -1415,41 -1403,14 +1414,41 @@@ Agent.prototype._cycle = function() 
  // to remove it?
  var agents = {};
  
 +// Backwards compatible with legacy getAgent(host, port);
 +function getAgent(options) {
 +  var agent;
 +  var host;
 +  var id;
 +  var port;
 +
 +  var _opts = {};
 +
 +  if (options instanceof String) {
 +    port = arguments[1] || 80;
 +    id = options + ':' + port;
 +    _opts.host = options;
 +    _opts.port = port;
 +  } else if (options instanceof Object) {
 +    if (options.port || options.host) {
 +      host = options.host || 'localhost';
 +      port = options.port || 80;
 +      id = host + port;
 +      _opts.host = host;
 +      _opts.port = port;
 +    } else if (options.socketPath) {
 +      id = options.socketPath;
 +      _opts.socketPath = options.socketPath;
 +    } else {
 +      throw new TypeError('Invalid options specification to getAgent');
 +    }
 +  } else {
 +    throw new TypeError('Invalid argument to getAgent');
 +  }
  
 -function getAgent(host, port) {
 -  port = port || 80;
 -  var id = host + ':' + port;
 -  var agent = agents[id];
 +  agent = agents[id];
  
    if (!agent) {
 -    agent = agents[id] = new Agent({ host: host, port: port });
 +    agent = agents[id] = new Agent(_opts);
    }
  
    return agent;
@@@ -1467,7 -1428,7 +1466,7 @@@ exports._requestFromAgent = function(op
  
  exports.request = function(options, cb) {
    if (options.agent === undefined) {
 -    options.agent = getAgent(options.host, options.port);
 +    options.agent = getAgent(options);
    } else if (options.agent === false) {
      options.agent = new Agent(options);
    }
@@@ -1704,8 -1665,6 +1703,8 @@@ exports.cat = function(url, encoding_, 
        headers = {},
        callback = null;
  
 +  console.error("http.cat will be removed in the near future. use http.get");
 +
    // parse the arguments for the various options... very ugly
    if (typeof(arguments[1]) == 'string') {
      encoding = arguments[1];
diff --combined lib/repl.js
@@@ -60,14 -60,27 +60,14 @@@ module.filename = process.cwd() + '/rep
  // hack for repl require to work properly with node_modules folders
  module.paths = require('module')._nodeModulePaths(module.filename);
  
 -
 -function resetContext() {
 -  context = vm.createContext();
 -  for (var i in global) context[i] = global[i];
 -  context.module = module;
 -  context.require = require;
 -  context.global = context;
 -  context.global.global = context;
 -  for (var i in require.cache) delete require.cache[i];
 -}
 -
 -
  // Can overridden with custom print functions, such as `probe` or `eyes.js`
  exports.writer = util.inspect;
  
  
  function REPLServer(prompt, stream) {
    var self = this;
 -  if (!context) resetContext();
 -  if (!exports.repl) exports.repl = this;
 -  self.context = context;
 +
 +  self.resetContext();
    self.bufferedCommand = '';
  
    if (stream) {
@@@ -80,7 -93,7 +80,7 @@@
      process.stdin.resume();
    }
  
-   self.prompt = prompt || '> ';
+   self.prompt = (prompt != undefined ? prompt : '> ');
  
    function complete(text) {
      return self.complete(text);
    this.commands = {};
    defineDefaultCommands(this);
  
-   if (rli.enabled && !disableColors) {
+   if (rli.enabled && !disableColors && exports.writer === util.inspect) {
      // Turn on ANSI coloring.
      exports.writer = function(obj, showHidden, depth) {
        return util.inspect(obj, showHidden, depth, true);
  
    rli.setPrompt(self.prompt);
  
 +  var sawSIGINT = false;
    rli.on('SIGINT', function() {
 -    if (self.bufferedCommand && self.bufferedCommand.length > 0) {
 -      rli.write('\n');
 -      self.bufferedCommand = '';
 -      self.displayPrompt();
 -    } else {
 -      rli.close();
 +    if (sawSIGINT) {
 +       rli.close();
 +       process.exit();
      }
 +    var bareInt = false;
 +    if (!(self.bufferedCommand && self.bufferedCommand.length > 0) &&
 +        rli.line.length === 0) {
 +      rli.write('\n(^C again to quit)');
 +      bareInt = true;
 +    }
 +    rli.line = '';
 +    rli.write('\n');
 +    self.bufferedCommand = '';
 +    self.displayPrompt();
 +    sawSIGINT = bareInt;
    });
  
    rli.addListener('line', function(cmd) {
 +    sawSIGINT = false;
      var skipCatchall = false;
      cmd = trimWhitespace(cmd);
  
              // First we attempt to eval as expression with parens.
              // This catches '{a : 1}' properly.
              ret = vm.runInContext('(' + self.bufferedCommand + ')',
 -                                  context,
 +                                  self.context,
                                    'repl');
              if (typeof ret !== 'function') success = true;
            } catch (e) {
  
            if (!success) {
              // Now as statement without parens.
 -            ret = vm.runInContext(self.bufferedCommand, context, 'repl');
 +            ret = vm.runInContext(self.bufferedCommand, self.context, 'repl');
            }
  
            if (ret !== undefined) {
 -            context._ = ret;
 +            self.context._ = ret;
              self.outputStream.write(exports.writer(ret) + '\n');
            }
  
            // It could also be an error from JSON.parse
            } else if (e &&
                       e.stack &&
-                      e.stack.match('Unexpected token ILLEGAL') &&
-                      e.stack.match(/Object.parse \(native\)/)) {
+                      e.stack.match(/^SyntaxError: Unexpected token .*\n/) &&
+                      e.stack.match(/\n    at Object.parse \(native\)\n/)) {
              throw e;
            }
          }
@@@ -212,33 -215,10 +212,33 @@@ exports.REPLServer = REPLServer
  // prompt is a string to print on each line for the prompt,
  // source is a stream to use for I/O, defaulting to stdin/stdout.
  exports.start = function(prompt, source) {
 -  return new REPLServer(prompt, source);
 +  var repl = new REPLServer(prompt, source);
 +  if (!exports.repl) exports.repl = repl;
 +  return repl;
  };
  
  
 +REPLServer.prototype.createContext = function() {
 +  var context = vm.createContext();
 +
 +  for (var i in global) context[i] = global[i];
 +  context.module = module;
 +  context.require = require;
 +  context.global = context;
 +  context.global.global = context;
 +
 +  return context;
 +};
 +
 +REPLServer.prototype.resetContext = function(force) {
 +  if (!context || force) {
 +    context = this.createContext();
 +    for (var i in require.cache) delete require.cache[i];
 +  }
 +
 +  this.context = context;
 +};
 +
  REPLServer.prototype.displayPrompt = function() {
    this.rli.setPrompt(this.bufferedCommand.length ? '... ' : this.prompt);
    this.rli.prompt();
@@@ -523,7 -503,7 +523,7 @@@ function defineDefaultCommands(repl) 
      action: function() {
        this.outputStream.write('Clearing context...\n');
        this.bufferedCommand = '';
 -      resetContext();
 +      this.resetContext(true);
        this.displayPrompt();
      }
    });
diff --combined lib/tls.js
@@@ -27,8 -27,6 +27,8 @@@ var stream = require('stream')
  var END_OF_FILE = 42;
  var assert = require('assert').ok;
  
 +var NPN_ENABLED = process.binding('constants').NPN_ENABLED;
 +
  var debug;
  if (process.env.NODE_DEBUG && /tls/.test(process.env.NODE_DEBUG)) {
    debug = function(a) { console.error('TLS:', a); };
  var Connection = null;
  try {
    Connection = process.binding('crypto').Connection;
 +  exports.NPN_ENABLED = NPN_ENABLED;
  } catch (e) {
    throw new Error('node.js not compiled with openssl crypto support.');
  }
  
 +// Convert protocols array into valid OpenSSL protocols list
 +// ("\x06spdy/2\x08http/1.1\x08http/1.0")
 +function convertNPNProtocols(NPNProtocols, out) {
 +  // If NPNProtocols is Array - translate it into buffer
 +  if (Array.isArray(NPNProtocols)) {
 +    var buff = new Buffer(NPNProtocols.reduce(function(p, c) {
 +      return p + 1 + Buffer.byteLength(c);
 +    }, 0));
 +
 +    NPNProtocols.reduce(function(offset, c) {
 +      var clen = Buffer.byteLength(c);
 +      buff[offset] = clen;
 +      buff.write(c, offset + 1);
 +
 +      return offset + 1 + clen;
 +    }, 0);
 +
 +    NPNProtocols = buff;
 +  }
 +
 +  // If it's already a Buffer - store it
 +  if (Buffer.isBuffer(NPNProtocols)) {
 +    out.NPNProtocols = NPNProtocols;
 +  }
 +};
  
  // Base class of both CleartextStream and EncryptedStream
  function CryptoStream(pair) {
@@@ -79,7 -51,7 +79,7 @@@
  
    this.readable = this.writable = true;
  
-   this._writeState = true;
+   this._paused = false;
    this._pending = [];
    this._pendingCallbacks = [];
    this._pendingBytes = 0;
@@@ -118,11 -90,10 +118,10 @@@ CryptoStream.prototype.write = function
  
    this._pending.push(data);
    this._pendingCallbacks.push(cb);
    this._pendingBytes += data.length;
  
    this.pair._writeCalled = true;
-   this.pair._cycle();
+   this.pair.cycle();
  
    return this._pendingBytes < 128 * 1024;
  };
  
  CryptoStream.prototype.pause = function() {
    debug('paused ' + (this == this.pair.cleartext ? 'cleartext' : 'encrypted'));
-   this._writeState = false;
+   this._paused = true;
  };
  
  
  CryptoStream.prototype.resume = function() {
    debug('resume ' + (this == this.pair.cleartext ? 'cleartext' : 'encrypted'));
-   this._writeState = true;
-   this.pair._cycle();
+   this._paused = false;
+   this.pair.cycle();
  };
  
  
@@@ -175,8 -146,8 +174,8 @@@ function parseCertString(s) 
  
  
  CryptoStream.prototype.getPeerCertificate = function() {
-   if (this.pair._ssl) {
-     var c = this.pair._ssl.getPeerCertificate();
+   if (this.pair.ssl) {
+     var c = this.pair.ssl.getPeerCertificate();
  
      if (c) {
        if (c.issuer) c.issuer = parseCertString(c.issuer);
  
  
  CryptoStream.prototype.getCipher = function(err) {
-   if (this.pair._ssl) {
-     return this.pair._ssl.getCurrentCipher();
+   if (this.pair.ssl) {
+     return this.pair.ssl.getCurrentCipher();
    } else {
      return null;
    }
  
  
  CryptoStream.prototype.end = function(d) {
-   if (this.writable) {
-     if (this.pair._done) return;
+   if (this.pair._doneFlag) return;
+   if (!this.writable) return;
  
-     if (d) {
-       this.write(d);
-     }
+   if (d) {
+     this.write(d);
+   }
  
-     this._pending.push(END_OF_FILE);
-     this._pendingCallbacks.push(null);
+   this._pending.push(END_OF_FILE);
+   this._pendingCallbacks.push(null);
  
-     // If this is an encrypted stream then we need to disable further 'data'
-     // events.
+   // If this is an encrypted stream then we need to disable further 'data'
+   // events.
  
-     this.writable = false;
+   this.writable = false;
  
-     this.pair._cycle();
-   }
+   this.pair.cycle();
  };
  
  
@@@ -229,8 -199,8 +227,8 @@@ CryptoStream.prototype.destroySoon = fu
  
  
  CryptoStream.prototype.destroy = function(err) {
-   if (this.pair._done) return;
-   this.pair._destroy();
+   if (this.pair._doneFlag) return;
+   this.pair.destroy();
  };
  
  
@@@ -239,9 -209,9 +237,9 @@@ CryptoStream.prototype._done = function
  
    if (this.pair.cleartext._doneFlag &&
        this.pair.encrypted._doneFlag &&
-       !this.pair._done) {
+       !this.pair._doneFlag) {
      // If both streams are done:
-     this.pair._destroy();
+     this.pair.destroy();
    }
  };
  
@@@ -269,7 -239,7 +267,7 @@@ CryptoStream.prototype._push = function
      return;
    }
  
-   while (this._writeState == true) {
+   while (!this._paused) {
      var bytesRead = 0;
      var chunkBytes = 0;
      var pool = new Buffer(16 * 4096); // alloc every time?
      do {
        chunkBytes = this._pusher(pool, bytesRead, pool.length - bytesRead);
  
-       if (this.pair._ssl && this.pair._ssl.error) {
-         this.pair._error();
+       if (this.pair.ssl && this.pair.ssl.error) {
+         this.pair.error();
          return;
        }
  
-       this.pair._maybeInitFinished();
+       this.pair.maybeInitFinished();
  
        if (chunkBytes >= 0) {
          bytesRead += chunkBytes;
        }
  
-     } while ((chunkBytes > 0) && (bytesRead < pool.length));
+     } while (chunkBytes > 0 && bytesRead < pool.length);
  
      assert(bytesRead >= 0);
  
@@@ -341,7 -311,7 +339,7 @@@ CryptoStream.prototype._pull = function
    assert(havePending || this._pendingBytes == 0);
  
    while (this._pending.length > 0) {
-     if (!this.pair._ssl) break;
+     if (!this.pair.ssl) break;
  
      var tmp = this._pending.shift();
      var cb = this._pendingCallbacks.shift();
          assert(this === this.pair.cleartext);
          debug('end cleartext');
  
-         this.pair._ssl.shutdown();
+         this.pair.ssl.shutdown();
  
          // TODO check if we get EAGAIN From shutdown, would have to do it
          // again. should unshift END_OF_FILE back onto pending and wait for
  
          this.pair.encrypted._destroyAfterPush = true;
        }
-       this.pair._cycle();
+       this.pair.cycle();
        this._done()
        return;
      }
  
      var rv = this._puller(tmp);
  
-     if (this.pair._ssl && this.pair._ssl.error) {
-       this.pair._error();
+     if (this.pair.ssl && this.pair.ssl.error) {
+       this.pair.error();
        return;
      }
  
-     this.pair._maybeInitFinished();
+     this.pair.maybeInitFinished();
  
      if (rv === 0 || rv < 0) {
        this._pending.unshift(tmp);
@@@ -412,8 -382,8 +410,8 @@@ util.inherits(CleartextStream, CryptoSt
  
  
  CleartextStream.prototype._internallyPendingBytes = function() {
-   if (this.pair._ssl) {
-     return this.pair._ssl.clearPending();
+   if (this.pair.ssl) {
+     return this.pair.ssl.clearPending();
    } else {
      return 0;
    }
  
  CleartextStream.prototype._puller = function(b) {
    debug('clearIn ' + b.length + ' bytes');
-   return this.pair._ssl.clearIn(b, 0, b.length);
+   return this.pair.ssl.clearIn(b, 0, b.length);
  };
  
  
  CleartextStream.prototype._pusher = function(pool, offset, length) {
    debug('reading from clearOut');
-   if (!this.pair._ssl) return -1;
-   return this.pair._ssl.clearOut(pool, offset, length);
+   if (!this.pair.ssl) return -1;
+   return this.pair.ssl.clearOut(pool, offset, length);
  };
  
  
@@@ -440,8 -410,8 +438,8 @@@ util.inherits(EncryptedStream, CryptoSt
  
  
  EncryptedStream.prototype._internallyPendingBytes = function() {
-   if (this.pair._ssl) {
-     return this.pair._ssl.encPending();
+   if (this.pair.ssl) {
+     return this.pair.ssl.encPending();
    } else {
      return 0;
    }
  
  EncryptedStream.prototype._puller = function(b) {
    debug('writing from encIn');
-   return this.pair._ssl.encIn(b, 0, b.length);
+   return this.pair.ssl.encIn(b, 0, b.length);
  };
  
  
  EncryptedStream.prototype._pusher = function(pool, offset, length) {
    debug('reading from encOut');
-   if (!this.pair._ssl) return -1;
-   return this.pair._ssl.encOut(pool, offset, length);
+   if (!this.pair.ssl) return -1;
+   return this.pair.ssl.encOut(pool, offset, length);
  };
  
  
   * Provides a pair of streams to do encrypted communication.
   */
  
 -function SecurePair(credentials, isServer, requestCert, rejectUnauthorized) {
 +function SecurePair(credentials, isServer, requestCert, rejectUnauthorized,
 +                    NPNProtocols) {
    if (!(this instanceof SecurePair)) {
      return new SecurePair(credentials,
                            isServer,
                            requestCert,
 -                          rejectUnauthorized);
 +                          rejectUnauthorized,
 +                          NPNProtocols);
    }
  
    var self = this;
    this._isServer = isServer ? true : false;
    this._encWriteState = true;
    this._clearWriteState = true;
-   this._done = false;
-   var crypto = require('crypto');
+   this._doneFlag = false;
  
    if (!credentials) {
      this.credentials = crypto.createCredentials();
    this._rejectUnauthorized = rejectUnauthorized ? true : false;
    this._requestCert = requestCert ? true : false;
  
-   this._ssl = new Connection(this.credentials.context,
+   this.ssl = new Connection(this.credentials.context,
                               this._isServer ? true : false,
                               this._requestCert,
                               this._rejectUnauthorized);
  
-     this._ssl.setNPNProtocols(NPNProtocols);
 +  if (NPN_ENABLED && NPNProtocols) {
++    this.ssl.setNPNProtocols(NPNProtocols);
 +    this.npnProtocol = null;
 +  }
  
    /* Acts as a r/w stream to the cleartext side of the stream. */
    this.cleartext = new CleartextStream(this);
    this.encrypted = new EncryptedStream(this);
  
    process.nextTick(function() {
-     self._ssl.start();
-     self._cycle();
+     self.ssl.start();
+     self.cycle();
    });
  }
  
@@@ -569,63 -531,54 +565,57 @@@ exports.createSecurePair = function(cre
   * Because it is also called everywhere, we also check if the connection has
   * completed negotiation and emit 'secure' from here if it has.
   */
- SecurePair.prototype._cycle = function(depth) {
+ SecurePair.prototype.cycle = function(depth) {
+   if (this._doneFlag) return;
    depth = depth ? depth : 0;
-   if (this._done) {
-     return;
-   }
  
-   if(depth == 0) this._writeCalled = false;
+   if (depth == 0) this._writeCalled = false;
  
    var established = this._secureEstablished;
  
-   if (!this._cycleEncryptedPullLock) {
-     this._cycleEncryptedPullLock = true;
+   if (!this.cycleEncryptedPullLock) {
+     this.cycleEncryptedPullLock = true;
      debug("encrypted._pull");
      this.encrypted._pull();
-     this._cycleEncryptedPullLock = false;
+     this.cycleEncryptedPullLock = false;
    }
  
-   if (!this._cycleCleartextPullLock) {
-     this._cycleCleartextPullLock = true;
+   if (!this.cycleCleartextPullLock) {
+     this.cycleCleartextPullLock = true;
      debug("cleartext._pull");
      this.cleartext._pull();
-     this._cycleCleartextPullLock = false;
+     this.cycleCleartextPullLock = false;
    }
  
-   if (!this._cycleCleartextPushLock) {
-     this._cycleCleartextPushLock = true;
+   if (!this.cycleCleartextPushLock) {
+     this.cycleCleartextPushLock = true;
      debug("cleartext._push");
      this.cleartext._push();
-     this._cycleCleartextPushLock = false;
+     this.cycleCleartextPushLock = false;
    }
  
-   if (!this._cycleEncryptedPushLock) {
-     this._cycleEncryptedPushLock = true;
+   if (!this.cycleEncryptedPushLock) {
+     this.cycleEncryptedPushLock = true;
      debug("encrypted._push");
      this.encrypted._push();
-     this._cycleEncryptedPushLock = false;
-   }
-   if (this._done) {
-     return;
+     this.cycleEncryptedPushLock = false;
    }
  
    if ((!established && this._secureEstablished) ||
        (depth == 0 && this._writeCalled)) {
      // If we were not established but now we are, let's cycle again.
      // Or if there is some data to write...
-     this._cycle(depth + 1);
+     this.cycle(depth + 1);
    }
  };
  
  
- SecurePair.prototype._maybeInitFinished = function() {
-   if (this._ssl && !this._secureEstablished && this._ssl.isInitFinished()) {
+ SecurePair.prototype.maybeInitFinished = function() {
+   if (this.ssl && !this._secureEstablished && this.ssl.isInitFinished()) {
 +    if (NPN_ENABLED) {
-       this.npnProtocol = this._ssl.getNegotiatedProtocol();
++      this.npnProtocol = this.ssl.getNegotiatedProtocol();
 +    }
      this._secureEstablished = true;
      debug('secure established');
      this.emit('secure');
  };
  
  
- SecurePair.prototype._destroy = function() {
+ SecurePair.prototype.destroy = function() {
    var self = this;
  
-   if (!this._done) {
-     this._done = true;
-     this._ssl.error = null;
-     this._ssl.close();
-     this._ssl = null;
+   if (!this._doneFlag) {
+     this._doneFlag = true;
+     this.ssl.error = null;
+     this.ssl.close();
+     this.ssl = null;
  
      self.encrypted.writable = self.encrypted.readable = false;
      self.cleartext.writable = self.cleartext.readable = false;
  
      process.nextTick(function() {
-       self.encrypted.emit('end');
-       if (self.encrypted.onend) self.encrypted.onend();
        self.encrypted.emit('close');
-       self.cleartext.emit('end');
-       if (self.cleartext.onend) self.cleartext.onend();
        self.cleartext.emit('close');
      });
    }
-   this._cycle();
  };
  
  
- SecurePair.prototype._error = function() {
+ SecurePair.prototype.error = function() {
    if (!this._secureEstablished) {
-     this._destroy();
+     this.destroy();
    } else {
-     var err = this._ssl.error;
-     this._ssl.error = null;
+     var err = this.ssl.error;
+     this.ssl.error = null;
  
      if (this._isServer &&
          this._rejectUnauthorized &&
          /peer did not return a certificate/.test(err.message)) {
        // Not really an error.
-       this._destroy();
+       this.destroy();
      } else {
        this.cleartext.emit('error', err);
      }
@@@ -773,9 -719,7 +756,9 @@@ function Server(/* [options], listener 
        key: self.key,
        cert: self.cert,
        ca: self.ca,
 +      ciphers: self.ciphers,
        secureProtocol: self.secureProtocol,
 +      secureOptions: self.secureOptions,
        crl: self.crl
      });
      creds.context.setCiphers('RC4-SHA:AES128-SHA:AES256-SHA');
      var pair = new SecurePair(creds,
                                true,
                                self.requestCert,
 -                              self.rejectUnauthorized);
 +                              self.rejectUnauthorized,
 +                              self.NPNProtocols);
  
      var cleartext = pipe(pair, socket);
      cleartext._controlReleased = false;
  
      pair.on('secure', function() {
        pair.cleartext.authorized = false;
 +      pair.cleartext.npnProtocol = pair.npnProtocol;
        if (!self.requestCert) {
          cleartext._controlReleased = true;
          self.emit('secureConnection', pair.cleartext, pair.encrypted);
        } else {
-         var verifyError = pair._ssl.verifyError();
+         var verifyError = pair.ssl.verifyError();
          if (verifyError) {
            pair.cleartext.authorizationError = verifyError;
  
            if (self.rejectUnauthorized) {
              socket.destroy();
-             pair._destroy();
+             pair.destroy();
            } else {
              cleartext._controlReleased = true;
              self.emit('secureConnection', pair.cleartext, pair.encrypted);
@@@ -849,10 -791,6 +832,10 @@@ Server.prototype.setOptions = function(
    if (options.ca) this.ca = options.ca;
    if (options.secureProtocol) this.secureProtocol = options.secureProtocol;
    if (options.crl) this.crl = options.crl;
 +  if (options.ciphers) this.ciphers = options.ciphers;
 +  if (options.secureProtocol) this.secureProtocol = options.secureProtocol;
 +  if (options.secureOptions) this.secureOptions = options.secureOptions;
 +  if (options.NPNProtocols) convertNPNProtocols(options.NPNProtocols, this);
  };
  
  
@@@ -895,19 -833,15 +878,19 @@@ exports.connect = function(port /* host
    var sslcontext = crypto.createCredentials(options);
    //sslcontext.context.setCiphers('RC4-SHA:AES128-SHA:AES256-SHA');
  
 -  var pair = new SecurePair(sslcontext, false);
 +  convertNPNProtocols(options.NPNProtocols, this);
 +  var pair = new SecurePair(sslcontext, false, true, false,
 +                            this.NPNProtocols);
  
    var cleartext = pipe(pair, socket);
  
    socket.connect(port, host);
  
    pair.on('secure', function() {
-     var verifyError = pair._ssl.verifyError();
+     var verifyError = pair.ssl.verifyError();
  
 +    cleartext.npnProtocol = pair.npnProtocol;
 +
      if (verifyError) {
        cleartext.authorized = false;
        cleartext.authorizationError = verifyError;
diff --combined src/node.cc
@@@ -107,7 -107,6 +107,7 @@@ static char *eval_string = NULL
  static int option_end_index = 0;
  static bool use_debug_agent = false;
  static bool debug_wait_connect = false;
 +static bool cov = false;
  static int debug_port=5858;
  static int max_stack_size = 0;
  
@@@ -1104,8 -1103,6 +1104,8 @@@ enum encoding ParseEncoding(Handle<Valu
      return UCS2;
    } else if (strcasecmp(*encoding, "binary") == 0) {
      return BINARY;
 +  } else if (strcasecmp(*encoding, "hex") == 0) {
 +    return HEX;
    } else if (strcasecmp(*encoding, "raw") == 0) {
      fprintf(stderr, "'raw' (array of integers) has been removed. "
                      "Use 'binary'.\n");
@@@ -1156,7 -1153,6 +1156,7 @@@ ssize_t DecodeBytes(v8::Handle<v8::Valu
  
    if (encoding == UTF8) return str->Utf8Length();
    else if (encoding == UCS2) return str->Length() * 2;
 +  else if (encoding == HEX) return str->Length() / 2;
  
    return str->Length();
  }
@@@ -1278,13 -1274,24 +1278,24 @@@ static void ReportException(TryCatch &t
  
    String::Utf8Value trace(try_catch.StackTrace());
  
-   if (trace.length() > 0) {
+   // range errors have a trace member set to undefined
+   if (trace.length() > 0 && !try_catch.StackTrace()->IsUndefined()) {
      fprintf(stderr, "%s\n", *trace);
    } else {
      // this really only happens for RangeErrors, since they're the only
-     // kind that won't have all this info in the trace.
+     // kind that won't have all this info in the trace, or when non-Error
+     // objects are thrown manually.
      Local<Value> er = try_catch.Exception();
-     String::Utf8Value msg(!er->IsObject() ? er->ToString()
+     bool isErrorObject = er->IsObject() &&
+       !(er->ToObject()->Get(String::New("message"))->IsUndefined()) &&
+       !(er->ToObject()->Get(String::New("name"))->IsUndefined());
+     if (isErrorObject) {
+       String::Utf8Value name(er->ToObject()->Get(String::New("name")));
+       fprintf(stderr, "%s: ", *name);
+     }
+     String::Utf8Value msg(!isErrorObject ? er->ToString()
                           : er->ToObject()->Get(String::New("message"))->ToString());
      fprintf(stderr, "%s\n", *msg);
    }
@@@ -1421,11 -1428,7 +1432,11 @@@ static Handle<Value> SetGid(const Argum
  
      if ((err = getgrnam_r(*grpnam, &grp, getbuf, ARRAY_SIZE(getbuf), &grpp)) ||
          grpp == NULL) {
 -      return ThrowException(ErrnoException(errno, "getgrnam_r"));
 +      if (errno == 0)
 +        return ThrowException(Exception::Error(
 +          String::New("setgid group id does not exist")));
 +      else
 +        return ThrowException(ErrnoException(errno, "getgrnam_r"));
      }
  
      gid = grpp->gr_gid;
@@@ -1460,11 -1463,7 +1471,11 @@@ static Handle<Value> SetUid(const Argum
  
      if ((err = getpwnam_r(*pwnam, &pwd, getbuf, ARRAY_SIZE(getbuf), &pwdp)) ||
          pwdp == NULL) {
 -      return ThrowException(ErrnoException(errno, "getpwnam_r"));
 +      if (errno == 0)
 +        return ThrowException(Exception::Error(
 +          String::New("setuid user id does not exist")));
 +      else
 +        return ThrowException(ErrnoException(errno, "getpwnam_r"));
      }
  
      uid = pwdp->pw_uid;
@@@ -1515,18 -1514,6 +1526,18 @@@ static void CheckStatus(EV_P_ ev_timer 
    }
  }
  
 +static Handle<Value> Uptime(const Arguments& args) {
 +  HandleScope scope;
 +  assert(args.Length() == 0);
 +
 +  double uptime =  Platform::GetUptime(true);
 +
 +  if (uptime < 0) {
 +    return Undefined();
 +  }
 +
 +  return scope.Close(Number::New(uptime));
 +}
  
  v8::Handle<v8::Value> MemoryUsage(const v8::Arguments& args) {
    HandleScope scope;
@@@ -1943,7 -1930,7 +1954,7 @@@ static Handle<Array> EnvEnumerator(cons
  }
  
  
 -static void Load(int argc, char *argv[]) {
 +Handle<Object> SetupProcessObject(int argc, char *argv[]) {
    HandleScope scope;
  
    int i, j;
  
  
  
 +  // process.arch
 +  process->Set(String::NewSymbol("arch"), String::New(ARCH));
 +
    // process.platform
    process->Set(String::NewSymbol("platform"), String::New(PLATFORM));
  
    process->Set(String::NewSymbol("ENV"), ENV);
  
    process->Set(String::NewSymbol("pid"), Integer::New(getpid()));
 +  process->Set(String::NewSymbol("cov"), cov ? True() : False());
  
    // -e, --eval
    if (eval_string) {
    NODE_SET_METHOD(process, "_kill", Kill);
  #endif // __POSIX__
  
 +  NODE_SET_METHOD(process, "uptime", Uptime);
    NODE_SET_METHOD(process, "memoryUsage", MemoryUsage);
  
    NODE_SET_METHOD(process, "binding", Binding);
    process->Set(String::NewSymbol("EventEmitter"),
                 EventEmitter::constructor_template->GetFunction());
  
 +  return process;
 +}
 +
 +
 +static void AtExit() {
 +  node::Stdio::Flush();
 +  node::Stdio::DisableRawMode(STDIN_FILENO);
 +}
 +
 +
 +static void SignalExit(int signal) {
 +  Stdio::DisableRawMode(STDIN_FILENO);
 +  _exit(1);
 +}
 +
 +
 +void Load(Handle<Object> process) {
    // Compile, execute the src/node.js file. (Which was included as static C
    // string in node_natives.h. 'natve_node' is the string containing that
    // source code.)
  
    // The node.js file returns a function 'f'
  
 +  atexit(AtExit);
 +
    TryCatch try_catch;
  
    Local<Value> f_value = ExecuteString(MainSource(),
@@@ -2176,7 -2139,6 +2187,7 @@@ static void PrintHelp() 
           "  --v8-options         print v8 command line options\n"
           "  --vars               print various compiled-in variables\n"
           "  --max-stack-size=val set max v8 stack size (bytes)\n"
 +         "  --cov                code coverage; writes node-cov.json \n"
           "\n"
           "Enviromental variables:\n"
           "NODE_PATH              ':'-separated list of directories\n"
  }
  
  // Parse node command line arguments.
 -static void ParseArgs(int *argc, char **argv) {
 +static void ParseArgs(int argc, char **argv) {
    int i;
  
    // TODO use parse opts
 -  for (i = 1; i < *argc; i++) {
 +  for (i = 1; i < argc; i++) {
      const char *arg = argv[i];
      if (strstr(arg, "--debug") == arg) {
        ParseDebugOpt(arg);
        argv[i] = const_cast<char*>("");
 +    } else if (!strcmp(arg, "--cov")) {
 +      cov = true;
 +      argv[i] = const_cast<char*>("");
      } else if (strcmp(arg, "--version") == 0 || strcmp(arg, "-v") == 0) {
        printf("%s\n", NODE_VERSION);
        exit(0);
        PrintHelp();
        exit(0);
      } else if (strcmp(arg, "--eval") == 0 || strcmp(arg, "-e") == 0) {
 -      if (*argc <= i + 1) {
 +      if (argc <= i + 1) {
          fprintf(stderr, "Error: --eval requires an argument\n");
          exit(1);
        }
  }
  
  
 -static void AtExit() {
 -  node::Stdio::Flush();
 -  node::Stdio::DisableRawMode(STDIN_FILENO);
 -}
 -
 -
 -static void SignalExit(int signal) {
 -  Stdio::DisableRawMode(STDIN_FILENO);
 -  _exit(1);
 -}
 -
 -
  static void EnableDebug(bool wait_connect) {
    // Start the debug thread and it's associated TCP server on port 5858.
    bool r = Debug::EnableAgent("node " NODE_VERSION, debug_port);
    assert(r);
  
    // Print out some information.
-   fprintf(stderr, "debugger listening on port %d\r\n", debug_port);
+   fprintf(stderr, "debugger listening on port %d", debug_port);
  }
  
  
@@@ -2287,12 -2258,12 +2298,12 @@@ static int RegisterSignalHandler(int si
  #endif // __POSIX__
  
  
 -int Start(int argc, char *argv[]) {
 +char** Init(int argc, char *argv[]) {
    // Hack aroung with the argv pointer. Used for process.title = "blah".
    argv = node::Platform::SetupArgs(argc, argv);
  
    // Parse a few arguments which are specific to Node.
 -  node::ParseArgs(&argc, argv);
 +  node::ParseArgs(argc, argv);
    // Parse the rest of the args (up to the 'option_end_index' (where '--' was
    // in the command line))
    int v8argc = node::option_end_index;
      eio_set_max_poll_reqs(10);
    }
  
 -  V8::Initialize();
 -  HandleScope handle_scope;
 -
    V8::SetFatalErrorHandler(node::OnFatalError);
  
  
  #endif // __POSIX__
    }
  
 +  return argv;
 +}
 +
 +
 +void EmitExit(v8::Handle<v8::Object> process) {
 +  // process.emit('exit')
 +  Local<Value> emit_v = process->Get(String::New("emit"));
 +  assert(emit_v->IsFunction());
 +  Local<Function> emit = Local<Function>::Cast(emit_v);
 +  Local<Value> args[] = { String::New("exit") };
 +  TryCatch try_catch;
 +  emit->Call(process, 1, args);
 +  if (try_catch.HasCaught()) {
 +    FatalException(try_catch);
 +  }
 +}
 +
 +
 +int Start(int argc, char *argv[]) {
 +  v8::V8::Initialize();
 +  v8::HandleScope handle_scope;
 +
 +  argv = Init(argc, argv);
 +
    // Create the one and only Context.
    Persistent<v8::Context> context = v8::Context::New();
    v8::Context::Scope context_scope(context);
  
 -  atexit(node::AtExit);
 +  Handle<Object> process = SetupProcessObject(argc, argv);
  
    // Create all the objects, load modules, do everything.
    // so your next reading stop should be node::Load()!
 -  node::Load(argc, argv);
 +  Load(process);
  
    // TODO Probably don't need to start this each time.
    // Avoids failing on test/simple/test-eio-race3.js though
    // watchers, it blocks.
    ev_loop(EV_DEFAULT_UC_ 0);
  
 -
 -  // process.emit('exit')
 -  Local<Value> emit_v = process->Get(String::New("emit"));
 -  assert(emit_v->IsFunction());
 -  Local<Function> emit = Local<Function>::Cast(emit_v);
 -  Local<Value> args[] = { String::New("exit") };
 -  TryCatch try_catch;
 -  emit->Call(process, 1, args);
 -  if (try_catch.HasCaught()) {
 -    FatalException(try_catch);
 -  }
 -
 +  EmitExit(process);
  
  #ifndef NDEBUG
    // Clean up.
diff --combined src/node.h
  
  namespace node {
  
 -int Start (int argc, char *argv[]);
 +int Start(int argc, char *argv[]);
 +
 +char** Init(int argc, char *argv[]);
 +v8::Handle<v8::Object> SetupProcessObject(int argc, char *argv[]);
 +void Load(v8::Handle<v8::Object> process);
 +void EmitExit(v8::Handle<v8::Object> process);
  
  #define NODE_PSYMBOL(s) Persistent<String>::New(String::NewSymbol(s))
  
  /* Converts a unixtime to V8 Date */
  #define NODE_UNIXTIME_V8(t) v8::Date::New(1000*static_cast<double>(t))
- #define NODE_V8_UNIXTIME(v) (static_cast<double>((v)->IntegerValue())/1000.0);
+ #define NODE_V8_UNIXTIME(v) (static_cast<double>((v)->NumberValue())/1000.0);
  
  #define NODE_DEFINE_CONSTANT(target, constant)                            \
    (target)->Set(v8::String::NewSymbol(#constant),                         \
@@@ -69,7 -64,7 +69,7 @@@ do 
                                    __callback##_TEM);                      \
  } while (0)
  
 -enum encoding {ASCII, UTF8, BASE64, UCS2, BINARY};
 +enum encoding {ASCII, UTF8, BASE64, UCS2, BINARY, HEX};
  enum encoding ParseEncoding(v8::Handle<v8::Value> encoding_v,
                              enum encoding _default = BINARY);
  void FatalException(v8::TryCatch &try_catch);
diff --combined src/platform_sunos.cc
@@@ -31,6 -31,7 +31,7 @@@
  #include <errno.h>
  #include <inttypes.h>
  #include <sys/types.h>
+ #include <sys/loadavg.h>
  
  #if (!defined(_LP64)) && (_FILE_OFFSET_BITS - 0 == 64)
  #define PROCFS_FILE_OFFSET_BITS_HACK 1
@@@ -50,7 -51,6 +51,7 @@@ namespace node 
  
  using namespace v8;
  
 +double Platform::prog_start_time = Platform::GetUptime();
  
  char** Platform::SetupArgs(int argc, char *argv[]) {
    return argv;
@@@ -224,7 -224,7 +225,7 @@@ double Platform::GetTotalMemory() 
    return 0.0;
  }
  
 -double Platform::GetUptime() {
 +double Platform::GetUptimeImpl() {
    kstat_ctl_t   *kc;
    kstat_t       *ksp;
    kstat_named_t *knp;
  }
  
  int Platform::GetLoadAvg(Local<Array> *loads) {
+   HandleScope scope;
+   double loadavg[3];
+   (void) getloadavg(loadavg, 3);
+   (*loads)->Set(0, Number::New(loadavg[LOADAVG_1MIN]));
+   (*loads)->Set(1, Number::New(loadavg[LOADAVG_5MIN]));
+   (*loads)->Set(2, Number::New(loadavg[LOADAVG_15MIN]));
    return 0;
  }
  
  
 +Handle<Value> Platform::GetInterfaceAddresses() {
 +  HandleScope scope;
 +  return scope.Close(Object::New());
 +}
 +
 +
  }  // namespace node
  
index 0000000,8269698..ccf101e
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,6 +1,6 @@@
 -node.js:134
+ before
++node.js:*
+         throw e; // process.nextTick error, or 'error' event on first tick
+         ^
+ RangeError: Maximum call stack size exceeded
index 0000000,5279758..9974e12
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,6 +1,6 @@@
 -node.js:134
+ before
++node.js:*
+         throw e; // process.nextTick error, or 'error' event on first tick
+         ^
+ MyCustomError: This is a custom message
index 0000000,7c22d2f..c24f813
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,6 +1,6 @@@
 -node.js:134
+ before
++node.js:*
+         throw e; // process.nextTick error, or 'error' event on first tick
+         ^
+ [object Object]
@@@ -441,46 -441,6 +441,46 @@@ assert.equal(12, Buffer.byteLength('Il 
  // slice(0,0).length === 0
  assert.equal(0, Buffer('hello').slice(0, 0).length);
  
 +// test hex toString
 +console.log('Create hex string from buffer');
 +var hexb = new Buffer(256);
 +for (var i = 0; i < 256; i ++) {
 +  hexb[i] = i;
 +}
 +var hexStr = hexb.toString('hex');
 +assert.equal(hexStr,
 +             '000102030405060708090a0b0c0d0e0f'+
 +             '101112131415161718191a1b1c1d1e1f'+
 +             '202122232425262728292a2b2c2d2e2f'+
 +             '303132333435363738393a3b3c3d3e3f'+
 +             '404142434445464748494a4b4c4d4e4f'+
 +             '505152535455565758595a5b5c5d5e5f'+
 +             '606162636465666768696a6b6c6d6e6f'+
 +             '707172737475767778797a7b7c7d7e7f'+
 +             '808182838485868788898a8b8c8d8e8f'+
 +             '909192939495969798999a9b9c9d9e9f'+
 +             'a0a1a2a3a4a5a6a7a8a9aaabacadaeaf'+
 +             'b0b1b2b3b4b5b6b7b8b9babbbcbdbebf'+
 +             'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf'+
 +             'd0d1d2d3d4d5d6d7d8d9dadbdcdddedf'+
 +             'e0e1e2e3e4e5e6e7e8e9eaebecedeeef'+
 +             'f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff');
 +
 +console.log('Create buffer from hex string');
 +var hexb2 = new Buffer(hexStr, 'hex');
 +for (var i = 0; i < 256; i ++) {
 +  assert.equal(hexb2[i], hexb[i]);
 +}
 +
 +// test an invalid slice end.
 +console.log('Try to slice off the end of the buffer');
 +var b = new Buffer([1,2,3,4,5]);
 +var b2 = b.toString('hex', 1, 10000);
 +var b3 = b.toString('hex', 1, 5);
 +var b4 = b.toString('hex', 1);
 +assert.equal(b2, b3);
 +assert.equal(b2, b4);
 +
  
  // Test slice on SlowBuffer GH-843
  var SlowBuffer = process.binding('buffer').SlowBuffer;
@@@ -540,20 -500,8 +540,25 @@@ console.log(z.length
  assert.equal(2, z.length);
  assert.equal(0x66, z[0]);
  assert.equal(0x6f, z[1]);
 +assert.equal(0, Buffer('hello').slice(0, 0).length)
 +
 +b = new Buffer(50);
 +b.fill("h");
 +for (var i = 0; i < b.length; i++) {
 +  assert.equal("h".charCodeAt(0), b[i]);
 +}
 +
 +b.fill(0);
 +for (var i = 0; i < b.length; i++) {
 +  assert.equal(0, b[i]);
 +}
 +
 +b.fill(1, 16, 32);
 +for (var i = 0; i < 16; i++) assert.equal(0, b[i]);
 +for (; i < 32; i++) assert.equal(1, b[i]);
 +for (; i < b.length; i++) assert.equal(0, b[i]);
+ var b = new SlowBuffer(10);
+ b.write('あいうえお', 'ucs2');
+ assert.equal(b.toString('ucs2'), 'あいうえお');
diff --combined wscript
+++ b/wscript
@@@ -143,13 -143,6 +143,6 @@@ def set_options(opt)
                  , dest='openssl_libpath'
                  )
  
-   opt.add_option( '--oprofile'
-                 , action='store_true'
-                 , default=False
-                 , help="add oprofile support"
-                 , dest='use_oprofile'
-                 )
    opt.add_option( '--gdb'
                  , action='store_true'
                  , default=False
@@@ -252,12 -245,8 +245,8 @@@ def configure(conf)
    conf.env["USE_SHARED_CARES"] = o.shared_cares or o.shared_cares_includes or o.shared_cares_libpath
    conf.env["USE_SHARED_LIBEV"] = o.shared_libev or o.shared_libev_includes or o.shared_libev_libpath
  
-   conf.env["USE_OPROFILE"] = o.use_oprofile
    conf.env["USE_GDBJIT"] = o.use_gdbjit
  
-   if o.use_oprofile:
-     conf.check(lib=['bfd', 'opagent'], uselib_store="OPROFILE")
    conf.check(lib='dl', uselib_store='DL')
    if not sys.platform.startswith("sunos") and not sys.platform.startswith("cygwin") and not sys.platform.startswith("win32"):
      conf.env.append_value("CCFLAGS", "-rdynamic")
    elif 'DEST_CPU' in conf.env and conf.env['DEST_CPU']:
      conf.env['DEST_CPU'] = canonical_cpu_type(conf.env['DEST_CPU'])
  
 -  conf.check(lib='rt', uselib_store='RT')
 +  have_librt = conf.check(lib='rt', uselib_store='RT')
 +
 +  have_monotonic = False
 +  if have_librt:
 +    code =  """
 +      #include <time.h>
 +      int main(void) {
 +        struct timespec now;
 +        clock_gettime(CLOCK_MONOTONIC, &now);
 +        return 0;
 +      }
 +    """
 +    have_monotonic = conf.check_cc(lib="rt", msg="Checking for CLOCK_MONOTONIC", fragment=code)
 +
 +  if have_monotonic:
 +    conf.env.append_value('CPPFLAGS', '-DHAVE_MONOTONIC_CLOCK=1')
 +  else:
 +    conf.env.append_value('CPPFLAGS', '-DHAVE_MONOTONIC_CLOCK=0')
  
    if sys.platform.startswith("sunos"):
      if not conf.check(lib='socket', uselib_store="SOCKET"):
    else:
      conf.env.append_value('CPPFLAGS', '-DHAVE_FDATASYNC=0')
  
 +  # arch
 +  conf.env.append_value('CPPFLAGS', '-DARCH="' + conf.env['DEST_CPU'] + '"')
 +
    # platform
    conf.env.append_value('CPPFLAGS', '-DPLATFORM="' + conf.env['DEST_OS'] + '"')
  
  
    # Configure default variant
    conf.setenv('default')
-   conf.env.append_value('CPPFLAGS', '-DNDEBUG')
    default_compile_flags = ['-g', '-O3']
    conf.env.append_value('CCFLAGS', default_compile_flags)
    conf.env.append_value('CXXFLAGS', default_compile_flags)
@@@ -587,12 -555,7 +575,7 @@@ def v8_cmd(bld, variant)
    else:
      snapshot = ""
  
-   if bld.env["USE_OPROFILE"]:
-     profile = "prof=oprofile"
-   else:
-     profile = ""
-   cmd_R = sys.executable + ' "%s" -j %d -C "%s" -Y "%s" visibility=default mode=%s %s toolchain=%s library=static %s %s'
+   cmd_R = sys.executable + ' "%s" -j %d -C "%s" -Y "%s" visibility=default mode=%s %s toolchain=%s library=static %s'
  
    cmd = cmd_R % ( scons
                  , Options.options.jobs
                  , arch
                  , toolchain
                  , snapshot
-                 , profile
                  )
  
    if bld.env["USE_GDBJIT"]:
@@@ -753,8 -715,8 +735,8 @@@ def build(bld)
      native_cc_debug = native_cc.clone("debug")
      native_cc_debug.rule = javascript_in_c_debug
  
-   native_cc.rule = javascript_in_c
-   
+   native_cc.rule = javascript_in_c_debug
    if bld.env["USE_DTRACE"]:
      dtrace = bld.new_task_gen(
        name   = "dtrace",
          , 'CPPFLAGS'  : " ".join(program.env["CPPFLAGS"]).replace('"', '\\"')
          , 'LIBFLAGS'  : " ".join(program.env["LIBFLAGS"]).replace('"', '\\"')
          , 'PREFIX'    : safe_path(program.env["PREFIX"])
-         , 'VERSION'   : '0.4.6' # FIXME should not be hard-coded, see NODE_VERSION_STRING in src/node_version.
+         , 'VERSION'   : '0.4.7' # FIXME should not be hard-coded, see NODE_VERSION_STRING in src/node_version.
          }
      return x