Browse Source

tls: improve TLSSocket & Server performance

Limit the number of closures created and functions called
throughout the lifecycle of TLSSocket, Server & connect.

PR-URL: https://github.com/nodejs/node/pull/15575
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Fedor Indutny <fedor.indutny@gmail.com>
v9.x-staging
Anatoli Papirovski 7 years ago
committed by Ruben Bridgewater
parent
commit
5723b5dbbc
No known key found for this signature in database GPG Key ID: F07496B3EB3C1762
  1. 655
      lib/_tls_wrap.js

655
lib/_tls_wrap.js

@ -37,211 +37,228 @@ const tls_wrap = process.binding('tls_wrap');
const TCP = process.binding('tcp_wrap').TCP;
const Pipe = process.binding('pipe_wrap').Pipe;
const errors = require('internal/errors');
const kConnectOptions = Symbol('connect-options');
const kDisableRenegotiation = Symbol('disable-renegotiation');
const kErrorEmitted = Symbol('error-emitted');
const kHandshakeTimeout = Symbol('handshake-timeout');
const kRes = Symbol('res');
const kSNICallback = Symbol('snicallback');
const noop = () => {};
function onhandshakestart() {
debug('onhandshakestart');
var self = this;
var ssl = self._handle;
var now = Timer.now();
const owner = this.owner;
const now = Timer.now();
assert(now >= ssl.lastHandshakeTime);
assert(now >= this.lastHandshakeTime);
if ((now - ssl.lastHandshakeTime) >= tls.CLIENT_RENEG_WINDOW * 1000) {
ssl.handshakes = 0;
if ((now - this.lastHandshakeTime) >= tls.CLIENT_RENEG_WINDOW * 1000) {
this.handshakes = 0;
}
var first = (ssl.lastHandshakeTime === 0);
ssl.lastHandshakeTime = now;
const first = (this.lastHandshakeTime === 0);
this.lastHandshakeTime = now;
if (first) return;
if (++ssl.handshakes > tls.CLIENT_RENEG_LIMIT) {
if (++this.handshakes > tls.CLIENT_RENEG_LIMIT) {
// Defer the error event to the next tick. We're being called from OpenSSL's
// state machine and OpenSSL is not re-entrant. We cannot allow the user's
// callback to destroy the connection right now, it would crash and burn.
setImmediate(function() {
var err = new errors.Error('ERR_TLS_SESSION_ATTACK');
self._emitTLSError(err);
});
setImmediate(emitSessionAttackError, owner);
}
if (this[kDisableRenegotiation] && ssl.handshakes > 0) {
if (owner[kDisableRenegotiation] && this.handshakes > 0) {
const err = new Error('TLS session renegotiation disabled for this socket');
self._emitTLSError(err);
owner._emitTLSError(err);
}
}
function emitSessionAttackError(socket) {
socket._emitTLSError(new errors.Error('ERR_TLS_SESSION_ATTACK'));
}
function onhandshakedone() {
// for future use
debug('onhandshakedone');
this._finishInit();
const owner = this.owner;
// `newSession` callback wasn't called yet
if (owner._newSessionPending) {
owner._securePending = true;
return;
}
owner._finishInit();
}
function loadSession(self, hello, cb) {
function loadSession(hello) {
const owner = this.owner;
var once = false;
function onSession(err, session) {
if (once)
return cb(new errors.Error('ERR_MULTIPLE_CALLBACK'));
return owner.destroy(new errors.Error('ERR_MULTIPLE_CALLBACK'));
once = true;
if (err)
return cb(err);
return owner.destroy(err);
if (!self._handle)
return cb(new errors.Error('ERR_SOCKET_CLOSED'));
if (owner._handle === null)
return owner.destroy(new errors.Error('ERR_SOCKET_CLOSED'));
self._handle.loadSession(session);
cb(null);
owner._handle.loadSession(session);
owner._handle.endParser();
}
if (hello.sessionId.length <= 0 ||
hello.tlsTicket ||
self.server &&
!self.server.emit('resumeSession', hello.sessionId, onSession)) {
cb(null);
owner.server &&
!owner.server.emit('resumeSession', hello.sessionId, onSession)) {
owner._handle.endParser();
}
}
function loadSNI(self, servername, cb) {
if (!servername || !self._SNICallback)
return cb(null);
function loadSNI(info) {
const owner = this.owner;
const servername = info.servername;
if (!servername || !owner._SNICallback)
return requestOCSP(owner, info);
var once = false;
self._SNICallback(servername, function(err, context) {
let once = false;
owner._SNICallback(servername, (err, context) => {
if (once)
return cb(new errors.Error('ERR_MULTIPLE_CALLBACK'));
return owner.destroy(new errors.Error('ERR_MULTIPLE_CALLBACK'));
once = true;
if (err)
return cb(err);
return owner.destroy(err);
if (!self._handle)
return cb(new errors.Error('ERR_SOCKET_CLOSED'));
if (owner._handle === null)
return owner.destroy(new errors.Error('ERR_SOCKET_CLOSED'));
// TODO(indutny): eventually disallow raw `SecureContext`
if (context)
self._handle.sni_context = context.context || context;
owner._handle.sni_context = context.context || context;
cb(null, self._handle.sni_context);
requestOCSP(owner, info);
});
}
function requestOCSP(self, hello, ctx, cb) {
if (!hello.OCSPRequest || !self.server)
return cb(null);
function requestOCSP(socket, info) {
if (!info.OCSPRequest || !socket.server)
return requestOCSPDone(socket);
if (!ctx)
ctx = self.server._sharedCreds;
let ctx = socket._handle.sni_context;
// TLS socket is using a `net.Server` instead of a tls.TLSServer.
// Some TLS properties like `server._sharedCreds` will not be present
if (!ctx)
return cb(null);
if (!ctx) {
ctx = socket.server._sharedCreds;
// TLS socket is using a `net.Server` instead of a tls.TLSServer.
// Some TLS properties like `server._sharedCreds` will not be present
if (!ctx)
return requestOCSPDone(socket);
}
// TODO(indutny): eventually disallow raw `SecureContext`
if (ctx.context)
ctx = ctx.context;
if (self.server.listenerCount('OCSPRequest') === 0) {
return cb(null);
} else {
self.server.emit('OCSPRequest',
ctx.getCertificate(),
ctx.getIssuer(),
onOCSP);
if (socket.server.listenerCount('OCSPRequest') === 0) {
return requestOCSPDone(socket);
}
var once = false;
function onOCSP(err, response) {
let once = false;
const onOCSP = (err, response) => {
if (once)
return cb(new errors.Error('ERR_MULTIPLE_CALLBACK'));
return socket.destroy(new errors.Error('ERR_MULTIPLE_CALLBACK'));
once = true;
if (err)
return cb(err);
return socket.destroy(err);
if (!self._handle)
return cb(new errors.Error('ERR_SOCKET_CLOSED'));
if (socket._handle === null)
return socket.destroy(new errors.Error('ERR_SOCKET_CLOSED'));
if (response)
self._handle.setOCSPResponse(response);
cb(null);
}
}
function onclienthello(hello) {
var self = this;
loadSession(self, hello, function(err) {
if (err)
return self.destroy(err);
socket._handle.setOCSPResponse(response);
requestOCSPDone(socket);
};
self._handle.endParser();
});
socket.server.emit('OCSPRequest',
ctx.getCertificate(),
ctx.getIssuer(),
onOCSP);
}
function oncertcb(info) {
var self = this;
var servername = info.servername;
loadSNI(self, servername, function(err, ctx) {
if (err)
return self.destroy(err);
requestOCSP(self, info, ctx, function(err) {
if (err)
return self.destroy(err);
if (!self._handle)
return self.destroy(new errors.Error('ERR_SOCKET_CLOSED'));
try {
self._handle.certCbDone();
} catch (e) {
self.destroy(e);
}
});
});
function requestOCSPDone(socket) {
try {
socket._handle.certCbDone();
} catch (e) {
socket.destroy(e);
}
}
function onnewsession(key, session) {
if (!this.server)
const owner = this.owner;
if (!owner.server)
return;
var self = this;
var once = false;
this._newSessionPending = true;
if (!this.server.emit('newSession', key, session, done))
done();
function done() {
const done = () => {
if (once)
return;
once = true;
if (!self._handle)
return self.destroy(new errors.Error('ERR_SOCKET_CLOSED'));
if (owner._handle === null)
return owner.destroy(new errors.Error('ERR_SOCKET_CLOSED'));
self._handle.newSessionDone();
this.newSessionDone();
self._newSessionPending = false;
if (self._securePending)
self._finishInit();
self._securePending = false;
}
owner._newSessionPending = false;
if (owner._securePending)
owner._finishInit();
owner._securePending = false;
};
owner._newSessionPending = true;
if (!owner.server.emit('newSession', key, session, done))
done();
}
function onocspresponse(resp) {
this.emit('OCSPResponse', resp);
this.owner.emit('OCSPResponse', resp);
}
function onerror(err) {
const owner = this.owner;
if (owner._writableState.errorEmitted)
return;
// Destroy socket if error happened before handshake's finish
if (!owner._secureEstablished) {
// When handshake fails control is not yet released,
// so self._tlsError will return null instead of actual error
owner.destroy(err);
} else if (owner._tlsOptions.isServer &&
owner._rejectUnauthorized &&
/peer did not return a certificate/.test(err.message)) {
// Ignore server's authorization errors
owner.destroy();
} else {
// Throw error
owner._emitTLSError(err);
}
owner._writableState.errorEmitted = true;
}
function initRead(tls, wrapped) {
@ -278,6 +295,7 @@ function TLSSocket(socket, options) {
this.alpnProtocol = null;
this.authorized = false;
this.authorizationError = null;
this[kRes] = null;
// Wrap plain JS Stream into StreamWrap
var wrap;
@ -369,7 +387,6 @@ TLSSocket.prototype.disableRenegotiation = function disableRenegotiation() {
};
TLSSocket.prototype._wrapHandle = function(wrap) {
var res;
var handle;
if (wrap)
@ -382,33 +399,42 @@ TLSSocket.prototype._wrapHandle = function(wrap) {
}
// Wrap socket's handle
var context = options.secureContext ||
options.credentials ||
tls.createSecureContext(options);
res = tls_wrap.wrap(handle._externalStream,
context.context,
!!options.isServer);
const context = options.secureContext ||
options.credentials ||
tls.createSecureContext(options);
const res = tls_wrap.wrap(handle._externalStream,
context.context,
!!options.isServer);
res._parent = handle;
res._parentWrap = wrap;
res._secureContext = context;
res.reading = handle.reading;
this[kRes] = res;
defineHandleReading(this, handle);
this.on('close', onSocketCloseDestroySSL);
return res;
};
// This eliminates a cyclic reference to TLSWrap
// Ref: https://github.com/nodejs/node/commit/f7620fb96d339f704932f9bb9a0dceb9952df2d4
function defineHandleReading(socket, handle) {
Object.defineProperty(handle, 'reading', {
get: function get() {
return res.reading;
get: () => {
return socket[kRes].reading;
},
set: function set(value) {
res.reading = value;
set: (value) => {
socket[kRes].reading = value;
}
});
}
this.on('close', function() {
// Make sure we are not doing it on OpenSSL's stack
setImmediate(destroySSL, this);
res = null;
});
return res;
};
function onSocketCloseDestroySSL() {
// Make sure we are not doing it on OpenSSL's stack
setImmediate(destroySSL, this);
this[kRes] = null;
}
function destroySSL(self) {
self._destroySSL();
@ -425,7 +451,6 @@ TLSSocket.prototype._destroySSL = function _destroySSL() {
};
TLSSocket.prototype._init = function(socket, wrap) {
var self = this;
var options = this._tlsOptions;
var ssl = this._handle;
@ -448,11 +473,11 @@ TLSSocket.prototype._init = function(socket, wrap) {
ssl.setVerifyMode(requestCert, rejectUnauthorized);
if (options.isServer) {
ssl.onhandshakestart = () => onhandshakestart.call(this);
ssl.onhandshakedone = () => onhandshakedone.call(this);
ssl.onclienthello = (hello) => onclienthello.call(this, hello);
ssl.oncertcb = (info) => oncertcb.call(this, info);
ssl.onnewsession = (key, session) => onnewsession.call(this, key, session);
ssl.onhandshakestart = onhandshakestart;
ssl.onhandshakedone = onhandshakedone;
ssl.onclienthello = loadSession;
ssl.oncertcb = loadSNI;
ssl.onnewsession = onnewsession;
ssl.lastHandshakeTime = 0;
ssl.handshakes = 0;
@ -465,35 +490,15 @@ TLSSocket.prototype._init = function(socket, wrap) {
ssl.enableCertCb();
}
} else {
ssl.onhandshakestart = function() {};
ssl.onhandshakedone = () => this._finishInit();
ssl.onocspresponse = (resp) => onocspresponse.call(this, resp);
ssl.onhandshakestart = noop;
ssl.onhandshakedone = this._finishInit.bind(this);
ssl.onocspresponse = onocspresponse;
if (options.session)
ssl.setSession(options.session);
}
ssl.onerror = function(err) {
if (self._writableState.errorEmitted)
return;
// Destroy socket if error happened before handshake's finish
if (!self._secureEstablished) {
// When handshake fails control is not yet released,
// so self._tlsError will return null instead of actual error
self.destroy(err);
} else if (options.isServer &&
rejectUnauthorized &&
/peer did not return a certificate/.test(err.message)) {
// Ignore server's authorization errors
self.destroy();
} else {
// Throw error
self._emitTLSError(err);
}
self._writableState.errorEmitted = true;
};
ssl.onerror = onerror;
// If custom SNICallback was given, or if
// there're SNI contexts to perform match against -
@ -526,17 +531,15 @@ TLSSocket.prototype._init = function(socket, wrap) {
// To prevent assertion in afterConnect() and properly kick off readStart
this.connecting = socket.connecting || !socket._handle;
socket.once('connect', function() {
self.connecting = false;
self.emit('connect');
socket.once('connect', () => {
this.connecting = false;
this.emit('connect');
});
}
// Assume `tls.connect()`
if (wrap) {
wrap.on('error', function(err) {
self._emitTLSError(err);
});
wrap.on('error', (err) => this._emitTLSError(err));
} else {
assert(!socket);
this.connecting = true;
@ -544,15 +547,15 @@ TLSSocket.prototype._init = function(socket, wrap) {
};
TLSSocket.prototype.renegotiate = function(options, callback) {
var requestCert = this._requestCert;
var rejectUnauthorized = this._rejectUnauthorized;
if (this.destroyed)
return;
if (typeof options.requestCert !== 'undefined')
let requestCert = this._requestCert;
let rejectUnauthorized = this._rejectUnauthorized;
if (options.requestCert !== undefined)
requestCert = !!options.requestCert;
if (typeof options.rejectUnauthorized !== 'undefined')
if (options.rejectUnauthorized !== undefined)
rejectUnauthorized = !!options.rejectUnauthorized;
if (requestCert !== this._requestCert ||
@ -572,9 +575,7 @@ TLSSocket.prototype.renegotiate = function(options, callback) {
this.write('');
if (callback) {
this.once('secure', function() {
callback(null);
});
this.once('secure', () => callback(null));
}
return true;
@ -614,18 +615,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._handle.getNegotiatedProtocol();
}
if (process.features.tls_alpn) {
this.alpnProtocol = this.ssl.getALPNNegotiatedProtocol();
this.alpnProtocol = this._handle.getALPNNegotiatedProtocol();
}
if (process.features.tls_sni && this._tlsOptions.isServer) {
@ -641,9 +636,7 @@ TLSSocket.prototype._finishInit = function() {
TLSSocket.prototype._start = function() {
if (this.connecting) {
this.once('connect', function() {
this._start();
});
this.once('connect', this._start);
return;
}
@ -717,6 +710,64 @@ TLSSocket.prototype.getProtocol = function() {
// TODO: support anonymous (nocert) and PSK
function onSocketSecure() {
if (this._requestCert) {
const verifyError = this._handle.verifyError();
if (verifyError) {
this.authorizationError = verifyError.code;
if (this._rejectUnauthorized)
this.destroy();
} else {
this.authorized = true;
}
}
if (!this.destroyed && this._releaseControl())
this._tlsOptions.server.emit('secureConnection', this);
}
function onSocketTLSError(err) {
if (!this._controlReleased && !this[kErrorEmitted]) {
this[kErrorEmitted] = true;
this._tlsOptions.server.emit('tlsClientError', err, this);
}
}
function onSocketClose(err) {
// Closed because of error - no need to emit it twice
if (err)
return;
// Emit ECONNRESET
if (!this._controlReleased && !this[kErrorEmitted]) {
this[kErrorEmitted] = true;
const connReset = new Error('socket hang up');
connReset.code = 'ECONNRESET';
this._tlsOptions.server.emit('tlsClientError', connReset, this);
}
}
function tlsConnectionListener(rawSocket) {
const socket = new TLSSocket(rawSocket, {
secureContext: this._sharedCreds,
isServer: true,
server: this,
requestCert: this.requestCert,
rejectUnauthorized: this.rejectUnauthorized,
handshakeTimeout: this[kHandshakeTimeout],
NPNProtocols: this.NPNProtocols,
ALPNProtocols: this.ALPNProtocols,
SNICallback: this[kSNICallback] || SNICallback
});
socket.on('secure', onSocketSecure);
socket[kErrorEmitted] = false;
socket.on('close', onSocketClose);
socket.on('_tlsError', onSocketTLSError);
}
// AUTHENTICATION MODES
//
// There are several levels of authentication that TLS/SSL supports.
@ -797,95 +848,43 @@ function Server(options, listener) {
this._contexts = [];
var self = this;
// Handle option defaults:
this.setOptions(options);
var sharedCreds = tls.createSecureContext({
pfx: self.pfx,
key: self.key,
passphrase: self.passphrase,
cert: self.cert,
ca: self.ca,
ciphers: self.ciphers,
ecdhCurve: self.ecdhCurve,
dhparam: self.dhparam,
secureProtocol: self.secureProtocol,
secureOptions: self.secureOptions,
honorCipherOrder: self.honorCipherOrder,
crl: self.crl,
sessionIdContext: self.sessionIdContext
pfx: this.pfx,
key: this.key,
passphrase: this.passphrase,
cert: this.cert,
ca: this.ca,
ciphers: this.ciphers,
ecdhCurve: this.ecdhCurve,
dhparam: this.dhparam,
secureProtocol: this.secureProtocol,
secureOptions: this.secureOptions,
honorCipherOrder: this.honorCipherOrder,
crl: this.crl,
sessionIdContext: this.sessionIdContext
});
this._sharedCreds = sharedCreds;
var timeout = options.handshakeTimeout || (120 * 1000);
this[kHandshakeTimeout] = options.handshakeTimeout || (120 * 1000);
this[kSNICallback] = options.SNICallback;
if (typeof timeout !== 'number') {
if (typeof this[kHandshakeTimeout] !== 'number') {
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'timeout', 'number');
}
if (self.sessionTimeout) {
sharedCreds.context.setSessionTimeout(self.sessionTimeout);
if (this.sessionTimeout) {
sharedCreds.context.setSessionTimeout(this.sessionTimeout);
}
if (self.ticketKeys) {
sharedCreds.context.setTicketKeys(self.ticketKeys);
if (this.ticketKeys) {
sharedCreds.context.setTicketKeys(this.ticketKeys);
}
// constructor call
net.Server.call(this, function(raw_socket) {
var socket = new TLSSocket(raw_socket, {
secureContext: sharedCreds,
isServer: true,
server: self,
requestCert: self.requestCert,
rejectUnauthorized: self.rejectUnauthorized,
handshakeTimeout: timeout,
NPNProtocols: self.NPNProtocols,
ALPNProtocols: self.ALPNProtocols,
SNICallback: options.SNICallback || SNICallback
});
socket.on('secure', function() {
if (socket._requestCert) {
var verifyError = socket._handle.verifyError();
if (verifyError) {
socket.authorizationError = verifyError.code;
if (socket._rejectUnauthorized)
socket.destroy();
} else {
socket.authorized = true;
}
}
if (!socket.destroyed && socket._releaseControl())
self.emit('secureConnection', socket);
});
var errorEmitted = false;
socket.on('close', function(err) {
// Closed because of error - no need to emit it twice
if (err)
return;
// Emit ECONNRESET
if (!socket._controlReleased && !errorEmitted) {
errorEmitted = true;
var connReset = new Error('socket hang up');
connReset.code = 'ECONNRESET';
self.emit('tlsClientError', connReset, socket);
}
});
socket.on('_tlsError', function(err) {
if (!socket._controlReleased && !errorEmitted) {
errorEmitted = true;
self.emit('tlsClientError', err, socket);
}
});
});
net.Server.call(this, tlsConnectionListener);
if (listener) {
this.on('secureConnection', listener);
@ -971,16 +970,17 @@ Server.prototype.addContext = function(servername, context) {
};
function SNICallback(servername, callback) {
var ctx;
const contexts = this.server._contexts;
this.server._contexts.some(function(elem) {
for (var i = 0; i < contexts.length; i++) {
const elem = contexts[i];
if (elem[0].test(servername)) {
ctx = elem[1];
return true;
callback(null, elem[1]);
return;
}
});
}
callback(null, ctx);
callback(null, undefined);
}
@ -1017,6 +1017,66 @@ function normalizeConnectArgs(listArgs) {
return (cb) ? [options, cb] : [options];
}
function onConnectSecure() {
const options = this[kConnectOptions];
// Check the size of DHE parameter above minimum requirement
// specified in options.
const ekeyinfo = this.getEphemeralKeyInfo();
if (ekeyinfo.type === 'DH' && ekeyinfo.size < options.minDHSize) {
const err = new errors.Error('ERR_TLS_DH_PARAM_SIZE', ekeyinfo.size);
this.emit('error', err);
this.destroy();
return;
}
let verifyError = this._handle.verifyError();
// Verify that server's identity matches it's certificate's names
// Unless server has resumed our existing session
if (!verifyError && !this.isSessionReused()) {
const hostname = options.servername ||
options.host ||
(options.socket && options.socket._host) ||
'localhost';
const cert = this.getPeerCertificate();
verifyError = options.checkServerIdentity(hostname, cert);
}
if (verifyError) {
this.authorized = false;
this.authorizationError = verifyError.code || verifyError.message;
if (options.rejectUnauthorized) {
this.destroy(verifyError);
return;
} else {
this.emit('secureConnect');
}
} else {
this.authorized = true;
this.emit('secureConnect');
}
// Uncork incoming data
this.removeListener('end', onConnectEnd);
}
function onConnectEnd() {
// NOTE: This logic is shared with _http_client.js
if (!this._hadError) {
const options = this[kConnectOptions];
this._hadError = true;
const error = new Error('socket hang up');
error.code = 'ECONNRESET';
error.path = options.path;
error.host = options.host;
error.port = options.port;
error.localAddress = options.localAddress;
this.destroy(error);
}
}
exports.connect = function(...args /* [port,] [host,] [options,] [cb] */) {
args = normalizeConnectArgs(args);
var options = args[0];
@ -1040,10 +1100,6 @@ exports.connect = function(...args /* [port,] [host,] [options,] [cb] */) {
'options.minDHSize is not a positive number: ' +
options.minDHSize);
var hostname = options.servername ||
options.host ||
(options.socket && options.socket._host) ||
'localhost';
const NPN = {};
const ALPN = {};
const context = options.secureContext || tls.createSecureContext(options);
@ -1062,6 +1118,8 @@ exports.connect = function(...args /* [port,] [host,] [options,] [cb] */) {
requestOCSP: options.requestOCSP
});
socket[kConnectOptions] = options;
if (cb)
socket.once('secureConnect', cb);
@ -1074,9 +1132,7 @@ exports.connect = function(...args /* [port,] [host,] [options,] [cb] */) {
localAddress: options.localAddress,
lookup: options.lookup
};
socket.connect(connectOpt, function() {
socket._start();
});
socket.connect(connectOpt, socket._start);
}
socket._releaseControl();
@ -1090,59 +1146,8 @@ exports.connect = function(...args /* [port,] [host,] [options,] [cb] */) {
if (options.socket)
socket._start();
socket.on('secure', function() {
// Check the size of DHE parameter above minimum requirement
// specified in options.
var ekeyinfo = socket.getEphemeralKeyInfo();
if (ekeyinfo.type === 'DH' && ekeyinfo.size < options.minDHSize) {
var err = new errors.Error('ERR_TLS_DH_PARAM_SIZE', ekeyinfo.size);
socket.emit('error', err);
socket.destroy();
return;
}
var verifyError = socket._handle.verifyError();
// Verify that server's identity matches it's certificate's names
// Unless server has resumed our existing session
if (!verifyError && !socket.isSessionReused()) {
var cert = socket.getPeerCertificate();
verifyError = options.checkServerIdentity(hostname, cert);
}
if (verifyError) {
socket.authorized = false;
socket.authorizationError = verifyError.code || verifyError.message;
if (options.rejectUnauthorized) {
socket.destroy(verifyError);
return;
} else {
socket.emit('secureConnect');
}
} else {
socket.authorized = true;
socket.emit('secureConnect');
}
// Uncork incoming data
socket.removeListener('end', onHangUp);
});
function onHangUp() {
// NOTE: This logic is shared with _http_client.js
if (!socket._hadError) {
socket._hadError = true;
var error = new Error('socket hang up');
error.code = 'ECONNRESET';
error.path = options.path;
error.host = options.host;
error.port = options.port;
error.localAddress = options.localAddress;
socket.destroy(error);
}
}
socket.once('end', onHangUp);
socket.on('secure', onConnectSecure);
socket.once('end', onConnectEnd);
return socket;
};

Loading…
Cancel
Save