Skip to content

Commit

Permalink
tls_wrap: propagate errors to write callbacks
Browse files Browse the repository at this point in the history
  • Loading branch information
indutny committed Jan 24, 2014
1 parent d019eac commit 640912d
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 53 deletions.
6 changes: 3 additions & 3 deletions lib/net.js
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,7 @@ Socket.prototype._writeGeneric = function(writev, data, encoding, cb) {
}

if (err)
return this._destroy(errnoException(err, 'write'), cb);
return this._destroy(errnoException(err, 'write', req.error), cb);

this._bytesDispatched += req.bytes;

Expand Down Expand Up @@ -745,7 +745,7 @@ Socket.prototype.__defineGetter__('bytesWritten', function() {
});


function afterWrite(status, handle, req) {
function afterWrite(status, handle, req, err) {
var self = handle.owner;
if (self !== process.stderr && self !== process.stdout)
debug('afterWrite', status);
Expand All @@ -757,7 +757,7 @@ function afterWrite(status, handle, req) {
}

if (status < 0) {
var ex = errnoException(status, 'write');
var ex = errnoException(status, 'write', err);
debug('write failure', ex);
self._destroy(ex, req.cb);
return;
Expand Down
7 changes: 5 additions & 2 deletions lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -677,10 +677,13 @@ exports.pump = exports.deprecate(function(readStream, writeStream, callback) {


var uv;
exports._errnoException = function(err, syscall) {
exports._errnoException = function(err, syscall, original) {
if (isUndefined(uv)) uv = process.binding('uv');
var errname = uv.errname(err);
var e = new Error(syscall + ' ' + errname);
var message = syscall + ' ' + errname;
if (original)
message += ' ' + original;
var e = new Error(message);
e.code = errname;
e.errno = errname;
e.syscall = syscall;
Expand Down
1 change: 1 addition & 0 deletions src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ namespace node {
V(domain_string, "domain") \
V(enter_string, "enter") \
V(errno_string, "errno") \
V(error_string, "error") \
V(exit_string, "exit") \
V(exponent_string, "exponent") \
V(exports_string, "exports") \
Expand Down
23 changes: 21 additions & 2 deletions src/stream_wrap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,9 @@ void StreamWrap::WriteBuffer(const FunctionCallbackInfo<Value>& args) {
req_wrap->Dispatched();
req_wrap_obj->Set(env->bytes_string(),
Integer::NewFromUnsigned(length, node_isolate));
const char* msg = wrap->callbacks()->Error();
if (msg != NULL)
req_wrap_obj->Set(env->error_string(), OneByteString(env->isolate(), msg));

if (err) {
req_wrap->~WriteWrap();
Expand Down Expand Up @@ -300,6 +303,9 @@ void StreamWrap::WriteStringImpl(const FunctionCallbackInfo<Value>& args) {
req_wrap->Dispatched();
req_wrap->object()->Set(env->bytes_string(),
Integer::NewFromUnsigned(data_size, node_isolate));
const char* msg = wrap->callbacks()->Error();
if (msg != NULL)
req_wrap_obj->Set(env->error_string(), OneByteString(env->isolate(), msg));

if (err) {
req_wrap->~WriteWrap();
Expand Down Expand Up @@ -401,6 +407,9 @@ void StreamWrap::Writev(const FunctionCallbackInfo<Value>& args) {
req_wrap->Dispatched();
req_wrap->object()->Set(env->bytes_string(),
Number::New(node_isolate, bytes));
const char* msg = wrap->callbacks()->Error();
if (msg != NULL)
req_wrap_obj->Set(env->error_string(), OneByteString(env->isolate(), msg));

if (err) {
req_wrap->~WriteWrap();
Expand Down Expand Up @@ -441,14 +450,19 @@ void StreamWrap::AfterWrite(uv_write_t* req, int status) {
// Unref handle property
Local<Object> req_wrap_obj = req_wrap->object();
req_wrap_obj->Delete(env->handle_string());
wrap->callbacks_->AfterWrite(req_wrap);
wrap->callbacks()->AfterWrite(req_wrap);

Local<Value> argv[] = {
Integer::New(status, node_isolate),
wrap->object(),
req_wrap_obj
req_wrap_obj,
Undefined()
};

const char* msg = wrap->callbacks()->Error();
if (msg != NULL)
argv[3] = OneByteString(env->isolate(), msg);

req_wrap->MakeCallback(env->oncomplete_string(), ARRAY_SIZE(argv), argv);

req_wrap->~WriteWrap();
Expand Down Expand Up @@ -499,6 +513,11 @@ void StreamWrap::AfterShutdown(uv_shutdown_t* req, int status) {
}


const char* StreamWrapCallbacks::Error() {
return NULL;
}


int StreamWrapCallbacks::DoWrite(WriteWrap* w,
uv_buf_t* bufs,
size_t count,
Expand Down
1 change: 1 addition & 0 deletions src/stream_wrap.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class StreamWrapCallbacks {
virtual ~StreamWrapCallbacks() {
}

virtual const char* Error();
virtual int DoWrite(WriteWrap* w,
uv_buf_t* bufs,
size_t count,
Expand Down
139 changes: 97 additions & 42 deletions src/tls_wrap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ static const int X509_NAME_FLAGS = ASN1_STRFLGS_ESC_CTRL
| XN_FLAG_FN_SN;


size_t TLSCallbacks::error_off_;
char TLSCallbacks::error_buf_[1024];


TLSCallbacks::TLSCallbacks(Environment* env,
Kind kind,
Handle<Object> sc,
Expand All @@ -71,15 +75,16 @@ TLSCallbacks::TLSCallbacks(Environment* env,
enc_out_(NULL),
clear_in_(NULL),
write_size_(0),
pending_write_item_(NULL),
started_(false),
established_(false),
shutdown_(false),
error_(NULL),
eof_(false) {
node::Wrap<TLSCallbacks>(object(), this);

// Initialize queue for clearIn writes
QUEUE_INIT(&write_item_queue_);
QUEUE_INIT(&pending_write_items_);

// We've our own session callbacks
SSL_CTX_sess_set_get_cb(sc_->ctx_, SSLWrap<TLSCallbacks>::GetSessionCallback);
Expand All @@ -102,25 +107,52 @@ TLSCallbacks::~TLSCallbacks() {
#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
sni_context_.Dispose();
#endif // SSL_CTRL_SET_TLSEXT_SERVERNAME_CB

// Move all writes to pending
MakePending();

// And destroy
while (!QUEUE_EMPTY(&pending_write_items_)) {
QUEUE* q = QUEUE_HEAD(&pending_write_items_);
QUEUE_REMOVE(q);

WriteItem* wi = QUEUE_DATA(q, WriteItem, member_);
delete wi;
}
}


void TLSCallbacks::InvokeQueued(int status) {
// Empty queue - ignore call
if (pending_write_item_ == NULL)
void TLSCallbacks::MakePending() {
// Aliases
QUEUE* from = &write_item_queue_;
QUEUE* to = &pending_write_items_;

if (QUEUE_EMPTY(from))
return;

QUEUE* q = &pending_write_item_->member_;
pending_write_item_ = NULL;
// Add items to pending
QUEUE_ADD(to, from);

// Empty original queue
QUEUE_INIT(from);
}


bool TLSCallbacks::InvokeQueued(int status) {
if (QUEUE_EMPTY(&pending_write_items_))
return false;

// Process old queue
while (q != &write_item_queue_) {
QUEUE* next = static_cast<QUEUE*>(QUEUE_NEXT(q));
WriteItem* wi = CONTAINER_OF(q, WriteItem, member_);
while (!QUEUE_EMPTY(&pending_write_items_)) {
QUEUE* q = QUEUE_HEAD(&pending_write_items_);
QUEUE_REMOVE(q);

WriteItem* wi = QUEUE_DATA(q, WriteItem, member_);
wi->cb_(&wi->w_->req_, status);
delete wi;
q = next;
}

return true;
}


Expand Down Expand Up @@ -276,16 +308,13 @@ void TLSCallbacks::EncOut() {
return;

// Split-off queue
if (established_ && !QUEUE_EMPTY(&write_item_queue_)) {
pending_write_item_ = CONTAINER_OF(QUEUE_NEXT(&write_item_queue_),
WriteItem,
member_);
QUEUE_INIT(&write_item_queue_);
}
if (established_ && !QUEUE_EMPTY(&write_item_queue_))
MakePending();

// No data to write
if (BIO_pending(enc_out_) == 0) {
InvokeQueued(0);
if (clear_in_->Length() == 0)
InvokeQueued(0);
return;
}

Expand Down Expand Up @@ -314,7 +343,6 @@ void TLSCallbacks::EncOut() {

void TLSCallbacks::EncOutCb(uv_write_t* req, int status) {
TLSCallbacks* callbacks = static_cast<TLSCallbacks*>(req->data);
Environment* env = callbacks->env();

// Handle error
if (status) {
Expand All @@ -323,12 +351,6 @@ void TLSCallbacks::EncOutCb(uv_write_t* req, int status) {
return;

// Notify about error
HandleScope handle_scope(env->isolate());
Context::Scope context_scope(env->context());
Local<Value> arg = String::Concat(
FIXED_ONE_BYTE_STRING(node_isolate, "write cb error, status: "),
Integer::New(status, node_isolate)->ToString());
callbacks->MakeCallback(env->onerror_string(), 1, &arg);
callbacks->InvokeQueued(status);
return;
}
Expand All @@ -342,7 +364,33 @@ void TLSCallbacks::EncOutCb(uv_write_t* req, int status) {
}


Local<Value> TLSCallbacks::GetSSLError(int status, int* err) {
int TLSCallbacks::PrintErrorsCb(const char* str, size_t len, void* arg) {
size_t to_copy = error_off_;
size_t avail = sizeof(error_buf_) - error_off_ - 1;

if (avail > to_copy)
to_copy = avail;

memcpy(error_buf_, str, avail);
error_off_ += avail;
assert(error_off_ < sizeof(error_buf_));

// Zero-terminate
error_buf_[error_off_] = '\0';

return 0;
}


const char* TLSCallbacks::PrintErrors() {
error_off_ = 0;
ERR_print_errors_cb(PrintErrorsCb, this);

return error_buf_;
}


Local<Value> TLSCallbacks::GetSSLError(int status, int* err, const char** msg) {
HandleScope scope(node_isolate);

*err = SSL_get_error(ssl_, status);
Expand All @@ -356,19 +404,18 @@ Local<Value> TLSCallbacks::GetSSLError(int status, int* err) {
break;
default:
{
BUF_MEM* mem;
BIO* bio;

assert(*err == SSL_ERROR_SSL || *err == SSL_ERROR_SYSCALL);

bio = BIO_new(BIO_s_mem());
assert(bio != NULL);
ERR_print_errors(bio);
BIO_get_mem_ptr(bio, &mem);
const char* buf = PrintErrors();

Local<String> message =
OneByteString(node_isolate, mem->data, mem->length);
OneByteString(node_isolate, buf, strlen(buf));
Local<Value> exception = Exception::Error(message);
BIO_free_all(bio);

if (msg != NULL) {
assert(*msg == NULL);
*msg = buf;
}

return scope.Close(exception);
}
Expand Down Expand Up @@ -409,7 +456,7 @@ void TLSCallbacks::ClearOut() {

if (read == -1) {
int err;
Handle<Value> arg = GetSSLError(read, &err);
Local<Value> arg = GetSSLError(read, &err, NULL);

if (!arg.IsEmpty()) {
MakeCallback(env()->onerror_string(), 1, &arg);
Expand Down Expand Up @@ -445,15 +492,25 @@ bool TLSCallbacks::ClearIn() {

// Error or partial write
int err;
Handle<Value> arg = GetSSLError(written, &err);
Local<Value> arg = GetSSLError(written, &err, &error_);
if (!arg.IsEmpty()) {
MakeCallback(env()->onerror_string(), 1, &arg);
MakePending();
if (!InvokeQueued(UV_EPROTO))
error_ = NULL;
clear_in_->Reset();
}

return false;
}


const char* TLSCallbacks::Error() {
const char* ret = error_;
error_ = NULL;
return ret;
}


int TLSCallbacks::DoWrite(WriteWrap* w,
uv_buf_t* bufs,
size_t count,
Expand Down Expand Up @@ -508,11 +565,9 @@ int TLSCallbacks::DoWrite(WriteWrap* w,
int err;
HandleScope handle_scope(env()->isolate());
Context::Scope context_scope(env()->context());
Handle<Value> arg = GetSSLError(written, &err);
if (!arg.IsEmpty()) {
MakeCallback(env()->onerror_string(), 1, &arg);
return -1;
}
Local<Value> arg = GetSSLError(written, &err, &error_);
if (!arg.IsEmpty())
return UV_EPROTO;

// No errors, queue rest
for (; i < count; i++)
Expand Down
Loading

0 comments on commit 640912d

Please sign in to comment.