node.js 4.2.2, module smtp-protocol, created a server. When starttls command is received, a TLS handshake occurs. With multiple workers in use, the server intermittently sends Alter close_notify (as captured by Java client TLS debug) after handshake and before any application data arrives (I can see the network data containing the Alert arrives before the application data is sent). This is easily reproducible with Java client. I have reduced the server side to mimic just the way the ssl connection is treated (i.e. redirection through a new tls server started on localhost), and made the TLS server echo the incoming data back. #!/usr/bin/env node var cluster = require('cluster'), fs = require('fs'), net = require('net'), tls = require('tls'); var argv = require('yargs') .usage('Usage: $0 [options]') .option('w', { alias: 'workers', default: 4, describe: 'number of concurrent listeners' }) .option('p', { alias: 'port', default: 25125, describe: 'port to listen on' }) .option('l', { alias: 'level', default: 'info', describe: 'debug level' }) .option('k', { alias: 'key', describe: 'keypair to use for TLS' }) .option('c', { alias: 'cert', describe: 'server cert to send' }) .argv; function tlsToString(buf, start, end){ var t = buf[start]; var len = (buf[start+3] << 8) | (buf[start+4] & 0xff); if (t == 23) return {pos: start+len+5}; // no need to log application messages if (end < start+len+5) return {pos: start+len+5}; end = start+len+5; var str = 'ContentType: ' + buf[start] + '\nProtocolVersion: ' + buf.toString('hex', start+1, start+3); str += '\nLength: ' + len; if (t==22){ // handshake var pos = start+5; var msgtype = buf[pos++]; str += '\nHandshakeType: ' + msgtype; str += '\nLength: ' + buf.toString('hex', pos, pos+3); if (msgtype == 11) str += '\n(here was certificate)'; else str += '\nContents: ' + buf.toString('hex', pos+3, end); }else str = str + '\nContents: ' + buf.toString('hex', start+5, end); return {pos: end, str: str}; } function tlsChunkToString(chunk){ var str = ''; var pos = 0; while(pos chunk.length) str+='\n------\nnot complete TLS packet in chunk; the subsequent messages will be garbled'; pos = r.pos; } if (str == '') return null; return str; } if (cluster.isMaster){ for(i = 0; i < argv.workers; i++) cluster.fork(); }else{ var tlsserverid = cluster.worker.id*1000; var opts = {port: argv.port, host: '0.0.0.0', key: argv.key, cert: argv.cert}; console.log('starting server on ' + opts.port + ' using key: ' + opts.key + ' and cert: ' + opts.cert); opts.key = fs.readFileSync(opts.key); opts.cert = fs.readFileSync(opts.cert); net.createServer(opts, function(stream){ stream.on('error', function(err){}); var tserver = tls.createServer(opts); tserver.id = tlsserverid++; tserver.mysock = stream.remoteAddress + ':' + stream.remotePort; tserver.listen(0, '127.0.0.1', function(){ var s = net.connect(tserver.address().port, '127.0.0.1'); s.on('error', function(err){ stream.end(); }); if (argv.level != 'info'){ s.on('data', function(chunk) { var str = tlsChunkToString(chunk); if (str) console.log('tserver for socket ' + tserver.mysock + ' tls id: ' + tserver.id + ' sends ' + chunk.length + ' bytes' + str); }); stream.on('data', function(chunk){ var str = tlsChunkToString(chunk); if (str) console.log('tserver for socket ' + tserver.mysock + ' tls id: ' + tserver.id + 'receives ' + chunk.length + ' bytes' + str); }); } s.pipe(stream).pipe(s); }); var index = 0; tserver.on('secureConnection', function(sec){ if (index++ > 0) return sec.end(); sec.pipe(sec); // echo if (argv.level != 'info') sec.on('data', function(chunk){ console.log(chunk.toString()); }); }); tserver.on('clientError', function(err){ console.trace('clientError', err); }); stream.on('close', function(){ tserver.close(); }); }).listen(opts.port, opts.host); } start like so: ./tls.js -w 1 -k key_cert.pem -c cert.pem works fine. ./tls.js -w 4 -k key_cert.pem -c cert.pem Intermittent failure. Specify -l debug to see the TLS handshake exchanges. (i.e.: $ java -cp . -DCONCURRENCY=4 blah.HTTPSStd otherhost 25125 EOF was not expected at this time) The blah.HTTPSStd is the rendition of an HTTPS-like client (to test that it is a correctly operating TLS client just point at google.com 443) using standard JDK7 classes: save the below to blah/HTTPSStd.java compile with $ javac blah/HTTPSStd.java run with $ java -cp . -DCONCURRENCY=4 blah.HTTPSStd otherhost 25125 to get extensive TLS diagnostics from the client side (really a lot, and not easy to contemplate because of concurrency involved), run with $ java -cp . -DCONCURRENCY=4 -Djavax.net.debug=all blah.HTTPSStd otherhost 25125 package blah; import java.net.*; import javax.net.ssl.*; import java.nio.channels.*; import java.security.cert.*; import java.io.*; public class HTTPSStd implements Runnable, X509TrustManager{ static String HOST; static int PORT; static final boolean PRINT = Boolean.getBoolean("PRINT"); public static void main(String [] args) throws Exception{ HOST = args[0]; PORT = Integer.parseInt(args[1]); TARGET = new InetSocketAddress(HOST, PORT); for(int i = Integer.getInteger("CONCURRENCY", 25); i-->0;) new Thread(new HTTPSStd()).start(); } public void run(){ for(int i = 0; i < 100; i++){ try{ oneShot(); }catch(Exception e){ System.out.println(e.getMessage()); System.exit(1); } } } public void oneShot() throws Exception { SSLContext context = SSLContext.getInstance("TLS"); context.init(null, new TrustManager[]{this}, null); Socket sock = context.getSocketFactory().createSocket(HOST, PORT); if (PRINT) System.out.println("started TLS\nsending GET"); sock.getOutputStream().write(("GET / HTTP/1.0\r\nHost: " + HOST + "\r\n\r\n").getBytes()); sock.getOutputStream().flush(); if (PRINT) System.out.println("sent\nwaiting for a response back"); BufferedReader br = new BufferedReader(new InputStreamReader(sock.getInputStream())); String s; while((s=br.readLine()) != null && !"".equals(s)) if (PRINT) System.out.println(s); sock.close(); if (s == null) throw new Exception("EOF was not expected at this time"); if (PRINT) System.out.println("phew! done"); } public X509Certificate[] getAcceptedIssuers(){ return new X509Certificate[0]; } public void checkServerTrusted(X509Certificate[] chain, String a){ } public void checkClientTrusted(X509Certificate[] chain, String a){ } }