Skip to content

Commit 2461915

Browse files
committed
Properly handle initial error packets
Those packets can happen in situations where mysql is refusing connections due to the max_connections setting.
1 parent 86a38e5 commit 2461915

File tree

4 files changed

+97
-3
lines changed

4 files changed

+97
-3
lines changed

Makefile

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@ test-simple:
22
@find test/simple/test-*.js | xargs -n 1 -t node
33
test-system:
44
@find test/system/test-*.js | xargs -n 1 -t node
5-
test: test-simple test-system
6-
test-all: test
5+
test-system-slow:
76
@find test/system/slow/test-*.js | xargs -n 1 -t node
7+
# Dangerous tests potentially effect the MySql server settings, don't run these in production!
8+
test-system-dangerous:
9+
@find test/system/dangerous/test-*.js | xargs -n 1 -t node
10+
test: test-simple test-system
11+
test-all: test test-system-slow test-system-dangerous
812
benchmark-node-mysql:
913
@find benchmark/node-mysql/*.js | xargs -n 1 -t node
1014
benchmark-php:

lib/mysql/parser.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,14 @@ Parser.prototype.write = function(buffer) {
121121

122122
// GREETING_PACKET
123123
case Parser.GREETING_PROTOCOL_VERSION:
124+
// Nice undocumented MySql gem, the initial greeting can be an error
125+
// packet. Happens for too many connections errors.
126+
if (c === 0xff) {
127+
packet.type = Parser.ERROR_PACKET;
128+
advance(Parser.ERROR_NUMBER);
129+
break;
130+
}
131+
124132
// 1 byte
125133
packet.type = Parser.GREETING_PACKET;
126134
packet.protocolVersion = c;
@@ -245,6 +253,13 @@ Parser.prototype.write = function(buffer) {
245253
packet.errorNumber += POWS[packet.index] * c;
246254

247255
if (packet.index == 1) {
256+
if (!this.greeted) {
257+
// Turns out error packets are confirming to the 4.0 protocol when
258+
// not greeted yet. Oh MySql, you are such a thing of beauty ...
259+
advance(Parser.ERROR_MESSAGE);
260+
break;
261+
}
262+
248263
advance();
249264
}
250265
break;

test/simple/test-parser.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,29 @@ test(function write() {
4545
);
4646
})();
4747

48-
(function testPacketSize() {
48+
(function testPacketNumber() {
4949
parser.write(new Buffer([42]));
5050
assert.strictEqual(packet.number, 42);
5151
assert.equal(parser.state, Parser.GREETING_PROTOCOL_VERSION);
5252
})();
5353

54+
(function testGreetingErrorPacket() {
55+
parser.write(new Buffer([0xff]));
56+
assert.equal(packet.type, Parser.ERROR_PACKET);
57+
assert.equal(parser.state, Parser.ERROR_NUMBER);
58+
59+
parser.write(new Buffer([5, 2]));
60+
assert.equal(packet.errorNumber, Math.pow(256, 0) * 5 + Math.pow(256, 1) * 2);
61+
62+
parser.write(new Buffer('Hello World'));
63+
assert.equal(packet.errorMessage, 'Hello World');
64+
65+
// Reset back to previous state
66+
packet.type = Parser.GREETING_PACKET;
67+
packet.received = 0;
68+
parser.state = Parser.GREETING_PROTOCOL_VERSION;
69+
})();
70+
5471
(function testGreetingPacket() {
5572
parser.write(new Buffer([15]));
5673
assert.equal(packet.type, Parser.GREETING_PACKET);
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
require('../../common');
2+
var Client = require('mysql').Client,
3+
mainClient = Client(TEST_CONFIG),
4+
originalMaxConnections;
5+
6+
7+
function setMaxConnectionsToOne() {
8+
mainClient.connect();
9+
// First we figure out the current max_connections value, so we can restore that after the test
10+
mainClient.query('SHOW VARIABLES WHERE Variable_name = ?', ['max_connections'], function(err, results) {
11+
if (err) throw err;
12+
13+
originalMaxConnections = parseInt(results[0].Value);
14+
if (originalMaxConnections === 1) {
15+
console.log(
16+
'MySql already had max_connections set to 1. '+
17+
'This probably happened because of a mal-function in this test, so re-setting to the MySql default of 100. '+
18+
'If you had a higher value configured, you need to manually fix this now.'
19+
);
20+
originalMaxConnections = 100;
21+
}
22+
23+
// Now we set max connections to 1, then we continue with our test
24+
mainClient.query('SET GLOBAL max_connections = ?', [1], function() {
25+
connectTwoClients();
26+
});
27+
});
28+
};
29+
30+
function connectTwoClients() {
31+
var client1 = Client(TEST_CONFIG);
32+
client1.connect(function(err) {
33+
if (err) {
34+
// There should be no error for the first connection, but if there is one
35+
// anyway, let's try to at least restore the server config before throwing
36+
restoreMaxConnections(function() {
37+
throw err;
38+
});
39+
return;
40+
}
41+
42+
var client2 = Client(TEST_CONFIG);
43+
client2.connect(function(err) {
44+
assert.strictEqual(err.number, Client.ERROR_CON_COUNT_ERROR);
45+
46+
client1.end();
47+
restoreMaxConnections();
48+
});
49+
});
50+
}
51+
52+
function restoreMaxConnections(cb) {
53+
mainClient.query('SET GLOBAL max_connections = ?', [originalMaxConnections], cb);
54+
mainClient.end();
55+
}
56+
57+
setMaxConnectionsToOne();
58+

0 commit comments

Comments
 (0)