Merge remote-tracking branch 'upstream/v0.10'
authorTimothy J Fontaine <tjfontaine@gmail.com>
Sun, 9 Feb 2014 00:45:27 +0000 (16:45 -0800)
committerTimothy J Fontaine <tjfontaine@gmail.com>
Sun, 9 Feb 2014 00:45:27 +0000 (16:45 -0800)
Conflicts:
deps/v8/src/preparser.cc
deps/v8/src/win32-math.h
doc/api/http.markdown
src/node_buffer.h
src/node_crypto.cc
src/node_file.cc
src/node_http_parser.cc

1  2 
doc/api/child_process.markdown
doc/api/crypto.markdown
doc/api/http.markdown
doc/api/path.markdown
lib/fs.js
src/node_buffer.h
src/node_crypto.cc
src/node_dtrace.cc
src/node_file.cc
src/node_zlib.cc

Simple merge
Simple merge
@@@ -584,54 -532,17 +584,55 @@@ want it to stay in the pool you can do 
        socket.emit("agentRemove");
      });
  
 -Alternatively, you could just opt out of pooling entirely using `agent:false`:
 +Alternatively, you could just opt out of pooling entirely using
 +`agent:false`:
  
 -    http.get({hostname:'localhost', port:80, path:'/', agent:false}, function (res) {
 -      // Do stuff
 +    http.get({
 +      hostname: 'localhost',
 +      port: 80,
 +      path: '/',
 +      agent: false  // create a new agent just for this one request
 +    }, function (res) {
 +      // Do stuff with response
      })
  
 +### new Agent([options])
 +
 +* `options` {Object} Set of configurable options to set on the agent.
 +  Can have the following fields:
 +  * `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`.
 +  * `maxSockets` {Number} Maximum number of sockets to allow per
 +    host.  Default = `Infinity`.
 +  * `maxFreeSockets` {Number} Maximum number of sockets to leave open
 +    in a free state.  Only relevant if `keepAlive` is set to `true`.
 +    Default = `256`.
 +
 +The default `http.globalAgent` that is used by `http.request` has all
 +of these values set to their respective defaults.
 +
 +To configure any of them, you must create your own `Agent` object.
 +
 +```javascript
 +var http = require('http');
 +var keepAliveAgent = new http.Agent({ keepAlive: true });
 +keepAliveAgent.request(options, onResponseCallback);
 +```
 +
  ### agent.maxSockets
  
- By default set to Infinity. Determines how many concurrent sockets the
- agent can have open per host.
 -By default set to 5. Determines how many concurrent sockets the agent can have
 -open per origin. Origin is either a 'host:port' or 'host:port:localAddress'
 -combination.
++By default set to Infinity. Determines how many concurrent sockets the agent
++can have open per origin. Origin is either a 'host:port' or
++'host:port:localAddress' combination.
 +
 +### agent.maxFreeSockets
 +
 +By default set to 256.  For Agents supporting HTTP KeepAlive, this
 +sets the maximum number of sockets that will be left open in the free
 +state.
  
  ### agent.sockets
  
Simple merge
diff --cc lib/fs.js
Simple merge
  // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
  // USE OR OTHER DEALINGS IN THE SOFTWARE.
  
 -#ifndef NODE_BUFFER_H_
 -#define NODE_BUFFER_H_
 +#ifndef SRC_NODE_BUFFER_H_
 +#define SRC_NODE_BUFFER_H_
  
  #include "node.h"
 -#include "node_object_wrap.h"
 +#include "smalloc.h"
  #include "v8.h"
 -#include <assert.h>
  
 -namespace node {
 -
 -/* A buffer is a chunk of memory stored outside the V8 heap, mirrored by an
 - * object in javascript. The object is not totally opaque, one can access
 - * individual bytes with [] and slice it into substrings or sub-buffers
 - * without copying memory.
 - */
 -
 -/*
 -   The C++ API for Buffer changed radically between v0.2 and v0.3, in fact
 -   it was the reason for bumping the version. In v0.2 JavaScript Buffers and
 -   C++ Buffers were in one-to-one correspondence via ObjectWrap. We found
 -   that it was faster to expose the C++ Buffers to JavaScript as a
 -   "SlowBuffer" which is used as a private backend to pure JavaScript
 -   "Buffer" objects - a 'Buffer' in v0.3 might look like this:
 -
 -   { _parent: s,
 -     _offset: 520,
 -     length: 5 }
 -
 -   Migrating code C++ Buffer code from v0.2 to v0.3 is difficult. Here are
 -   some tips:
 -    - buffer->data() calls should become Buffer::Data(buffer) calls.
 -    - buffer->length() calls should become Buffer::Length(buffer) calls.
 -    - There should not be any ObjectWrap::Unwrap<Buffer>() calls. You should
 -      not be storing pointers to Buffer objects at all - as they are
 -      now considered internal structures. Instead consider making a
 -      JavaScript reference to the buffer.
 -
 -   See the source code node-png as an example of a module which successfully
 -   compiles on both v0.2 and v0.3 while making heavy use of the C++ Buffer
 -   API.
 -
 - */
 -
 -
 -class NODE_EXTERN Buffer: public ObjectWrap {
 - public:
 -  // mirrors deps/v8/src/objects.h
 -  static const unsigned int kMaxLength = 0x3fffffff;
 -
 -  static v8::Persistent<v8::FunctionTemplate> constructor_template;
 -
 -  static bool HasInstance(v8::Handle<v8::Value> val);
 -
 -  static inline char* Data(v8::Handle<v8::Value> val) {
 -    assert(val->IsObject());
 -    void* data = val.As<v8::Object>()->GetIndexedPropertiesExternalArrayData();
 -    return static_cast<char*>(data);
 -  }
 -
 -  static inline char* Data(Buffer *b) {
 -    return Buffer::Data(b->handle_);
 -  }
 -
 -  static inline size_t Length(v8::Handle<v8::Value> val) {
 -    assert(val->IsObject());
 -    int len = val.As<v8::Object>()
 -              ->GetIndexedPropertiesExternalArrayDataLength();
 -    return static_cast<size_t>(len);
 -  }
 -
 -  static inline size_t Length(Buffer *b) {
 -    return Buffer::Length(b->handle_);
 -  }
 -
 -  // This is verbose to be explicit with inline commenting
 -  static inline bool IsWithinBounds(size_t off, size_t len, size_t max) {
 -    // Asking to seek too far into the buffer
 -    // check to avoid wrapping in subsequent subtraction
 -    if (off > max)
 -      return false;
 +#if defined(NODE_WANT_INTERNALS)
 +#include "env.h"
 +#endif  // defined(NODE_WANT_INTERNALS)
  
 -    // Asking for more than is left over in the buffer
 -    if (max - off < len)
 -      return false;
 -
 -    // Otherwise we're in bounds
 -    return true;
 -  }
 -
 -  ~Buffer();
 -
 -  typedef void (*free_callback)(char *data, void *hint);
 -
 -  // C++ API for constructing fast buffer
 -  static v8::Handle<v8::Object> New(v8::Handle<v8::String> string);
 -
 -  static void Initialize(v8::Handle<v8::Object> target);
 -
 -  // public constructor
 -  static Buffer* New(size_t length);
 -  // public constructor - data is copied
 -  static Buffer* New(const char *data, size_t len);
 -  // public constructor
 -  static Buffer* New(char *data, size_t length,
 -                     free_callback callback, void *hint);
 -
 -  private:
 -  static v8::Handle<v8::Value> New(const v8::Arguments &args);
 -
 -  template <encoding encoding>
 -  static v8::Handle<v8::Value> StringSlice(const v8::Arguments &args);
 -  static v8::Handle<v8::Value> BinarySlice(const v8::Arguments &args);
 -  static v8::Handle<v8::Value> AsciiSlice(const v8::Arguments &args);
 -  static v8::Handle<v8::Value> Base64Slice(const v8::Arguments &args);
 -  static v8::Handle<v8::Value> Utf8Slice(const v8::Arguments &args);
 -  static v8::Handle<v8::Value> Ucs2Slice(const v8::Arguments &args);
 -  static v8::Handle<v8::Value> HexSlice(const v8::Arguments &args);
 -
 -  template <encoding encoding>
 -  static v8::Handle<v8::Value> StringWrite(const v8::Arguments &args);
 -  static v8::Handle<v8::Value> BinaryWrite(const v8::Arguments &args);
 -  static v8::Handle<v8::Value> Base64Write(const v8::Arguments &args);
 -  static v8::Handle<v8::Value> AsciiWrite(const v8::Arguments &args);
 -  static v8::Handle<v8::Value> Utf8Write(const v8::Arguments &args);
 -  static v8::Handle<v8::Value> Ucs2Write(const v8::Arguments &args);
 -  static v8::Handle<v8::Value> HexWrite(const v8::Arguments &args);
 -  static v8::Handle<v8::Value> ReadFloatLE(const v8::Arguments &args);
 -  static v8::Handle<v8::Value> ReadFloatBE(const v8::Arguments &args);
 -  static v8::Handle<v8::Value> ReadDoubleLE(const v8::Arguments &args);
 -  static v8::Handle<v8::Value> ReadDoubleBE(const v8::Arguments &args);
 -  static v8::Handle<v8::Value> WriteFloatLE(const v8::Arguments &args);
 -  static v8::Handle<v8::Value> WriteFloatBE(const v8::Arguments &args);
 -  static v8::Handle<v8::Value> WriteDoubleLE(const v8::Arguments &args);
 -  static v8::Handle<v8::Value> WriteDoubleBE(const v8::Arguments &args);
 -  static v8::Handle<v8::Value> ByteLength(const v8::Arguments &args);
 -  static v8::Handle<v8::Value> MakeFastBuffer(const v8::Arguments &args);
 -  static v8::Handle<v8::Value> Fill(const v8::Arguments &args);
 -  static v8::Handle<v8::Value> Copy(const v8::Arguments &args);
 -
 -  Buffer(v8::Handle<v8::Object> wrapper, size_t length);
 -  void Replace(char *data, size_t length, free_callback callback, void *hint);
 -
 -  size_t length_;
 -  char* data_;
 -  free_callback callback_;
 -  void* callback_hint_;
 -};
 -
 -
 -}  // namespace node buffer
 -
 -#endif  // NODE_BUFFER_H_
 +namespace node {
 +namespace Buffer {
 +
 +static const unsigned int kMaxLength = smalloc::kMaxLength;
 +
 +NODE_EXTERN bool HasInstance(v8::Handle<v8::Value> val);
 +NODE_EXTERN bool HasInstance(v8::Handle<v8::Object> val);
 +NODE_EXTERN char* Data(v8::Handle<v8::Value> val);
 +NODE_EXTERN char* Data(v8::Handle<v8::Object> val);
 +NODE_EXTERN size_t Length(v8::Handle<v8::Value> val);
 +NODE_EXTERN size_t Length(v8::Handle<v8::Object> val);
 +
 +// public constructor
 +NODE_EXTERN v8::Local<v8::Object> New(size_t length);
 +// public constructor from string
 +NODE_EXTERN v8::Local<v8::Object> New(v8::Handle<v8::String> string,
 +                                      enum encoding enc = UTF8);
 +// public constructor - data is copied
 +// TODO(trevnorris): should be something like Copy()
 +NODE_EXTERN v8::Local<v8::Object> New(const char* data, size_t len);
 +// public constructor - data is used, callback is passed data on object gc
 +NODE_EXTERN v8::Local<v8::Object> New(char* data,
 +                                      size_t length,
 +                                      smalloc::FreeCallback callback,
 +                                      void* hint);
 +
 +// public constructor - data is used.
 +// TODO(trevnorris): should be New() for consistency
 +NODE_EXTERN v8::Local<v8::Object> Use(char* data, uint32_t len);
 +
++// This is verbose to be explicit with inline commenting
++static inline bool IsWithinBounds(size_t off, size_t len, size_t max) {
++  // Asking to seek too far into the buffer
++  // check to avoid wrapping in subsequent subtraction
++  if (off > max)
++    return false;
++
++  // Asking for more than is left over in the buffer
++  if (max - off < len)
++    return false;
++
++  // Otherwise we're in bounds
++  return true;
++}
++
 +// Internal. Not for public consumption. We can't define these in
 +// src/node_internals.h due to a circular dependency issue with
 +// the smalloc.h and node_internals.h headers.
 +#if defined(NODE_WANT_INTERNALS)
 +v8::Local<v8::Object> New(Environment* env, size_t size);
 +v8::Local<v8::Object> New(Environment* env, const char* data, size_t len);
 +v8::Local<v8::Object> New(Environment* env,
 +                          char* data,
 +                          size_t length,
 +                          smalloc::FreeCallback callback,
 +                          void* hint);
 +v8::Local<v8::Object> Use(Environment* env, char* data, uint32_t length);
 +#endif  // defined(NODE_WANT_INTERNALS)
 +
 +}  // namespace Buffer
 +}  // namespace node
 +
 +#endif  // SRC_NODE_BUFFER_H_
@@@ -1813,249 -2054,281 +1813,249 @@@ void Connection::New(const FunctionCall
  #endif
  
  
 -class Cipher : public ObjectWrap {
 - public:
 -  static void Initialize (v8::Handle<v8::Object> target) {
 -    HandleScope scope;
 -
 -    Local<FunctionTemplate> t = FunctionTemplate::New(New);
 +  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;
 +  }
  
 -    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);
 +  // Always allow a connection. We'll reject in javascript.
 +  SSL_set_verify(conn->ssl_, verify_mode, VerifyCallback);
  
 -    target->Set(String::NewSymbol("Cipher"), t->GetFunction());
 +  if (is_server) {
 +    SSL_set_accept_state(conn->ssl_);
 +  } else {
 +    SSL_set_connect_state(conn->ssl_);
    }
 +}
  
  
 -  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;
 -    }
 -
 -    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);
 +void Connection::SSLInfoCallback(const SSL *ssl_, int where, int ret) {
 +  if (!(where & (SSL_CB_HANDSHAKE_START | SSL_CB_HANDSHAKE_DONE)))
 +    return;
  
 -    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;
 -  }
 +  // 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();
 +  HandleScope handle_scope(env->isolate());
 +  Context::Scope context_scope(env->context());
  
 -  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 (where & SSL_CB_HANDSHAKE_START) {
 +    conn->MakeCallback(env->onhandshakestart_string(), 0, NULL);
    }
  
 -  int SetAutoPadding(bool auto_padding) {
 -    if (!initialised_) return 0;
 -    return EVP_CIPHER_CTX_set_padding(&ctx, auto_padding ? 1 : 0);
 +  if (where & SSL_CB_HANDSHAKE_DONE) {
 +    conn->MakeCallback(env->onhandshakedone_string(), 0, NULL);
    }
 +}
  
 -  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;
 -  }
  
 +void Connection::EncIn(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(args.GetIsolate());
  
 - protected:
 +  Connection* conn = Unwrap<Connection>(args.This());
  
 -  static Handle<Value> New(const Arguments& args) {
 -    HandleScope scope;
 +  if (args.Length() < 3) {
 +    return ThrowTypeError("Takes 3 parameters");
 +  }
  
 -    Cipher *cipher = new Cipher();
 -    cipher->Wrap(args.This());
 -    return args.This();
 +  if (!Buffer::HasInstance(args[0])) {
 +    return ThrowTypeError("Second argument should be a buffer");
    }
  
 -  static Handle<Value> CipherInit(const Arguments& args) {
 -    HandleScope scope;
 +  char* buffer_data = Buffer::Data(args[0]);
 +  size_t buffer_length = Buffer::Length(args[0]);
  
 -    Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This());
 +  size_t off = args[1]->Int32Value();
 +  size_t len = args[2]->Int32Value();
-   if (off + len > buffer_length) {
 -    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")));
 -    }
++  if (!Buffer::IsWithinBounds(off, len, buffer_length))
 +    return ThrowError("off + len > buffer.length");
-   }
  
 -    ASSERT_IS_BUFFER(args[1]);
 -    ssize_t key_buf_len = Buffer::Length(args[1]);
 +  int bytes_written;
 +  char* data = buffer_data + off;
 +
 +  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;
  
 -    if (key_buf_len < 0) {
 -      Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
 -      return ThrowException(exception);
 +      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* 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);
 +  args.GetReturnValue().Set(bytes_written);
 +}
  
 -    String::Utf8Value cipherType(args[0]);
  
 -    bool r = cipher->CipherInit(*cipherType, key_buf, key_buf_len);
 +void Connection::ClearOut(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(args.GetIsolate());
  
 -    delete [] key_buf;
 +  Connection* conn = Unwrap<Connection>(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");
    }
  
 +  char* buffer_data = Buffer::Data(args[0]);
 +  size_t buffer_length = Buffer::Length(args[0]);
  
 -  static Handle<Value> CipherInitIv(const Arguments& args) {
 -    Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This());
 +  size_t off = args[1]->Int32Value();
 +  size_t len = args[2]->Int32Value();
-   if (off + len > buffer_length) {
 -    HandleScope scope;
++  if (!Buffer::IsWithinBounds(off, len, buffer_length))
 +    return ThrowError("off + len > buffer.length");
-   }
  
 +  if (!SSL_is_init_finished(conn->ssl_)) {
 +    int 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")));
 +    if (conn->is_server()) {
 +      rv = SSL_accept(conn->ssl_);
 +      conn->HandleSSLError("SSL_accept:ClearOut",
 +                           rv,
 +                           kZeroIsAnError,
 +                           kSyscallError);
 +    } else {
 +      rv = SSL_connect(conn->ssl_);
 +      conn->HandleSSLError("SSL_connect:ClearOut",
 +                           rv,
 +                           kZeroIsAnError,
 +                           kSyscallError);
      }
  
 -    ASSERT_IS_BUFFER(args[1]);
 -    ssize_t key_len = Buffer::Length(args[1]);
 -
 -    if (key_len < 0) {
 -      Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
 -      return ThrowException(exception);
 +    if (rv < 0) {
 +      return args.GetReturnValue().Set(rv);
      }
 +  }
  
 -    ASSERT_IS_BUFFER(args[2]);
 -    ssize_t iv_len = Buffer::Length(args[2]);
 +  int bytes_read = SSL_read(conn->ssl_, buffer_data + off, len);
 +  conn->HandleSSLError("SSL_read:ClearOut",
 +                       bytes_read,
 +                       kZeroIsNotAnError,
 +                       kSyscallError);
 +  conn->SetShutdownFlags();
  
 -    if (iv_len < 0) {
 -      Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
 -      return ThrowException(exception);
 -    }
 +  args.GetReturnValue().Set(bytes_read);
 +}
  
 -    char* key_buf = new char[key_len];
 -    ssize_t key_written = DecodeWrite(key_buf, key_len, args[1], BINARY);
 -    assert(key_written == key_len);
  
 -    char* iv_buf = new char[iv_len];
 -    ssize_t iv_written = DecodeWrite(iv_buf, iv_len, args[2], BINARY);
 -    assert(iv_written == iv_len);
 +void Connection::ClearPending(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(args.GetIsolate());
 +  Connection* conn = Unwrap<Connection>(args.This());
 +  int bytes_pending = BIO_pending(conn->bio_read_);
 +  args.GetReturnValue().Set(bytes_pending);
 +}
 +
  
 -    String::Utf8Value cipherType(args[0]);
 +void Connection::EncPending(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(args.GetIsolate());
 +  Connection* conn = Unwrap<Connection>(args.This());
 +  int bytes_pending = BIO_pending(conn->bio_write_);
 +  args.GetReturnValue().Set(bytes_pending);
 +}
  
 -    bool r = cipher->CipherInitIv(*cipherType, key_buf,key_len,iv_buf,iv_len);
  
 -    delete [] key_buf;
 -    delete [] iv_buf;
 +void Connection::EncOut(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(args.GetIsolate());
  
 -    if (!r) return ThrowCryptoError(ERR_get_error());
 +  Connection* conn = Unwrap<Connection>(args.This());
  
 -    return args.This();
 +  if (args.Length() < 3) {
 +    return ThrowTypeError("Takes 3 parameters");
    }
  
 -  static Handle<Value> CipherUpdate(const Arguments& args) {
 -    Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This());
 +  if (!Buffer::HasInstance(args[0])) {
 +    return ThrowTypeError("Second argument should be a buffer");
 +  }
  
 -    HandleScope scope;
 +  char* buffer_data = Buffer::Data(args[0]);
 +  size_t buffer_length = Buffer::Length(args[0]);
  
 -    ASSERT_IS_STRING_OR_BUFFER(args[0]);
 +  size_t off = args[1]->Int32Value();
 +  size_t len = args[2]->Int32Value();
-   if (off + len > buffer_length) {
 -    // 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;
 -    } else {
 -      char* buf = Buffer::Data(args[0]);
 -      size_t buflen = Buffer::Length(args[0]);
 -      r = cipher->CipherUpdate(buf, buflen, &out, &out_len);
 -    }
++  if (!Buffer::IsWithinBounds(off, len, buffer_length))
 +    return ThrowError("off + len > buffer.length");
-   }
  
 -    if (r == 0) {
 -      delete[] out;
 -      return ThrowCryptoTypeError(ERR_get_error());
 -    }
 +  int bytes_read = BIO_read(conn->bio_write_, buffer_data + off, len);
  
 -    Local<Value> outString;
 -    outString = Encode(out, out_len, BUFFER);
 +  conn->HandleBIOError(conn->bio_write_, "BIO_read:EncOut", bytes_read);
 +  conn->SetShutdownFlags();
  
 -    if (out) delete[] out;
 +  args.GetReturnValue().Set(bytes_read);
 +}
  
 -    return scope.Close(outString);
 -  }
  
 -  static Handle<Value> SetAutoPadding(const Arguments& args) {
 -    HandleScope scope;
 -    Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This());
 +void Connection::ClearIn(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(args.GetIsolate());
  
 -    cipher->SetAutoPadding(args.Length() < 1 || args[0]->BooleanValue());
 +  Connection* conn = Unwrap<Connection>(args.This());
  
 -    return Undefined();
 +  if (args.Length() < 3) {
 +    return ThrowTypeError("Takes 3 parameters");
    }
  
 -  static Handle<Value> CipherFinal(const Arguments& args) {
 -    Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This());
 +  if (!Buffer::HasInstance(args[0])) {
 +    return ThrowTypeError("Second argument should be a buffer");
 +  }
  
 -    HandleScope scope;
 +  char* buffer_data = Buffer::Data(args[0]);
 +  size_t buffer_length = Buffer::Length(args[0]);
  
 -    unsigned char* out_value = NULL;
 -    int out_len = -1;
 -    Local<Value> outString ;
 +  size_t off = args[1]->Int32Value();
 +  size_t len = args[2]->Int32Value();
-   if (off + len > buffer_length) {
 -    int r = cipher->CipherFinal(&out_value, &out_len);
++  if (!Buffer::IsWithinBounds(off, len, buffer_length))
 +    return ThrowError("off + len > buffer.length");
-   }
  
 -    if (out_len <= 0 || r == 0) {
 -      delete[] out_value;
 -      out_value = NULL;
 -      if (r == 0) return ThrowCryptoTypeError(ERR_get_error());
 +  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);
      }
  
 -    outString = Encode(out_value, out_len, BUFFER);
 -
 -    delete [] out_value;
 -    return scope.Close(outString);
 -  }
 -
 -  Cipher () : ObjectWrap ()
 -  {
 -    initialised_ = false;
 -  }
 -
 -  ~Cipher () {
 -    if (initialised_) {
 -      EVP_CIPHER_CTX_cleanup(&ctx);
 +    if (rv < 0) {
 +      return args.GetReturnValue().Set(rv);
      }
    }
  
Simple merge
@@@ -727,108 -708,50 +727,108 @@@ static void Open(const FunctionCallback
  // 3 length    how much to write
  // 4 position  if integer, position to write at in the file.
  //             if null, write from the current position
 -static Handle<Value> Write(const Arguments& args) {
 -  HandleScope scope;
 +static void WriteBuffer(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope scope(node_isolate);
  
 -  if (!args[0]->IsInt32()) {
 -    return THROW_BAD_ARGS;
 -  }
 +  assert(args[0]->IsInt32());
 +  assert(Buffer::HasInstance(args[1]));
  
    int fd = args[0]->Int32Value();
 +  Local<Object> obj = args[1].As<Object>();
 +  const char* buf = Buffer::Data(obj);
 +  size_t buffer_length = Buffer::Length(obj);
 +  size_t off = args[2]->Uint32Value();
 +  size_t len = args[3]->Uint32Value();
 +  int64_t pos = GET_OFFSET(args[4]);
 +  Local<Value> cb = args[5];
  
 -  if (!Buffer::HasInstance(args[1])) {
 -    return ThrowException(Exception::Error(
 -                String::New("Second argument needs to be a buffer")));
 -  }
 +  if (off > buffer_length)
 +    return ThrowRangeError("offset out of bounds");
 +  if (len > buffer_length)
 +    return ThrowRangeError("length out of bounds");
 +  if (off + len < off)
 +    return ThrowRangeError("off + len overflow");
-   if (off + len > buffer_length)
++  if (!Buffer::IsWithinBounds(off, len, buffer_length))
 +    return ThrowRangeError("off + len > buffer.length");
  
 -  Local<Object> buffer_obj = args[1]->ToObject();
 -  char *buffer_data = Buffer::Data(buffer_obj);
 -  size_t buffer_length = Buffer::Length(buffer_obj);
 +  buf += off;
  
 -  size_t off = args[2]->Int32Value();
 -  if (off >= buffer_length) {
 -    return ThrowException(Exception::Error(
 -          String::New("Offset is out of bounds")));
 +  if (cb->IsFunction()) {
 +    ASYNC_CALL(write, cb, fd, buf, len, pos)
 +    return;
    }
  
 -  ssize_t len = args[3]->Int32Value();
 -  if (!Buffer::IsWithinBounds(off, len, buffer_length)) {
 -    return ThrowException(Exception::Error(
 -          String::New("off + len > buffer.length")));
 -  }
 +  SYNC_CALL(write, NULL, fd, buf, len, pos)
 +  args.GetReturnValue().Set(SYNC_RESULT);
 +}
  
 -  ASSERT_OFFSET(args[4]);
 -  int64_t pos = GET_OFFSET(args[4]);
  
 -  char * buf = (char*)buffer_data + off;
 -  Local<Value> cb = args[5];
 +// Wrapper for write(2).
 +//
 +// bytesWritten = write(fd, string, position, enc, callback)
 +// 0 fd        integer. file descriptor
 +// 1 string    non-buffer values are converted to strings
 +// 2 position  if integer, position to write at in the file.
 +//             if null, write from the current position
 +// 3 enc       encoding of string
 +static void WriteString(const FunctionCallbackInfo<Value>& args) {
 +  HandleScope handle_scope(args.GetIsolate());
 +  Environment* env = Environment::GetCurrent(args.GetIsolate());
  
 -  if (cb->IsFunction()) {
 -    ASYNC_CALL(write, cb, fd, buf, len, pos)
 -  } else {
 -    SYNC_CALL(write, 0, fd, buf, len, pos)
 -    return scope.Close(Integer::New(SYNC_RESULT));
 +  if (!args[0]->IsInt32())
 +    return ThrowTypeError("First argument must be file descriptor");
 +
 +  Local<Value> cb;
 +  Local<Value> string = args[1];
 +  int fd = args[0]->Int32Value();
 +  char* buf = NULL;
 +  int64_t pos;
 +  size_t len;
 +  bool must_free = false;
 +
 +  // will assign buf and len if string was external
 +  if (!StringBytes::GetExternalParts(string,
 +                                     const_cast<const char**>(&buf),
 +                                     &len)) {
 +    enum encoding enc = ParseEncoding(args[3], UTF8);
 +    len = StringBytes::StorageSize(string, enc);
 +    buf = new char[len];
 +    // StorageSize may return too large a char, so correct the actual length
 +    // by the write size
 +    len = StringBytes::Write(buf, len, args[1], enc);
 +    must_free = true;
 +  }
 +  pos = GET_OFFSET(args[2]);
 +  cb = args[4];
 +
 +  if (!cb->IsFunction()) {
 +    SYNC_CALL(write, NULL, fd, buf, len, pos)
 +    if (must_free)
 +      delete[] buf;
 +    return args.GetReturnValue().Set(SYNC_RESULT);
    }
 +
 +  FSReqWrap* req_wrap = new FSReqWrap(env, "write", must_free ? buf : NULL);
 +  int err = uv_fs_write(env->event_loop(),
 +                        &req_wrap->req_,
 +                        fd,
 +                        buf,
 +                        len,
 +                        pos,
 +                        After);
 +  req_wrap->object()->Set(env->oncomplete_string(), cb);
 +  req_wrap->Dispatched();
 +  if (err < 0) {
 +    uv_fs_t* req = &req_wrap->req_;
 +    req->result = err;
 +    req->path = NULL;
 +    After(req);
 +  }
 +
 +  return args.GetReturnValue().Set(req_wrap->persistent());
  }
  
 +
  /*
   * Wrapper for read(2).
   *
@@@ -871,9 -796,10 +871,8 @@@ static void Read(const FunctionCallback
    }
  
    len = args[3]->Int32Value();
-   if (off + len > buffer_length) {
-     return ThrowError("Length extends beyond buffer");
 -  if (!Buffer::IsWithinBounds(off, len, buffer_length)) {
 -    return ThrowException(Exception::Error(
 -          String::New("Length extends beyond buffer")));
--  }
++  if (!Buffer::IsWithinBounds(off, len, buffer_length))
++    return ThrowRangeError("Length extends beyond buffer");
  
    pos = GET_OFFSET(args[4]);
  
Simple merge