Merge remote-tracking branch 'upstream/v0.10'
authorTimothy J Fontaine <tjfontaine@gmail.com>
Fri, 25 Oct 2013 18:26:05 +0000 (11:26 -0700)
committerTimothy J Fontaine <tjfontaine@gmail.com>
Fri, 25 Oct 2013 18:26:05 +0000 (11:26 -0700)
Conflicts:
ChangeLog
deps/uv/ChangeLog
deps/uv/include/uv-darwin.h
deps/uv/src/unix/fsevents.c
deps/uv/src/unix/process.c
deps/uv/src/version.c
doc/api/addons.markdown
doc/api/cluster.markdown
doc/api/http.markdown
lib/http.js
lib/tls.js
src/node_crypto.cc
src/node_http_parser.cc
src/node_version.h
src/pipe_wrap.cc
src/v8abbr.h
src/v8ustack.d
test/simple/test-http-pipeline-flood.js

17 files changed:
1  2 
ChangeLog
README.md
doc/api/addons.markdown
doc/api/child_process.markdown
doc/api/cluster.markdown
doc/api/fs.markdown
doc/api/http.markdown
doc/api/net.markdown
doc/api/os.markdown
doc/api/stream.markdown
lib/_debugger.js
lib/_tls_legacy.js
lib/fs.js
src/node_crypto.cc
src/node_file.cc
src/pipe_wrap.cc
test/simple/test-crypto-verify-failure.js

diff --cc ChangeLog
+++ b/ChangeLog
 -2013.10.18, Version 0.10.21 (Stable)
 +2013.08.21, Version 0.11.7 (Unstable), be52549bfa5311208b5fcdb3ba09210460fa9ceb
 +
 +* uv: upgrade to v0.11.13
 +
 +* v8: upgrade to 3.20.17
 +
 +* buffer: adhere to INSPECT_MAX_BYTES (Timothy J Fontaine)
 +
 +* buffer: fix regression for large buffer creation (Trevor Norris)
 +
 +* buffer: don't throw if slice length too long (Trevor Norris)
 +
 +* buffer: Buffer(buf) constructor copies into the proper buffer (Ben Noordhuis)
 +
 +* cli: remove --max-stack-size (Ben Noordhuis)
 +
 +* cli: unknown command line options are errors (Ben Noordhuis)
 +
 +* child_process: exec accept buffer as an encoding (Seth Fitzsimmons)
 +
 +* crypto: make randomBytes/pbkdf2 callbacks domain aware (Ben Noordhuis)
 +
 +* domain: deprecate domain.dispose(). (Forrest L Norvell)
 +
 +* fs: Expose birthtime on stat objects (isaacs)
 +
 +* http: Only send connection:keep-alive if necessary (isaacs)
 +
 +* repl: Catch syntax errors better (isaacs, Nathan Rajlich)
 +
 +* stream: change default highWaterMark for objectMode to 16 (Mathias Buus)
 +
 +* stream: make setEncoding/pause/resume chainable (Julian Gruber, isaacs)
 +
 +* util: pass opts to custom inspect functions (Timothy J Fontaine)
 +
 +* vm: rewritten to behave like Contextify (Domenic Denicola)
 +
 +
 +2013.08.21, Version 0.11.6 (Unstable), 04018d4b3938fd30ba14822e79195e4af2be36f6
 +
 +* uv: Upgrade to v0.11.8
 +
 +* v8: upgrade v8 to 3.20.14.1
 +
 +* build: disable SSLv2 by default (Ben Noordhuis)
 +
 +* build: don't auto-destroy existing configuration (Ben Noordhuis)
 +
 +* crypto: add TLS 1.1 and 1.2 to secureProtocol list (Matthias Bartelmeß)
 +
 +* crypto: fix memory leak in randomBytes() error path (Ben Noordhuis)
 +
 +* dgram: don't call into js when send cb is omitted (Ben Noordhuis)
 +
 +* dgram: fix regression in string argument handling (Ben Noordhuis)
 +
 +* domains: performance improvements (Trevor Norris)
 +
 +* events: EventEmitter = require('events') (Jake Verbaten)
 +
 +* http: Add write()/end() callbacks (isaacs)
 +
 +* http: Consistent 'finish' event semantics (isaacs)
 +
 +* http: Prefer 'binary' over 'ascii' (isaacs)
 +
 +* http: Support legacy agent.addRequest API (isaacs)
 +
 +* http: Write hex/base64 chunks properly (isaacs)
 +
 +* http: add agent.maxFreeSockets option (isaacs)
 +
 +* http: provide access to raw headers/trailers (isaacs)
 +
 +* http: removed headers stay removed (James Halliday)
 +
 +* http,timers: improve callback performance (Ben Noordhuis)
 +
 +* net: family option in net.connect (Vsevolod Strukchinsky)
 +
 +* readline: pause stdin before turning off terminal raw mode (Daniel Chatfield)
 +
 +* smalloc: allow different external array types (Trevor Norris)
 +
 +* smalloc: expose ExternalArraySize (Trevor Norris)
 +
 +* stream: Short-circuit buffer pushes when flowing (isaacs)
 +
 +* tls: handle errors on socket before releasing it (Fedor Indutny)
 +
 +* util: fix isPrimitive check (Trevor Norris)
 +
 +* util: isObject should always return boolean (Trevor Norris)
 +
 +
 +2013.08.06, Version 0.11.5 (Unstable), 6f92da2dd106b0c63fde563284f83e08e2a521b5
 +
 +* v8: upgrade to 3.20.11
 +
 +* uv: upgrade to v0.11.7
 +
 +* buffer: return offset for end of last write (Trevor Norris)
 +
 +* build: embed the mdb_v8.so into the binary (Timothy J Fontaine)
 +
 +* build: fix --without-ssl build (Ben Noordhuis)
 +
 +* child_process: add 'shell' option to .exec() (Ben Noordhuis)
 +
 +* dgram: report send errors to cb, don't pass bytes (Ben Noordhuis)
 +
 +* fs: write strings directly to disk (Trevor Norris)
 +
 +* https: fix default port (Koichi Kobayashi)
 +
 +* openssl: use asm for sha, md5, rmd (Fedor Indutny)
 +
 +* os: add mac address to networkInterfaces() output (Brian White)
 +
 +* smalloc: introduce smalloc module (Trevor Norris)
 +
 +* stream: Simplify flowing, passive data listening (streams3) (isaacs)
 +
 +* tls: asynchronous SNICallback (Fedor Indutny)
 +
 +* tls: share tls tickets key between cluster workers (Fedor Indutny)
 +
 +* util: don't throw on circular %j input to format() (Ben Noordhuis)
 +
 +
 +2013.07.12, Version 0.11.4 (Unstable), b5b84197ed037918fd1a26e5cb87cce7c812ca55
 +
 +* npm: Upgrade to 1.3.4
 +
 +* v8: Upgrade to v3.20.2
 +
 +* c-ares: Upgrade to piscisaureus/cares@805d153
 +
 +* timers: setImmediate process full queue each turn (Ben Noordhuis)
 +
 +* http: Add agent.get/request methods (isaacs)
 +
 +* http: Proper KeepAlive behavior (isaacs)
 +
 +* configure: fix the --without-ssl option (Nathan Rajlich)
 +
 +* buffer: propagate originating parent (Trevor Norris)
 +
 +* tls_wrap: return Error not throw for missing cert (Timothy J Fontaine)
 +
 +* src: enable native v8 typed arrays (Ben Noordhuis)
 +
 +* stream: objectMode transform should allow falsey values (Jeff Barczewski)
 +
 +* slab_allocator: remove SlabAllocator (Trevor Norris)
 +
 +* crypto: fix memory leak in LoadPKCS12 (Fedor Indutny)
 +
 +* tls: export TLSSocket (Fedor Indutny)
 +
 +* zlib: allow changing of level and strategy (Brian White)
 +
 +* zlib: allow custom flush type for flush() (Brian White)
 +
 +
 +2013.06.26, Version 0.11.3 (Unstable), 38c0c47bbe280ddc42054418091571e532d82a1e
 +
 +* uv: Upgrade to v0.11.5
 +
 +* c-ares: upgrade to 1.10.0
 +
 +* v8: upgrade to v3.19.13
 +
 +* punycode: update to v1.2.3 (Mathias Bynens)
 +
 +* debugger: break on uncaught exception (Miroslav Bajtos)
 +
 +* child_process: emit 'disconnect' asynchronously (Ben Noordhuis)
 +
 +* dtrace: enable uv's probes if enabled (Timothy J Fontaine)
 +
 +* dtrace: unify dtrace and systemtap interfaces (Timothy J Fontaine)
 +
 +* buffer: New API for backing data store (Trevor Norris)
 +
 +* buffer: return `this` in fill() for chainability (Brian White)
 +
 +* build: fix include order for building on windows (Timothy J Fontaine)
 +
 +* build: add android support (Linus Mårtensson)
 +
 +* readline: strip ctrl chars for prompt width calc (Krzysztof Chrapka)
 +
 +* tls: introduce TLSSocket based on tls_wrap binding (Fedor Indutny)
 +
 +* tls: add localAddress and localPort properties (Ben Noordhuis)
 +
 +* crypto: free excessive memory in NodeBIO (Fedor Indutny)
 +
 +* process: remove maxTickDepth (Trevor Norris)
 +
 +* timers: use uv_now instead of Date.now (Timothy J Fontaine)
 +
 +* util: Add debuglog, deprecate console lookalikes (isaacs)
 +
 +* module: use path.sep instead of a custom solution (Robert Kowalski)
 +
 +* http: don't escape request path, reject bad chars (Ben Noordhuis)
 +
 +* net: emit dns 'lookup' event before connect (Ben Noordhuis)
 +
 +* dns: add getServers and setServers (Timothy J Fontaine)
 +
 +
 +2013.05.13, Version 0.11.2 (Unstable), 5d3dc0e4c3369dfb00b7b13e08936c2e652fa696
 +
 +* 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.10.18, Version 0.10.21 (Stable), e2da042844a830fafb8031f6c477eb4f96195210
+ * uv: Upgrade to v0.10.18
+ * crypto: clear errors from verify failure (Timothy J Fontaine)
+ * dtrace: interpret two byte strings (Dave Pacheco)
+ * fs: fix fs.truncate() file content zeroing bug (Ben Noordhuis)
+ * http: provide backpressure for pipeline flood (isaacs)
+ * tls: fix premature connection termination (Ben Noordhuis)
+ 2013.09.30, Version 0.10.20 (Stable), d7234c8d50a1af73f60d2d3c0cc7eed17429a481
+ * tls: fix sporadic hang and partial reads (Fedor Indutny)
+   - fixes "npm ERR! cb() never called!"
  2013.09.24, Version 0.10.19 (Stable), 6b5e6a5a3ec8d994c9aab3b800b9edbf1b287904
  
  * uv: Upgrade to v0.10.17
diff --cc README.md
+++ b/README.md
@@@ -24,15 -24,23 +24,28 @@@ non-standard name, run the following in
      make
      make install
  
 +Prerequisites (Windows only):
 +
 +    * Python 2.6 or 2.7
 +    * Visual Studio 2010 or 2012
 +
  Windows:
  
 -    vcbuild.bat
 +    vcbuild nosign
  
