Skip to content

Commit

Permalink
Properly handle initial error packets
Browse files Browse the repository at this point in the history
Those packets can happen in situations where mysql is refusing connections due
to the max_connections setting.
  • Loading branch information
felixge committed Oct 30, 2010
1 parent 86a38e5 commit 2461915
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 3 deletions.
8 changes: 6 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@ test-simple:
@find test/simple/test-*.js | xargs -n 1 -t node
test-system:
@find test/system/test-*.js | xargs -n 1 -t node
test: test-simple test-system
test-all: test
test-system-slow:
@find test/system/slow/test-*.js | xargs -n 1 -t node
# Dangerous tests potentially effect the MySql server settings, don't run these in production!
test-system-dangerous:
@find test/system/dangerous/test-*.js | xargs -n 1 -t node
test: test-simple test-system
test-all: test test-system-slow test-system-dangerous
benchmark-node-mysql:
@find benchmark/node-mysql/*.js | xargs -n 1 -t node
benchmark-php:
Expand Down
15 changes: 15 additions & 0 deletions lib/mysql/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,14 @@ Parser.prototype.write = function(buffer) {

// GREETING_PACKET
case Parser.GREETING_PROTOCOL_VERSION:
// Nice undocumented MySql gem, the initial greeting can be an error
// packet. Happens for too many connections errors.
if (c === 0xff) {
packet.type = Parser.ERROR_PACKET;
advance(Parser.ERROR_NUMBER);
break;
}

// 1 byte
packet.type = Parser.GREETING_PACKET;
packet.protocolVersion = c;
Expand Down Expand Up @@ -245,6 +253,13 @@ Parser.prototype.write = function(buffer) {
packet.errorNumber += POWS[packet.index] * c;

if (packet.index == 1) {
if (!this.greeted) {
// Turns out error packets are confirming to the 4.0 protocol when
// not greeted yet. Oh MySql, you are such a thing of beauty ...
advance(Parser.ERROR_MESSAGE);
break;
}

advance();
}
break;
Expand Down
19 changes: 18 additions & 1 deletion test/simple/test-parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,29 @@ test(function write() {
);
})();

(function testPacketSize() {
(function testPacketNumber() {
parser.write(new Buffer([42]));
assert.strictEqual(packet.number, 42);
assert.equal(parser.state, Parser.GREETING_PROTOCOL_VERSION);
})();

(function testGreetingErrorPacket() {
parser.write(new Buffer([0xff]));
assert.equal(packet.type, Parser.ERROR_PACKET);
assert.equal(parser.state, Parser.ERROR_NUMBER);

parser.write(new Buffer([5, 2]));
assert.equal(packet.errorNumber, Math.pow(256, 0) * 5 + Math.pow(256, 1) * 2);

parser.write(new Buffer('Hello World'));
assert.equal(packet.errorMessage, 'Hello World');

// Reset back to previous state
packet.type = Parser.GREETING_PACKET;
packet.received = 0;
parser.state = Parser.GREETING_PROTOCOL_VERSION;
})();

(function testGreetingPacket() {
parser.write(new Buffer([15]));
assert.equal(packet.type, Parser.GREETING_PACKET);
Expand Down
58 changes: 58 additions & 0 deletions test/system/dangerous/test-client-max-connections.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
require('../../common');
var Client = require('mysql').Client,
mainClient = Client(TEST_CONFIG),
originalMaxConnections;


function setMaxConnectionsToOne() {
mainClient.connect();
// First we figure out the current max_connections value, so we can restore that after the test
mainClient.query('SHOW VARIABLES WHERE Variable_name = ?', ['max_connections'], function(err, results) {
if (err) throw err;

originalMaxConnections = parseInt(results[0].Value);
if (originalMaxConnections === 1) {
console.log(
'MySql already had max_connections set to 1. '+
'This probably happened because of a mal-function in this test, so re-setting to the MySql default of 100. '+
'If you had a higher value configured, you need to manually fix this now.'
);
originalMaxConnections = 100;
}

// Now we set max connections to 1, then we continue with our test
mainClient.query('SET GLOBAL max_connections = ?', [1], function() {
connectTwoClients();
});
});
};

function connectTwoClients() {
var client1 = Client(TEST_CONFIG);
client1.connect(function(err) {
if (err) {
// There should be no error for the first connection, but if there is one
// anyway, let's try to at least restore the server config before throwing
restoreMaxConnections(function() {
throw err;
});
return;
}

var client2 = Client(TEST_CONFIG);
client2.connect(function(err) {
assert.strictEqual(err.number, Client.ERROR_CON_COUNT_ERROR);

client1.end();
restoreMaxConnections();
});
});
}

function restoreMaxConnections(cb) {
mainClient.query('SET GLOBAL max_connections = ?', [originalMaxConnections], cb);
mainClient.end();
}

setMaxConnectionsToOne();

0 comments on commit 2461915

Please sign in to comment.