diff --git a/Readme.md b/Readme.md index 6f6b2dd..812a7dd 100644 --- a/Readme.md +++ b/Readme.md @@ -54,11 +54,12 @@ pino(transport) ### Events -| Name | Callback Signature | Description | -|---------------|----------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------| -| `open` | `(address: AddressInfo) => void` | Emitted when the TCP or UDP connection is established. | -| `socketError` | `(error: Error) => void` | Emitted when an error occurs on the TCP or UDP socket. The socket won't be closed. | -| `close` | `(hadError: Boolean) => void` | Emitted after the TCP or UDP socket is closed. The argument `hadError` is a boolean which says if the socket was closed due to a transmission error. | +| Name | Callback Signature | Description | +|--------------------|-----------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------| +| `open` | `(address: AddressInfo) => void` | Emitted when the TCP or UDP connection is established. | +| `socketError` | `(error: Error) => void` | Emitted when an error occurs on the TCP or UDP socket. The socket won't be closed. | +| `close` | `(hadError: Boolean) => void` | Emitted after the TCP or UDP socket is closed. The argument `hadError` is a boolean which says if the socket was closed due to a transmission error. | +| `reconnectFailure` | `(error: Error|undefined) => void` | Emitted when the maximum number of backoffs (i.e., reconnect tries) is reached on a TCP connection. | **IMPORTANT:** In version prior to 6.0, an `error` event was emitted on the writable stream when an error occurs on the TCP or UDP socket. In other words, it was not possible to write data to the writable stream after an error occurs on the TCP or UDP socket. diff --git a/lib/TcpConnection.js b/lib/TcpConnection.js index 4215df6..4ff030f 100644 --- a/lib/TcpConnection.js +++ b/lib/TcpConnection.js @@ -130,8 +130,8 @@ module.exports = function factory (userOptions) { } } - function reconnect () { - retryBackoff.backoff() + function reconnect (err) { + retryBackoff.backoff(err) } // end: connection handlers @@ -143,8 +143,8 @@ module.exports = function factory (userOptions) { options.onSocketClose(socketError) } } - if (options.reconnect) { - reconnect() + if (options.reconnect && hadError) { + reconnect(socketError) } else { outputStream.emit('close', hadError) } @@ -218,7 +218,7 @@ module.exports = function factory (userOptions) { } }) }) - retry.on('fail', (err) => process.stderr.write(`could not reconnect: ${err.message}`)) + retry.on('fail', (err) => outputStream.emit('reconnectFailure', err)) return retry } diff --git a/test/recovery.js b/test/recovery.js index a989762..8d21e5d 100644 --- a/test/recovery.js +++ b/test/recovery.js @@ -62,7 +62,9 @@ test('recovery', function (done) { // make sure that no number is missing expect(logNumbers).to.deep.eq(Array.from({ length: logNumbers.length }, (_, i) => i + 1)) } finally { - done() + tcpConnection.end(() => { + done() + }) } }) } diff --git a/test/tcpBackoff.js b/test/tcpBackoff.js index 6075a6f..e190b0c 100644 --- a/test/tcpBackoff.js +++ b/test/tcpBackoff.js @@ -22,7 +22,7 @@ test('tcp backoff', function testTcpBackoff (done) { const nextBackoffDelay = exponentialStrategy.next() // initial, 10, 100... next delay should be 1000 expect(nextBackoffDelay).to.eq(1000) - tcpConnection.end('', 'utf8', () => done()) + tcpConnection.end(() => done()) } } }) diff --git a/test/tcpReconnect.js b/test/tcpReconnect.js index 6dcd993..d2f068a 100644 --- a/test/tcpReconnect.js +++ b/test/tcpReconnect.js @@ -133,4 +133,37 @@ test('tcp reconnect after initial failure', async function testTcpReconnectAfter expect(failureCount).to.gte(counter) expect(received.length).to.eq(1) expect(received[0].data.toString('utf8')).to.eq(`log${counter}\n`) + tcpConnection.end() +}) + +test('tcp no reconnect when socket is gracefully closed', function testTcpNoReconnectSocketGracefullyClosed (done) { + let msgCount = 0 + let tcpConnection + const server = startServer({ next }) + function next (msg) { + switch (msg.action) { + case 'started': + connect(msg.address, msg.port) + break + case 'data': + msgCount += 1 + // gracefully close the socket, it should not reconnect + tcpConnection._socket.destroy() + } + } + function connect (address, port) { + tcpConnection = TcpConnection({ + address, + port, + reconnect: true + }) + tcpConnection.write('data', 'utf8', () => { /* ignore */ }) + tcpConnection.on('close', () => { + expect(msgCount).to.eq(1) + // tcp connection should be closed! + server.close(() => { + done() + }) + }) + } }) diff --git a/test/tcpRetyFail.js b/test/tcpRetyFail.js new file mode 100644 index 0000000..e9aa14b --- /dev/null +++ b/test/tcpRetyFail.js @@ -0,0 +1,26 @@ +'use strict' +/* eslint-env node, mocha */ + +const { expect } = require('chai') +const TcpConnection = require('../lib/TcpConnection') + +test('tcp retry fail', function testTcpRetryFail (done) { + let socketErrorCount = 0 + const tcpConnection = TcpConnection({ + address: '127.0.0.1', + port: 0, + reconnect: true, + reconnectTries: 2 + } + ) + tcpConnection.on('socketError', () => { + socketErrorCount++ + }) + tcpConnection.on('reconnectFailure', (lastError) => { + expect(socketErrorCount).to.eq(3) + expect(lastError).to.be.an('error') + tcpConnection.end(() => { + done() + }) + }) +}) diff --git a/test/tcpSocketError.js b/test/tcpSocketError.js index fb22df0..d97b8b8 100644 --- a/test/tcpSocketError.js +++ b/test/tcpSocketError.js @@ -19,7 +19,9 @@ test('close connection', function (done) { tcpConnection.write('test', 'utf8', (err) => { // cannot write expect(err.message).to.eq('write after end') - done() + tcpConnection.end(() => { + done() + }) }) }) }) @@ -40,9 +42,7 @@ test('retry connection', function (done) { // TCP connection is still writable expect(tcpConnection.writableEnded).to.eq(false) if (counter === 2) { - tcpConnection.end(() => { - done() - }) + tcpConnection.end(() => done()) } }) })