Skip to content

Commit 0c99f56

Browse files
committed
Make errors from the parser catchable
1 parent 4d5332d commit 0c99f56

File tree

5 files changed

+94
-11
lines changed

5 files changed

+94
-11
lines changed

Changes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ you spot any mistakes.
1414
* Default `connectTimeout` to 10 seconds
1515
* Fix domain binding with `conn.connect`
1616
* Fix `packet.default` to actually be a string
17+
* Fix `PARSER_*` errors to be catchable
1718
* Fix `PROTOCOL_PACKETS_OUT_OF_ORDER` error to be catchable #844
1819
* Return `Query` object from `pool.query` like `conn.query` #830
1920
* Use `EventEmitter.listenerCount` when possible for faster counting

lib/protocol/Parser.js

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ function Parser(options) {
1313
this._offset = 0;
1414
this._packetEnd = null;
1515
this._packetHeader = null;
16+
this._packetOffset = null;
1617
this._onError = options.onError || function(err) { throw err; };
1718
this._onPacket = options.onPacket || function() {};
1819
this._nextPacketNumber = 0;
@@ -44,7 +45,8 @@ Parser.prototype.write = function(buffer) {
4445
'Expected: ' + this._nextPacketNumber
4546
);
4647

47-
err.code = 'PROTOCOL_PACKETS_OUT_OF_ORDER';
48+
err.code = 'PROTOCOL_PACKETS_OUT_OF_ORDER';
49+
err.fatal = true;
4850

4951
this._onError(err);
5052
}
@@ -56,10 +58,11 @@ Parser.prototype.write = function(buffer) {
5658
break;
5759
}
5860

59-
this._packetEnd = this._offset + this._packetHeader.length;
61+
this._packetEnd = this._offset + this._packetHeader.length;
62+
this._packetOffset = this._offset;
6063