+ You can download pre-built binaries for various operating systems from
+ [http://nodejs.org/download/](http://nodejs.org/download/).  The Windows
+ and OS X installers will prompt you for the location to install to.
+ The tarballs are self-contained; you can extract them to a local directory
+ with:
+     tar xzf /path/to/node-<version>-<platform>-<arch>.tar.gz
+ Or system-wide with:
+     cd /usr/local && tar --strip-components 1 -xzf \
+                          /path/to/node-<version>-<platform>-<arch>.tar.gz
  ### To run the tests:
  
  Unix/Macintosh:
@@@ -345,12 -344,15 +346,17 @@@ prototype
  
      using namespace v8;
  
-     MyObject::MyObject() {};
-     MyObject::~MyObject() {};
+     Persistent<Function> MyObject::constructor;
+     MyObject::MyObject(double value) : value_(value) {
+     }
+     MyObject::~MyObject() {
+     }
  
      void MyObject::Init(Handle<Object> exports) {
 +      Isolate* isolate = Isolate::GetCurrent();
 +
        // Prepare constructor template
        Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
        tpl->SetClassName(String::NewSymbol("MyObject"));
      }
  
      Handle<Value> MyObject::New(const Arguments& args) {
 -      HandleScope scope;
 +      Isolate* isolate = Isolate::GetCurrent();
 +      HandleScope scope(isolate);
  
-       MyObject* obj = new MyObject();
-       obj->counter_ = args[0]->IsUndefined() ? 0 : args[0]->NumberValue();
-       obj->Wrap(args.This());
-       return args.This();
+       if (args.IsConstructCall()) {
+         // Invoked as constructor: `new MyObject(...)`
+         double value = args[0]->IsUndefined() ? 0 : args[0]->NumberValue();
+         MyObject* obj = new MyObject(value);
+         obj->Wrap(args.This());
+         return args.This();
+       } else {
+         // Invoked as plain function `MyObject(...)`, turn into construct call.
+         const int argc = 1;
+         Local<Value> argv[argc] = { args[0] };
+         return scope.Close(constructor->NewInstance(argc, argv));
+       }
      }
  
      Handle<Value> MyObject::PlusOne(const Arguments& args) {
 -      HandleScope scope;
 +      Isolate* isolate = Isolate::GetCurrent();
 +      HandleScope scope(isolate);
  
        MyObject* obj = ObjectWrap::Unwrap<MyObject>(args.This());
-       obj->counter_ += 1;
+       obj->value_ += 1;
  
-       return scope.Close(Number::New(obj->counter_));
+       return scope.Close(Number::New(obj->value_));
      }
  
  Test it with:
@@@ -459,13 -465,15 +472,16 @@@ The implementation is similar to the ab
  
      using namespace v8;
  
-     MyObject::MyObject() {};
-     MyObject::~MyObject() {};
      Persistent<Function> MyObject::constructor;
  
+     MyObject::MyObject(double value) : value_(value) {
+     }
+     MyObject::~MyObject() {
+     }
      void MyObject::Init() {
 +      Isolate* isolate = Isolate::GetCurrent();
        // Prepare constructor template
        Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
        tpl->SetClassName(String::NewSymbol("MyObject"));
        // Prototype
        tpl->PrototypeTemplate()->Set(String::NewSymbol("plusOne"),
            FunctionTemplate::New(PlusOne)->GetFunction());
++<<<<<<< HEAD
 +
 +      constructor = Persistent<Function>::New(isolate, tpl->GetFunction());
++=======
+       constructor = Persistent<Function>::New(tpl->GetFunction());
++>>>>>>> upstream/v0.10
      }
  
      Handle<Value> MyObject::New(const Arguments& args) {
 -      HandleScope scope;
 +      Isolate* isolate = Isolate::GetCurrent();
 +      HandleScope scope(isolate);
  
-       MyObject* obj = new MyObject();
-       obj->counter_ = args[0]->IsUndefined() ? 0 : args[0]->NumberValue();
-       obj->Wrap(args.This());
-       return args.This();
+       if (args.IsConstructCall()) {
+         // Invoked as constructor: `new MyObject(...)`
+         double value = args[0]->IsUndefined() ? 0 : args[0]->NumberValue();
+         MyObject* obj = new MyObject(value);
+         obj->Wrap(args.This());
+         return args.This();
+       } else {
+         // Invoked as plain function `MyObject(...)`, turn into construct call.
+         const int argc = 1;
+         Local<Value> argv[argc] = { args[0] };
+         return scope.Close(constructor->NewInstance(argc, argv));
+       }
      }
  
      Handle<Value> MyObject::NewInstance(const Arguments& args) {
      }
  
      Handle<Value> MyObject::PlusOne(const Arguments& args) {
 -      HandleScope scope;
 +      Isolate* isolate = Isolate::GetCurrent();
 +      HandleScope scope(isolate);
  
        MyObject* obj = ObjectWrap::Unwrap<MyObject>(args.This());
-       obj->counter_ += 1;
+       obj->value_ += 1;
  
-       return scope.Close(Number::New(obj->counter_));
+       return scope.Close(Number::New(obj->value_));
      }
  
  Test it with:
@@@ -600,31 -611,37 +627,45 @@@ The implementation of `myobject.cc` is 
  
      using namespace v8;
  
-     MyObject::MyObject() {};
-     MyObject::~MyObject() {};
      Persistent<Function> MyObject::constructor;
  
+     MyObject::MyObject(double value) : value_(value) {
+     }
+     MyObject::~MyObject() {
+     }
      void MyObject::Init() {
 +      Isolate* isolate = Isolate::GetCurrent();
 +
        // Prepare constructor template
        Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
        tpl->SetClassName(String::NewSymbol("MyObject"));
        tpl->InstanceTemplate()->SetInternalFieldCount(1);
++<<<<<<< HEAD
 +
 +      constructor = Persistent<Function>::New(isolate, tpl->GetFunction());
++=======
+       constructor = Persistent<Function>::New(tpl->GetFunction());
++>>>>>>> upstream/v0.10
      }
  
      Handle<Value> MyObject::New(const Arguments& args) {
 -      HandleScope scope;
 +      Isolate* isolate = Isolate::GetCurrent();
 +      HandleScope scope(isolate);
  
-       MyObject* obj = new MyObject();
-       obj->val_ = args[0]->IsUndefined() ? 0 : args[0]->NumberValue();
-       obj->Wrap(args.This());
-       return args.This();
+       if (args.IsConstructCall()) {
+         // Invoked as constructor: `new MyObject(...)`
+         double value = args[0]->IsUndefined() ? 0 : args[0]->NumberValue();
+         MyObject* obj = new MyObject(value);
+         obj->Wrap(args.This());
+         return args.This();
+       } else {
+         // Invoked as plain function `MyObject(...)`, turn into construct call.
+         const int argc = 1;
+         Local<Value> argv[argc] = { args[0] };
+         return scope.Close(constructor->NewInstance(argc, argv));
+       }
      }
  
      Handle<Value> MyObject::NewInstance(const Arguments& args) {
@@@ -464,12 -475,8 +475,12 @@@ See also: `child_process.exec()` and `c
    * `cwd` {String} Current working directory of the child process
    * `env` {Object} Environment key-value pairs
    * `encoding` {String} (Default: 'utf8')
 +  * `shell` {String} Shell to execute the command with
 +    (Default: '/bin/sh' on UNIX, 'cmd.exe' on Windows,  The shell should
 +     understand the `-c` switch on UNIX or `/s /c` on Windows. On Windows,
 +     command line parsing should be compatible with `cmd.exe`.)
    * `timeout` {Number} (Default: 0)
-   * `maxBuffer` {Number} (Default: 200*1024)
+   * `maxBuffer` {Number} (Default: `200*1024`)
    * `killSignal` {String} (Default: 'SIGTERM')
  * `callback` {Function} called with the output when process terminates
    * `error` {Error}
Simple merge
Simple merge
@@@ -457,13 -423,8 +457,13 @@@ Options
   - `Agent` object: explicitly use the passed in `Agent`.
   - `false`: opts out of connection pooling with an Agent, defaults request to
     `Connection: close`.
 +- `keepAlive`: {Boolean} Keep sockets around in a pool to be used
 +  by other requests in the future. Default = `false`
 +- `keepAliveMsecs`: {Integer} When using HTTP KeepAlive, how often to
 +  send TCP KeepAlive packets over sockets being kept alive.  Default =
 +  `1000`.  Only relevant if `keepAlive` is set to `true`.
  
- `http.request()` returns an instance of the `http.ClientRequest`
+ `http.request()` returns an instance of the [http.ClientRequest][]
  class. The `ClientRequest` instance is a writable stream. If one needs to
  upload a file with a POST request, then write to the `ClientRequest` object.
  
Simple merge
Simple merge
Simple merge
Simple merge
index 9792b89,0000000..a42a380
mode 100644,000000..100644
--- /dev/null
@@@ -1,880 -1,0 +1,879 @@@
-     var read = this._buffer.use(this.pair.ssl, out, size);
 +// 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 assert = require('assert');
 +var crypto = require('crypto');
 +var events = require('events');
 +var stream = require('stream');
 +var tls = require('tls');
 +var util = require('util');
 +
 +var Timer = process.binding('timer_wrap').Timer;
 +var Connection = null;
 +try {
 +  Connection = process.binding('crypto').Connection;
 +} catch (e) {
 +  throw new Error('node.js not compiled with openssl crypto support.');
 +}
 +
 +var debug = util.debuglog('tls-legacy');
 +
 +function SlabBuffer() {
 +  this.create();
 +}
 +
 +
 +SlabBuffer.prototype.create = function create() {
 +  this.isFull = false;
 +  this.pool = new Buffer(tls.SLAB_BUFFER_SIZE);
 +  this.offset = 0;
 +  this.remaining = this.pool.length;
 +};
 +
 +
 +SlabBuffer.prototype.use = function use(context, fn, size) {
 +  if (this.remaining === 0) {
 +    this.isFull = true;
 +    return 0;
 +  }
 +
 +  var actualSize = this.remaining;
 +
 +  if (!util.isNull(size)) actualSize = Math.min(size, actualSize);
 +
 +  var bytes = fn.call(context, this.pool, this.offset, actualSize);
 +  if (bytes > 0) {
 +    this.offset += bytes;
 +    this.remaining -= bytes;
 +  }
 +
 +  assert(this.remaining >= 0);
 +
 +  return bytes;
 +};
 +
 +
 +var slabBuffer = null;
 +
 +
 +// Base class of both CleartextStream and EncryptedStream
 +function CryptoStream(pair, options) {
 +  stream.Duplex.call(this, options);
 +
 +  this.pair = pair;
 +  this._pending = null;
 +  this._pendingEncoding = '';
 +  this._pendingCallback = null;
 +  this._doneFlag = false;
 +  this._retryAfterPartial = false;
 +  this._halfRead = false;
 +  this._sslOutCb = null;
 +  this._resumingSession = false;
 +  this._reading = true;
 +  this._destroyed = false;
 +  this._ended = false;
 +  this._finished = false;
 +  this._opposite = null;
 +
 +  if (util.isNull(slabBuffer)) slabBuffer = new SlabBuffer();
 +  this._buffer = slabBuffer;
 +
 +  this.once('finish', onCryptoStreamFinish);
 +
 +  // net.Socket calls .onend too
 +  this.once('end', onCryptoStreamEnd);
 +}
 +util.inherits(CryptoStream, stream.Duplex);
 +
 +
 +function onCryptoStreamFinish() {
 +  this._finished = true;
 +
 +  if (this === this.pair.cleartext) {
 +    debug('cleartext.onfinish');
 +    if (this.pair.ssl) {
 +      // Generate close notify
 +      // NOTE: first call checks if client has sent us shutdown,
 +      // second call enqueues shutdown into the BIO.
 +      if (this.pair.ssl.shutdown() !== 1) {
 +        if (this.pair.ssl && this.pair.ssl.error)
 +          return this.pair.error();
 +
 +        this.pair.ssl.shutdown();
 +      }
 +
 +      if (this.pair.ssl && this.pair.ssl.error)
 +        return this.pair.error();
 +    }
 +  } else {
 +    debug('encrypted.onfinish');
 +  }
 +
 +  // Try to read just to get sure that we won't miss EOF
 +  if (this._opposite.readable) this._opposite.read(0);
 +
 +  if (this._opposite._ended) {
 +    this._done();
 +
 +    // No half-close, sorry
 +    if (this === this.pair.cleartext) this._opposite._done();
 +  }
 +}
 +
 +
 +function onCryptoStreamEnd() {
 +  this._ended = true;
 +  if (this === this.pair.cleartext) {
 +    debug('cleartext.onend');
 +  } else {
 +    debug('encrypted.onend');
 +  }
 +}
 +
 +
 +// NOTE: Called once `this._opposite` is set.
 +CryptoStream.prototype.init = function init() {
 +  var self = this;
 +  this._opposite.on('sslOutEnd', function() {
 +    if (self._sslOutCb) {
 +      var cb = self._sslOutCb;
 +      self._sslOutCb = null;
 +      cb(null);
 +    }
 +  });
 +};
 +
 +
 +CryptoStream.prototype._write = function write(data, encoding, cb) {
 +  assert(util.isNull(this._pending));
 +
 +  // Black-hole data
 +  if (!this.pair.ssl) return cb(null);
 +
 +  // When resuming session don't accept any new data.
 +  // And do not put too much data into openssl, before writing it from encrypted
 +  // side.
 +  //
 +  // TODO(indutny): Remove magic number, use watermark based limits
 +  if (!this._resumingSession &&
 +      this._opposite._internallyPendingBytes() < 128 * 1024) {
 +    // Write current buffer now
 +    var written;
 +    if (this === this.pair.cleartext) {
 +      debug('cleartext.write called with %d bytes', data.length);
 +      written = this.pair.ssl.clearIn(data, 0, data.length);
 +    } else {
 +      debug('encrypted.write called with %d bytes', data.length);
 +      written = this.pair.ssl.encIn(data, 0, data.length);
 +    }
 +
 +    // Handle and report errors
 +    if (this.pair.ssl && this.pair.ssl.error) {
 +      return cb(this.pair.error(true));
 +    }
 +
 +    // Force SSL_read call to cycle some states/data inside OpenSSL
 +    this.pair.cleartext.read(0);
 +
 +    // Cycle encrypted data
 +    if (this.pair.encrypted._internallyPendingBytes())
 +      this.pair.encrypted.read(0);
 +
 +    // Get NPN and Server name when ready
 +    this.pair.maybeInitFinished();
 +
 +    // Whole buffer was written
 +    if (written === data.length) {
 +      if (this === this.pair.cleartext) {
 +        debug('cleartext.write succeed with ' + written + ' bytes');
 +      } else {
 +        debug('encrypted.write succeed with ' + written + ' bytes');
 +      }
 +
 +      // Invoke callback only when all data read from opposite stream
 +      if (this._opposite._halfRead) {
 +        assert(util.isNull(this._sslOutCb));
 +        this._sslOutCb = cb;
 +      } else {
 +        cb(null);
 +      }
 +      return;
 +    } else if (written !== 0 && written !== -1) {
 +      assert(!this._retryAfterPartial);
 +      this._retryAfterPartial = true;
 +      this._write(data.slice(written), encoding, cb);
 +      this._retryAfterPartial = false;
 +      return;
 +    }
 +  } else {
 +    debug('cleartext.write queue is full');
 +
 +    // Force SSL_read call to cycle some states/data inside OpenSSL
 +    this.pair.cleartext.read(0);
 +  }
 +
 +  // No write has happened
 +  this._pending = data;
 +  this._pendingEncoding = encoding;
 +  this._pendingCallback = cb;
 +
 +  if (this === this.pair.cleartext) {
 +    debug('cleartext.write queued with %d bytes', data.length);
 +  } else {
 +    debug('encrypted.write queued with %d bytes', data.length);
 +  }
 +};
 +
 +
 +CryptoStream.prototype._writePending = function writePending() {
 +  var data = this._pending,
 +      encoding = this._pendingEncoding,
 +      cb = this._pendingCallback;
 +
 +  this._pending = null;
 +  this._pendingEncoding = '';
 +  this._pendingCallback = null;
 +  this._write(data, encoding, cb);
 +};
 +
 +
 +CryptoStream.prototype._read = function read(size) {
 +  // XXX: EOF?!
 +  if (!this.pair.ssl) return this.push(null);
 +
 +  // Wait for session to be resumed
 +  // Mark that we're done reading, but don't provide data or EOF
 +  if (this._resumingSession || !this._reading) return this.push('');
 +
 +  var out;
 +  if (this === this.pair.cleartext) {
 +    debug('cleartext.read called with %d bytes', size);
 +    out = this.pair.ssl.clearOut;
 +  } else {
 +    debug('encrypted.read called with %d bytes', size);
 +    out = this.pair.ssl.encOut;
 +  }
 +
 +  var bytesRead = 0,
 +      start = this._buffer.offset;
 +  do {
-       size -= read;
++    var read = this._buffer.use(this.pair.ssl, out, size - bytesRead);
 +    if (read > 0) {
 +      bytesRead += read;
-       socket.destroy();
 +    }
 +
 +    // Handle and report errors
 +    if (this.pair.ssl && this.pair.ssl.error) {
 +      this.pair.error();
 +      break;
 +    }
 +
 +    // Get NPN and Server name when ready
 +    this.pair.maybeInitFinished();
 +
 +    // `maybeInitFinished()` can emit the 'secure' event which
 +    // in turn destroys the connection in case of authentication
 +    // failure and sets `this.pair.ssl` to `null`.
 +  } while (read > 0 &&
 +           !this._buffer.isFull &&
 +           bytesRead < size &&
 +           this.pair.ssl !== null);
 +
 +  // Create new buffer if previous was filled up
 +  var pool = this._buffer.pool;
 +  if (this._buffer.isFull) this._buffer.create();
 +
 +  assert(bytesRead >= 0);
 +
 +  if (this === this.pair.cleartext) {
 +    debug('cleartext.read succeed with %d bytes', bytesRead);
 +  } else {
 +    debug('encrypted.read succeed with %d bytes', bytesRead);
 +  }
 +
 +  // Try writing pending data
 +  if (!util.isNull(this._pending)) this._writePending();
 +  if (!util.isNull(this._opposite._pending)) this._opposite._writePending();
 +
 +  if (bytesRead === 0) {
 +    // EOF when cleartext has finished and we have nothing to read
 +    if (this._opposite._finished && this._internallyPendingBytes() === 0) {
 +      // Perform graceful shutdown
 +      this._done();
 +
 +      // No half-open, sorry!
 +      if (this === this.pair.cleartext)
 +        this._opposite._done();
 +
 +      // EOF
 +      this.push(null);
 +    } else {
 +      // Bail out
 +      this.push('');
 +    }
 +  } else {
 +    // Give them requested data
 +    this.push(pool.slice(start, start + bytesRead));
 +  }
 +
 +  // Let users know that we've some internal data to read
 +  var halfRead = this._internallyPendingBytes() !== 0;
 +
 +  // Smart check to avoid invoking 'sslOutEnd' in the most of the cases
 +  if (this._halfRead !== halfRead) {
 +    this._halfRead = halfRead;
 +
 +    // Notify listeners about internal data end
 +    if (!halfRead) {
 +      if (this === this.pair.cleartext) {
 +        debug('cleartext.sslOutEnd');
 +      } else {
 +        debug('encrypted.sslOutEnd');
 +      }
 +
 +      this.emit('sslOutEnd');
 +    }
 +  }
 +};
 +
 +
 +CryptoStream.prototype.setTimeout = function(timeout, callback) {
 +  if (this.socket) this.socket.setTimeout(timeout, callback);
 +};
 +
 +
 +CryptoStream.prototype.setNoDelay = function(noDelay) {
 +  if (this.socket) this.socket.setNoDelay(noDelay);
 +};
 +
 +
 +CryptoStream.prototype.setKeepAlive = function(enable, initialDelay) {
 +  if (this.socket) this.socket.setKeepAlive(enable, initialDelay);
 +};
 +
 +CryptoStream.prototype.__defineGetter__('bytesWritten', function() {
 +  return this.socket ? this.socket.bytesWritten : 0;
 +});
 +
 +CryptoStream.prototype.getPeerCertificate = function() {
 +  if (this.pair.ssl) {
 +    var c = this.pair.ssl.getPeerCertificate();
 +
 +    if (c) {
 +      if (c.issuer) c.issuer = tls.parseCertString(c.issuer);
 +      if (c.subject) c.subject = tls.parseCertString(c.subject);
 +      return c;
 +    }
 +  }
 +
 +  return null;
 +};
 +
 +CryptoStream.prototype.getSession = function() {
 +  if (this.pair.ssl) {
 +    return this.pair.ssl.getSession();
 +  }
 +
 +  return null;
 +};
 +
 +CryptoStream.prototype.isSessionReused = function() {
 +  if (this.pair.ssl) {
 +    return this.pair.ssl.isSessionReused();
 +  }
 +
 +  return null;
 +};
 +
 +CryptoStream.prototype.getCipher = function(err) {
 +  if (this.pair.ssl) {
 +    return this.pair.ssl.getCurrentCipher();
 +  } else {
 +    return null;
 +  }
 +};
 +
 +
 +CryptoStream.prototype.end = function(chunk, encoding) {
 +  if (this === this.pair.cleartext) {
 +    debug('cleartext.end');
 +  } else {
 +    debug('encrypted.end');
 +  }
 +
 +  // Write pending data first
 +  if (!util.isNull(this._pending)) this._writePending();
 +
 +  this.writable = false;
 +
 +  stream.Duplex.prototype.end.call(this, chunk, encoding);
 +};
 +
 +
 +CryptoStream.prototype.destroySoon = function(err) {
 +  if (this === this.pair.cleartext) {
 +    debug('cleartext.destroySoon');
 +  } else {
 +    debug('encrypted.destroySoon');
 +  }
 +
 +  if (this.writable)
 +    this.end();
 +
 +  if (this._writableState.finished && this._opposite._ended) {
 +    this.destroy();
 +  } else {
 +    // Wait for both `finish` and `end` events to ensure that all data that
 +    // was written on this side was read from the other side.
 +    var self = this;
 +    var waiting = 1;
 +    function finish() {
 +      if (--waiting === 0) self.destroy();
 +    }
 +    this._opposite.once('end', finish);
 +    if (!this._finished) {
 +      this.once('finish', finish);
 +      ++waiting;
 +    }
 +  }
 +};
 +
 +
 +CryptoStream.prototype.destroy = function(err) {
 +  if (this._destroyed) return;
 +  this._destroyed = true;
 +  this.readable = this.writable = false;
 +
 +  // Destroy both ends
 +  if (this === this.pair.cleartext) {
 +    debug('cleartext.destroy');
 +  } else {
 +    debug('encrypted.destroy');
 +  }
 +  this._opposite.destroy();
 +
 +  var self = this;
 +  process.nextTick(function() {
 +    // Force EOF
 +    self.push(null);
 +
 +    // Emit 'close' event
 +    self.emit('close', err ? true : false);
 +  });
 +};
 +
 +
 +CryptoStream.prototype._done = function() {
 +  this._doneFlag = true;
 +
 +  if (this === this.pair.encrypted && !this.pair._secureEstablished)
 +    return this.pair.error();
 +
 +  if (this.pair.cleartext._doneFlag &&
 +      this.pair.encrypted._doneFlag &&
 +      !this.pair._doneFlag) {
 +    // If both streams are done:
 +    this.pair.destroy();
 +  }
 +};
 +
 +
 +// readyState is deprecated. Don't use it.
 +Object.defineProperty(CryptoStream.prototype, 'readyState', {
 +  get: function() {
 +    if (this._connecting) {
 +      return 'opening';
 +    } else if (this.readable && this.writable) {
 +      return 'open';
 +    } else if (this.readable && !this.writable) {
 +      return 'readOnly';
 +    } else if (!this.readable && this.writable) {
 +      return 'writeOnly';
 +    } else {
 +      return 'closed';
 +    }
 +  }
 +});
 +
 +
 +function CleartextStream(pair, options) {
 +  CryptoStream.call(this, pair, options);
 +
 +  // This is a fake kludge to support how the http impl sits
 +  // on top of net Sockets
 +  var self = this;
 +  this._handle = {
 +    readStop: function() {
 +      self._reading = false;
 +    },
 +    readStart: function() {
 +      if (self._reading && self._readableState.length > 0) return;
 +      self._reading = true;
 +      self.read(0);
 +      if (self._opposite.readable) self._opposite.read(0);
 +    }
 +  };
 +}
 +util.inherits(CleartextStream, CryptoStream);
 +
 +
 +CleartextStream.prototype._internallyPendingBytes = function() {
 +  if (this.pair.ssl) {
 +    return this.pair.ssl.clearPending();
 +  } else {
 +    return 0;
 +  }
 +};
 +
 +
 +CleartextStream.prototype.address = function() {
 +  return this.socket && this.socket.address();
 +};
 +
 +
 +CleartextStream.prototype.__defineGetter__('remoteAddress', function() {
 +  return this.socket && this.socket.remoteAddress;
 +});
 +
 +
 +CleartextStream.prototype.__defineGetter__('remotePort', function() {
 +  return this.socket && this.socket.remotePort;
 +});
 +
 +
 +CleartextStream.prototype.__defineGetter__('localAddress', function() {
 +  return this.socket && this.socket.localAddress;
 +});
 +
 +
 +CleartextStream.prototype.__defineGetter__('localPort', function() {
 +  return this.socket && this.socket.localPort;
 +});
 +
 +
 +function EncryptedStream(pair, options) {
 +  CryptoStream.call(this, pair, options);
 +}
 +util.inherits(EncryptedStream, CryptoStream);
 +
 +
 +EncryptedStream.prototype._internallyPendingBytes = function() {
 +  if (this.pair.ssl) {
 +    return this.pair.ssl.encPending();
 +  } else {
 +    return 0;
 +  }
 +};
 +
 +
 +function onhandshakestart() {
 +  debug('onhandshakestart');
 +
 +  var self = this;
 +  var ssl = self.ssl;
 +  var now = Timer.now();
 +
 +  assert(now >= ssl.lastHandshakeTime);
 +
 +  if ((now - ssl.lastHandshakeTime) >= tls.CLIENT_RENEG_WINDOW * 1000) {
 +    ssl.handshakes = 0;
 +  }
 +
 +  var first = (ssl.lastHandshakeTime === 0);
 +  ssl.lastHandshakeTime = now;
 +  if (first) return;
 +
 +  if (++ssl.handshakes > tls.CLIENT_RENEG_LIMIT) {
 +    // Defer the error event to the next tick. We're being called from OpenSSL's
 +    // state machine and OpenSSL is not re-entrant. We cannot allow the user's
 +    // callback to destroy the connection right now, it would crash and burn.
 +    setImmediate(function() {
 +      var err = new Error('TLS session renegotiation attack detected.');
 +      if (self.cleartext) self.cleartext.emit('error', err);
 +    });
 +  }
 +}
 +
 +
 +function onhandshakedone() {
 +  // for future use
 +  debug('onhandshakedone');
 +}
 +
 +
 +function onclienthello(hello) {
 +  var self = this,
 +      once = false;
 +
 +  this._resumingSession = true;
 +  function callback(err, session) {
 +    if (once) return;
 +    once = true;
 +
 +    if (err) return self.socket.destroy(err);
 +
 +    self.ssl.loadSession(session);
 +    self.ssl.endParser();
 +
 +    // Cycle data
 +    self._resumingSession = false;
 +    self.cleartext.read(0);
 +    self.encrypted.read(0);
 +  }
 +
 +  if (hello.sessionId.length <= 0 ||
 +      !this.server ||
 +      !this.server.emit('resumeSession', hello.sessionId, callback)) {
 +    callback(null, null);
 +  }
 +}
 +
 +
 +function onnewsession(key, session) {
 +  if (!this.server) return;
 +  this.server.emit('newSession', key, session);
 +}
 +
 +
 +/**
 + * Provides a pair of streams to do encrypted communication.
 + */
 +
 +function SecurePair(credentials, isServer, requestCert, rejectUnauthorized,
 +                    options) {
 +  if (!(this instanceof SecurePair)) {
 +    return new SecurePair(credentials,
 +                          isServer,
 +                          requestCert,
 +                          rejectUnauthorized,
 +                          options);
 +  }
 +
 +  var self = this;
 +
 +  options || (options = {});
 +
 +  events.EventEmitter.call(this);
 +
 +  this.server = options.server;
 +  this._secureEstablished = false;
 +  this._isServer = isServer ? true : false;
 +  this._encWriteState = true;
 +  this._clearWriteState = true;
 +  this._doneFlag = false;
 +  this._destroying = false;
 +
 +  if (!credentials) {
 +    this.credentials = crypto.createCredentials();
 +  } else {
 +    this.credentials = credentials;
 +  }
 +
 +  if (!this._isServer) {
 +    // For clients, we will always have either a given ca list or be using
 +    // default one
 +    requestCert = true;
 +  }
 +
 +  this._rejectUnauthorized = rejectUnauthorized ? true : false;
 +  this._requestCert = requestCert ? true : false;
 +
 +  this.ssl = new Connection(this.credentials.context,
 +                            this._isServer ? true : false,
 +                            this._isServer ? this._requestCert :
 +                                             options.servername,
 +                            this._rejectUnauthorized);
 +
 +  if (this._isServer) {
 +    this.ssl.onhandshakestart = onhandshakestart.bind(this);
 +    this.ssl.onhandshakedone = onhandshakedone.bind(this);
 +    this.ssl.onclienthello = onclienthello.bind(this);
 +    this.ssl.onnewsession = onnewsession.bind(this);
 +    this.ssl.lastHandshakeTime = 0;
 +    this.ssl.handshakes = 0;
 +  }
 +
 +  if (process.features.tls_sni) {
 +    if (this._isServer && options.SNICallback) {
 +      this.ssl.setSNICallback(options.SNICallback);
 +    }
 +    this.servername = null;
 +  }
 +
 +  if (process.features.tls_npn && options.NPNProtocols) {
 +    this.ssl.setNPNProtocols(options.NPNProtocols);
 +    this.npnProtocol = null;
 +  }
 +
 +  /* Acts as a r/w stream to the cleartext side of the stream. */
 +  this.cleartext = new CleartextStream(this, options.cleartext);
 +
 +  /* Acts as a r/w stream to the encrypted side of the stream. */
 +  this.encrypted = new EncryptedStream(this, options.encrypted);
 +
 +  /* Let streams know about each other */
 +  this.cleartext._opposite = this.encrypted;
 +  this.encrypted._opposite = this.cleartext;
 +  this.cleartext.init();
 +  this.encrypted.init();
 +
 +  process.nextTick(function() {
 +    /* The Connection may be destroyed by an abort call */
 +    if (self.ssl) {
 +      self.ssl.start();
 +    }
 +  });
 +}
 +
 +util.inherits(SecurePair, events.EventEmitter);
 +
 +
 +exports.createSecurePair = function(credentials,
 +                                    isServer,
 +                                    requestCert,
 +                                    rejectUnauthorized) {
 +  var pair = new SecurePair(credentials,
 +                            isServer,
 +                            requestCert,
 +                            rejectUnauthorized);
 +  return pair;
 +};
 +
 +
 +SecurePair.prototype.maybeInitFinished = function() {
 +  if (this.ssl && !this._secureEstablished && this.ssl.isInitFinished()) {
 +    if (process.features.tls_npn) {
 +      this.npnProtocol = this.ssl.getNegotiatedProtocol();
 +    }
 +
 +    if (process.features.tls_sni) {
 +      this.servername = this.ssl.getServername();
 +    }
 +
 +    this._secureEstablished = true;
 +    debug('secure established');
 +    this.emit('secure');
 +  }
 +};
 +
 +
 +SecurePair.prototype.destroy = function() {
 +  if (this._destroying) return;
 +
 +  if (!this._doneFlag) {
 +    debug('SecurePair.destroy');
 +    this._destroying = true;
 +
 +    // SecurePair should be destroyed only after it's streams
 +    this.cleartext.destroy();
 +    this.encrypted.destroy();
 +
 +    this._doneFlag = true;
 +    this.ssl.error = null;
 +    this.ssl.close();
 +    this.ssl = null;
 +  }
 +};
 +
 +
 +SecurePair.prototype.error = function(returnOnly) {
 +  var err = this.ssl.error;
 +  this.ssl.error = null;
 +
 +  if (!this._secureEstablished) {
 +    // Emit ECONNRESET instead of zero return
 +    if (!err || err.message === 'ZERO_RETURN') {
 +      var connReset = new Error('socket hang up');
 +      connReset.code = 'ECONNRESET';
 +      connReset.sslError = err && err.message;
 +
 +      err = connReset;
 +    }
 +    this.destroy();
 +    if (!returnOnly) this.emit('error', err);
 +  } else if (this._isServer &&
 +             this._rejectUnauthorized &&
 +             /peer did not return a certificate/.test(err.message)) {
 +    // Not really an error.
 +    this.destroy();
 +  } else {
 +    if (!returnOnly) this.cleartext.emit('error', err);
 +  }
 +  return err;
 +};
 +
 +
 +exports.pipe = function pipe(pair, socket) {
 +  pair.encrypted.pipe(socket);
 +  socket.pipe(pair.encrypted);
 +
 +  pair.encrypted.on('close', function() {
 +    process.nextTick(function() {
 +      // Encrypted should be unpiped from socket to prevent possible
 +      // write after destroy.
 +      pair.encrypted.unpipe(socket);
++      socket.destroySoon();
 +    });
 +  });
 +
 +  pair.fd = socket.fd;
 +  var cleartext = pair.cleartext;
 +  cleartext.socket = socket;
 +  cleartext.encrypted = pair.encrypted;
 +  cleartext.authorized = false;
 +
 +  // cycle the data whenever the socket drains, so that
 +  // we can pull some more into it.  normally this would
 +  // be handled by the fact that pipe() triggers read() calls
 +  // on writable.drain, but CryptoStreams are a bit more
 +  // complicated.  Since the encrypted side actually gets
 +  // its data from the cleartext side, we have to give it a
 +  // light kick to get in motion again.
 +  socket.on('drain', function() {
 +    if (pair.encrypted._pending)
 +      pair.encrypted._writePending();
 +    if (pair.cleartext._pending)
 +      pair.cleartext._writePending();
 +    pair.encrypted.read(0);
 +    pair.cleartext.read(0);
 +  });
 +
 +  function onerror(e) {
 +    if (cleartext._controlReleased) {
 +      cleartext.emit('error', e);
 +    }
 +  }
 +
 +  function onclose() {
 +    socket.removeListener('error', onerror);
 +    socket.removeListener('timeout', ontimeout);
 +  }
 +
 +  function ontimeout() {
 +    cleartext.emit('timeout');
 +  }
 +
 +  socket.on('error', onerror);
 +  socket.on('close', onclose);
 +  socket.on('timeout', ontimeout);
 +
 +  return cleartext;
 +};
diff --cc lib/fs.js
Simple merge
@@@ -1664,1599 -2057,1744 +1664,1602 @@@ int Connection::SelectSNIContextCallbac
  }
  #endif
  
 +void Connection::New(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -class Cipher : public ObjectWrap {
 - public:
 -  static void Initialize (v8::Handle<v8::Object> target) {
 -    HandleScope scope;
 -
 -    Local<FunctionTemplate> t = FunctionTemplate::New(New);
 -
 -    t->InstanceTemplate()->SetInternalFieldCount(1);
 -
 -    NODE_SET_PROTOTYPE_METHOD(t, "init", CipherInit);
 -    NODE_SET_PROTOTYPE_METHOD(t, "initiv", CipherInitIv);
 -    NODE_SET_PROTOTYPE_METHOD(t, "update", CipherUpdate);
 -    NODE_SET_PROTOTYPE_METHOD(t, "setAutoPadding", SetAutoPadding);
 -    NODE_SET_PROTOTYPE_METHOD(t, "final", CipherFinal);
 -
 -    target->Set(String::NewSymbol("Cipher"), t->GetFunction());
 +  if (args.Length() < 1 || !args[0]->IsObject()) {
 +    return ThrowError("First argument must be a crypto module Credentials");
    }
  
 +  SecureContext* sc = WeakObject::Unwrap<SecureContext>(args[0]->ToObject());
 +  Environment* env = sc->env();
  
 -  bool CipherInit(char* cipherType, char* key_buf, int key_buf_len) {
 -    cipher = EVP_get_cipherbyname(cipherType);
 -    if(!cipher) {
 -      fprintf(stderr, "node-crypto : Unknown cipher %s\n", cipherType);
 -      return false;
 -    }
 +  bool is_server = args[1]->BooleanValue();
  
 -    unsigned char key[EVP_MAX_KEY_LENGTH],iv[EVP_MAX_IV_LENGTH];
 -    int key_len = EVP_BytesToKey(cipher, EVP_md5(), NULL,
 -      (unsigned char*) key_buf, key_buf_len, 1, key, iv);
 +  SSLWrap<Connection>::Kind kind =
 +      is_server ? SSLWrap<Connection>::kServer : SSLWrap<Connection>::kClient;
 +  Connection* conn = new Connection(env, args.This(), sc, kind);
 +  conn->ssl_ = SSL_new(sc->ctx_);
 +  conn->bio_read_ = NodeBIO::New();
 +  conn->bio_write_ = NodeBIO::New();
  
 -    EVP_CIPHER_CTX_init(&ctx);
 -    EVP_CipherInit_ex(&ctx, cipher, NULL, NULL, NULL, true);
 -    if (!EVP_CIPHER_CTX_set_key_length(&ctx, key_len)) {
 -      fprintf(stderr, "node-crypto : Invalid key length %d\n", key_len);
 -      EVP_CIPHER_CTX_cleanup(&ctx);
 -      return false;
 -    }
 -    EVP_CipherInit_ex(&ctx, NULL, NULL,
 -      (unsigned char*)key,
 -      (unsigned char*)iv, true);
 -    initialised_ = true;
 -    return true;
 -  }
 -
 -
 -  bool CipherInitIv(char* cipherType,
 -                    char* key,
 -                    int key_len,
 -                    char* iv,
 -                    int iv_len) {
 -    cipher = EVP_get_cipherbyname(cipherType);
 -    if(!cipher) {
 -      fprintf(stderr, "node-crypto : Unknown cipher %s\n", cipherType);
 -      return false;
 -    }
 -    /* OpenSSL versions up to 0.9.8l failed to return the correct
 -       iv_length (0) for ECB ciphers */
 -    if (EVP_CIPHER_iv_length(cipher) != iv_len &&
 -      !(EVP_CIPHER_mode(cipher) == EVP_CIPH_ECB_MODE && iv_len == 0)) {
 -      fprintf(stderr, "node-crypto : Invalid IV length %d\n", iv_len);
 -      return false;
 -    }
 -    EVP_CIPHER_CTX_init(&ctx);
 -    EVP_CipherInit_ex(&ctx, cipher, NULL, NULL, NULL, true);
 -    if (!EVP_CIPHER_CTX_set_key_length(&ctx, key_len)) {
 -      fprintf(stderr, "node-crypto : Invalid key length %d\n", key_len);
 -      EVP_CIPHER_CTX_cleanup(&ctx);
 -      return false;
 -    }
 -    EVP_CipherInit_ex(&ctx, NULL, NULL,
 -      (unsigned char*)key,
 -      (unsigned char*)iv, true);
 -    initialised_ = true;
 -    return true;
 -  }
 +  SSL_set_app_data(conn->ssl_, conn);
  
 -  int CipherUpdate(char* data, int len, unsigned char** out, int* out_len) {
 -    if (!initialised_) return 0;
 -    *out_len = len+EVP_CIPHER_CTX_block_size(&ctx);
 -    *out = new unsigned char[*out_len];
 -    return EVP_CipherUpdate(&ctx, *out, out_len, (unsigned char*)data, len);
 -  }
 +  if (is_server)
 +    SSL_set_info_callback(conn->ssl_, SSLInfoCallback);
  
 -  int SetAutoPadding(bool auto_padding) {
 -    if (!initialised_) return 0;
 -    return EVP_CIPHER_CTX_set_padding(&ctx, auto_padding ? 1 : 0);
 +#ifdef OPENSSL_NPN_NEGOTIATED
 +  if (is_server) {
 +    // Server should advertise NPN protocols
 +    SSL_CTX_set_next_protos_advertised_cb(
 +        sc->ctx_,
 +        SSLWrap<Connection>::AdvertiseNextProtoCallback,
 +        conn);
 +  } else {
 +    // Client should select protocol from advertised
 +    // If server supports NPN
 +    SSL_CTX_set_next_proto_select_cb(
 +        sc->ctx_,
 +        SSLWrap<Connection>::SelectNextProtoCallback,
 +        conn);
    }
 +#endif
  
 -  int CipherFinal(unsigned char** out, int *out_len) {
 -    if (!initialised_) return 0;
 -    *out = new unsigned char[EVP_CIPHER_CTX_block_size(&ctx)];
 -    int r = EVP_CipherFinal_ex(&ctx,*out, out_len);
 -    EVP_CIPHER_CTX_cleanup(&ctx);
 -    initialised_ = false;
 -    return r;
 +#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
 +  if (is_server) {
 +    SSL_CTX_set_tlsext_servername_callback(sc->ctx_, SelectSNIContextCallback_);
 +  } else {
 +    const String::Utf8Value servername(args[2]);
 +    SSL_set_tlsext_host_name(conn->ssl_, *servername);
    }
 +#endif
  
 +  SSL_set_bio(conn->ssl_, conn->bio_read_, conn->bio_write_);
  
 - protected:
 -
 -  static Handle<Value> New(const Arguments& args) {
 -    HandleScope scope;
 -
 -    Cipher *cipher = new Cipher();
 -    cipher->Wrap(args.This());
 -    return args.This();
 -  }
 -
 -  static Handle<Value> CipherInit(const Arguments& args) {
 -    HandleScope scope;
 +#ifdef SSL_MODE_RELEASE_BUFFERS
 +  long mode = SSL_get_mode(conn->ssl_);
 +  SSL_set_mode(conn->ssl_, mode | SSL_MODE_RELEASE_BUFFERS);
 +#endif
  
 -    Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This());
  
 -    if (args.Length() <= 1
 -        || !args[0]->IsString()
 -        || !(args[1]->IsString() || Buffer::HasInstance(args[1])))
 -    {
 -      return ThrowException(Exception::Error(String::New(
 -        "Must give cipher-type, key")));
 +  int verify_mode;
 +  if (is_server) {
 +    bool request_cert = args[2]->BooleanValue();
 +    if (!request_cert) {
 +      // Note reject_unauthorized ignored.
 +      verify_mode = SSL_VERIFY_NONE;
 +    } else {
 +      bool reject_unauthorized = args[3]->BooleanValue();
 +      verify_mode = SSL_VERIFY_PEER;
 +      if (reject_unauthorized)
 +        verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
      }
 +  } else {
 +    // Note request_cert and reject_unauthorized are ignored for clients.
 +    verify_mode = SSL_VERIFY_NONE;
 +  }
  
 -    ASSERT_IS_BUFFER(args[1]);
 -    ssize_t key_buf_len = Buffer::Length(args[1]);
  
 -    if (key_buf_len < 0) {
 -      Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
 -      return ThrowException(exception);
 -    }
 +  // Always allow a connection. We'll reject in javascript.
 +  SSL_set_verify(conn->ssl_, verify_mode, VerifyCallback);
  
 -    char* key_buf = new char[key_buf_len];
 -    ssize_t key_written = DecodeWrite(key_buf, key_buf_len, args[1], BINARY);
 -    assert(key_written == key_buf_len);
 +  if (is_server) {
 +    SSL_set_accept_state(conn->ssl_);
 +  } else {
 +    SSL_set_connect_state(conn->ssl_);
 +  }
 +}
  
 -    String::Utf8Value cipherType(args[0]);
  
 -    bool r = cipher->CipherInit(*cipherType, key_buf, key_buf_len);
 +void Connection::SSLInfoCallback(const SSL *ssl_, int where, int ret) {
 +  if (!(where & (SSL_CB_HANDSHAKE_START | SSL_CB_HANDSHAKE_DONE)))
 +    return;
  
 -    delete [] key_buf;
 +  // Be compatible with older versions of OpenSSL. SSL_get_app_data() wants
 +  // a non-const SSL* in OpenSSL <= 0.9.7e.
 +  SSL* ssl = const_cast<SSL*>(ssl_);
 +  Connection* conn = static_cast<Connection*>(SSL_get_app_data(ssl));
 +  Environment* env = conn->env();
 +  Context::Scope context_scope(env->context());
 +  HandleScope handle_scope(env->isolate());
  
 -    if (!r) return ThrowCryptoError(ERR_get_error());
 +  if (where & SSL_CB_HANDSHAKE_START) {
 +    MakeCallback(env,
 +                 conn->weak_object(node_isolate),
 +                 env->onhandshakestart_string());
 +  }
  
 -    return args.This();
 +  if (where & SSL_CB_HANDSHAKE_DONE) {
 +    MakeCallback(env,
 +                 conn->weak_object(node_isolate),
 +                 env->onhandshakedone_string());
    }
 +}
  
  
 -  static Handle<Value> CipherInitIv(const Arguments& args) {
 -    Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This());
 +void Connection::EncIn(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -    HandleScope scope;
 +  Connection* conn = Connection::Unwrap(args.This());
  
 +  if (args.Length() < 3) {
 +    return ThrowTypeError("Takes 3 parameters");
 +  }
  
 -    if (args.Length() <= 2
 -        || !args[0]->IsString()
 -        || !(args[1]->IsString() || Buffer::HasInstance(args[1]))
 -        || !(args[2]->IsString() || Buffer::HasInstance(args[2])))
 -    {
 -      return ThrowException(Exception::Error(String::New(
 -        "Must give cipher-type, key, and iv as argument")));
 -    }
 +  if (!Buffer::HasInstance(args[0])) {
 +    return ThrowTypeError("Second argument should be a buffer");
 +  }
  
 -    ASSERT_IS_BUFFER(args[1]);
 -    ssize_t key_len = Buffer::Length(args[1]);
 +  char* buffer_data = Buffer::Data(args[0]);
 +  size_t buffer_length = Buffer::Length(args[0]);
  
 -    if (key_len < 0) {
 -      Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
 -      return ThrowException(exception);
 -    }
 +  size_t off = args[1]->Int32Value();
 +  size_t len = args[2]->Int32Value();
 +  if (off + len > buffer_length) {
 +    return ThrowError("off + len > buffer.length");
 +  }
  
 -    ASSERT_IS_BUFFER(args[2]);
 -    ssize_t iv_len = Buffer::Length(args[2]);
 +  int bytes_written;
 +  char* data = buffer_data + off;
  
 -    if (iv_len < 0) {
 -      Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
 -      return ThrowException(exception);
 -    }
 +  if (conn->is_server() && !conn->hello_parser_.IsEnded()) {
 +    // Just accumulate data, everything will be pushed to BIO later
 +    if (conn->hello_parser_.IsPaused()) {
 +      bytes_written = 0;
 +    } else {
 +      // Copy incoming data to the internal buffer
 +      // (which has a size of the biggest possible TLS frame)
 +      size_t available = sizeof(conn->hello_data_) - conn->hello_offset_;
 +      size_t copied = len < available ? len : available;
 +      memcpy(conn->hello_data_ + conn->hello_offset_, data, copied);
 +      conn->hello_offset_ += copied;
  
 -    char* key_buf = new char[key_len];
 -    ssize_t key_written = DecodeWrite(key_buf, key_len, args[1], BINARY);
 -    assert(key_written == key_len);
 +      conn->hello_parser_.Parse(conn->hello_data_, conn->hello_offset_);
 +      bytes_written = copied;
 +    }
 +  } else {
 +    bytes_written = BIO_write(conn->bio_read_, data, len);
 +    conn->HandleBIOError(conn->bio_read_, "BIO_write", bytes_written);
 +    conn->SetShutdownFlags();
 +  }
  
 -    char* iv_buf = new char[iv_len];
 -    ssize_t iv_written = DecodeWrite(iv_buf, iv_len, args[2], BINARY);
 -    assert(iv_written == iv_len);
 +  args.GetReturnValue().Set(bytes_written);
 +}
  
 -    String::Utf8Value cipherType(args[0]);
  
 -    bool r = cipher->CipherInitIv(*cipherType, key_buf,key_len,iv_buf,iv_len);
 +void Connection::ClearOut(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -    delete [] key_buf;
 -    delete [] iv_buf;
 +  Connection* conn = Connection::Unwrap(args.This());
  
 -    if (!r) return ThrowCryptoError(ERR_get_error());
 +  if (args.Length() < 3) {
 +    return ThrowTypeError("Takes 3 parameters");
 +  }
  
 -    return args.This();
 +  if (!Buffer::HasInstance(args[0])) {
 +    return ThrowTypeError("Second argument should be a buffer");
    }
  
 -  static Handle<Value> CipherUpdate(const Arguments& args) {
 -    Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This());
 +  char* buffer_data = Buffer::Data(args[0]);
 +  size_t buffer_length = Buffer::Length(args[0]);
  
 -    HandleScope scope;
 +  size_t off = args[1]->Int32Value();
 +  size_t len = args[2]->Int32Value();
 +  if (off + len > buffer_length) {
 +    return ThrowError("off + len > buffer.length");
 +  }
  
 -    ASSERT_IS_STRING_OR_BUFFER(args[0]);
 +  if (!SSL_is_init_finished(conn->ssl_)) {
 +    int rv;
  
 -    // Only copy the data if we have to, because it's a string
 -    unsigned char* out = 0;
 -    int out_len = 0, r;
 -    if (args[0]->IsString()) {
 -      Local<String> string = args[0].As<String>();
 -      enum encoding encoding = ParseEncoding(args[1], BINARY);
 -      if (!StringBytes::IsValidString(string, encoding))
 -        return ThrowTypeError("Bad input string");
 -      size_t buflen = StringBytes::StorageSize(string, encoding);
 -      char* buf = new char[buflen];
 -      size_t written = StringBytes::Write(buf, buflen, string, encoding);
 -      r = cipher->CipherUpdate(buf, written, &out, &out_len);
 -      delete[] buf;
 +    if (conn->is_server()) {
 +      rv = SSL_accept(conn->ssl_);
 +      conn->HandleSSLError("SSL_accept:ClearOut",
 +                           rv,
 +                           kZeroIsAnError,
 +                           kSyscallError);
      } else {
 -      char* buf = Buffer::Data(args[0]);
 -      size_t buflen = Buffer::Length(args[0]);
 -      r = cipher->CipherUpdate(buf, buflen, &out, &out_len);
 +      rv = SSL_connect(conn->ssl_);
 +      conn->HandleSSLError("SSL_connect:ClearOut",
 +                           rv,
 +                           kZeroIsAnError,
 +                           kSyscallError);
      }
  
 -    if (r == 0) {
 -      delete[] out;
 -      return ThrowCryptoTypeError(ERR_get_error());
 +    if (rv < 0) {
 +      return args.GetReturnValue().Set(rv);
      }
 -
 -    Local<Value> outString;
 -    outString = Encode(out, out_len, BUFFER);
 -
 -    if (out) delete[] out;
 -
 -    return scope.Close(outString);
    }
  
 -  static Handle<Value> SetAutoPadding(const Arguments& args) {
 -    HandleScope scope;
 -    Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This());
 +  int bytes_read = SSL_read(conn->ssl_, buffer_data + off, len);
 +  conn->HandleSSLError("SSL_read:ClearOut",
 +                       bytes_read,
 +                       kZeroIsNotAnError,
 +                       kSyscallError);
 +  conn->SetShutdownFlags();
  
 -    cipher->SetAutoPadding(args.Length() < 1 || args[0]->BooleanValue());
 -
 -    return Undefined();
 -  }
 +  args.GetReturnValue().Set(bytes_read);
 +}
  
 -  static Handle<Value> CipherFinal(const Arguments& args) {
 -    Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This());
  
 -    HandleScope scope;
 +void Connection::ClearPending(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
 +  Connection* conn = Connection::Unwrap(args.This());
 +  int bytes_pending = BIO_pending(conn->bio_read_);
 +  args.GetReturnValue().Set(bytes_pending);
 +}
  
 -    unsigned char* out_value = NULL;
 -    int out_len = -1;
 -    Local<Value> outString ;
  
 -    int r = cipher->CipherFinal(&out_value, &out_len);
 +void Connection::EncPending(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
 +  Connection* conn = Connection::Unwrap(args.This());
 +  int bytes_pending = BIO_pending(conn->bio_write_);
 +  args.GetReturnValue().Set(bytes_pending);
 +}
  
 -    if (out_len <= 0 || r == 0) {
 -      delete[] out_value;
 -      out_value = NULL;
 -      if (r == 0) return ThrowCryptoTypeError(ERR_get_error());
 -    }
  
 -    outString = Encode(out_value, out_len, BUFFER);
 +void Connection::EncOut(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -    delete [] out_value;
 -    return scope.Close(outString);
 -  }
 +  Connection* conn = Connection::Unwrap(args.This());
  
 -  Cipher () : ObjectWrap ()
 -  {
 -    initialised_ = false;
 +  if (args.Length() < 3) {
 +    return ThrowTypeError("Takes 3 parameters");
    }
  
 -  ~Cipher () {
 -    if (initialised_) {
 -      EVP_CIPHER_CTX_cleanup(&ctx);
 -    }
 +  if (!Buffer::HasInstance(args[0])) {
 +    return ThrowTypeError("Second argument should be a buffer");
    }
  
 - private:
 +  char* buffer_data = Buffer::Data(args[0]);
 +  size_t buffer_length = Buffer::Length(args[0]);
  
 -  EVP_CIPHER_CTX ctx; /* coverity[member_decl] */
 -  const EVP_CIPHER *cipher; /* coverity[member_decl] */
 -  bool initialised_;
 -};
 +  size_t off = args[1]->Int32Value();
 +  size_t len = args[2]->Int32Value();
 +  if (off + len > buffer_length) {
 +    return ThrowError("off + len > buffer.length");
 +  }
  
 +  int bytes_read = BIO_read(conn->bio_write_, buffer_data + off, len);
  
 +  conn->HandleBIOError(conn->bio_write_, "BIO_read:EncOut", bytes_read);
 +  conn->SetShutdownFlags();
  
 -class Decipher : public ObjectWrap {
 - public:
 -  static void
 -  Initialize (v8::Handle<v8::Object> target)
 -  {
 -    HandleScope scope;
 +  args.GetReturnValue().Set(bytes_read);
 +}
  
 -    Local<FunctionTemplate> t = FunctionTemplate::New(New);
  
 -    t->InstanceTemplate()->SetInternalFieldCount(1);
 +void Connection::ClearIn(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -    NODE_SET_PROTOTYPE_METHOD(t, "init", DecipherInit);
 -    NODE_SET_PROTOTYPE_METHOD(t, "initiv", DecipherInitIv);
 -    NODE_SET_PROTOTYPE_METHOD(t, "update", DecipherUpdate);
 -    NODE_SET_PROTOTYPE_METHOD(t, "final", DecipherFinal);
 -    NODE_SET_PROTOTYPE_METHOD(t, "finaltol", DecipherFinal); // remove someday
 -    NODE_SET_PROTOTYPE_METHOD(t, "setAutoPadding", SetAutoPadding);
 +  Connection* conn = Connection::Unwrap(args.This());
  
 -    target->Set(String::NewSymbol("Decipher"), t->GetFunction());
 +  if (args.Length() < 3) {
 +    return ThrowTypeError("Takes 3 parameters");
    }
  
 -  bool DecipherInit(char* cipherType, char* key_buf, int key_buf_len) {
 -    cipher_ = EVP_get_cipherbyname(cipherType);
 -
 -    if(!cipher_) {
 -      fprintf(stderr, "node-crypto : Unknown cipher %s\n", cipherType);
 -      return false;
 -    }
 -
 -    unsigned char key[EVP_MAX_KEY_LENGTH],iv[EVP_MAX_IV_LENGTH];
 -    int key_len = EVP_BytesToKey(cipher_,
 -                                 EVP_md5(),
 -                                 NULL,
 -                                 (unsigned char*)(key_buf),
 -                                 key_buf_len,
 -                                 1,
 -                                 key,
 -                                 iv);
 -
 -    EVP_CIPHER_CTX_init(&ctx);
 -    EVP_CipherInit_ex(&ctx, cipher_, NULL, NULL, NULL, false);
 -    if (!EVP_CIPHER_CTX_set_key_length(&ctx, key_len)) {
 -      fprintf(stderr, "node-crypto : Invalid key length %d\n", key_len);
 -      EVP_CIPHER_CTX_cleanup(&ctx);
 -      return false;
 -    }
 -    EVP_CipherInit_ex(&ctx, NULL, NULL,
 -      (unsigned char*)key,
 -      (unsigned char*)iv, false);
 -    initialised_ = true;
 -    return true;
 -  }
 -
 -
 -  bool DecipherInitIv(char* cipherType,
 -                      char* key,
 -                      int key_len,
 -                      char* iv,
 -                      int iv_len) {
 -    cipher_ = EVP_get_cipherbyname(cipherType);
 -    if(!cipher_) {
 -      fprintf(stderr, "node-crypto : Unknown cipher %s\n", cipherType);
 -      return false;
 -    }
 -    /* OpenSSL versions up to 0.9.8l failed to return the correct
 -      iv_length (0) for ECB ciphers */
 -    if (EVP_CIPHER_iv_length(cipher_) != iv_len &&
 -      !(EVP_CIPHER_mode(cipher_) == EVP_CIPH_ECB_MODE && iv_len == 0)) {
 -      fprintf(stderr, "node-crypto : Invalid IV length %d\n", iv_len);
 -      return false;
 -    }
 -    EVP_CIPHER_CTX_init(&ctx);
 -    EVP_CipherInit_ex(&ctx, cipher_, NULL, NULL, NULL, false);
 -    if (!EVP_CIPHER_CTX_set_key_length(&ctx, key_len)) {
 -      fprintf(stderr, "node-crypto : Invalid key length %d\n", key_len);
 -      EVP_CIPHER_CTX_cleanup(&ctx);
 -      return false;
 -    }
 -    EVP_CipherInit_ex(&ctx, NULL, NULL,
 -      (unsigned char*)key,
 -      (unsigned char*)iv, false);
 -    initialised_ = true;
 -    return true;
 -  }
 -
 -  int DecipherUpdate(char* data, int len, unsigned char** out, int* out_len) {
 -    if (!initialised_) {
 -      *out_len = 0;
 -      *out = NULL;
 -      return 0;
 -    }
 -
 -    *out_len = len+EVP_CIPHER_CTX_block_size(&ctx);
 -    *out = new unsigned char[*out_len];
 -
 -    return EVP_CipherUpdate(&ctx, *out, out_len, (unsigned char*)data, len);
 +  if (!Buffer::HasInstance(args[0])) {
 +    return ThrowTypeError("Second argument should be a buffer");
    }
  
 -  int SetAutoPadding(bool auto_padding) {
 -    if (!initialised_) return 0;
 -    return EVP_CIPHER_CTX_set_padding(&ctx, auto_padding ? 1 : 0);
 -  }
 +  char* buffer_data = Buffer::Data(args[0]);
 +  size_t buffer_length = Buffer::Length(args[0]);
  
 -  // coverity[alloc_arg]
 -  int DecipherFinal(unsigned char** out, int *out_len) {
 -    int r;
 +  size_t off = args[1]->Int32Value();
 +  size_t len = args[2]->Int32Value();
 +  if (off + len > buffer_length) {
 +    return ThrowError("off + len > buffer.length");
 +  }
  
 -    if (!initialised_) {
 -      *out_len = 0;
 -      *out = NULL;
 -      return 0;
 +  if (!SSL_is_init_finished(conn->ssl_)) {
 +    int rv;
 +    if (conn->is_server()) {
 +      rv = SSL_accept(conn->ssl_);
 +      conn->HandleSSLError("SSL_accept:ClearIn",
 +                           rv,
 +                           kZeroIsAnError,
 +                           kSyscallError);
 +    } else {
 +      rv = SSL_connect(conn->ssl_);
 +      conn->HandleSSLError("SSL_connect:ClearIn",
 +                           rv,
 +                           kZeroIsAnError,
 +                           kSyscallError);
      }
  
 -    *out = new unsigned char[EVP_CIPHER_CTX_block_size(&ctx)];
 -    r = EVP_CipherFinal_ex(&ctx,*out,out_len);
 -    EVP_CIPHER_CTX_cleanup(&ctx);
 -    initialised_ = false;
 -    return r;
 +    if (rv < 0) {
 +      return args.GetReturnValue().Set(rv);
 +    }
    }
  
 +  int bytes_written = SSL_write(conn->ssl_, buffer_data + off, len);
  
 - protected:
 +  conn->HandleSSLError("SSL_write:ClearIn",
 +                       bytes_written,
 +                       len == 0 ? kZeroIsNotAnError : kZeroIsAnError,
 +                       kSyscallError);
 +  conn->SetShutdownFlags();
  
 -  static Handle<Value> New (const Arguments& args) {
 -    HandleScope scope;
 -
 -    Decipher *cipher = new Decipher();
 -    cipher->Wrap(args.This());
 -    return args.This();
 -  }
 -
 -  static Handle<Value> DecipherInit(const Arguments& args) {
 -    Decipher *cipher = ObjectWrap::Unwrap<Decipher>(args.This());
 +  args.GetReturnValue().Set(bytes_written);
 +}
  
 -    HandleScope scope;
  
 -    if (args.Length() <= 1
 -        || !args[0]->IsString()
 -        || !(args[1]->IsString() || Buffer::HasInstance(args[1])))
 -    {
 -      return ThrowException(Exception::Error(String::New(
 -        "Must give cipher-type, key as argument")));
 -    }
 +void Connection::Start(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -    ASSERT_IS_BUFFER(args[1]);
 -    ssize_t key_len = Buffer::Length(args[1]);
 +  Connection* conn = Connection::Unwrap(args.This());
  
 -    if (key_len < 0) {
 -      Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
 -      return ThrowException(exception);
 +  int rv = 0;
 +  if (!SSL_is_init_finished(conn->ssl_)) {
 +    if (conn->is_server()) {
 +      rv = SSL_accept(conn->ssl_);
 +      conn->HandleSSLError("SSL_accept:Start",
 +                           rv,
 +                           kZeroIsAnError,
 +                           kSyscallError);
 +    } else {
 +      rv = SSL_connect(conn->ssl_);
 +      conn->HandleSSLError("SSL_connect:Start",
 +                           rv,
 +                           kZeroIsAnError,
 +                           kSyscallError);
      }
 +  }
 +  args.GetReturnValue().Set(rv);
 +}
  
 -    char* key_buf = new char[key_len];
 -    ssize_t key_written = DecodeWrite(key_buf, key_len, args[1], BINARY);
 -    assert(key_written == key_len);
 -
 -    String::Utf8Value cipherType(args[0]);
 -
 -    bool r = cipher->DecipherInit(*cipherType, key_buf,key_len);
  
 -    delete [] key_buf;
 +void Connection::Shutdown(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -    if (!r) {
 -      return ThrowException(Exception::Error(String::New("DecipherInit error")));
 -    }
 +  Connection* conn = Connection::Unwrap(args.This());
  
 -    return args.This();
 +  if (conn->ssl_ == NULL) {
 +    return args.GetReturnValue().Set(false);
    }
  
 -  static Handle<Value> DecipherInitIv(const Arguments& args) {
 -    Decipher *cipher = ObjectWrap::Unwrap<Decipher>(args.This());
 -
 -    HandleScope scope;
 +  int rv = SSL_shutdown(conn->ssl_);
 +  conn->HandleSSLError("SSL_shutdown", rv, kZeroIsNotAnError, kIgnoreSyscall);
 +  conn->SetShutdownFlags();
 +  args.GetReturnValue().Set(rv);
 +}
  
 -    if (args.Length() <= 2
 -        || !args[0]->IsString()
 -        || !(args[1]->IsString() || Buffer::HasInstance(args[1]))
 -        || !(args[2]->IsString() || Buffer::HasInstance(args[2])))
 -    {
 -      return ThrowException(Exception::Error(String::New(
 -        "Must give cipher-type, key, and iv as argument")));
 -    }
  
 -    ASSERT_IS_BUFFER(args[1]);
 -    ssize_t key_len = Buffer::Length(args[1]);
 +void Connection::Close(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -    if (key_len < 0) {
 -      Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
 -      return ThrowException(exception);
 -    }
 +  Connection* conn = Connection::Unwrap(args.This());
  
 -    ASSERT_IS_BUFFER(args[2]);
 -    ssize_t iv_len = Buffer::Length(args[2]);
 +  if (conn->ssl_ != NULL) {
 +    SSL_free(conn->ssl_);
 +    conn->ssl_ = NULL;
 +  }
 +}
  
 -    if (iv_len < 0) {
 -      Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
 -      return ThrowException(exception);
 -    }
  
 -    char* key_buf = new char[key_len];
 -    ssize_t key_written = DecodeWrite(key_buf, key_len, args[1], BINARY);
 -    assert(key_written == key_len);
 +#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
 +void Connection::GetServername(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -    char* iv_buf = new char[iv_len];
 -    ssize_t iv_written = DecodeWrite(iv_buf, iv_len, args[2], BINARY);
 -    assert(iv_written == iv_len);
 +  Connection* conn = Connection::Unwrap(args.This());
  
 -    String::Utf8Value cipherType(args[0]);
 +  if (conn->is_server() && !conn->servername_.IsEmpty()) {
 +    args.GetReturnValue().Set(conn->servername_);
 +  } else {
 +    args.GetReturnValue().Set(false);
 +  }
 +}
  
 -    bool r = cipher->DecipherInitIv(*cipherType, key_buf,key_len,iv_buf,iv_len);
  
 -    delete [] key_buf;
 -    delete [] iv_buf;
 +void Connection::SetSNICallback(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -    if (!r) {
 -      return ThrowException(Exception::Error(String::New("DecipherInitIv error")));
 -    }
 +  Connection* conn = Connection::Unwrap(args.This());
  
 -    return args.This();
 +  if (args.Length() < 1 || !args[0]->IsFunction()) {
 +    return ThrowError("Must give a Function as first argument");
    }
  
 -  static Handle<Value> DecipherUpdate(const Arguments& args) {
 -    HandleScope scope;
 +  Local<Object> obj = Object::New();
 +  obj->Set(FIXED_ONE_BYTE_STRING(node_isolate, "onselect"), args[0]);
 +  conn->sniObject_.Reset(node_isolate, obj);
 +}
 +#endif
  
 -    Decipher *cipher = ObjectWrap::Unwrap<Decipher>(args.This());
  
 -    ASSERT_IS_STRING_OR_BUFFER(args[0]);
 +void CipherBase::Initialize(Environment* env, Handle<Object> target) {
 +  Local<FunctionTemplate> t = FunctionTemplate::New(New);
  
 -    // Only copy the data if we have to, because it's a string
 -    unsigned char* out = 0;
 -    int out_len = 0, r;
 -    if (args[0]->IsString()) {
 -      Local<String> string = args[0].As<String>();
 -      enum encoding encoding = ParseEncoding(args[1], BINARY);
 -      if (!StringBytes::IsValidString(string, encoding))
 -        return ThrowTypeError("Bad input string");
 -      size_t buflen = StringBytes::StorageSize(string, encoding);
 -      char* buf = new char[buflen];
 -      size_t written = StringBytes::Write(buf, buflen, string, encoding);
 -      r = cipher->DecipherUpdate(buf, written, &out, &out_len);
 -      delete[] buf;
 -    } else {
 -      char* buf = Buffer::Data(args[0]);
 -      size_t buflen = Buffer::Length(args[0]);
 -      r = cipher->DecipherUpdate(buf, buflen, &out, &out_len);
 -    }
 +  t->InstanceTemplate()->SetInternalFieldCount(1);
  
 -    if (r == 0) {
 -      delete[] out;
 -      return ThrowCryptoTypeError(ERR_get_error());
 -    }
 +  NODE_SET_PROTOTYPE_METHOD(t, "init", Init);
 +  NODE_SET_PROTOTYPE_METHOD(t, "initiv", InitIv);
 +  NODE_SET_PROTOTYPE_METHOD(t, "update", Update);
 +  NODE_SET_PROTOTYPE_METHOD(t, "final", Final);
 +  NODE_SET_PROTOTYPE_METHOD(t, "setAutoPadding", SetAutoPadding);
  
 -    Local<Value> outString;
 -    outString = Encode(out, out_len, BUFFER);
 +  target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "CipherBase"),
 +              t->GetFunction());
 +}
  
 -    if (out) delete [] out;
  
 -    return scope.Close(outString);
 -  }
 +void CipherBase::New(const FunctionCallbackInfo<Value>& args) {
 +  assert(args.IsConstructCall() == true);
 +  CipherKind kind = args[0]->IsTrue() ? kCipher : kDecipher;
 +  new CipherBase(args.GetIsolate(), args.This(), kind);
 +}
  
 -  static Handle<Value> SetAutoPadding(const Arguments& args) {
 -    HandleScope scope;
 -    Decipher *cipher = ObjectWrap::Unwrap<Decipher>(args.This());
  
 -    cipher->SetAutoPadding(args.Length() < 1 || args[0]->BooleanValue());
 +void CipherBase::Init(const char* cipher_type,
 +                      const char* key_buf,
 +                      int key_buf_len) {
 +  HandleScope scope(node_isolate);
  
 -    return Undefined();
 +  assert(cipher_ == NULL);
 +  cipher_ = EVP_get_cipherbyname(cipher_type);
 +  if (cipher_ == NULL) {
 +    return ThrowError("Unknown cipher");
    }
  
 -  static Handle<Value> DecipherFinal(const Arguments& args) {
 -    HandleScope scope;
 +  unsigned char key[EVP_MAX_KEY_LENGTH];
 +  unsigned char iv[EVP_MAX_IV_LENGTH];
  
 -    Decipher *cipher = ObjectWrap::Unwrap<Decipher>(args.This());
 +  int key_len = EVP_BytesToKey(cipher_,
 +                               EVP_md5(),
 +                               NULL,
 +                               reinterpret_cast<const unsigned char*>(key_buf),
 +                               key_buf_len,
 +                               1,
 +                               key,
 +                               iv);
  
 -    unsigned char* out_value = NULL;
 -    int out_len = -1;
 -    Local<Value> outString;
 +  EVP_CIPHER_CTX_init(&ctx_);
 +  EVP_CipherInit_ex(&ctx_, cipher_, NULL, NULL, NULL, kind_ == kCipher);
 +  if (!EVP_CIPHER_CTX_set_key_length(&ctx_, key_len)) {
 +    EVP_CIPHER_CTX_cleanup(&ctx_);
 +    return ThrowError("Invalid key length");
 +  }
  
 -    int r = cipher->DecipherFinal(&out_value, &out_len);
 +  EVP_CipherInit_ex(&ctx_,
 +                    NULL,
 +                    NULL,
 +                    reinterpret_cast<unsigned char*>(key),
 +                    reinterpret_cast<unsigned char*>(iv),
 +                    kind_ == kCipher);
 +  initialised_ = true;
 +}
  
 -    if (out_len <= 0 || r == 0) {
 -      delete [] out_value; // allocated even if out_len == 0
 -      out_value = NULL;
 -      if (r == 0) return ThrowCryptoTypeError(ERR_get_error());
 -    }
  
 -    outString = Encode(out_value, out_len, BUFFER);
 -    delete [] out_value;
 -    return scope.Close(outString);
 -  }
 +void CipherBase::Init(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -  Decipher () : ObjectWrap () {
 -    initialised_ = false;
 -  }
 +  CipherBase* cipher = WeakObject::Unwrap<CipherBase>(args.This());
  
 -  ~Decipher () {
 -    if (initialised_) {
 -      EVP_CIPHER_CTX_cleanup(&ctx);
 -    }
 +  if (args.Length() < 2 ||
 +      !(args[0]->IsString() && Buffer::HasInstance(args[1]))) {
 +    return ThrowError("Must give cipher-type, key");
    }
  
 - private:
 +  const String::Utf8Value cipher_type(args[0]);
 +  const char* key_buf = Buffer::Data(args[1]);
 +  ssize_t key_buf_len = Buffer::Length(args[1]);
 +  cipher->Init(*cipher_type, key_buf, key_buf_len);
 +}
  
 -  EVP_CIPHER_CTX ctx;
 -  const EVP_CIPHER *cipher_;
 -  bool initialised_;
 -};
  
 +void CipherBase::InitIv(const char* cipher_type,
 +                        const char* key,
 +                        int key_len,
 +                        const char* iv,
 +                        int iv_len) {
 +  HandleScope scope(node_isolate);
  
 +  cipher_ = EVP_get_cipherbyname(cipher_type);
 +  if (cipher_ == NULL) {
 +    return ThrowError("Unknown cipher");
 +  }
  
 +  /* OpenSSL versions up to 0.9.8l failed to return the correct
 +     iv_length (0) for ECB ciphers */
 +  if (EVP_CIPHER_iv_length(cipher_) != iv_len &&
 +      !(EVP_CIPHER_mode(cipher_) == EVP_CIPH_ECB_MODE && iv_len == 0)) {
 +    return ThrowError("Invalid IV length");
 +  }
 +  EVP_CIPHER_CTX_init(&ctx_);
 +  EVP_CipherInit_ex(&ctx_, cipher_, NULL, NULL, NULL, kind_ == kCipher);
 +  if (!EVP_CIPHER_CTX_set_key_length(&ctx_, key_len)) {
 +    EVP_CIPHER_CTX_cleanup(&ctx_);
 +    return ThrowError("Invalid key length");
 +  }
  
 -class Hmac : public ObjectWrap {
 - public:
 -  static void Initialize (v8::Handle<v8::Object> target) {
 -    HandleScope scope;
 +  EVP_CipherInit_ex(&ctx_,
 +                    NULL,
 +                    NULL,
 +                    reinterpret_cast<const unsigned char*>(key),
 +                    reinterpret_cast<const unsigned char*>(iv),
 +                    kind_ == kCipher);
 +  initialised_ = true;
 +}
  
 -    Local<FunctionTemplate> t = FunctionTemplate::New(New);
  
 -    t->InstanceTemplate()->SetInternalFieldCount(1);
 +void CipherBase::InitIv(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -    NODE_SET_PROTOTYPE_METHOD(t, "init", HmacInit);
 -    NODE_SET_PROTOTYPE_METHOD(t, "update", HmacUpdate);
 -    NODE_SET_PROTOTYPE_METHOD(t, "digest", HmacDigest);
 +  CipherBase* cipher = WeakObject::Unwrap<CipherBase>(args.This());
  
 -    target->Set(String::NewSymbol("Hmac"), t->GetFunction());
 +  if (args.Length() < 3 || !args[0]->IsString()) {
 +    return ThrowError("Must give cipher-type, key, and iv as argument");
    }
  
 -  bool HmacInit(char* hashType, char* key, int key_len) {
 -    md = EVP_get_digestbyname(hashType);
 -    if(!md) {
 -      fprintf(stderr, "node-crypto : Unknown message digest %s\n", hashType);
 -      return false;
 -    }
 -    HMAC_CTX_init(&ctx);
 -    if (key_len == 0) {
 -      HMAC_Init(&ctx, "", 0, md);
 -    } else {
 -      HMAC_Init(&ctx, key, key_len, md);
 -    }
 -    initialised_ = true;
 -    return true;
 +  ASSERT_IS_BUFFER(args[1]);
 +  ASSERT_IS_BUFFER(args[2]);
  
 -  }
 +  const String::Utf8Value cipher_type(args[0]);
 +  ssize_t key_len = Buffer::Length(args[1]);
 +  const char* key_buf = Buffer::Data(args[1]);
 +  ssize_t iv_len = Buffer::Length(args[2]);
 +  const char* iv_buf = Buffer::Data(args[2]);
 +  cipher->InitIv(*cipher_type, key_buf, key_len, iv_buf, iv_len);
 +}
  
 -  int HmacUpdate(char* data, int len) {
 -    if (!initialised_) return 0;
 -    HMAC_Update(&ctx, (unsigned char*)data, len);
 -    return 1;
 -  }
  
 -  int HmacDigest(unsigned char** md_value, unsigned int *md_len) {
 -    if (!initialised_) return 0;
 -    *md_value = new unsigned char[EVP_MAX_MD_SIZE];
 -    HMAC_Final(&ctx, *md_value, md_len);
 -    HMAC_CTX_cleanup(&ctx);
 -    initialised_ = false;
 -    return 1;
 +bool CipherBase::Update(const char* data,
 +                        int len,
 +                        unsigned char** out,
 +                        int* out_len) {
 +  if (!initialised_)
 +    return 0;
 +  *out_len = len + EVP_CIPHER_CTX_block_size(&ctx_);
 +  *out = new unsigned char[*out_len];
 +  return EVP_CipherUpdate(&ctx_,
 +                          *out,
 +                          out_len,
 +                          reinterpret_cast<const unsigned char*>(data),
 +                          len);
 +}
 +
 +
 +void CipherBase::Update(const FunctionCallbackInfo<Value>& args) {
 +  Environment* env = Environment::GetCurrent(args.GetIsolate());
 +  HandleScope handle_scope(args.GetIsolate());
 +
 +  CipherBase* cipher = WeakObject::Unwrap<CipherBase>(args.This());
 +
 +  ASSERT_IS_STRING_OR_BUFFER(args[0]);
 +
 +  unsigned char* out = NULL;
 +  bool r;
 +  int out_len = 0;
 +
 +  // Only copy the data if we have to, because it's a string
 +  if (args[0]->IsString()) {
 +    Local<String> string = args[0].As<String>();
 +    enum encoding encoding = ParseEncoding(args[1], BINARY);
 +    if (!StringBytes::IsValidString(string, encoding))
 +      return ThrowTypeError("Bad input string");
 +    size_t buflen = StringBytes::StorageSize(string, encoding);
 +    char* buf = new char[buflen];
 +    size_t written = StringBytes::Write(buf, buflen, string, encoding);
 +    r = cipher->Update(buf, written, &out, &out_len);
 +    delete[] buf;
 +  } else {
 +    char* buf = Buffer::Data(args[0]);
 +    size_t buflen = Buffer::Length(args[0]);
 +    r = cipher->Update(buf, buflen, &out, &out_len);
    }
  
 +  if (!r) {
 +    delete[] out;
 +    return ThrowCryptoTypeError(ERR_get_error());
 +  }
  
 - protected:
 +  Local<Object> buf = Buffer::New(env, reinterpret_cast<char*>(out), out_len);
 +  if (out)
 +    delete[] out;
  
 -  static Handle<Value> New (const Arguments& args) {
 -    HandleScope scope;
 +  args.GetReturnValue().Set(buf);
 +}
  
 -    Hmac *hmac = new Hmac();
 -    hmac->Wrap(args.This());
 -    return args.This();
 -  }
  
 -  static Handle<Value> HmacInit(const Arguments& args) {
 -    Hmac *hmac = ObjectWrap::Unwrap<Hmac>(args.This());
 +bool CipherBase::SetAutoPadding(bool auto_padding) {
 +  if (!initialised_)
 +    return false;
 +  return EVP_CIPHER_CTX_set_padding(&ctx_, auto_padding);
 +}
  
 -    HandleScope scope;
  
 -    if (args.Length() == 0 || !args[0]->IsString()) {
 -      return ThrowException(Exception::Error(String::New(
 -        "Must give hashtype string as argument")));
 -    }
 +void CipherBase::SetAutoPadding(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
 +  CipherBase* cipher = WeakObject::Unwrap<CipherBase>(args.This());
 +  cipher->SetAutoPadding(args.Length() < 1 || args[0]->BooleanValue());
 +}
  
 -    ASSERT_IS_BUFFER(args[1]);
 -    ssize_t len = Buffer::Length(args[1]);
  
 -    if (len < 0) {
 -      Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
 -      return ThrowException(exception);
 -    }
 +bool CipherBase::Final(unsigned char** out, int *out_len) {
 +  if (!initialised_)
 +    return false;
  
 -    String::Utf8Value hashType(args[0]);
 +  *out = new unsigned char[EVP_CIPHER_CTX_block_size(&ctx_)];
 +  bool r = EVP_CipherFinal_ex(&ctx_, *out, out_len);
 +  EVP_CIPHER_CTX_cleanup(&ctx_);
 +  initialised_ = false;
  
 -    bool r;
 +  return r;
 +}
  
 -    if( Buffer::HasInstance(args[1])) {
 -      char* buffer_data = Buffer::Data(args[1]);
 -      size_t buffer_length = Buffer::Length(args[1]);
  
 -      r = hmac->HmacInit(*hashType, buffer_data, buffer_length);
 -    } else {
 -      char* buf = new char[len];
 -      ssize_t written = DecodeWrite(buf, len, args[1], BINARY);
 -      assert(written == len);
 +void CipherBase::Final(const FunctionCallbackInfo<Value>& args) {
 +  Environment* env = Environment::GetCurrent(args.GetIsolate());
 +  HandleScope handle_scope(args.GetIsolate());
  
 -      r = hmac->HmacInit(*hashType, buf, len);
 +  CipherBase* cipher = WeakObject::Unwrap<CipherBase>(args.This());
  
 -      delete [] buf;
 -    }
 +  unsigned char* out_value = NULL;
 +  int out_len = -1;
 +  Local<Value> outString;
  
 -    if (!r) {
 -      return ThrowException(Exception::Error(String::New("hmac error")));
 -    }
 +  bool r = cipher->Final(&out_value, &out_len);
  
 -    return args.This();
 +  if (out_len <= 0 || !r) {
 +    delete[] out_value;
 +    out_value = NULL;
 +    out_len = 0;
 +    if (!r)
 +      return ThrowCryptoTypeError(ERR_get_error());
    }
  
 -  static Handle<Value> HmacUpdate(const Arguments& args) {
 -    Hmac *hmac = ObjectWrap::Unwrap<Hmac>(args.This());
 +  args.GetReturnValue().Set(
 +      Buffer::New(env, reinterpret_cast<char*>(out_value), out_len));
 +}
  
 -    HandleScope scope;
  
 -    ASSERT_IS_STRING_OR_BUFFER(args[0]);
 +void Hmac::Initialize(Environment* env, v8::Handle<v8::Object> target) {
 +  Local<FunctionTemplate> t = FunctionTemplate::New(New);
  
 -    // Only copy the data if we have to, because it's a string
 -    int r;
 -    if (args[0]->IsString()) {
 -      Local<String> string = args[0].As<String>();
 -      enum encoding encoding = ParseEncoding(args[1], BINARY);
 -      if (!StringBytes::IsValidString(string, encoding))
 -        return ThrowTypeError("Bad input string");
 -      size_t buflen = StringBytes::StorageSize(string, encoding);
 -      char* buf = new char[buflen];
 -      size_t written = StringBytes::Write(buf, buflen, string, encoding);
 -      r = hmac->HmacUpdate(buf, written);
 -      delete[] buf;
 -    } else {
 -      char* buf = Buffer::Data(args[0]);
 -      size_t buflen = Buffer::Length(args[0]);
 -      r = hmac->HmacUpdate(buf, buflen);
 -    }
 +  t->InstanceTemplate()->SetInternalFieldCount(1);
  
 -    if (!r) {
 -      Local<Value> exception = Exception::TypeError(String::New("HmacUpdate fail"));
 -      return ThrowException(exception);
 -    }
 +  NODE_SET_PROTOTYPE_METHOD(t, "init", HmacInit);
 +  NODE_SET_PROTOTYPE_METHOD(t, "update", HmacUpdate);
 +  NODE_SET_PROTOTYPE_METHOD(t, "digest", HmacDigest);
  
 -    return args.This();
 -  }
 +  target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "Hmac"), t->GetFunction());
 +}
  
 -  static Handle<Value> HmacDigest(const Arguments& args) {
 -    Hmac *hmac = ObjectWrap::Unwrap<Hmac>(args.This());
  
 -    HandleScope scope;
 +void Hmac::New(const FunctionCallbackInfo<Value>& args) {
 +  new Hmac(args.GetIsolate(), args.This());
 +}
  
 -    enum encoding encoding = BUFFER;
 -    if (args.Length() >= 1) {
 -      encoding = ParseEncoding(args[0]->ToString(), BUFFER);
 -    }
  
 -    unsigned char* md_value = NULL;
 -    unsigned int md_len = 0;
 -    Local<Value> outString;
 +void Hmac::HmacInit(const char* hash_type, const char* key, int key_len) {
 +  HandleScope scope(node_isolate);
  
 -    int r = hmac->HmacDigest(&md_value, &md_len);
 -    if (r == 0) {
 -      md_value = NULL;
 -      md_len = 0;
 -    }
 +  assert(md_ == NULL);
 +  md_ = EVP_get_digestbyname(hash_type);
 +  if (md_ == NULL) {
 +    return ThrowError("Unknown message digest");
 +  }
 +  HMAC_CTX_init(&ctx_);
 +  if (key_len == 0) {
 +    HMAC_Init(&ctx_, "", 0, md_);
 +  } else {
 +    HMAC_Init(&ctx_, key, key_len, md_);
 +  }
 +  initialised_ = true;
 +}
  
 -    outString = StringBytes::Encode(
 -          reinterpret_cast<const char*>(md_value), md_len, encoding);
  
 -    delete[] md_value;
 -    return scope.Close(outString);
 -  }
 +void Hmac::HmacInit(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -  Hmac () : ObjectWrap () {
 -    initialised_ = false;
 -  }
 +  Hmac* hmac = WeakObject::Unwrap<Hmac>(args.This());
  
 -  ~Hmac () {
 -    if (initialised_) {
 -      HMAC_CTX_cleanup(&ctx);
 -    }
 +  if (args.Length() < 2 || !args[0]->IsString()) {
 +    return ThrowError("Must give hashtype string, key as arguments");
    }
  
 - private:
 +  ASSERT_IS_BUFFER(args[1]);
  
 -  HMAC_CTX ctx; /* coverity[member_decl] */
 -  const EVP_MD *md; /* coverity[member_decl] */
 -  bool initialised_;
 -};
 +  const String::Utf8Value hash_type(args[0]);
 +  const char* buffer_data = Buffer::Data(args[1]);
 +  size_t buffer_length = Buffer::Length(args[1]);
 +  hmac->HmacInit(*hash_type, buffer_data, buffer_length);
 +}
  
  
 -class Hash : public ObjectWrap {
 - public:
 -  static void Initialize (v8::Handle<v8::Object> target) {
 -    HandleScope scope;
 +bool Hmac::HmacUpdate(const char* data, int len) {
 +  if (!initialised_)
 +    return false;
 +  HMAC_Update(&ctx_, reinterpret_cast<const unsigned char*>(data), len);
 +  return true;
 +}
  
 -    Local<FunctionTemplate> t = FunctionTemplate::New(New);
  
 -    t->InstanceTemplate()->SetInternalFieldCount(1);
 +void Hmac::HmacUpdate(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -    NODE_SET_PROTOTYPE_METHOD(t, "update", HashUpdate);
 -    NODE_SET_PROTOTYPE_METHOD(t, "digest", HashDigest);
 +  Hmac* hmac = WeakObject::Unwrap<Hmac>(args.This());
  
 -    target->Set(String::NewSymbol("Hash"), t->GetFunction());
 -  }
 +  ASSERT_IS_STRING_OR_BUFFER(args[0]);
  
 -  bool HashInit (const char* hashType) {
 -    md = EVP_get_digestbyname(hashType);
 -    if(!md) return false;
 -    EVP_MD_CTX_init(&mdctx);
 -    EVP_DigestInit_ex(&mdctx, md, NULL);
 -    initialised_ = true;
 -    return true;
 +  // Only copy the data if we have to, because it's a string
 +  bool r;
 +  if (args[0]->IsString()) {
 +    Local<String> string = args[0].As<String>();
 +    enum encoding encoding = ParseEncoding(args[1], BINARY);
 +    if (!StringBytes::IsValidString(string, encoding))
 +      return ThrowTypeError("Bad input string");
 +    size_t buflen = StringBytes::StorageSize(string, encoding);
 +    char* buf = new char[buflen];
 +    size_t written = StringBytes::Write(buf, buflen, string, encoding);
 +    r = hmac->HmacUpdate(buf, written);
 +    delete[] buf;
 +  } else {
 +    char* buf = Buffer::Data(args[0]);
 +    size_t buflen = Buffer::Length(args[0]);
 +    r = hmac->HmacUpdate(buf, buflen);
    }
  
 -  int HashUpdate(char* data, int len) {
 -    if (!initialised_) return 0;
 -    EVP_DigestUpdate(&mdctx, data, len);
 -    return 1;
 +  if (!r) {
 +    return ThrowTypeError("HmacUpdate fail");
    }
 +}
  
  
 - protected:
 -
 -  static Handle<Value> New (const Arguments& args) {
 -    HandleScope scope;
 +bool Hmac::HmacDigest(unsigned char** md_value, unsigned int* md_len) {
 +  if (!initialised_)
 +    return false;
 +  *md_value = new unsigned char[EVP_MAX_MD_SIZE];
 +  HMAC_Final(&ctx_, *md_value, md_len);
 +  HMAC_CTX_cleanup(&ctx_);
 +  initialised_ = false;
 +  return true;
 +}
  
 -    if (args.Length() == 0 || !args[0]->IsString()) {
 -      return ThrowException(Exception::Error(String::New(
 -        "Must give hashtype string as argument")));
 -    }
  
 -    String::Utf8Value hashType(args[0]);
 +void Hmac::HmacDigest(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -    Hash *hash = new Hash();
 -    if (!hash->HashInit(*hashType)) {
 -      delete hash;
 -      return ThrowException(Exception::Error(String::New(
 -        "Digest method not supported")));
 -    }
 +  Hmac* hmac = WeakObject::Unwrap<Hmac>(args.This());
  
 -    hash->Wrap(args.This());
 -    return args.This();
 +  enum encoding encoding = BUFFER;
 +  if (args.Length() >= 1) {
 +    encoding = ParseEncoding(args[0]->ToString(), BUFFER);
    }
  
 -  static Handle<Value> HashUpdate(const Arguments& args) {
 -    HandleScope scope;
 -
 -    Hash *hash = ObjectWrap::Unwrap<Hash>(args.This());
 +  unsigned char* md_value = NULL;
 +  unsigned int md_len = 0;
  
 -    ASSERT_IS_STRING_OR_BUFFER(args[0]);
 -
 -    // Only copy the data if we have to, because it's a string
 -    int r;
 -    if (args[0]->IsString()) {
 -      Local<String> string = args[0].As<String>();
 -      enum encoding encoding = ParseEncoding(args[1], BINARY);
 -      if (!StringBytes::IsValidString(string, encoding))
 -        return ThrowTypeError("Bad input string");
 -      size_t buflen = StringBytes::StorageSize(string, encoding);
 -      char* buf = new char[buflen];
 -      size_t written = StringBytes::Write(buf, buflen, string, encoding);
 -      r = hash->HashUpdate(buf, written);
 -      delete[] buf;
 -    } else {
 -      char* buf = Buffer::Data(args[0]);
 -      size_t buflen = Buffer::Length(args[0]);
 -      r = hash->HashUpdate(buf, buflen);
 -    }
 +  bool r = hmac->HmacDigest(&md_value, &md_len);
 +  if (!r) {
 +    md_value = NULL;
 +    md_len = 0;
 +  }
  
 -    if (!r) {
 -      Local<Value> exception = Exception::TypeError(String::New("HashUpdate fail"));
 -      return ThrowException(exception);
 -    }
 +  Local<Value> rc = StringBytes::Encode(
 +        reinterpret_cast<const char*>(md_value), md_len, encoding);
 +  delete[] md_value;
 +  args.GetReturnValue().Set(rc);
 +}
  
 -    return args.This();
 -  }
  
 -  static Handle<Value> HashDigest(const Arguments& args) {
 -    HandleScope scope;
 +void Hash::Initialize(Environment* env, v8::Handle<v8::Object> target) {
 +  Local<FunctionTemplate> t = FunctionTemplate::New(New);
  
 -    Hash *hash = ObjectWrap::Unwrap<Hash>(args.This());
 +  t->InstanceTemplate()->SetInternalFieldCount(1);
  
 -    if (!hash->initialised_) {
 -      return ThrowException(Exception::Error(String::New("Not initialized")));
 -    }
 +  NODE_SET_PROTOTYPE_METHOD(t, "update", HashUpdate);
 +  NODE_SET_PROTOTYPE_METHOD(t, "digest", HashDigest);
  
 -    enum encoding encoding = BUFFER;
 -    if (args.Length() >= 1) {
 -      encoding = ParseEncoding(args[0]->ToString(), BUFFER);
 -    }
 +  target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "Hash"), t->GetFunction());
 +}
  
 -    unsigned char md_value[EVP_MAX_MD_SIZE];
 -    unsigned int md_len;
  
 -    EVP_DigestFinal_ex(&hash->mdctx, md_value, &md_len);
 -    EVP_MD_CTX_cleanup(&hash->mdctx);
 -    hash->initialised_ = false;
 +void Hash::New(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -    return scope.Close(StringBytes::Encode(
 -          reinterpret_cast<const char*>(md_value), md_len, encoding));
 +  if (args.Length() == 0 || !args[0]->IsString()) {
 +    return ThrowError("Must give hashtype string as argument");
    }
  
 -  Hash () : ObjectWrap () {
 -    initialised_ = false;
 -  }
 +  const String::Utf8Value hash_type(args[0]);
  
 -  ~Hash () {
 -    if (initialised_) {
 -      EVP_MD_CTX_cleanup(&mdctx);
 -    }
 +  Hash* hash = new Hash(args.GetIsolate(), args.This());
 +  if (!hash->HashInit(*hash_type)) {
 +    return ThrowError("Digest method not supported");
    }
 +}
  
 - private:
  
 -  EVP_MD_CTX mdctx; /* coverity[member_decl] */
 -  const EVP_MD *md; /* coverity[member_decl] */
 -  bool initialised_;
 -};
 +bool Hash::HashInit(const char* hash_type) {
 +  assert(md_ == NULL);
 +  md_ = EVP_get_digestbyname(hash_type);
 +  if (md_ == NULL)
 +    return false;
 +  EVP_MD_CTX_init(&mdctx_);
 +  EVP_DigestInit_ex(&mdctx_, md_, NULL);
 +  initialised_ = true;
 +  return true;
 +}
  
 -class Sign : public ObjectWrap {
 - public:
 -  static void
 -  Initialize (v8::Handle<v8::Object> target) {
 -    HandleScope scope;
  
 -    Local<FunctionTemplate> t = FunctionTemplate::New(New);
 +bool Hash::HashUpdate(const char* data, int len) {
 +  if (!initialised_)
 +    return false;
 +  EVP_DigestUpdate(&mdctx_, data, len);
 +  return true;
 +}
  
 -    t->InstanceTemplate()->SetInternalFieldCount(1);
  
 -    NODE_SET_PROTOTYPE_METHOD(t, "init", SignInit);
 -    NODE_SET_PROTOTYPE_METHOD(t, "update", SignUpdate);
 -    NODE_SET_PROTOTYPE_METHOD(t, "sign", SignFinal);
 +void Hash::HashUpdate(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -    target->Set(String::NewSymbol("Sign"), t->GetFunction());
 -  }
 +  Hash* hash = WeakObject::Unwrap<Hash>(args.This());
  
 -  bool SignInit (const char* signType) {
 -    md = EVP_get_digestbyname(signType);
 -    if(!md) {
 -      printf("Unknown message digest %s\n", signType);
 -      return false;
 -    }
 -    EVP_MD_CTX_init(&mdctx);
 -    EVP_SignInit_ex(&mdctx, md, NULL);
 -    initialised_ = true;
 -    return true;
 +  ASSERT_IS_STRING_OR_BUFFER(args[0]);
  
 +  // Only copy the data if we have to, because it's a string
 +  bool r;
 +  if (args[0]->IsString()) {
 +    Local<String> string = args[0].As<String>();
 +    enum encoding encoding = ParseEncoding(args[1], BINARY);
 +    if (!StringBytes::IsValidString(string, encoding))
 +      return ThrowTypeError("Bad input string");
 +    size_t buflen = StringBytes::StorageSize(string, encoding);
 +    char* buf = new char[buflen];
 +    size_t written = StringBytes::Write(buf, buflen, string, encoding);
 +    r = hash->HashUpdate(buf, written);
 +    delete[] buf;
 +  } else {
 +    char* buf = Buffer::Data(args[0]);
 +    size_t buflen = Buffer::Length(args[0]);
 +    r = hash->HashUpdate(buf, buflen);
    }
  
 -  int SignUpdate(char* data, int len) {
 -    if (!initialised_) return 0;
 -    EVP_SignUpdate(&mdctx, data, len);
 -    return 1;
 +  if (!r) {
 +    return ThrowTypeError("HashUpdate fail");
    }
 +}
  
 -  int SignFinal(unsigned char** md_value,
 -                unsigned int *md_len,
 -                char* key_pem,
 -                int key_pemLen) {
 -    if (!initialised_) return 0;
  
 -    BIO *bp = NULL;
 -    EVP_PKEY* pkey;
 -    bp = BIO_new(BIO_s_mem());
 -    if(!BIO_write(bp, key_pem, key_pemLen)) return 0;
 +void Hash::HashDigest(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -    pkey = PEM_read_bio_PrivateKey( bp, NULL, NULL, NULL );
 -    if (pkey == NULL) return 0;
 +  Hash* hash = WeakObject::Unwrap<Hash>(args.This());
  
 -    EVP_SignFinal(&mdctx, *md_value, md_len, pkey);
 -    EVP_MD_CTX_cleanup(&mdctx);
 -    initialised_ = false;
 -    EVP_PKEY_free(pkey);
 -    BIO_free(bp);
 -    return 1;
 +  if (!hash->initialised_) {
 +    return ThrowError("Not initialized");
    }
  
 -
 - protected:
 -
 -  static Handle<Value> New (const Arguments& args) {
 -    HandleScope scope;
 -
 -    Sign *sign = new Sign();
 -    sign->Wrap(args.This());
 -
 -    return args.This();
 +  enum encoding encoding = BUFFER;
 +  if (args.Length() >= 1) {
 +    encoding = ParseEncoding(args[0]->ToString(), BUFFER);
    }
  
 -  static Handle<Value> SignInit(const Arguments& args) {
 -    HandleScope scope;
 +  unsigned char md_value[EVP_MAX_MD_SIZE];
 +  unsigned int md_len;
  
 -    Sign *sign = ObjectWrap::Unwrap<Sign>(args.This());
 +  EVP_DigestFinal_ex(&hash->mdctx_, md_value, &md_len);
 +  EVP_MD_CTX_cleanup(&hash->mdctx_);
 +  hash->initialised_ = false;
  
 -    if (args.Length() == 0 || !args[0]->IsString()) {
 -      return ThrowException(Exception::Error(String::New(
 -        "Must give signtype string as argument")));
 -    }
 +  Local<Value> rc = StringBytes::Encode(
 +      reinterpret_cast<const char*>(md_value), md_len, encoding);
 +  args.GetReturnValue().Set(rc);
 +}
  
 -    String::Utf8Value signType(args[0]);
  
 -    bool r = sign->SignInit(*signType);
 +void Sign::Initialize(Environment* env, v8::Handle<v8::Object> target) {
 +  Local<FunctionTemplate> t = FunctionTemplate::New(New);
  
 -    if (!r) {
 -      return ThrowException(Exception::Error(String::New("SignInit error")));
 -    }
 +  t->InstanceTemplate()->SetInternalFieldCount(1);
  
 -    return args.This();
 -  }
 +  NODE_SET_PROTOTYPE_METHOD(t, "init", SignInit);
 +  NODE_SET_PROTOTYPE_METHOD(t, "update", SignUpdate);
 +  NODE_SET_PROTOTYPE_METHOD(t, "sign", SignFinal);
  
 -  static Handle<Value> SignUpdate(const Arguments& args) {
 -    Sign *sign = ObjectWrap::Unwrap<Sign>(args.This());
 +  target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "Sign"), t->GetFunction());
 +}
  
 -    HandleScope scope;
  
 -    ASSERT_IS_STRING_OR_BUFFER(args[0]);
 +void Sign::New(const FunctionCallbackInfo<Value>& args) {
 +  new Sign(args.GetIsolate(), args.This());
 +}
  
 -    // Only copy the data if we have to, because it's a string
 -    int r;
 -    if (args[0]->IsString()) {
 -      Local<String> string = args[0].As<String>();
 -      enum encoding encoding = ParseEncoding(args[1], BINARY);
 -      if (!StringBytes::IsValidString(string, encoding))
 -        return ThrowTypeError("Bad input string");
 -      size_t buflen = StringBytes::StorageSize(string, encoding);
 -      char* buf = new char[buflen];
 -      size_t written = StringBytes::Write(buf, buflen, string, encoding);
 -      r = sign->SignUpdate(buf, written);
 -      delete[] buf;
 -    } else {
 -      char* buf = Buffer::Data(args[0]);
 -      size_t buflen = Buffer::Length(args[0]);
 -      r = sign->SignUpdate(buf, buflen);
 -    }
  
 -    if (!r) {
 -      Local<Value> exception = Exception::TypeError(String::New("SignUpdate fail"));
 -      return ThrowException(exception);
 -    }
 +void Sign::SignInit(const char* sign_type) {
 +  HandleScope scope(node_isolate);
  
 -    return args.This();
 +  assert(md_ == NULL);
 +  md_ = EVP_get_digestbyname(sign_type);
 +  if (!md_) {
 +    return ThrowError("Uknown message digest");
    }
 +  EVP_MD_CTX_init(&mdctx_);
 +  EVP_SignInit_ex(&mdctx_, md_, NULL);
 +  initialised_ = true;
 +}
  
 -  static Handle<Value> SignFinal(const Arguments& args) {
 -    Sign *sign = ObjectWrap::Unwrap<Sign>(args.This());
  
 -    HandleScope scope;
 +void Sign::SignInit(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -    unsigned char* md_value;
 -    unsigned int md_len;
 -    Local<Value> outString;
 +  Sign* sign = WeakObject::Unwrap<Sign>(args.This());
  
 -    ASSERT_IS_BUFFER(args[0]);
 -    ssize_t len = Buffer::Length(args[0]);
 +  if (args.Length() == 0 || !args[0]->IsString()) {
 +    return ThrowError("Must give signtype string as argument");
 +  }
  
 -    enum encoding encoding = BUFFER;
 -    if (args.Length() >= 2) {
 -      encoding = ParseEncoding(args[1]->ToString(), BUFFER);
 -    }
 +  const String::Utf8Value sign_type(args[0]);
 +  sign->SignInit(*sign_type);
 +}
  
 -    char* buf = new char[len];
 -    ssize_t written = DecodeWrite(buf, len, args[0], BUFFER);
 -    assert(written == len);
  
 -    md_len = 8192; // Maximum key size is 8192 bits
 -    md_value = new unsigned char[md_len];
 +bool Sign::SignUpdate(const char* data, int len) {
 +  if (!initialised_)
 +    return false;
 +  EVP_SignUpdate(&mdctx_, data, len);
 +  return true;
 +}
  
 -    int r = sign->SignFinal(&md_value, &md_len, buf, len);
 -    if (r == 0) {
 -      md_value = NULL;
 -      md_len = r;
 -    }
  
 -    delete [] buf;
 +void Sign::SignUpdate(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -    outString = StringBytes::Encode(
 -        reinterpret_cast<const char*>(md_value), md_len, encoding);
 +  Sign* sign = WeakObject::Unwrap<Sign>(args.This());
  
 -    delete [] md_value;
 -    return scope.Close(outString);
 -  }
 +  ASSERT_IS_STRING_OR_BUFFER(args[0]);
  
 -  Sign () : ObjectWrap () {
 -    initialised_ = false;
 +  // Only copy the data if we have to, because it's a string
 +  int r;
 +  if (args[0]->IsString()) {
 +    Local<String> string = args[0].As<String>();
 +    enum encoding encoding = ParseEncoding(args[1], BINARY);
 +    if (!StringBytes::IsValidString(string, encoding))
 +      return ThrowTypeError("Bad input string");
 +    size_t buflen = StringBytes::StorageSize(string, encoding);
 +    char* buf = new char[buflen];
 +    size_t written = StringBytes::Write(buf, buflen, string, encoding);
 +    r = sign->SignUpdate(buf, written);
 +    delete[] buf;
 +  } else {
 +    char* buf = Buffer::Data(args[0]);
 +    size_t buflen = Buffer::Length(args[0]);
 +    r = sign->SignUpdate(buf, buflen);
    }
  
 -  ~Sign () {
 -    if (initialised_) {
 -      EVP_MD_CTX_cleanup(&mdctx);
 -    }
 +  if (!r) {
 +    return ThrowTypeError("SignUpdate fail");
    }
 +}
  
 - private:
  
 -  EVP_MD_CTX mdctx; /* coverity[member_decl] */
 -  const EVP_MD *md; /* coverity[member_decl] */
 -  bool initialised_;
 -};
 +bool Sign::SignFinal(unsigned char** md_value,
 +                     unsigned int *md_len,
 +                     const char* key_pem,
 +                     int key_pem_len) {
 +  if (!initialised_)
 +    return false;
 +
 +  BIO* bp = NULL;
 +  EVP_PKEY* pkey = NULL;
 +  bp = BIO_new(BIO_s_mem());
 +  if (!BIO_write(bp, key_pem, key_pem_len))
 +    return false;
 +
 +  pkey = PEM_read_bio_PrivateKey(bp, NULL, NULL, NULL);
 +  if (pkey == NULL)
 +    return 0;
 +
 +  EVP_SignFinal(&mdctx_, *md_value, md_len, pkey);
 +  EVP_MD_CTX_cleanup(&mdctx_);
 +  initialised_ = false;
 +  EVP_PKEY_free(pkey);
 +  BIO_free_all(bp);
 +  return true;
 +}
  
 -class Verify : public ObjectWrap {
 - public:
 -  static void Initialize (v8::Handle<v8::Object> target) {
 -    HandleScope scope;
  
 -    Local<FunctionTemplate> t = FunctionTemplate::New(New);
 +void Sign::SignFinal(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -    t->InstanceTemplate()->SetInternalFieldCount(1);
 +  Sign* sign = WeakObject::Unwrap<Sign>(args.This());
  
 -    NODE_SET_PROTOTYPE_METHOD(t, "init", VerifyInit);
 -    NODE_SET_PROTOTYPE_METHOD(t, "update", VerifyUpdate);
 -    NODE_SET_PROTOTYPE_METHOD(t, "verify", VerifyFinal);
 +  unsigned char* md_value;
 +  unsigned int md_len;
  
 -    target->Set(String::NewSymbol("Verify"), t->GetFunction());
 +  enum encoding encoding = BUFFER;
 +  if (args.Length() >= 2) {
 +    encoding = ParseEncoding(args[1]->ToString(), BUFFER);
    }
  
 +  ASSERT_IS_BUFFER(args[0]);
 +  ssize_t len = Buffer::Length(args[0]);
 +  char* buf = Buffer::Data(args[0]);
 +
 +  md_len = 8192;  // Maximum key size is 8192 bits
 +  md_value = new unsigned char[md_len];
  
 -  bool VerifyInit (const char* verifyType) {
 -    md = EVP_get_digestbyname(verifyType);
 -    if(!md) {
 -      fprintf(stderr, "node-crypto : Unknown message digest %s\n", verifyType);
 -      return false;
 -    }
 -    EVP_MD_CTX_init(&mdctx);
 -    EVP_VerifyInit_ex(&mdctx, md, NULL);
 -    initialised_ = true;
 -    return true;
 +  bool r = sign->SignFinal(&md_value, &md_len, buf, len);
 +  if (!r) {
 +    delete[] md_value;
 +    md_value = NULL;
 +    md_len = 0;
    }
  
 -
 -  int VerifyUpdate(char* data, int len) {
 -    if (!initialised_) return 0;
 -    EVP_VerifyUpdate(&mdctx, data, len);
 -    return 1;
 -  }
 +  Local<Value> rc = StringBytes::Encode(
 +      reinterpret_cast<const char*>(md_value), md_len, encoding);
 +  delete[] md_value;
 +  args.GetReturnValue().Set(rc);
 +}
  
  
 -  int VerifyFinal(char* key_pem, int key_pemLen, unsigned char* sig, int siglen) {
 -    if (!initialised_) return 0;
 +void Verify::Initialize(Environment* env, v8::Handle<v8::Object> target) {
 +  Local<FunctionTemplate> t = FunctionTemplate::New(New);
  
 -    ClearErrorOnReturn clear_error_on_return;
 -    (void) &clear_error_on_return;  // Silence compiler warning.
 +  t->InstanceTemplate()->SetInternalFieldCount(1);
  
 -    EVP_PKEY* pkey = NULL;
 -    BIO *bp = NULL;
 -    X509 *x509 = NULL;
 -    int r = 0;
 +  NODE_SET_PROTOTYPE_METHOD(t, "init", VerifyInit);
 +  NODE_SET_PROTOTYPE_METHOD(t, "update", VerifyUpdate);
 +  NODE_SET_PROTOTYPE_METHOD(t, "verify", VerifyFinal);
  
 -    bp = BIO_new(BIO_s_mem());
 -    if (bp == NULL) {
 -      ERR_print_errors_fp(stderr);
 -      return 0;
 -    }
 -    if(!BIO_write(bp, key_pem, key_pemLen)) {
 -      ERR_print_errors_fp(stderr);
 -      return 0;
 -    }
 +  target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "Verify"), t->GetFunction());
 +}
  
 -    // Check if this is a PKCS#8 or RSA public key before trying as X.509.
 -    // Split this out into a separate function once we have more than one
 -    // consumer of public keys.
 -    if (strncmp(key_pem, PUBLIC_KEY_PFX, PUBLIC_KEY_PFX_LEN) == 0) {
 -      pkey = PEM_read_bio_PUBKEY(bp, NULL, NULL, NULL);
 -      if (pkey == NULL) {
 -        ERR_print_errors_fp(stderr);
 -        return 0;
 -      }
 -    } else if (strncmp(key_pem, PUBRSA_KEY_PFX, PUBRSA_KEY_PFX_LEN) == 0) {
 -      RSA* rsa = PEM_read_bio_RSAPublicKey(bp, NULL, NULL, NULL);
 -      if (rsa) {
 -        pkey = EVP_PKEY_new();
 -        if (pkey) EVP_PKEY_set1_RSA(pkey, rsa);
 -        RSA_free(rsa);
 -      }
 -      if (pkey == NULL) {
 -        ERR_print_errors_fp(stderr);
 -        return 0;
 -      }
 -    } else {
 -      // X.509 fallback
 -      x509 = PEM_read_bio_X509(bp, NULL, NULL, NULL);
 -      if (x509 == NULL) {
 -        ERR_print_errors_fp(stderr);
 -        return 0;
 -      }
  
 -      pkey = X509_get_pubkey(x509);
 -      if (pkey == NULL) {
 -        ERR_print_errors_fp(stderr);
 -        return 0;
 -      }
 -    }
 +void Verify::New(const FunctionCallbackInfo<Value>& args) {
 +  new Verify(args.GetIsolate(), args.This());
 +}
  
 -    r = EVP_VerifyFinal(&mdctx, sig, siglen, pkey);
  
 -    if(pkey != NULL)
 -      EVP_PKEY_free (pkey);
 -    if (x509 != NULL)
 -      X509_free(x509);
 -    if (bp != NULL)
 -      BIO_free(bp);
 -    EVP_MD_CTX_cleanup(&mdctx);
 -    initialised_ = false;
 +void Verify::VerifyInit(const char* verify_type) {
 +  HandleScope scope(node_isolate);
  
 -    return r;
 +  assert(md_ == NULL);
 +  md_ = EVP_get_digestbyname(verify_type);
 +  if (md_ == NULL) {
 +    return ThrowError("Unknown message digest");
    }
  
 +  EVP_MD_CTX_init(&mdctx_);
 +  EVP_VerifyInit_ex(&mdctx_, md_, NULL);
 +  initialised_ = true;
 +}
  
 - protected:
  
 -  static Handle<Value> New (const Arguments& args) {
 -    HandleScope scope;
 +void Verify::VerifyInit(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -    Verify *verify = new Verify();
 -    verify->Wrap(args.This());
 +  Verify* verify = WeakObject::Unwrap<Verify>(args.This());
  
 -    return args.This();
 +  if (args.Length() == 0 || !args[0]->IsString()) {
 +    return ThrowError("Must give verifytype string as argument");
    }
  
 +  const String::Utf8Value verify_type(args[0]);
 +  verify->VerifyInit(*verify_type);
 +}
  
 -  static Handle<Value> VerifyInit(const Arguments& args) {
 -    Verify *verify = ObjectWrap::Unwrap<Verify>(args.This());
  
 -    HandleScope scope;
 +bool Verify::VerifyUpdate(const char* data, int len) {
 +  if (!initialised_)
 +    return false;
 +  EVP_VerifyUpdate(&mdctx_, data, len);
 +  return true;
 +}
  
 -    if (args.Length() == 0 || !args[0]->IsString()) {
 -      return ThrowException(Exception::Error(String::New(
 -        "Must give verifytype string as argument")));
 -    }
  
 -    String::Utf8Value verifyType(args[0]);
 +void Verify::VerifyUpdate(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -    bool r = verify->VerifyInit(*verifyType);
 +  Verify* verify = WeakObject::Unwrap<Verify>(args.This());
  
 -    if (!r) {
 -      return ThrowException(Exception::Error(String::New("VerifyInit error")));
 -    }
 +  ASSERT_IS_STRING_OR_BUFFER(args[0]);
  
 -    return args.This();
 +  // Only copy the data if we have to, because it's a string
 +  bool r;
 +  if (args[0]->IsString()) {
 +    Local<String> string = args[0].As<String>();
 +    enum encoding encoding = ParseEncoding(args[1], BINARY);
 +    if (!StringBytes::IsValidString(string, encoding))
 +      return ThrowTypeError("Bad input string");
 +    size_t buflen = StringBytes::StorageSize(string, encoding);
 +    char* buf = new char[buflen];
 +    size_t written = StringBytes::Write(buf, buflen, string, encoding);
 +    r = verify->VerifyUpdate(buf, written);
 +    delete[] buf;
 +  } else {
 +    char* buf = Buffer::Data(args[0]);
 +    size_t buflen = Buffer::Length(args[0]);
 +    r = verify->VerifyUpdate(buf, buflen);
    }
  
 +  if (!r) {
 +    return ThrowTypeError("VerifyUpdate fail");
 +  }
 +}
  
 -  static Handle<Value> VerifyUpdate(const Arguments& args) {
 -    HandleScope scope;
  
 -    Verify *verify = ObjectWrap::Unwrap<Verify>(args.This());
 +bool Verify::VerifyFinal(const char* key_pem,
 +                         int key_pem_len,
 +                         const char* sig,
 +                         int siglen) {
 +  HandleScope scope(node_isolate);
  
 -    ASSERT_IS_STRING_OR_BUFFER(args[0]);
 +  if (!initialised_) {
 +    ThrowError("Verify not initalised");
 +    return false;
 +  }
  
 -    // Only copy the data if we have to, because it's a string
 -    int r;
 -    if (args[0]->IsString()) {
 -      Local<String> string = args[0].As<String>();
 -      enum encoding encoding = ParseEncoding(args[1], BINARY);
 -      if (!StringBytes::IsValidString(string, encoding))
 -        return ThrowTypeError("Bad input string");
 -      size_t buflen = StringBytes::StorageSize(string, encoding);
 -      char* buf = new char[buflen];
 -      size_t written = StringBytes::Write(buf, buflen, string, encoding);
 -      r = verify->VerifyUpdate(buf, written);
 -      delete[] buf;
 -    } else {
 -      char* buf = Buffer::Data(args[0]);
 -      size_t buflen = Buffer::Length(args[0]);
 -      r = verify->VerifyUpdate(buf, buflen);
 -    }
++  ClearErrorOnReturn clear_error_on_return;
++  (void) &clear_error_on_return;  // Silence compiler warning.
 -    if (!r) {
 -      Local<Value> exception = Exception::TypeError(String::New("VerifyUpdate fail"));
 -      return ThrowException(exception);
 +  EVP_PKEY* pkey = NULL;
 +  BIO* bp = NULL;
 +  X509* x509 = NULL;
 +  bool fatal = true;
 +  int r = 0;
 +
 +  bp = BIO_new(BIO_s_mem());
 +  if (bp == NULL)
 +    goto exit;
 +
 +  if (!BIO_write(bp, key_pem, key_pem_len))
 +    goto exit;
 +
 +  // Check if this is a PKCS#8 or RSA public key before trying as X.509.
 +  // Split this out into a separate function once we have more than one
 +  // consumer of public keys.
 +  if (strncmp(key_pem, PUBLIC_KEY_PFX, PUBLIC_KEY_PFX_LEN) == 0) {
 +    pkey = PEM_read_bio_PUBKEY(bp, NULL, NULL, NULL);
 +    if (pkey == NULL)
 +      goto exit;
 +  } else if (strncmp(key_pem, PUBRSA_KEY_PFX, PUBRSA_KEY_PFX_LEN) == 0) {
 +    RSA* rsa = PEM_read_bio_RSAPublicKey(bp, NULL, NULL, NULL);
 +    if (rsa) {
 +      pkey = EVP_PKEY_new();
 +      if (pkey)
 +        EVP_PKEY_set1_RSA(pkey, rsa);
 +      RSA_free(rsa);
      }
 +    if (pkey == NULL)
 +      goto exit;
 +  } else {
 +    // X.509 fallback
 +    x509 = PEM_read_bio_X509(bp, NULL, NULL, NULL);
 +    if (x509 == NULL)
 +      goto exit;
  
 -    return args.This();
 +    pkey = X509_get_pubkey(x509);
 +    if (pkey == NULL)
 +      goto exit;
    }
  
 +  fatal = false;
 +  r = EVP_VerifyFinal(&mdctx_,
 +                      reinterpret_cast<const unsigned char*>(sig),
 +                      siglen,
 +                      pkey);
  
 -  static Handle<Value> VerifyFinal(const Arguments& args) {
 -    HandleScope scope;
 + exit:
 +  if (pkey != NULL)
 +    EVP_PKEY_free(pkey);
 +  if (bp != NULL)
 +    BIO_free_all(bp);
 +  if (x509 != NULL)
 +    X509_free(x509);
  
 -    Verify *verify = ObjectWrap::Unwrap<Verify>(args.This());
 +  EVP_MD_CTX_cleanup(&mdctx_);
 +  initialised_ = false;
  
 -    ASSERT_IS_BUFFER(args[0]);
 -    ssize_t klen = Buffer::Length(args[0]);
 +  if (fatal) {
 +    unsigned long err = ERR_get_error();
 +    ThrowCryptoError(err);
 +    return false;
 +  }
  
 -    if (klen < 0) {
 -      Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
 -      return ThrowException(exception);
 -    }
 +  return r == 1;
 +}
  
 -    char* kbuf = new char[klen];
 -    ssize_t kwritten = DecodeWrite(kbuf, klen, args[0], BINARY);
 -    assert(kwritten == klen);
  
 -    ASSERT_IS_STRING_OR_BUFFER(args[1]);
 +void Verify::VerifyFinal(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -    // BINARY works for both buffers and binary strings.
 -    enum encoding encoding = BINARY;
 -    if (args.Length() >= 3) {
 -      encoding = ParseEncoding(args[2]->ToString(), BINARY);
 -    }
 +  Verify* verify = WeakObject::Unwrap<Verify>(args.This());
  
 -    ssize_t hlen = StringBytes::Size(args[1], encoding);
 +  ASSERT_IS_BUFFER(args[0]);
 +  char* kbuf = Buffer::Data(args[0]);
 +  ssize_t klen = Buffer::Length(args[0]);
  
 -    if (hlen < 0) {
 -      delete[] kbuf;
 -      Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
 -      return ThrowException(exception);
 -    }
 +  ASSERT_IS_STRING_OR_BUFFER(args[1]);
 +  // BINARY works for both buffers and binary strings.
 +  enum encoding encoding = BINARY;
 +  if (args.Length() >= 3) {
 +    encoding = ParseEncoding(args[2]->ToString(), BINARY);
 +  }
  
 -    unsigned char* hbuf = new unsigned char[hlen];
 -    ssize_t hwritten = StringBytes::Write(
 -        reinterpret_cast<char*>(hbuf), hlen, args[1], encoding);
 -    assert(hwritten == hlen);
 +  ssize_t hlen = StringBytes::Size(args[1], encoding);
  
 -    int r;
 -    r = verify->VerifyFinal(kbuf, klen, hbuf, hlen);
 +  // only copy if we need to, because it's a string.
 +  char* hbuf;
 +  if (args[1]->IsString()) {
 +    hbuf = new char[hlen];
 +    ssize_t hwritten = StringBytes::Write(hbuf, hlen, args[1], encoding);
 +    assert(hwritten == hlen);
 +  } else {
 +    hbuf = Buffer::Data(args[1]);
 +  }
  
 -    delete[] kbuf;
 +  bool rc = verify->VerifyFinal(kbuf, klen, hbuf, hlen);
 +  if (args[1]->IsString()) {
      delete[] hbuf;
 -
 -    return Boolean::New(r && r != -1);
    }
 +  args.GetReturnValue().Set(rc);
 +}
  
 -  Verify () : ObjectWrap () {
 -    initialised_ = false;
 -  }
  
 -  ~Verify () {
 -    if (initialised_) {
 -      EVP_MD_CTX_cleanup(&mdctx);
 -    }
 -  }
 +void DiffieHellman::Initialize(Environment* env, Handle<Object> target) {
 +  Local<FunctionTemplate> t = FunctionTemplate::New(New);
  
 - private:
 +  t->InstanceTemplate()->SetInternalFieldCount(1);
  
 -  EVP_MD_CTX mdctx; /* coverity[member_decl] */
 -  const EVP_MD *md; /* coverity[member_decl] */
 -  bool initialised_;
 +  NODE_SET_PROTOTYPE_METHOD(t, "generateKeys", GenerateKeys);
 +  NODE_SET_PROTOTYPE_METHOD(t, "computeSecret", ComputeSecret);
 +  NODE_SET_PROTOTYPE_METHOD(t, "getPrime", GetPrime);
 +  NODE_SET_PROTOTYPE_METHOD(t, "getGenerator", GetGenerator);
 +  NODE_SET_PROTOTYPE_METHOD(t, "getPublicKey", GetPublicKey);
 +  NODE_SET_PROTOTYPE_METHOD(t, "getPrivateKey", GetPrivateKey);
 +  NODE_SET_PROTOTYPE_METHOD(t, "setPublicKey", SetPublicKey);
 +  NODE_SET_PROTOTYPE_METHOD(t, "setPrivateKey", SetPrivateKey);
  
 -};
 +  target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "DiffieHellman"),
 +              t->GetFunction());
  
 -class DiffieHellman : public ObjectWrap {
 - public:
 -  static void Initialize(v8::Handle<v8::Object> target) {
 -    HandleScope scope;
 +  Local<FunctionTemplate> t2 = FunctionTemplate::New(DiffieHellmanGroup);
 +  t2->InstanceTemplate()->SetInternalFieldCount(1);
  
 -    Local<FunctionTemplate> t = FunctionTemplate::New(New);
 +  NODE_SET_PROTOTYPE_METHOD(t2, "generateKeys", GenerateKeys);
 +  NODE_SET_PROTOTYPE_METHOD(t2, "computeSecret", ComputeSecret);
 +  NODE_SET_PROTOTYPE_METHOD(t2, "getPrime", GetPrime);
 +  NODE_SET_PROTOTYPE_METHOD(t2, "getGenerator", GetGenerator);
 +  NODE_SET_PROTOTYPE_METHOD(t2, "getPublicKey", GetPublicKey);
 +  NODE_SET_PROTOTYPE_METHOD(t2, "getPrivateKey", GetPrivateKey);
  
 -    t->InstanceTemplate()->SetInternalFieldCount(1);
 +  target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "DiffieHellmanGroup"),
 +              t2->GetFunction());
 +}
  
 -    NODE_SET_PROTOTYPE_METHOD(t, "generateKeys", GenerateKeys);
 -    NODE_SET_PROTOTYPE_METHOD(t, "computeSecret", ComputeSecret);
 -    NODE_SET_PROTOTYPE_METHOD(t, "getPrime", GetPrime);
 -    NODE_SET_PROTOTYPE_METHOD(t, "getGenerator", GetGenerator);
 -    NODE_SET_PROTOTYPE_METHOD(t, "getPublicKey", GetPublicKey);
 -    NODE_SET_PROTOTYPE_METHOD(t, "getPrivateKey", GetPrivateKey);
 -    NODE_SET_PROTOTYPE_METHOD(t, "setPublicKey", SetPublicKey);
 -    NODE_SET_PROTOTYPE_METHOD(t, "setPrivateKey", SetPrivateKey);
  
 -    target->Set(String::NewSymbol("DiffieHellman"), t->GetFunction());
 +bool DiffieHellman::Init(int primeLength) {
 +  dh = DH_new();
 +  DH_generate_parameters_ex(dh, primeLength, DH_GENERATOR_2, 0);
 +  bool result = VerifyContext();
 +  if (!result)
 +    return false;
 +  initialised_ = true;
 +  return true;
 +}
  
 -    Local<FunctionTemplate> t2 = FunctionTemplate::New(DiffieHellmanGroup);
 -    t2->InstanceTemplate()->SetInternalFieldCount(1);
  
 -    NODE_SET_PROTOTYPE_METHOD(t2, "generateKeys", GenerateKeys);
 -    NODE_SET_PROTOTYPE_METHOD(t2, "computeSecret", ComputeSecret);
 -    NODE_SET_PROTOTYPE_METHOD(t2, "getPrime", GetPrime);
 -    NODE_SET_PROTOTYPE_METHOD(t2, "getGenerator", GetGenerator);
 -    NODE_SET_PROTOTYPE_METHOD(t2, "getPublicKey", GetPublicKey);
 -    NODE_SET_PROTOTYPE_METHOD(t2, "getPrivateKey", GetPrivateKey);
 +bool DiffieHellman::Init(const char* p, int p_len) {
 +  dh = DH_new();
 +  dh->p = BN_bin2bn(reinterpret_cast<const unsigned char*>(p), p_len, 0);
 +  dh->g = BN_new();
 +  if (!BN_set_word(dh->g, 2))
 +    return false;
 +  bool result = VerifyContext();
 +  if (!result)
 +    return false;
 +  initialised_ = true;
 +  return true;
 +}
  
 -    target->Set(String::NewSymbol("DiffieHellmanGroup"), t2->GetFunction());
 -  }
  
 -  bool Init(int primeLength) {
 -    dh = DH_new();
 -    DH_generate_parameters_ex(dh, primeLength, DH_GENERATOR_2, 0);
 -    bool result = VerifyContext();
 -    if (!result) return false;
 -    initialised_ = true;
 -    return true;
 -  }
 +bool DiffieHellman::Init(const char* p, int p_len, const char* g, int g_len) {
 +  dh = DH_new();
 +  dh->p = BN_bin2bn(reinterpret_cast<const unsigned char*>(p), p_len, 0);
 +  dh->g = BN_bin2bn(reinterpret_cast<const unsigned char*>(g), g_len, 0);
 +  initialised_ = true;
 +  return true;
 +}
  
 -  bool Init(unsigned char* p, int p_len) {
 -    dh = DH_new();
 -    dh->p = BN_bin2bn(p, p_len, 0);
 -    dh->g = BN_new();
 -    if (!BN_set_word(dh->g, 2)) return false;
 -    bool result = VerifyContext();
 -    if (!result) return false;
 -    initialised_ = true;
 -    return true;
 -  }
  
 -  bool Init(unsigned char* p, int p_len, unsigned char* g, int g_len) {
 -    dh = DH_new();
 -    dh->p = BN_bin2bn(p, p_len, 0);
 -    dh->g = BN_bin2bn(g, g_len, 0);
 -    initialised_ = true;
 -    return true;
 +void DiffieHellman::DiffieHellmanGroup(
 +    const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
 +
 +  DiffieHellman* diffieHellman =
 +      new DiffieHellman(args.GetIsolate(), args.This());
 +
 +  if (args.Length() != 1 || !args[0]->IsString()) {
 +    return ThrowError("No group name given");
    }
  
 - protected:
 -  static Handle<Value> DiffieHellmanGroup(const Arguments& args) {
 -    HandleScope scope;
 +  const String::Utf8Value group_name(args[0]);
 +  for (unsigned int i = 0; i < ARRAY_SIZE(modp_groups); ++i) {
 +    const modp_group* it = modp_groups + i;
  
 -    DiffieHellman* diffieHellman = new DiffieHellman();
 +    if (strcasecmp(*group_name, it->name) != 0)
 +      continue;
  
 -    if (args.Length() != 1 || !args[0]->IsString()) {
 -      return ThrowException(Exception::Error(
 -          String::New("No group name given")));
 -    }
 +    diffieHellman->Init(it->prime,
 +                        it->prime_size,
 +                        it->gen,
 +                        it->gen_size);
 +    return;
 +  }
  
 -    String::Utf8Value group_name(args[0]);
 +  ThrowError("Unknown group");
 +}
  
 -    modp_group* it = modp_groups;
  
 -    while(it->name != NULL) {
 -      if (!strcasecmp(*group_name, it->name))
 -          break;
 -      it++;
 -    }
 +void DiffieHellman::New(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -    if (it->name != NULL) {
 -      diffieHellman->Init(it->prime, it->prime_size,
 -              it->gen, it->gen_size);
 +  DiffieHellman* diffieHellman =
 +      new DiffieHellman(args.GetIsolate(), args.This());
 +  bool initialized = false;
 +
 +  if (args.Length() > 0) {
 +    if (args[0]->IsInt32()) {
 +      initialized = diffieHellman->Init(args[0]->Int32Value());
      } else {
 -      return ThrowException(Exception::Error(
 -          String::New("Unknown group")));
 +      initialized = diffieHellman->Init(Buffer::Data(args[0]),
 +                                        Buffer::Length(args[0]));
      }
 -
 -    diffieHellman->Wrap(args.This());
 -
 -    return args.This();
    }
  
 -  static Handle<Value> New(const Arguments& args) {
 -    HandleScope scope;
 -
 -    DiffieHellman* diffieHellman = new DiffieHellman();
 -    bool initialized = false;
 +  if (!initialized) {
 +    return ThrowError("Initialization failed");
 +  }
 +}
  
 -    if (args.Length() > 0) {
 -      if (args[0]->IsInt32()) {
 -        initialized = diffieHellman->Init(args[0]->Int32Value());
 -      } else {
 -        initialized = diffieHellman->Init(
 -                reinterpret_cast<unsigned char*>(Buffer::Data(args[0])),
 -                Buffer::Length(args[0]));
 -      }
 -    }
  
 -    if (!initialized) {
 -      return ThrowException(Exception::Error(
 -            String::New("Initialization failed")));
 -    }
 +void DiffieHellman::GenerateKeys(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -    diffieHellman->Wrap(args.This());
 +  DiffieHellman* diffieHellman =
 +      WeakObject::Unwrap<DiffieHellman>(args.This());
  
 -    return args.This();
 +  if (!diffieHellman->initialised_) {
 +    return ThrowError("Not initialized");
    }
  
 -  static Handle<Value> GenerateKeys(const Arguments& args) {
 -    DiffieHellman* diffieHellman =
 -      ObjectWrap::Unwrap<DiffieHellman>(args.This());
 -
 -    HandleScope scope;
 +  if (!DH_generate_key(diffieHellman->dh)) {
 +    return ThrowError("Key generation failed");
 +  }
  
 -    if (!diffieHellman->initialised_) {
 -      return ThrowException(Exception::Error(
 -            String::New("Not initialized")));
 -    }
 +  int dataSize = BN_num_bytes(diffieHellman->dh->pub_key);
 +  char* data = new char[dataSize];
 +  BN_bn2bin(diffieHellman->dh->pub_key,
 +            reinterpret_cast<unsigned char*>(data));
  
 -    if (!DH_generate_key(diffieHellman->dh)) {
 -      return ThrowException(Exception::Error(
 -            String::New("Key generation failed")));
 -    }
 +  args.GetReturnValue().Set(Encode(data, dataSize, BUFFER));
 +  delete[] data;
 +}
  
 -    Local<Value> outString;
  
 -    int dataSize = BN_num_bytes(diffieHellman->dh->pub_key);
 -    char* data = new char[dataSize];
 -    BN_bn2bin(diffieHellman->dh->pub_key,
 -        reinterpret_cast<unsigned char*>(data));
 +void DiffieHellman::GetPrime(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -    outString = Encode(data, dataSize, BUFFER);
 -    delete[] data;
 +  DiffieHellman* diffieHellman =
 +      WeakObject::Unwrap<DiffieHellman>(args.This());
  
 -    return scope.Close(outString);
 +  if (!diffieHellman->initialised_) {
 +    return ThrowError("Not initialized");
    }
  
 -  static Handle<Value> GetPrime(const Arguments& args) {
 -    DiffieHellman* diffieHellman =
 -      ObjectWrap::Unwrap<DiffieHellman>(args.This());
 -
 -    HandleScope scope;
 +  int dataSize = BN_num_bytes(diffieHellman->dh->p);
 +  char* data = new char[dataSize];
 +  BN_bn2bin(diffieHellman->dh->p, reinterpret_cast<unsigned char*>(data));
  
 -    if (!diffieHellman->initialised_) {
 -      return ThrowException(Exception::Error(String::New("Not initialized")));
 -    }
 -
 -    int dataSize = BN_num_bytes(diffieHellman->dh->p);
 -    char* data = new char[dataSize];
 -    BN_bn2bin(diffieHellman->dh->p, reinterpret_cast<unsigned char*>(data));
 +  args.GetReturnValue().Set(Encode(data, dataSize, BUFFER));
 +  delete[] data;
 +}
  
 -    Local<Value> outString;
  
 -    outString = Encode(data, dataSize, BUFFER);
 +void DiffieHellman::GetGenerator(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -    delete[] data;
 +  DiffieHellman* diffieHellman =
 +      WeakObject::Unwrap<DiffieHellman>(args.This());
  
 -    return scope.Close(outString);
 +  if (!diffieHellman->initialised_) {
 +    return ThrowError("Not initialized");
    }
  
 -  static Handle<Value> GetGenerator(const Arguments& args) {
 -    DiffieHellman* diffieHellman =
 -      ObjectWrap::Unwrap<DiffieHellman>(args.This());
 -
 -    HandleScope scope;
 -
 -    if (!diffieHellman->initialised_) {
 -      return ThrowException(Exception::Error(String::New("Not initialized")));
 -    }
 +  int dataSize = BN_num_bytes(diffieHellman->dh->g);
 +  char* data = new char[dataSize];
 +  BN_bn2bin(diffieHellman->dh->g, reinterpret_cast<unsigned char*>(data));
  
 -    int dataSize = BN_num_bytes(diffieHellman->dh->g);
 -    char* data = new char[dataSize];
 -    BN_bn2bin(diffieHellman->dh->g, reinterpret_cast<unsigned char*>(data));
 +  args.GetReturnValue().Set(Encode(data, dataSize, BUFFER));
 +  delete[] data;
 +}
  
 -    Local<Value> outString;
  
 -    outString = Encode(data, dataSize, BUFFER);
 +void DiffieHellman::GetPublicKey(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -    delete[] data;
 +  DiffieHellman* diffieHellman =
 +      WeakObject::Unwrap<DiffieHellman>(args.This());
  
 -    return scope.Close(outString);
 +  if (!diffieHellman->initialised_) {
 +    return ThrowError("Not initialized");
    }
  
 -  static Handle<Value> GetPublicKey(const Arguments& args) {
 -    DiffieHellman* diffieHellman =
 -      ObjectWrap::Unwrap<DiffieHellman>(args.This());
 -
 -    HandleScope scope;
 -
 -    if (!diffieHellman->initialised_) {
 -      return ThrowException(Exception::Error(String::New("Not initialized")));
 -    }
 +  if (diffieHellman->dh->pub_key == NULL) {
 +    return ThrowError("No public key - did you forget to generate one?");
 +  }
  
 -    if (diffieHellman->dh->pub_key == NULL) {
 -      return ThrowException(Exception::Error(
 -            String::New("No public key - did you forget to generate one?")));
 -    }
 +  int dataSize = BN_num_bytes(diffieHellman->dh->pub_key);
 +  char* data = new char[dataSize];
 +  BN_bn2bin(diffieHellman->dh->pub_key,
 +            reinterpret_cast<unsigned char*>(data));
  
 -    int dataSize = BN_num_bytes(diffieHellman->dh->pub_key);
 -    char* data = new char[dataSize];
 -    BN_bn2bin(diffieHellman->dh->pub_key,
 -        reinterpret_cast<unsigned char*>(data));
 +  args.GetReturnValue().Set(Encode(data, dataSize, BUFFER));
 +  delete[] data;
 +}
  
 -    Local<Value> outString;
  
 -    outString = Encode(data, dataSize, BUFFER);
 +void DiffieHellman::GetPrivateKey(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -    delete[] data;
 +  DiffieHellman* diffieHellman =
 +      WeakObject::Unwrap<DiffieHellman>(args.This());
  
 -    return scope.Close(outString);
 +  if (!diffieHellman->initialised_) {
 +    return ThrowError("Not initialized");
    }
  
 -  static Handle<Value> GetPrivateKey(const Arguments& args) {
 -    DiffieHellman* diffieHellman =
 -      ObjectWrap::Unwrap<DiffieHellman>(args.This());
 +  if (diffieHellman->dh->priv_key == NULL) {
 +    return ThrowError("No private key - did you forget to generate one?");
 +  }
  
 -    HandleScope scope;
 +  int dataSize = BN_num_bytes(diffieHellman->dh->priv_key);
 +  char* data = new char[dataSize];
 +  BN_bn2bin(diffieHellman->dh->priv_key,
 +            reinterpret_cast<unsigned char*>(data));
  
 -    if (!diffieHellman->initialised_) {
 -      return ThrowException(Exception::Error(String::New("Not initialized")));
 -    }
 +  args.GetReturnValue().Set(Encode(data, dataSize, BUFFER));
 +  delete[] data;
 +}
  
 -    if (diffieHellman->dh->priv_key == NULL) {
 -      return ThrowException(Exception::Error(
 -            String::New("No private key - did you forget to generate one?")));
 -    }
  
 -    int dataSize = BN_num_bytes(diffieHellman->dh->priv_key);
 -    char* data = new char[dataSize];
 -    BN_bn2bin(diffieHellman->dh->priv_key,
 -        reinterpret_cast<unsigned char*>(data));
 +void DiffieHellman::ComputeSecret(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -    Local<Value> outString;
 +  DiffieHellman* diffieHellman =
 +      WeakObject::Unwrap<DiffieHellman>(args.This());
  
 -    outString = Encode(data, dataSize, BUFFER);
 +  if (!diffieHellman->initialised_) {
 +    return ThrowError("Not initialized");
 +  }
  
 -    delete[] data;
 +  ClearErrorOnReturn clear_error_on_return;
 +  (void) &clear_error_on_return;  // Silence compiler warning.
 +  BIGNUM* key = NULL;
  
 -    return scope.Close(outString);
 +  if (args.Length() == 0) {
 +    return ThrowError("First argument must be other party's public key");
 +  } else {
 +    ASSERT_IS_BUFFER(args[0]);
 +    key = BN_bin2bn(
 +        reinterpret_cast<unsigned char*>(Buffer::Data(args[0])),
 +        Buffer::Length(args[0]),
 +        0);
    }
  
 -  static Handle<Value> ComputeSecret(const Arguments& args) {
 -    HandleScope scope;
 +  int dataSize = DH_size(diffieHellman->dh);
 +  char* data = new char[dataSize];
  
 -    DiffieHellman* diffieHellman =
 -      ObjectWrap::Unwrap<DiffieHellman>(args.This());
 -
 -    if (!diffieHellman->initialised_) {
 -      return ThrowException(Exception::Error(String::New("Not initialized")));
 -    }
 +  int size = DH_compute_key(reinterpret_cast<unsigned char*>(data),
 +                            key,
 +                            diffieHellman->dh);
  
 -    ClearErrorOnReturn clear_error_on_return;
 -    (void) &clear_error_on_return;  // Silence compiler warning.
 -    BIGNUM* key = 0;
 +  if (size == -1) {
 +    int checkResult;
 +    int checked;
  
 -    if (args.Length() == 0) {
 -      return ThrowException(Exception::Error(
 -            String::New("First argument must be other party's public key")));
 -    } else {
 -      ASSERT_IS_BUFFER(args[0]);
 -      key = BN_bin2bn(
 -        reinterpret_cast<unsigned char*>(Buffer::Data(args[0])),
 -        Buffer::Length(args[0]), 0);
 -    }
 +    checked = DH_check_pub_key(diffieHellman->dh, key, &checkResult);
 +    BN_free(key);
 +    delete[] data;
  
 -    int dataSize = DH_size(diffieHellman->dh);
 -    char* data = new char[dataSize];
 -
 -    int size = DH_compute_key(reinterpret_cast<unsigned char*>(data),
 -      key, diffieHellman->dh);
 -
 -    if (size == -1) {
 -      int checkResult;
 -      int checked;
 -
 -      checked = DH_check_pub_key(diffieHellman->dh, key, &checkResult);
 -      BN_free(key);
 -      delete[] data;
 -
 -      if (!checked) {
 -        return ThrowException(Exception::Error(String::New("Invalid key")));
 -      } else if (checkResult) {
 -        if (checkResult & DH_CHECK_PUBKEY_TOO_SMALL) {
 -          return ThrowException(Exception::Error(
 -                String::New("Supplied key is too small")));
 -        } else if (checkResult & DH_CHECK_PUBKEY_TOO_LARGE) {
 -          return ThrowException(Exception::Error(
 -                String::New("Supplied key is too large")));
 -        } else {
 -          return ThrowException(Exception::Error(String::New("Invalid key")));
 -        }
 +    if (!checked) {
 +      return ThrowError("Invalid key");
 +    } else if (checkResult) {
 +      if (checkResult & DH_CHECK_PUBKEY_TOO_SMALL) {
 +        return ThrowError("Supplied key is too small");
 +      } else if (checkResult & DH_CHECK_PUBKEY_TOO_LARGE) {
 +        return ThrowError("Supplied key is too large");
        } else {
 -        return ThrowException(Exception::Error(String::New("Invalid key")));
 +        return ThrowError("Invalid key");
        }
 +    } else {
 +      return ThrowError("Invalid key");
      }
 +  }
  
 -    BN_free(key);
 -    assert(size >= 0);
 -
 -    // DH_size returns number of bytes in a prime number
 -    // DH_compute_key returns number of bytes in a remainder of exponent, which
 -    // may have less bytes than a prime number. Therefore add 0-padding to the
 -    // allocated buffer.
 -    if (size != dataSize) {
 -      assert(dataSize > size);
 -      memmove(data + dataSize - size, data, size);
 -      memset(data, 0, dataSize - size);
 -    }
 -
 -    Local<Value> outString;
 -
 -    outString = Encode(data, dataSize, BUFFER);
 +  BN_free(key);
 +  assert(size >= 0);
  
 -    delete[] data;
 -    return scope.Close(outString);
 +  // DH_size returns number of bytes in a prime number
 +  // DH_compute_key returns number of bytes in a remainder of exponent, which
 +  // may have less bytes than a prime number. Therefore add 0-padding to the
 +  // allocated buffer.
 +  if (size != dataSize) {
 +    assert(dataSize > size);
 +    memmove(data + dataSize - size, data, size);
 +    memset(data, 0, dataSize - size);
    }
  
 -  static Handle<Value> SetPublicKey(const Arguments& args) {
 -    HandleScope scope;
 +  args.GetReturnValue().Set(Encode(data, dataSize, BUFFER));
 +  delete[] data;
 +}
  
 -    DiffieHellman* diffieHellman =
 -      ObjectWrap::Unwrap<DiffieHellman>(args.This());
  
 -    if (!diffieHellman->initialised_) {
 -      return ThrowException(Exception::Error(String::New("Not initialized")));
 -    }
 +void DiffieHellman::SetPublicKey(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -    if (args.Length() == 0) {
 -      return ThrowException(Exception::Error(
 -            String::New("First argument must be public key")));
 -    } else {
 -      ASSERT_IS_BUFFER(args[0]);
 -      diffieHellman->dh->pub_key =
 -        BN_bin2bn(
 -          reinterpret_cast<unsigned char*>(Buffer::Data(args[0])),
 -          Buffer::Length(args[0]), 0);
 -    }
 +  DiffieHellman* diffieHellman =
 +      WeakObject::Unwrap<DiffieHellman>(args.This());
  
 -    return args.This();
 +  if (!diffieHellman->initialised_) {
 +    return ThrowError("Not initialized");
    }
  
 -  static Handle<Value> SetPrivateKey(const Arguments& args) {
 -    HandleScope scope;
 -
 -    DiffieHellman* diffieHellman =
 -      ObjectWrap::Unwrap<DiffieHellman>(args.This());
 +  if (args.Length() == 0) {
 +    return ThrowError("First argument must be public key");
 +  } else {
 +    ASSERT_IS_BUFFER(args[0]);
 +    diffieHellman->dh->pub_key = BN_bin2bn(
 +        reinterpret_cast<unsigned char*>(Buffer::Data(args[0])),
 +        Buffer::Length(args[0]), 0);
 +  }
 +}
  
 -    if (!diffieHellman->initialised_) {
 -      return ThrowException(Exception::Error(
 -            String::New("Not initialized")));
 -    }
  
 -    if (args.Length() == 0) {
 -      return ThrowException(Exception::Error(
 -            String::New("First argument must be private key")));
 -    } else {
 -      ASSERT_IS_BUFFER(args[0]);
 -      diffieHellman->dh->priv_key =
 -        BN_bin2bn(
 -          reinterpret_cast<unsigned char*>(Buffer::Data(args[0])),
 -          Buffer::Length(args[0]), 0);
 -    }
 +void DiffieHellman::SetPrivateKey(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -    return args.This();
 -  }
 +  DiffieHellman* diffieHellman =
 +      WeakObject::Unwrap<DiffieHellman>(args.This());
  
 -  DiffieHellman() : ObjectWrap() {
 -    initialised_ = false;
 -    dh = NULL;
 +  if (!diffieHellman->initialised_) {
 +    return ThrowError("Not initialized");
    }
  
 -  ~DiffieHellman() {
 -    if (dh != NULL) {
 -      DH_free(dh);
 -    }
 +  if (args.Length() == 0) {
 +    return ThrowError("First argument must be private key");
 +  } else {
 +    ASSERT_IS_BUFFER(args[0]);
 +    diffieHellman->dh->priv_key = BN_bin2bn(
 +        reinterpret_cast<unsigned char*>(Buffer::Data(args[0])),
 +        Buffer::Length(args[0]),
 +        0);
    }
 +}
  
 - private:
 -  bool VerifyContext() {
 -    int codes;
 -    if (!DH_check(dh, &codes)) return false;
 -    if (codes & DH_CHECK_P_NOT_SAFE_PRIME) return false;
 -    if (codes & DH_CHECK_P_NOT_PRIME) return false;
 -    if (codes & DH_UNABLE_TO_CHECK_GENERATOR) return false;
 -    if (codes & DH_NOT_SUITABLE_GENERATOR) return false;
 -    return true;
 -  }
  
 -  bool initialised_;
 -  DH* dh;
 -};
 +bool DiffieHellman::VerifyContext() {
 +  int codes;
 +  if (!DH_check(dh, &codes))
 +    return false;
 +  if (codes & DH_CHECK_P_NOT_SAFE_PRIME)
 +    return false;
 +  if (codes & DH_CHECK_P_NOT_PRIME)
 +    return false;
 +  if (codes & DH_UNABLE_TO_CHECK_GENERATOR)
 +    return false;
 +  if (codes & DH_NOT_SUITABLE_GENERATOR)
 +    return false;
 +  return true;
 +}
  
  
 +// TODO(bnoordhuis) Turn into proper RAII class.
  struct pbkdf2_req {
    uv_work_t work_req;
 +  Environment* env;
    int err;
    char* pass;
    size_t passlen;
Simple merge
@@@ -272,15 -264,17 +272,18 @@@ void PipeWrap::AfterConnect(uv_connect_
  }
  
  
 -Handle<Value> PipeWrap::Open(const Arguments& args) {
 -  HandleScope scope;
 +void PipeWrap::Open(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -  UNWRAP(PipeWrap)
 +  PipeWrap* wrap;
 +  NODE_UNWRAP(args.This(), PipeWrap, wrap);
  
 -  if (uv_pipe_open(&wrap->handle_, args[0]->Int32Value())) {
 -    uv_err_t err = uv_last_error(wrap->handle_.loop);
 -    return ThrowException(UVException(err.code, "uv_pipe_open"));
 -  }
 +  int fd = args[0]->Int32Value();
  
-   uv_pipe_open(&wrap->handle_, fd);
 -  return scope.Close(v8::Null());
++  int err = uv_pipe_open(&wrap->handle_, fd);
++
++  if (err != 0)
++    ThrowException(UVException(err, "uv_pipe_open"));
  }
  
  
index 0000000,6162d16..09c43dd
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,81 +1,83 @@@
 -  process.nextTick(function() {
+ // 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 common = require('../common');
+ var assert = require('assert');
+ try {
+   var crypto = require('crypto');
+   var tls = require('tls');
+ } catch (e) {
+   console.log('Not compiled with OPENSSL support.');
+   process.exit();
+ }
+ crypto.DEFAULT_ENCODING = 'buffer';
+ var fs = require('fs');
+ var certPem = fs.readFileSync(common.fixturesDir + '/test_cert.pem', 'ascii');
+ var options = {
+   key: fs.readFileSync(common.fixturesDir + '/keys/agent1-key.pem'),
+   cert: fs.readFileSync(common.fixturesDir + '/keys/agent1-cert.pem')
+ };
+ var canSend = true;
+ var server = tls.Server(options, function(socket) {
 -      socket.destroy();
++  setImmediate(function() {
+       console.log('sending');
+       verify();
++      setImmediate(function() {
++        socket.destroy();
++      });
+   });
+ });
+ var client;
+ function verify() {
+   console.log('verify');
+   var verified = crypto.createVerify('RSA-SHA1')
+                      .update('Test')
+                      .verify(certPem, 'asdfasdfas', 'base64');
+ }
+ server.listen(common.PORT, function() {
+   client = tls.connect({
+     port: common.PORT,
+     rejectUnauthorized: false
+   }, function() {
+     verify();
+   }).on('data', function(data) {
+     console.log(data);
+   }).on('error', function(err) {
+     throw err;
+   }).on('close', function() {
+     server.close();
+   }).resume();
+ });
+ server.unref();