summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/api/tls.markdown5
-rw-r--r--lib/_tls_legacy.js22
-rw-r--r--lib/_tls_wrap.js29
-rw-r--r--src/env.h1
-rw-r--r--src/node_crypto.cc19
-rw-r--r--src/node_crypto.h8
-rw-r--r--src/tls_wrap.cc10
-rw-r--r--src/tls_wrap.h16
-rw-r--r--test/simple/test-tls-session-cache.js16
9 files changed, 111 insertions, 15 deletions
diff --git a/doc/api/tls.markdown b/doc/api/tls.markdown
index 2a4c312b50..372bbd990f 100644
--- a/doc/api/tls.markdown
+++ b/doc/api/tls.markdown
@@ -484,10 +484,11 @@ established - it will be forwarded here.
### Event: 'newSession'
-`function (sessionId, sessionData) { }`
+`function (sessionId, sessionData, callback) { }`
Emitted on creation of TLS session. May be used to store sessions in external
-storage.
+storage. `callback` must be invoked eventually, otherwise no data will be
+sent or received from secure connection.
NOTE: adding this event listener will have an effect only on connections
established after addition of event listener.
diff --git a/lib/_tls_legacy.js b/lib/_tls_legacy.js
index be3c8aff55..6916c1e0d7 100644
--- a/lib/_tls_legacy.js
+++ b/lib/_tls_legacy.js
@@ -653,7 +653,27 @@ function onclienthello(hello) {
function onnewsession(key, session) {
if (!this.server) return;
- this.server.emit('newSession', key, session);
+
+ var self = this;
+ var once = false;
+
+ self.server.emit('newSession', key, session, function() {
+ if (once)
+ return;
+ once = true;
+
+ if (self.ssl)
+ self.ssl.newSessionDone();
+ });
+}
+
+
+function onnewsessiondone() {
+ if (!this.server) return;
+
+ // Cycle through data
+ this.cleartext.read(0);
+ this.encrypted.read(0);
}
diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js
index 3cda50d2d5..c0af83a7ef 100644
--- a/lib/_tls_wrap.js
+++ b/lib/_tls_wrap.js
@@ -138,8 +138,25 @@ function onclienthello(hello) {
function onnewsession(key, session) {
- if (this.server)
- this.server.emit('newSession', key, session);
+ if (!this.server)
+ return;
+
+ var self = this;
+ var once = false;
+
+ this._newSessionPending = true;
+ this.server.emit('newSession', key, session, function() {
+ if (once)
+ return;
+ once = true;
+
+ self.ssl.newSessionDone();
+
+ self._newSessionPending = false;
+ if (self._securePending)
+ self._finishInit();
+ self._securePending = false;
+ });
}
@@ -164,6 +181,8 @@ function TLSSocket(socket, options) {
this._tlsOptions = options;
this._secureEstablished = false;
+ this._securePending = false;
+ this._newSessionPending = false;
this._controlReleased = false;
this._SNICallback = null;
this.ssl = null;
@@ -347,6 +366,12 @@ TLSSocket.prototype._releaseControl = function() {
};
TLSSocket.prototype._finishInit = function() {
+ // `newSession` callback wasn't called yet
+ if (this._newSessionPending) {
+ this._securePending = true;
+ return;
+ }
+
if (process.features.tls_npn) {
this.npnProtocol = this.ssl.getNegotiatedProtocol();
}
diff --git a/src/env.h b/src/env.h
index f1cd4a125f..99f0e2092b 100644
--- a/src/env.h
+++ b/src/env.h
@@ -121,6 +121,7 @@ namespace node {
V(onhandshakestart_string, "onhandshakestart") \
V(onmessage_string, "onmessage") \
V(onnewsession_string, "onnewsession") \
+ V(onnewsessiondone_string, "onnewsessiondone") \
V(onread_string, "onread") \
V(onselect_string, "onselect") \
V(onsignal_string, "onsignal") \
diff --git a/src/node_crypto.cc b/src/node_crypto.cc
index 1d5fd25c08..d0a26a605c 100644
--- a/src/node_crypto.cc
+++ b/src/node_crypto.cc
@@ -857,6 +857,7 @@ void SSLWrap<Base>::AddMethods(Handle<FunctionTemplate> t) {
NODE_SET_PROTOTYPE_METHOD(t, "renegotiate", Renegotiate);
NODE_SET_PROTOTYPE_METHOD(t, "shutdown", Shutdown);
NODE_SET_PROTOTYPE_METHOD(t, "getTLSTicket", GetTLSTicket);
+ NODE_SET_PROTOTYPE_METHOD(t, "newSessionDone", NewSessionDone);
#ifdef SSL_set_max_send_fragment
NODE_SET_PROTOTYPE_METHOD(t, "setMaxSendFragment", SetMaxSendFragment);
@@ -929,6 +930,7 @@ int SSLWrap<Base>::NewSessionCallback(SSL* s, SSL_SESSION* sess) {
reinterpret_cast<char*>(sess->session_id),
sess->session_id_length);
Local<Value> argv[] = { session, buff };
+ w->new_session_wait_ = true;
w->MakeCallback(env->onnewsession_string(), ARRAY_SIZE(argv), argv);
return 0;
@@ -1267,6 +1269,16 @@ void SSLWrap<Base>::GetTLSTicket(const FunctionCallbackInfo<Value>& args) {
}
+template <class Base>
+void SSLWrap<Base>::NewSessionDone(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(args.GetIsolate());
+
+ Base* w = Unwrap<Base>(args.This());
+ w->new_session_wait_ = false;
+ w->NewSessionDoneCb();
+}
+
+
#ifdef SSL_set_max_send_fragment
template <class Base>
void SSLWrap<Base>::SetMaxSendFragment(
@@ -1651,6 +1663,13 @@ void Connection::SetShutdownFlags() {
}
+void Connection::NewSessionDoneCb() {
+ HandleScope scope(env()->isolate());
+
+ MakeCallback(env()->onnewsessiondone_string(), 0, NULL);
+}
+
+
void Connection::Initialize(Environment* env, Handle<Object> target) {
Local<FunctionTemplate> t = FunctionTemplate::New(Connection::New);
t->InstanceTemplate()->SetInternalFieldCount(1);
diff --git a/src/node_crypto.h b/src/node_crypto.h
index 889d270c5c..729d4fc7f4 100644
--- a/src/node_crypto.h
+++ b/src/node_crypto.h
@@ -137,7 +137,8 @@ class SSLWrap {
: env_(env),
kind_(kind),
next_sess_(NULL),
- session_callbacks_(false) {
+ session_callbacks_(false),
+ new_session_wait_(false) {
ssl_ = SSL_new(sc->ctx_);
assert(ssl_ != NULL);
}
@@ -162,6 +163,7 @@ class SSLWrap {
inline void enable_session_callbacks() { session_callbacks_ = true; }
inline bool is_server() const { return kind_ == kServer; }
inline bool is_client() const { return kind_ == kClient; }
+ inline bool is_waiting_new_session() const { return new_session_wait_; }
protected:
static void InitNPN(SecureContext* sc, Base* base);
@@ -188,6 +190,7 @@ class SSLWrap {
static void Renegotiate(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Shutdown(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetTLSTicket(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void NewSessionDone(const v8::FunctionCallbackInfo<v8::Value>& args);
#ifdef SSL_set_max_send_fragment
static void SetMaxSendFragment(
@@ -219,6 +222,7 @@ class SSLWrap {
SSL_SESSION* next_sess_;
SSL* ssl_;
bool session_callbacks_;
+ bool new_session_wait_;
ClientHelloParser hello_parser_;
#ifdef OPENSSL_NPN_NEGOTIATED
@@ -291,6 +295,7 @@ class Connection : public SSLWrap<Connection>, public AsyncWrap {
void ClearError();
void SetShutdownFlags();
+ void NewSessionDoneCb();
Connection(Environment* env,
v8::Local<v8::Object> wrap,
@@ -319,6 +324,7 @@ class Connection : public SSLWrap<Connection>, public AsyncWrap {
friend class ClientHelloParser;
friend class SecureContext;
+ friend class SSLWrap<Connection>;
};
class CipherBase : public BaseObject {
diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc
index 4e5b07f7d5..0e63444d7f 100644
--- a/src/tls_wrap.cc
+++ b/src/tls_wrap.cc
@@ -81,6 +81,7 @@ TLSCallbacks::TLSCallbacks(Environment* env,
established_(false),
shutdown_(false),
error_(NULL),
+ cycle_depth_(0),
eof_(false) {
node::Wrap<TLSCallbacks>(object(), this);
@@ -158,6 +159,11 @@ bool TLSCallbacks::InvokeQueued(int status) {
}
+void TLSCallbacks::NewSessionDoneCb() {
+ Cycle();
+}
+
+
void TLSCallbacks::InitSSL() {
// Initialize SSL
enc_in_ = NodeBIO::New();
@@ -309,6 +315,10 @@ void TLSCallbacks::EncOut() {
if (write_size_ != 0)
return;
+ // Wait for `newSession` callback to be invoked
+ if (is_waiting_new_session())
+ return;
+
// Split-off queue
if (established_ && !QUEUE_EMPTY(&write_item_queue_))
MakePending();
diff --git a/src/tls_wrap.h b/src/tls_wrap.h
index 946cc1c64d..646e70108f 100644
--- a/src/tls_wrap.h
+++ b/src/tls_wrap.h
@@ -102,11 +102,18 @@ class TLSCallbacks : public crypto::SSLWrap<TLSCallbacks>,
void ClearOut();
void MakePending();
bool InvokeQueued(int status);
+ void NewSessionDoneCb();
inline void Cycle() {
- ClearIn();
- ClearOut();
- EncOut();
+ // Prevent recursion
+ if (++cycle_depth_ > 1)
+ return;
+
+ for (; cycle_depth_ > 0; cycle_depth_--) {
+ ClearIn();
+ ClearOut();
+ EncOut();
+ }
}
v8::Local<v8::Value> GetSSLError(int status, int* err, const char** msg);
@@ -144,6 +151,7 @@ class TLSCallbacks : public crypto::SSLWrap<TLSCallbacks>,
bool established_;
bool shutdown_;
const char* error_;
+ int cycle_depth_;
// If true - delivered EOF to the js-land, either after `close_notify`, or
// after the `UV_EOF` on socket.
@@ -155,6 +163,8 @@ class TLSCallbacks : public crypto::SSLWrap<TLSCallbacks>,
static size_t error_off_;
static char error_buf_[1024];
+
+ friend class SSLWrap<TLSCallbacks>;
};
} // namespace node
diff --git a/test/simple/test-tls-session-cache.js b/test/simple/test-tls-session-cache.js
index 4619365752..a60ff949c4 100644
--- a/test/simple/test-tls-session-cache.js
+++ b/test/simple/test-tls-session-cache.js
@@ -64,12 +64,16 @@ function doTest(testOptions, callback) {
++requestCount;
cleartext.end();
});
- server.on('newSession', function(id, data) {
- assert.ok(!session);
- session = {
- id: id,
- data: data
- };
+ server.on('newSession', function(id, data, cb) {
+ // Emulate asynchronous store
+ setTimeout(function() {
+ assert.ok(!session);
+ session = {
+ id: id,
+ data: data
+ };
+ cb();
+ }, 1000);
});
server.on('resumeSession', function(id, callback) {
++resumeCount;