tls_wrap: reach error reporting for UV_EPROTO
authorFedor Indutny <fedor@indutny.com>
Tue, 26 Jan 2016 20:01:12 +0000 (15:01 -0500)
committerMyles Borins <mborins@us.ibm.com>
Wed, 2 Mar 2016 22:01:11 +0000 (14:01 -0800)
Do not swallow error details when reporting UV_EPROTO asynchronously,
and when creating artificial errors.

Fix: #3692
PR-URL: https://github.com/nodejs/node/pull/4885
Reviewed-By: Shigeki Ohtsu <ohtsu@iij.ad.jp>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
lib/net.js
src/stream_base.h
src/tls_wrap.cc
src/tls_wrap.h
test/parallel/test-tls-junk-server.js [new file with mode: 0644]

index ea19201..be3dc14 100644 (file)
@@ -762,7 +762,7 @@ function afterWrite(status, handle, req, err) {
   }
 
   if (status < 0) {
-    var ex = exceptionWithHostPort(status, 'write', req.address, req.port);
+    var ex = errnoException(status, 'write', req.error);
     debug('write failure', ex);
     self._destroy(ex, req.cb);
     return;
index d0e1d4b..fad2ddd 100644 (file)
@@ -22,8 +22,15 @@ class StreamReq {
   explicit StreamReq(DoneCb cb) : cb_(cb) {
   }
 
-  inline void Done(int status) {
-    cb_(static_cast<Req*>(this), status);
+  inline void Done(int status, const char* error_str = nullptr) {
+    Req* req = static_cast<Req*>(this);
+    Environment* env = req->env();
+    if (error_str != nullptr) {
+      req->object()->Set(env->error_string(),
+                         OneByteString(env->isolate(), error_str));
+    }
+
+    cb_(req, status);
   }
 
  private:
index d7bf4ed..85730b3 100644 (file)
@@ -92,7 +92,7 @@ void TLSWrap::MakePending() {
 }
 
 
-bool TLSWrap::InvokeQueued(int status) {
+bool TLSWrap::InvokeQueued(int status, const char* error_str) {
   if (pending_write_items_.IsEmpty())
     return false;
 
@@ -100,7 +100,7 @@ bool TLSWrap::InvokeQueued(int status) {
   WriteItemList queue;
   pending_write_items_.MoveBack(&queue);
   while (WriteItem* wi = queue.PopFront()) {
-    wi->w_->Done(status);
+    wi->w_->Done(status, error_str);
     delete wi;
   }
 
@@ -484,11 +484,12 @@ bool TLSWrap::ClearIn() {
 
   // Error or partial write
   int err;
-  Local<Value> arg = GetSSLError(written, &err, &error_);
+  const char* error_str = nullptr;
+  Local<Value> arg = GetSSLError(written, &err, &error_str);
   if (!arg.IsEmpty()) {
     MakePending();
-    if (!InvokeQueued(UV_EPROTO))
-      ClearError();
+    InvokeQueued(UV_EPROTO, error_str);
+    delete[] error_str;
     clear_in_->Reset();
   }
 
@@ -589,8 +590,15 @@ int TLSWrap::DoWrite(WriteWrap* w,
     return 0;
   }
 
-  if (ssl_ == nullptr)
+  if (ssl_ == nullptr) {
+    ClearError();
+
+    static char msg[] = "Write after DestroySSL";
+    char* tmp = new char[sizeof(msg)];
+    memcpy(tmp, msg, sizeof(msg));
+    error_ = tmp;
     return UV_EPROTO;
+  }
 
   crypto::MarkPopErrorOnReturn mark_pop_error_on_return;
 
@@ -775,7 +783,7 @@ void TLSWrap::DestroySSL(const FunctionCallbackInfo<Value>& args) {
   wrap->MakePending();
 
   // And destroy
-  wrap->InvokeQueued(UV_ECANCELED);
+  wrap->InvokeQueued(UV_ECANCELED, "Canceled because of SSL destruction");
 
   // Destroy the SSL structure and friends
   wrap->SSLWrap<TLSWrap>::DestroySSL();
index 31d1952..471a920 100644 (file)
@@ -89,7 +89,7 @@ class TLSWrap : public AsyncWrap,
   bool ClearIn();
   void ClearOut();
   void MakePending();
-  bool InvokeQueued(int status);
+  bool InvokeQueued(int status, const char* error_str = nullptr);
 
   inline void Cycle() {
     // Prevent recursion
diff --git a/test/parallel/test-tls-junk-server.js b/test/parallel/test-tls-junk-server.js
new file mode 100644 (file)
index 0000000..4eb8129
--- /dev/null
@@ -0,0 +1,29 @@
+'use strict';
+const common = require('../common');
+
+if (!common.hasCrypto) {
+  console.log('1..0 # Skipped: missing crypto');
+  return;
+}
+
+const assert = require('assert');
+const https = require('https');
+const net = require('net');
+
+const server = net.createServer(function(s) {
+  s.once('data', function() {
+    s.end('I was waiting for you, hello!', function() {
+      s.destroy();
+    });
+  });
+});
+
+server.listen(common.PORT, function() {
+  const req = https.request({ port: common.PORT });
+  req.end();
+
+  req.once('error', common.mustCall(function(err) {
+    assert(/unknown protocol/.test(err.message));
+    server.close();
+  }));
+});