Skip to content

Commit 11ab1da

Browse files
authored
Close connection on SSL connection errors (brianc#2082)
* Close connection on SSL connection errors Fixes brianc#2079 * Fix test * Remove console.log * Fix tests, implement same behavior for native client * Fix tests
1 parent 727f1a0 commit 11ab1da

File tree

5 files changed

+83
-8
lines changed

5 files changed

+83
-8
lines changed

Diff for: packages/pg-pool/test/error-handling.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -211,14 +211,14 @@ describe('pool error handling', function () {
211211
const pool = new Pool({ max: 1, port: closeServer.address().port, host: 'localhost' })
212212
pool.connect((err) => {
213213
expect(err).to.be.an(Error)
214-
if (err.errno) {
215-
expect(err.errno).to.be('ECONNRESET')
214+
if (err.code) {
215+
expect(err.code).to.be('ECONNRESET')
216216
}
217217
})
218218
pool.connect((err) => {
219219
expect(err).to.be.an(Error)
220-
if (err.errno) {
221-
expect(err.errno).to.be('ECONNRESET')
220+
if (err.code) {
221+
expect(err.code).to.be('ECONNRESET')
222222
}
223223
closeServer.close(() => {
224224
pool.end(done)

Diff for: packages/pg/lib/connection.js

+18-3
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,13 @@ Connection.prototype.connect = function (port, host) {
8585
this.stream.once('data', function (buffer) {
8686
var responseCode = buffer.toString('utf8')
8787
switch (responseCode) {
88-
case 'N': // Server does not support SSL connections
89-
return self.emit('error', new Error('The server does not support SSL connections'))
9088
case 'S': // Server supports SSL connections, continue with a secure connection
9189
break
90+
case 'N': // Server does not support SSL connections
91+
self.stream.end()
92+
return self.emit('error', new Error('The server does not support SSL connections'))
9293
default: // Any other response byte, including 'E' (ErrorResponse) indicating a server error
94+
self.stream.end()
9395
return self.emit('error', new Error('There was an error establishing an SSL connection'))
9496
}
9597
var tls = require('tls')
@@ -112,9 +114,18 @@ Connection.prototype.connect = function (port, host) {
112114
options.servername = host
113115
}
114116
self.stream = tls.connect(options)
115-
self.attachListeners(self.stream)
116117
self.stream.on('error', reportStreamError)
117118

119+
// send SSLRequest packet
120+
const buff = Buffer.alloc(8)
121+
buff.writeUInt32BE(8)
122+
buff.writeUInt32BE(80877103, 4)
123+
if (self.stream.writable) {
124+
self.stream.write(buff)
125+
}
126+
127+
self.attachListeners(self.stream)
128+
118129
self.emit('sslconnect')
119130
})
120131
}
@@ -345,6 +356,10 @@ Connection.prototype.end = function () {
345356
// 0x58 = 'X'
346357
this.writer.add(emptyBuffer)
347358
this._ending = true
359+
if (!this.stream.writable) {
360+
this.stream.end()
361+
return
362+
}
348363
return this.stream.write(END_BUFFER, () => {
349364
this.stream.end()
350365
})

Diff for: packages/pg/lib/native/client.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,10 @@ Client.prototype._connect = function (cb) {
8989
this.connectionParameters.getLibpqConnectionString(function (err, conString) {
9090
if (err) return cb(err)
9191
self.native.connect(conString, function (err) {
92-
if (err) return cb(err)
92+
if (err) {
93+
self.native.end()
94+
return cb(err)
95+
}
9396

9497
// set internal states to connected
9598
self._connected = true

Diff for: packages/pg/test/integration/gh-issues/2056-tests.js

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ var assert = require('assert')
55

66
const suite = new helper.Suite()
77

8+
89
suite.test('All queries should return a result array', (done) => {
910
const client = new helper.pg.Client()
1011
client.connect()

Diff for: packages/pg/test/integration/gh-issues/2079-tests.js

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
2+
"use strict"
3+
var helper = require('./../test-helper')
4+
var assert = require('assert')
5+
6+
const suite = new helper.Suite()
7+
8+
// makes a backend server that responds with a non 'S' ssl response buffer
9+
let makeTerminatingBackend = (byte) => {
10+
const { createServer } = require('net')
11+
12+
const server = createServer((socket) => {
13+
// attach a listener so the socket can drain
14+
// https://www.postgresql.org/docs/9.3/protocol-message-formats.html
15+
socket.on('data', (buff) => {
16+
const code = buff.readInt32BE(4)
17+
// I don't see anything in the docs about 80877104
18+
// but libpq is sending it...
19+
if (code === 80877103 || code === 80877104) {
20+
const packet = Buffer.from(byte, 'utf-8')
21+
socket.write(packet)
22+
}
23+
})
24+
socket.on('close', () => {
25+
server.close()
26+
})
27+
})
28+
29+
server.listen()
30+
const { port } = server.address()
31+
return port
32+
}
33+
34+
suite.test('SSL connection error allows event loop to exit', (done) => {
35+
const port = makeTerminatingBackend('N')
36+
const client = new helper.pg.Client({ ssl: 'require', port })
37+
// since there was a connection error the client's socket should be closed
38+
// and the event loop will have no refs and exit cleanly
39+
client.connect((err) => {
40+
assert(err instanceof Error)
41+
done()
42+
})
43+
})
44+
45+
46+
suite.test('Non "S" response code allows event loop to exit', (done) => {
47+
const port = makeTerminatingBackend('X')
48+
const client = new helper.pg.Client({ ssl: 'require', port })
49+
// since there was a connection error the client's socket should be closed
50+
// and the event loop will have no refs and exit cleanly
51+
client.connect((err) => {
52+
assert(err instanceof Error)
53+
done()
54+
})
55+
})
56+

0 commit comments

Comments
 (0)