Skip to content

Commit 7ada680

Browse files
committed
tls: destroy SSL once it is out of use
Do not keep SSL structure in memory once socket is closed. This should lower the memory usage in many cases. Fix: #1522 PR-URL: #1529 Reviewed-By: Shigeki Ohtsu <ohtsu@iij.ad.jp>
1 parent b3a7da1 commit 7ada680

File tree

5 files changed

+58
-9
lines changed

5 files changed

+58
-9
lines changed

lib/_tls_wrap.js

+9
Original file line numberDiff line numberDiff line change
@@ -295,9 +295,15 @@ TLSSocket.prototype._wrapHandle = function(handle) {
295295
}
296296
});
297297

298+
this.on('close', this._destroySSL);
299+
298300
return res;
299301
};
300302

303+
TLSSocket.prototype._destroySSL = function _destroySSL() {
304+
return this.ssl.destroySSL();
305+
};
306+
301307
TLSSocket.prototype._init = function(socket, wrap) {
302308
var self = this;
303309
var options = this._tlsOptions;
@@ -416,6 +422,9 @@ TLSSocket.prototype.renegotiate = function(options, callback) {
416422
var requestCert = this._requestCert,
417423
rejectUnauthorized = this._rejectUnauthorized;
418424

425+
if (this.destroyed)
426+
return;
427+
419428
if (typeof options.requestCert !== 'undefined')
420429
requestCert = !!options.requestCert;
421430
if (typeof options.rejectUnauthorized !== 'undefined')

src/node_crypto.cc

+11
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ template int SSLWrap<TLSWrap>::SelectNextProtoCallback(
131131
void* arg);
132132
#endif
133133
template int SSLWrap<TLSWrap>::TLSExtStatusCallback(SSL* s, void* arg);
134+
template void SSLWrap<TLSWrap>::DestroySSL();
134135

135136

136137
static void crypto_threadid_cb(CRYPTO_THREADID* tid) {
@@ -1871,6 +1872,16 @@ void SSLWrap<Base>::SSLGetter(Local<String> property,
18711872
}
18721873

18731874

1875+
template <class Base>
1876+
void SSLWrap<Base>::DestroySSL() {
1877+
if (ssl_ == nullptr)
1878+
return;
1879+
1880+
SSL_free(ssl_);
1881+
ssl_ = nullptr;
1882+
}
1883+
1884+
18741885
void Connection::OnClientHelloParseEnd(void* arg) {
18751886
Connection* conn = static_cast<Connection*>(arg);
18761887

src/node_crypto.h

+3-4
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,7 @@ class SSLWrap {
144144
}
145145

146146
virtual ~SSLWrap() {
147-
if (ssl_ != nullptr) {
148-
SSL_free(ssl_);
149-
ssl_ = nullptr;
150-
}
147+
DestroySSL();
151148
if (next_sess_ != nullptr) {
152149
SSL_SESSION_free(next_sess_);
153150
next_sess_ = nullptr;
@@ -221,6 +218,8 @@ class SSLWrap {
221218
static void SSLGetter(v8::Local<v8::String> property,
222219
const v8::PropertyCallbackInfo<v8::Value>& info);
223220

221+
void DestroySSL();
222+
224223
inline Environment* ssl_env() const {
225224
return env_;
226225
}

src/tls_wrap.cc

+34-5
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ void TLSWrap::Receive(const FunctionCallbackInfo<Value>& args) {
208208
uv_buf_t buf;
209209

210210
// Copy given buffer entirely or partiall if handle becomes closed
211-
while (len > 0 && !wrap->IsClosing()) {
211+
while (len > 0 && wrap->IsAlive() && !wrap->IsClosing()) {
212212
wrap->stream_->OnAlloc(len, &buf);
213213
size_t copy = buf.len > len ? len : buf.len;
214214
memcpy(buf.base, data, copy);
@@ -282,6 +282,9 @@ void TLSWrap::EncOut() {
282282
if (established_ && !write_item_queue_.IsEmpty())
283283
MakePending();
284284

285+
if (ssl_ == nullptr)
286+
return;
287+
285288
// No data to write
286289
if (BIO_pending(enc_out_) == 0) {
287290
if (clear_in_->Length() == 0)
@@ -396,7 +399,8 @@ void TLSWrap::ClearOut() {
396399
if (eof_)
397400
return;
398401

399-
CHECK_NE(ssl_, nullptr);
402+
if (ssl_ == nullptr)
403+
return;
400404

401405
char out[kClearOutChunkSize];
402406
int read;
@@ -451,6 +455,9 @@ bool TLSWrap::ClearIn() {
451455
if (!hello_parser_.IsEnded())
452456
return false;
453457

458+
if (ssl_ == nullptr)
459+
return false;
460+
454461
int written = 0;
455462
while (clear_in_->Length() > 0) {
456463
size_t avail = 0;
@@ -503,7 +510,7 @@ int TLSWrap::GetFD() {
503510

504511

505512
bool TLSWrap::IsAlive() {
506-
return stream_->IsAlive();
513+
return ssl_ != nullptr && stream_->IsAlive();
507514
}
508515

509516

@@ -573,6 +580,9 @@ int TLSWrap::DoWrite(WriteWrap* w,
573580
return 0;
574581
}
575582

583+
if (ssl_ == nullptr)
584+
return UV_EPROTO;
585+
576586
int written = 0;
577587
for (i = 0; i < count; i++) {
578588
written = SSL_write(ssl_, bufs[i].base, bufs[i].len);
@@ -660,7 +670,10 @@ void TLSWrap::DoRead(ssize_t nread,
660670
}
661671

662672
// Only client connections can receive data
663-
CHECK_NE(ssl_, nullptr);
673+
if (ssl_ == nullptr) {
674+
OnRead(UV_EPROTO, nullptr);
675+
return;
676+
}
664677

665678
// Commit read data
666679
NodeBIO* enc_in = NodeBIO::FromBIO(enc_in_);
@@ -680,7 +693,7 @@ void TLSWrap::DoRead(ssize_t nread,
680693

681694

682695
int TLSWrap::DoShutdown(ShutdownWrap* req_wrap) {
683-
if (SSL_shutdown(ssl_) == 0)
696+
if (ssl_ != nullptr && SSL_shutdown(ssl_) == 0)
684697
SSL_shutdown(ssl_);
685698
shutdown_ = true;
686699
EncOut();
@@ -696,6 +709,9 @@ void TLSWrap::SetVerifyMode(const FunctionCallbackInfo<Value>& args) {
696709
if (args.Length() < 2 || !args[0]->IsBoolean() || !args[1]->IsBoolean())
697710
return env->ThrowTypeError("Bad arguments, expected two booleans");
698711

712+
if (wrap->ssl_ == nullptr)
713+
return env->ThrowTypeError("SetVerifyMode after destroySSL");
714+
699715
int verify_mode;
700716
if (wrap->is_server()) {
701717
bool request_cert = args[0]->IsTrue();
@@ -735,6 +751,14 @@ void TLSWrap::EnableHelloParser(const FunctionCallbackInfo<Value>& args) {
735751
}
736752

737753

754+
void TLSWrap::DestroySSL(const FunctionCallbackInfo<Value>& args) {
755+
TLSWrap* wrap = Unwrap<TLSWrap>(args.Holder());
756+
wrap->SSLWrap<TLSWrap>::DestroySSL();
757+
delete wrap->clear_in_;
758+
wrap->clear_in_ = nullptr;
759+
}
760+
761+
738762
void TLSWrap::OnClientHelloParseEnd(void* arg) {
739763
TLSWrap* c = static_cast<TLSWrap*>(arg);
740764
c->Cycle();
@@ -747,6 +771,8 @@ void TLSWrap::GetServername(const FunctionCallbackInfo<Value>& args) {
747771

748772
TLSWrap* wrap = Unwrap<TLSWrap>(args.Holder());
749773

774+
CHECK_NE(wrap->ssl_, nullptr);
775+
750776
const char* servername = SSL_get_servername(wrap->ssl_,
751777
TLSEXT_NAMETYPE_host_name);
752778
if (servername != nullptr) {
@@ -771,6 +797,8 @@ void TLSWrap::SetServername(const FunctionCallbackInfo<Value>& args) {
771797
if (!wrap->is_client())
772798
return;
773799

800+
CHECK_NE(wrap->ssl_, nullptr);
801+
774802
#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
775803
node::Utf8Value servername(env->isolate(), args[0].As<String>());
776804
SSL_set_tlsext_host_name(wrap->ssl_, *servername);
@@ -830,6 +858,7 @@ void TLSWrap::Initialize(Handle<Object> target,
830858
env->SetProtoMethod(t, "setVerifyMode", SetVerifyMode);
831859
env->SetProtoMethod(t, "enableSessionCallbacks", EnableSessionCallbacks);
832860
env->SetProtoMethod(t, "enableHelloParser", EnableHelloParser);
861+
env->SetProtoMethod(t, "destroySSL", DestroySSL);
833862

834863
StreamBase::AddMethods<TLSWrap>(env, t, StreamBase::kFlagHasWritev);
835864
SSLWrap<TLSWrap>::AddMethods(env, t);

src/tls_wrap.h

+1
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ class TLSWrap : public crypto::SSLWrap<TLSWrap>,
132132
const v8::FunctionCallbackInfo<v8::Value>& args);
133133
static void EnableHelloParser(
134134
const v8::FunctionCallbackInfo<v8::Value>& args);
135+
static void DestroySSL(const v8::FunctionCallbackInfo<v8::Value>& args);
135136

136137
#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
137138
static void GetServername(const v8::FunctionCallbackInfo<v8::Value>& args);

0 commit comments

Comments
 (0)