tls: destroy SSL once it is out of use
authorFedor Indutny <fedor@indutny.com>
Sun, 26 Apr 2015 12:26:57 +0000 (14:26 +0200)
committerFedor Indutny <fedor@indutny.com>
Thu, 30 Apr 2015 09:01:56 +0000 (11:01 +0200)
Do not keep SSL structure in memory once socket is closed. This should
lower the memory usage in many cases.

Fix: https://github.com/iojs/io.js/issues/1522
PR-URL: https://github.com/iojs/io.js/pull/1529
Reviewed-By: Shigeki Ohtsu <ohtsu@iij.ad.jp>
lib/_tls_wrap.js
src/node_crypto.cc
src/node_crypto.h
src/tls_wrap.cc
src/tls_wrap.h

index c1037a7..84b02a7 100644 (file)
@@ -295,9 +295,15 @@ TLSSocket.prototype._wrapHandle = function(handle) {
     }
   });
 
+  this.on('close', this._destroySSL);
+
   return res;
 };
 
+TLSSocket.prototype._destroySSL = function _destroySSL() {
+  return this.ssl.destroySSL();
+};
+
 TLSSocket.prototype._init = function(socket, wrap) {
   var self = this;
   var options = this._tlsOptions;
@@ -416,6 +422,9 @@ TLSSocket.prototype.renegotiate = function(options, callback) {
   var requestCert = this._requestCert,
       rejectUnauthorized = this._rejectUnauthorized;
 
+  if (this.destroyed)
+    return;
+
   if (typeof options.requestCert !== 'undefined')
     requestCert = !!options.requestCert;
   if (typeof options.rejectUnauthorized !== 'undefined')
index 04b5036..1a7388b 100644 (file)
@@ -131,6 +131,7 @@ template int SSLWrap<TLSWrap>::SelectNextProtoCallback(
     void* arg);
 #endif
 template int SSLWrap<TLSWrap>::TLSExtStatusCallback(SSL* s, void* arg);
+template void SSLWrap<TLSWrap>::DestroySSL();
 
 
 static void crypto_threadid_cb(CRYPTO_THREADID* tid) {
@@ -1871,6 +1872,16 @@ void SSLWrap<Base>::SSLGetter(Local<String> property,
 }
 
 
+template <class Base>
+void SSLWrap<Base>::DestroySSL() {
+  if (ssl_ == nullptr)
+    return;
+
+  SSL_free(ssl_);
+  ssl_ = nullptr;
+}
+
+
 void Connection::OnClientHelloParseEnd(void* arg) {
   Connection* conn = static_cast<Connection*>(arg);
 
index 75ffe4f..8fec4bb 100644 (file)
@@ -144,10 +144,7 @@ class SSLWrap {
   }
 
   virtual ~SSLWrap() {
-    if (ssl_ != nullptr) {
-      SSL_free(ssl_);
-      ssl_ = nullptr;
-    }
+    DestroySSL();
     if (next_sess_ != nullptr) {
       SSL_SESSION_free(next_sess_);
       next_sess_ = nullptr;
@@ -221,6 +218,8 @@ class SSLWrap {
   static void SSLGetter(v8::Local<v8::String> property,
                         const v8::PropertyCallbackInfo<v8::Value>& info);
 
+  void DestroySSL();
+
   inline Environment* ssl_env() const {
     return env_;
   }
index c774a84..c013bf9 100644 (file)
@@ -208,7 +208,7 @@ void TLSWrap::Receive(const FunctionCallbackInfo<Value>& args) {
   uv_buf_t buf;
 
   // Copy given buffer entirely or partiall if handle becomes closed
-  while (len > 0 && !wrap->IsClosing()) {
+  while (len > 0 && wrap->IsAlive() && !wrap->IsClosing()) {
     wrap->stream_->OnAlloc(len, &buf);
     size_t copy = buf.len > len ? len : buf.len;
     memcpy(buf.base, data, copy);
@@ -282,6 +282,9 @@ void TLSWrap::EncOut() {
   if (established_ && !write_item_queue_.IsEmpty())
     MakePending();
 
+  if (ssl_ == nullptr)
+    return;
+
   // No data to write
   if (BIO_pending(enc_out_) == 0) {
     if (clear_in_->Length() == 0)
@@ -396,7 +399,8 @@ void TLSWrap::ClearOut() {
   if (eof_)
     return;
 
-  CHECK_NE(ssl_, nullptr);
+  if (ssl_ == nullptr)
+    return;
 
   char out[kClearOutChunkSize];
   int read;
@@ -451,6 +455,9 @@ bool TLSWrap::ClearIn() {
   if (!hello_parser_.IsEnded())
     return false;
 
+  if (ssl_ == nullptr)
+    return false;
+
   int written = 0;
   while (clear_in_->Length() > 0) {
     size_t avail = 0;
@@ -503,7 +510,7 @@ int TLSWrap::GetFD() {
 
 
 bool TLSWrap::IsAlive() {
-  return stream_->IsAlive();
+  return ssl_ != nullptr && stream_->IsAlive();
 }
 
 
@@ -573,6 +580,9 @@ int TLSWrap::DoWrite(WriteWrap* w,
     return 0;
   }
 
+  if (ssl_ == nullptr)
+    return UV_EPROTO;
+
   int written = 0;
   for (i = 0; i < count; i++) {
     written = SSL_write(ssl_, bufs[i].base, bufs[i].len);
@@ -660,7 +670,10 @@ void TLSWrap::DoRead(ssize_t nread,
   }
 
   // Only client connections can receive data
-  CHECK_NE(ssl_, nullptr);
+  if (ssl_ == nullptr) {
+    OnRead(UV_EPROTO, nullptr);
+    return;
+  }
 
   // Commit read data
   NodeBIO* enc_in = NodeBIO::FromBIO(enc_in_);
@@ -680,7 +693,7 @@ void TLSWrap::DoRead(ssize_t nread,
 
 
 int TLSWrap::DoShutdown(ShutdownWrap* req_wrap) {
-  if (SSL_shutdown(ssl_) == 0)
+  if (ssl_ != nullptr && SSL_shutdown(ssl_) == 0)
     SSL_shutdown(ssl_);
   shutdown_ = true;
   EncOut();
@@ -696,6 +709,9 @@ void TLSWrap::SetVerifyMode(const FunctionCallbackInfo<Value>& args) {
   if (args.Length() < 2 || !args[0]->IsBoolean() || !args[1]->IsBoolean())
     return env->ThrowTypeError("Bad arguments, expected two booleans");
 
+  if (wrap->ssl_ == nullptr)
+    return env->ThrowTypeError("SetVerifyMode after destroySSL");
+
   int verify_mode;
   if (wrap->is_server()) {
     bool request_cert = args[0]->IsTrue();
@@ -735,6 +751,14 @@ void TLSWrap::EnableHelloParser(const FunctionCallbackInfo<Value>& args) {
 }
 
 
+void TLSWrap::DestroySSL(const FunctionCallbackInfo<Value>& args) {
+  TLSWrap* wrap = Unwrap<TLSWrap>(args.Holder());
+  wrap->SSLWrap<TLSWrap>::DestroySSL();
+  delete wrap->clear_in_;
+  wrap->clear_in_ = nullptr;
+}
+
+
 void TLSWrap::OnClientHelloParseEnd(void* arg) {
   TLSWrap* c = static_cast<TLSWrap*>(arg);
   c->Cycle();
@@ -747,6 +771,8 @@ void TLSWrap::GetServername(const FunctionCallbackInfo<Value>& args) {
 
   TLSWrap* wrap = Unwrap<TLSWrap>(args.Holder());
 
+  CHECK_NE(wrap->ssl_, nullptr);
+
   const char* servername = SSL_get_servername(wrap->ssl_,
                                               TLSEXT_NAMETYPE_host_name);
   if (servername != nullptr) {
@@ -771,6 +797,8 @@ void TLSWrap::SetServername(const FunctionCallbackInfo<Value>& args) {
   if (!wrap->is_client())
     return;
 
+  CHECK_NE(wrap->ssl_, nullptr);
+
 #ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
   node::Utf8Value servername(env->isolate(), args[0].As<String>());
   SSL_set_tlsext_host_name(wrap->ssl_, *servername);
@@ -830,6 +858,7 @@ void TLSWrap::Initialize(Handle<Object> target,
   env->SetProtoMethod(t, "setVerifyMode", SetVerifyMode);
   env->SetProtoMethod(t, "enableSessionCallbacks", EnableSessionCallbacks);
   env->SetProtoMethod(t, "enableHelloParser", EnableHelloParser);
+  env->SetProtoMethod(t, "destroySSL", DestroySSL);
 
   StreamBase::AddMethods<TLSWrap>(env, t, StreamBase::kFlagHasWritev);
   SSLWrap<TLSWrap>::AddMethods(env, t);
index 9f09535..25088d3 100644 (file)
@@ -132,6 +132,7 @@ class TLSWrap : public crypto::SSLWrap<TLSWrap>,
       const v8::FunctionCallbackInfo<v8::Value>& args);
   static void EnableHelloParser(
       const v8::FunctionCallbackInfo<v8::Value>& args);
+  static void DestroySSL(const v8::FunctionCallbackInfo<v8::Value>& args);
 
 #ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
   static void GetServername(const v8::FunctionCallbackInfo<v8::Value>& args);