6164
if (this._packetHeader.length === MAX_PACKET_LENGTH) {
62-
this._longPacketBuffers.push(this._buffer.slice(this._offset, this._packetEnd));
65+
this._longPacketBuffers.push(this._buffer.slice(this._packetOffset, this._packetEnd));
6366

6467
this._advanceToNextPacket();
6568
continue;
@@ -73,6 +76,15 @@ Parser.prototype.write = function(buffer) {
7376
try {
7477
this._onPacket(this._packetHeader);
7578
hadException = false;
79+
} catch (err) {
80+
if (typeof err.code !== 'string' || err.code.substr(0, 7) !== 'PARSER_') {
81+
// Rethrow unknown errors
82+
throw err;
83+
}
84+
85+
// Pass down parser errors
86+
this._onError(err);
87+
hadException = false;
7688
} finally {
7789
this._advanceToNextPacket();
7890

@@ -133,7 +145,10 @@ Parser.prototype.parseUnsignedNumber = function(bytes) {
133145
var value = 0;
134146

135147
if (bytes > 4) {
136-
throw new Error('parseUnsignedNumber: Supports only up to 4 bytes');
148+
var err = new Error('parseUnsignedNumber: Supports only up to 4 bytes');
149+
err.offset = (this._offset - this._packetOffset - 1);
150+
err.code = 'PARSER_UNSIGNED_TOO_LONG';
151+
throw err;
137152
}
138153

139154
while (offset >= this._offset) {
@@ -168,7 +183,10 @@ Parser.prototype.parseLengthCodedBuffer = function() {
168183

169184
Parser.prototype.parseLengthCodedNumber = function parseLengthCodedNumber() {
170185
if (this._offset >= this._buffer.length) {
171-
throw new Error('Parser: read past end');
186+
var err = new Error('Parser: read past end');
187+
err.offset = (this._offset - this._packetOffset);
188+
err.code = 'PARSER_READ_PAST_END';
189+
throw err;
172190
}
173191

174192
var bits = this._buffer[this._offset++];
@@ -187,7 +205,10 @@ Parser.prototype.parseLengthCodedNumber = function parseLengthCodedNumber() {
187205
case 254:
188206
break;
189207
default:
190-
throw new Error('parseLengthCodedNumber: Unexpected first byte: 0x' + bits.toString(16));
208+
var err = new Error('Unexpected first byte' + (bits ? ': 0x' + bits.toString(16) : ''));
209+
err.offset = (this._offset - this._packetOffset - 1);
210+
err.code = 'PARSER_BAD_LENGTH_BYTE';
211+
throw err;
191212
}
192213

193214
var low = this.parseUnsignedNumber(4);
@@ -201,10 +222,13 @@ Parser.prototype.parseLengthCodedNumber = function parseLengthCodedNumber() {
201222
return value;
202223
}
203224

204-
throw new Error(
225+
var err = new Error(
205226
'parseLengthCodedNumber: JS precision range exceeded, ' +
206227
'number is >= 53 bit: "' + value + '"'
207228
);
229+
err.offset = (this._offset - this._packetOffset - 8);
230+
err.code = 'PARSER_JS_PRECISION_RANGE_EXCEEDED';
231+
throw err;
208232
}
209233

210234
value = low + (MUL_32BIT * high);
@@ -239,7 +263,10 @@ Parser.prototype._nullByteOffset = function() {
239263
offset++;
240264

241265
if (offset >= this._buffer.length) {
242-
throw new Error('Offset of null terminated string not found.');
266+
var err = new Error('Offset of null terminated string not found.');
267+
err.offset = (this._offset - this._packetOffset);
268+
err.code = 'PARSER_MISSING_NULL_BYTE';
269+
throw err;
243270
}
244271
}
245272

@@ -374,10 +401,12 @@ Parser.prototype._combineLongPacketBuffers = function() {
374401
this._longPacketBuffers = [];
375402
this._offset = 0;
376403
this._packetEnd = this._buffer.length - trailingPacketBytes;
404+
this._packetOffset = 0;
377405
};
378406

379407
Parser.prototype._advanceToNextPacket = function() {
380408
this._offset = this._packetEnd;
381409
this._packetHeader = null;
382410
this._packetEnd = null;
411+
this._packetOffset = null;
383412
};

lib/protocol/Protocol.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ function Protocol(options) {
2929
this._handshakeInitializationPacket = null;
3030

3131
this._parser = new Parser({
32-
onError : this.handleNetworkError.bind(this),
32+
onError : this.handleParserError.bind(this),
3333
onPacket : this._parsePacket.bind(this),
3434
config : this._config
3535
});
@@ -330,6 +330,15 @@ Protocol.prototype.handleNetworkError = function(err) {
330330
}
331331
};
332332

333+
Protocol.prototype.handleParserError = function handleParserError(err) {
334+
var sequence = this._queue[0];
335+
if (sequence) {
336+
sequence.end(err);
337+
} else {
338+
this._delegateError(err);
339+
}
340+
};
341+
333342
Protocol.prototype._delegateError = function(err, sequence) {
334343
// Stop delegating errors after the first fatal error
335344
if (this._fatalError) {

lib/protocol/packets/FieldPacket.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ FieldPacket.prototype.parse = function(parser) {
2828
this.orgName = parser.parseLengthCodedString();
2929

3030
if (parser.parseLengthCodedNumber() !== 0x0c) {
31-
throw new TypeError('Received invalid field length');
31+
var err = new TypeError('Received invalid field length');
32+
err.code = 'PARSER_INVALID_FIELD_LENGTH';
33+
throw err;
3234
}
3335

3436
this.charsetNr = parser.parseUnsignedNumber(2);
@@ -39,7 +41,9 @@ FieldPacket.prototype.parse = function(parser) {
3941

4042
var filler = parser.parseBuffer(2);
4143
if (filler[0] !== 0x0 || filler[1] !== 0x0) {
42-
throw new TypeError('Received invalid filler');
44+
var err = new TypeError('Received invalid filler');
45+
err.code = 'PARSER_INVALID_FILLER';
46+
throw err;
4347
}
4448

4549
// parsed flags
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
var assert = require('assert');
2+
var common = require('../../common');
3+
var connection = common.createConnection({port: common.fakeServerPort});
4+
var Packets = require(common.lib + '/protocol/packets');
5+
var PacketWriter = require(common.lib + '/protocol/PacketWriter');
6+
var server = common.createFakeServer();
7+
8+
server.listen(common.fakeServerPort, function(err) {
9+
assert.ifError(err);
10+
11+
connection.query('SELECT value FROM stuff', function (err) {
12+
assert.ok(err, 'got err');
13+
assert.equal(err.code, 'PARSER_READ_PAST_END');
14+
assert.equal(!!err.fatal, false);
15+
assert.equal(err.offset, 4);
16+
connection.destroy();
17+
server.destroy();
18+
});
19+
});
20+
21+
server.on('connection', function(conn) {
22+
conn.handshake();
23+
conn.on('query', function(packet) {
24+
switch (packet.sql) {
25+
case 'SELECT value FROM stuff':
26+
this._sendPacket(new Packets.ResultSetHeaderPacket({
27+
fieldCount: 1
28+
}));
29+
30+
var writer = new PacketWriter();
31+
writer.writeLengthCodedString('def');
32+
this._socket.write(writer.toBuffer(this._parser));
33+
this._parser.resetPacketNumber();
34+
break;
35+
default:
36+
this._handlePacketQuery(packet);
37+
break;
38+
}
39+
});
40+
});

0 commit comments

Comments
 (0)