Skip to content

Commit b53f6e2

Browse files
Merge pull request #374 from sidorares/fix-char-encoding
Fixes #302
2 parents ac28fb6 + 96d6e56 commit b53f6e2

39 files changed

+475
-154
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ node_js:
88
- '0.12'
99
- '4.5'
1010
- '5.12'
11-
- '6.4'
11+
- '6.5'
1212

1313

1414
script:

examples/server.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ server.on('connection', function(conn) {
2626
connectionId: 1234,
2727
statusFlags: 2,
2828
characterSet: 8,
29-
capabilityFlags: 0xffffff,
29+
//capabilityFlags: 0xffffff,
30+
//capabilityFlags: -2113931265,
31+
capabilityFlags: 2181036031,
3032
authCallback: authenticate
3133
});
3234

index.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,14 @@ exports.__defineGetter__('createPoolClusterPromise', function () {
4646
return require('./promise.js').createPoolCluster;
4747
});
4848

49-
module.exports.Types = require('./lib/constants/types.js');
49+
exports.__defineGetter__('Types', function () {
50+
return require('./lib/constants/types.js');
51+
});
52+
53+
exports.__defineGetter__('Charsets', function () {
54+
return require('./lib/constants/charsets.js');
55+
});
56+
57+
exports.__defineGetter__('CharsetToEncoding', function () {
58+
return require('./lib/constants/charset_encodings.js');
59+
});

lib/commands/client_handshake.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ var util = require('util');
33
var Command = require('./command.js');
44
var Packets = require('../packets/index.js');
55
var ClientConstants = require('../constants/client.js');
6+
var CharsetToEncoding = require('../constants/charset_encodings.js');
67

78
function ClientHandshake (clientFlags)
89
{
@@ -17,7 +18,7 @@ ClientHandshake.prototype.start = function () {
1718
};
1819

1920
ClientHandshake.prototype.sendSSLRequest = function (connection) {
20-
var sslRequest = new Packets.SSLRequest(this.clientFlags);
21+
var sslRequest = new Packets.SSLRequest(this.clientFlags, connection.config.charsetNumber);
2122
connection.writePacket(sslRequest.toPacket());
2223
};
2324

@@ -85,6 +86,7 @@ ClientHandshake.prototype.handshakeInit = function (helloPacket, connection) {
8586
flagNames(this.handshake.capabilityFlags).join(', '));
8687
}
8788
connection.serverCapabilityFlags = this.handshake.capabilityFlags;
89+
connection.serverEncoding = CharsetToEncoding[this.handshake.characterSet];
8890
connection.connectionId = this.handshake.connectionId;
8991
var serverSSLSupport = this.handshake.capabilityFlags & ClientConstants.SSL;
9092

lib/commands/command.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Command.prototype.execute = function (packet, connection) {
2525
}
2626

2727
if (packet && packet.isError()) {
28-
var err = packet.asError();
28+
var err = packet.asError(connection.serverEncoding);
2929
if (this.onResult) {
3030
this.onResult(err);
3131
} else {

lib/commands/execute.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ Execute.prototype.buildParserFromFields = function (fields, connection) {
4040
};
4141

4242
Execute.prototype.start = function (packet, connection) {
43-
var executePacket = new Packets.Execute(this.statement.id, this.parameters);
43+
var executePacket = new Packets.Execute(this.statement.id, this.parameters, connection.config.charsetNumber);
4444
connection.writePacket(executePacket.toPacket(1));
4545
return Execute.prototype.resultsetHeader;
4646
};

lib/commands/prepare.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ function Prepare (options, callback)
2121
util.inherits(Prepare, Command);
2222

2323
Prepare.prototype.start = function (packet, connection) {
24-
connection.writePacket(new Packets.PrepareStatement(this.query).toPacket(1));
24+
var cmdPacket = new Packets.PrepareStatement(this.query, connection.config.charsetNumber);
25+
connection.writePacket(cmdPacket.toPacket(1));
2526
return Prepare.prototype.prepareHeader;
2627
};
2728

lib/commands/query.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ Query.prototype.start = function (packet, connection) {
3535
console.log(' Sending query command: %s', this.sql);
3636
}
3737
this._connection = connection;
38-
var cmdPacket = new Packets.Query(this.sql);
38+
var cmdPacket = new Packets.Query(this.sql, connection.config.charsetNumber);
3939
connection.writePacket(cmdPacket.toPacket(1));
4040
return Query.prototype.resultsetHeader;
4141
};
@@ -85,7 +85,7 @@ Query.prototype.doneInsert = function (rs) {
8585
};
8686

8787
Query.prototype.resultsetHeader = function (packet, connection) {
88-
var rs = new Packets.ResultSetHeader(packet, connection.config.bigNumberStrings);
88+
var rs = new Packets.ResultSetHeader(packet, connection.config.bigNumberStrings, connection.serverEncoding);
8989
this._fieldCount = rs.fieldCount;
9090
if (connection.config.debug) {
9191
console.log(' Resultset header received, expecting ' + rs.fieldCount + ' column definition packets');

lib/commands/server_handshake.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ ServerHandshake.prototype.start = function (packet, connection) {
3939
ServerHandshake.prototype.readClientReply = function (packet, connection) {
4040
// check auth here
4141
var clientHelloReply = new Packets.HandshakeResponse.fromPacket(packet);
42+
43+
// TODO check we don't have something similar already
44+
connection.clientHelloReply = clientHelloReply;
45+
4246
if (this.args.authCallback) {
4347
try {
4448
this.args.authCallback({
@@ -73,6 +77,7 @@ ServerHandshake.prototype.readClientReply = function (packet, connection) {
7377
ServerHandshake.prototype.dispatchCommands = function (packet, connection) {
7478
// command from client to server
7579
var knownCommand = true;
80+
var encoding = connection.clientHelloReply.encoding;
7681
var commandCode = packet.readInt8();
7782
switch (commandCode) {
7883
case CommandCode.QUIT:
@@ -85,7 +90,7 @@ ServerHandshake.prototype.dispatchCommands = function (packet, connection) {
8590

8691
case CommandCode.INIT_DB:
8792
if (connection.listeners('init_db').length) {
88-
var schemaName = packet.readString();
93+
var schemaName = packet.readString(encoding);
8994
connection.emit('init_db', schemaName);
9095
} else {
9196
connection.writeOk();
@@ -94,7 +99,7 @@ ServerHandshake.prototype.dispatchCommands = function (packet, connection) {
9499

95100
case CommandCode.QUERY:
96101
if (connection.listeners('query').length) {
97-
var query = packet.readString();
102+
var query = packet.readString(undefined, encoding);
98103
connection.emit('query', query);
99104
} else {
100105
connection.writeError({
@@ -107,7 +112,7 @@ ServerHandshake.prototype.dispatchCommands = function (packet, connection) {
107112
case CommandCode.FIELD_LIST:
108113
if (connection.listeners('field_list').length) {
109114
var table = packet.readNullTerminatedString();
110-
var fields = packet.readString();
115+
var fields = packet.readString(encoding);
111116
connection.emit('field_list', table, fields);
112117
} else {
113118
connection.writeError({

lib/compile_binary_parser.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ var vm = require('vm');
22

33
var FieldFlags = require('./constants/field_flags.js');
44
var Charsets = require('./constants/charsets.js');
5+
var CharsetToEncoding = require('./constants/charset_encodings.js');
56
var Types = require('./constants/types.js');
67
var srcEscape = require('./helpers').srcEscape;
78

@@ -98,7 +99,6 @@ function compile (fields, options, config) {
9899
function readCodeFor (field, config, options) {
99100
var supportBigNumbers = options.supportBigNumbers || config.supportBigNumbers;
100101
var bigNumberStrings = options.bigNumberStrings || config.bigNumberStrings;
101-
102102
var unsigned = field.flags & FieldFlags.UNSIGNED;
103103
switch (field.columnType) {
104104
case Types.TINY:
@@ -131,11 +131,11 @@ function readCodeFor (field, config, options) {
131131
if (config.decimalNumbers) {
132132
return 'packet.parseLengthCodedFloat();';
133133
}
134-
return 'packet.readLengthCodedString();';
134+
return 'packet.readLengthCodedString("ascii");';
135135
case Types.GEOMETRY:
136136
return 'packet.parseGeometryValue();';
137137
case Types.JSON:
138-
return 'JSON.parse(packet.readLengthCodedString());';
138+
return 'JSON.parse(packet.readLengthCodedString("' + CharsetToEncoding[field.characterSet] + '"));';
139139
case Types.LONGLONG:
140140
if (!supportBigNumbers) {
141141
return unsigned ? 'packet.readInt64JSNumber();' : 'packet.readSInt64JSNumber();';
@@ -150,7 +150,7 @@ function readCodeFor (field, config, options) {
150150
if (field.characterSet == Charsets.BINARY) {
151151
return 'packet.readLengthCodedBuffer();';
152152
} else {
153-
return 'packet.readLengthCodedString();';
153+
return 'packet.readLengthCodedString("' + CharsetToEncoding[field.characterSet] + '")';
154154
}
155155
}
156156
}

lib/compile_text_parser.js

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
var Types = require('./constants/types');
2-
var Charsets = require('./constants/charsets');
1+
var Types = require('./constants/types.js');
2+
var Charsets = require('./constants/charsets.js');
3+
var CharsetToEncoding = require('./constants/charset_encodings.js');
34
var vm = require('vm');
45
var srcEscape = require('./helpers').srcEscape;
56

@@ -13,18 +14,18 @@ function compile (fields, options, config) {
1314

1415
// node-mysql typeCast compatibility wrapper
1516
// see https://github.com/mysqljs/mysql/blob/96fdd0566b654436624e2375c7b6604b1f50f825/lib/protocol/packets/Field.js
16-
function wrap(field, type, packet) {
17+
function wrap (field, type, packet, encoding) {
1718
return {
1819
type: type,
1920
length: field.columnLength,
2021
db: field.schema,
2122
table: field.table,
2223
name: field.name,
23-
string: function() { return packet.readLengthCodedString(); },
24+
string: function () { return packet.readLengthCodedString(encoding); },
2425
buffer: function () { return packet.readLengthCodedBuffer(); },
2526
geometry: function () { return packet.parseGeometryValue(); }
2627
};
27-
};
28+
}
2829

2930
var result = [];
3031
var i = 0;
@@ -73,9 +74,10 @@ function compile (fields, options, config) {
7374
} else {
7475
lvalue = ' this[' + srcEscape(fields[i].name) + ']';
7576
}
76-
var readCode = readCodeFor(fields[i].columnType, fields[i].characterSet, config, options);
77+
var encoding = CharsetToEncoding[fields[i].characterSet];
78+
var readCode = readCodeFor(fields[i].columnType, fields[i].characterSet, encoding, config, options);
7779
if (typeof options.typeCast === 'function') {
78-
result.push(lvalue + ' = options.typeCast(wrap(fields[' + i + '], ' + srcEscape(typeNames[fields[i].columnType]) + ', packet), function() { return ' + readCode + ';})');
80+
result.push(lvalue + ' = options.typeCast(wrap(fields[' + i + '], ' + srcEscape(typeNames[fields[i].columnType]) + ', packet, "' + encoding + '"), function() { return ' + readCode + ';})');
7981
} else if (options.typeCast === false) {
8082
result.push(lvalue + ' = packet.readLengthCodedBuffer();');
8183
} else {
@@ -98,7 +100,7 @@ function compile (fields, options, config) {
98100
return vm.runInThisContext(src);
99101
}
100102

101-
function readCodeFor (type, charset, config, options) {
103+
function readCodeFor (type, charset, encoding, config, options) {
102104
var supportBigNumbers = options.supportBigNumbers || config.supportBigNumbers;
103105
var bigNumberStrings = options.bigNumberStrings || config.bigNumberStrings;
104106

@@ -124,29 +126,29 @@ function readCodeFor (type, charset, config, options) {
124126
if (config.decimalNumbers) {
125127
return 'packet.parseLengthCodedFloat()';
126128
}
127-
return 'packet.readLengthCodedString()';
129+
return 'packet.readLengthCodedString("ascii")';
128130
case Types.DATE:
129131
if (config.dateStrings) {
130-
return 'packet.readLengthCodedString()';
132+
return 'packet.readLengthCodedString("ascii")';
131133
}
132134
return 'packet.parseDate()';
133135
case Types.DATETIME:
134136
case Types.TIMESTAMP:
135137
if (config.dateStrings) {
136-
return 'packet.readLengthCodedString()';
138+
return 'packet.readLengthCodedString("ascii")';
137139
}
138140
return 'packet.parseDateTime()';
139141
case Types.TIME:
140-
return 'packet.readLengthCodedString()';
142+
return 'packet.readLengthCodedString("ascii")';
141143
case Types.GEOMETRY:
142144
return 'packet.parseGeometryValue()';
143145
case Types.JSON:
144-
return 'JSON.parse(packet.readLengthCodedString())';
146+
return 'JSON.parse(packet.readLengthCodedString("' + encoding + '"))';
145147
default:
146148
if (charset == Charsets.BINARY) {
147149
return 'packet.readLengthCodedBuffer()';
148150
} else {
149-
return 'packet.readLengthCodedString()';
151+
return 'packet.readLengthCodedString("' + encoding + '")';
150152
}
151153
}
152154
}

lib/connection.js

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ var Packet = require('./packets/packet.js');
1111
var Packets = require('./packets/index.js');
1212
var Commands = require('./commands/index.js');
1313
var ConnectionConfig = require('./connection_config.js');
14+
var CharsetToEncoding = require('./constants/charset_encodings.js');
1415

1516
var _connectionId = 0;
1617
var noop = function () {};
@@ -76,7 +77,7 @@ function Connection (opts)
7677

7778
this._outOfOrderPackets = [];
7879

79-
this.stream.once('error', function(err) {
80+
this.stream.once('error', function (err) {
8081
connection._handleNetworkError(err);
8182
});
8283

@@ -108,15 +109,15 @@ function Connection (opts)
108109
connection.threadId = handshakeCommand.handshake.connectionId;
109110
connection.emit('connect', handshakeCommand.handshake);
110111
});
111-
handshakeCommand.on('error', function(err) {
112+
handshakeCommand.on('error', function (err) {
112113
connection._notifyError(err);
113114
});
114115
this.addCommand(handshakeCommand);
115116
}
116117
}
117118
util.inherits(Connection, EventEmitter);
118119

119-
Connection.prototype._handleNetworkError = function(err) {
120+
Connection.prototype._handleNetworkError = function (err) {
120121
var connection = this;
121122
err.fatal = true;
122123
// stop receiving packets
@@ -176,6 +177,7 @@ Connection.prototype.writePacket = function (packet) {
176177
packet.writeHeader(this.sequenceId);
177178
if (this.config.debug) {
178179
console.log(this._internalId + ' ' + this.connectionId + ' <== ' + this._command._commandName + '#' + this._command.stateName() + '(' + [this.sequenceId, packet._name, packet.length()].join(',') + ')');
180+
console.log(this._internalId + ' ' + this.connectionId + ' <== ' + packet.buffer.toString('hex'));
179181
}
180182
this.sequenceId++;
181183
if (this.sequenceId == 256) {
@@ -201,24 +203,24 @@ if (tls.TLSSocket) {
201203
});
202204

203205
var rejectUnauthorized = this.config.ssl.rejectUnauthorized;
204-
var secureEstablished = false;
205-
var secureSocket = new tls.TLSSocket(connection.stream, {
206+
var secureEstablished = false;
207+
var secureSocket = new tls.TLSSocket(connection.stream, {
206208
rejectUnauthorized : rejectUnauthorized,
207209
requestCert : true,
208210
secureContext : secureContext,
209211
isServer : false
210212
});
211213

212214
// error handler for secure socket
213-
secureSocket.on('_tlsError', function(err) {
215+
secureSocket.on('_tlsError', function (err) {
214216
if (secureEstablished) {
215217
connection._handleNetworkError(err);
216218
} else {
217219
onSecure(err);
218220
}
219221
});
220222

221-
secureSocket.on('secure', function() {
223+
secureSocket.on('secure', function () {
222224
secureEstablished = true;
223225
onSecure(rejectUnauthorized ? this.ssl.verifyError() : null);
224226
});
@@ -439,7 +441,7 @@ Connection.prototype.resume = function resume () {
439441
};
440442

441443
Connection.prototype.keyFromFields = function keyFromFields (fields, options) {
442-
var res = (typeof options.nestTables) + '/' + options.nestTables + '/' + options.rowsAsArray +
444+
var res = (typeof options.nestTables) + '/' + options.nestTables + '/' + options.rowsAsArray
443445
+ options.supportBigNumbers + '/' + options.bigNumberStrings;
444446
for (var i = 0; i < fields.length; ++i) {
445447
res += '/' + fields[i].name + ':' + fields[i].columnType + ':' + fields[i].flags;
@@ -660,14 +662,14 @@ Connection.prototype.writeColumns = function (columns) {
660662
var connection = this;
661663
this.writePacket(Packets.ResultSetHeader.toPacket(columns.length));
662664
columns.forEach(function (column) {
663-
connection.writePacket(Packets.ColumnDefinition.toPacket(column));
665+
connection.writePacket(Packets.ColumnDefinition.toPacket(column, connection.serverConfig.encoding));
664666
});
665667
this.writeEof();
666668
};
667669

668670
// row is array of columns, not hash
669671
Connection.prototype.writeTextRow = function (column) {
670-
this.writePacket(Packets.TextRow.toPacket(column));
672+
this.writePacket(Packets.TextRow.toPacket(column, this.serverConfig.encoding));
671673
};
672674

673675
Connection.prototype.writeTextResult = function (rows, columns) {
@@ -691,14 +693,18 @@ Connection.prototype.writeOk = function (args) {
691693
if (!args) {
692694
args = {affectedRows: 0};
693695
}
694-
this.writePacket(Packets.OK.toPacket(args));
696+
this.writePacket(Packets.OK.toPacket(args, this.serverConfig.encoding));
695697
};
696698

697699
Connection.prototype.writeError = function (args) {
698-
this.writePacket(Packets.Error.toPacket(args));
700+
// if we want to send error before initial hello was sent, use default encoding
701+
var encoding = this.serverConfig ? this.serverConfig.encoding : 'cesu8';
702+
this.writePacket(Packets.Error.toPacket(args, this.serverConfig.encoding));
699703
};
700704

701705
Connection.prototype.serverHandshake = function serverHandshake (args) {
706+
this.serverConfig = args;
707+
this.serverConfig.encoding = CharsetToEncoding[this.serverConfig.characterSet];
702708
return this.addCommand(new Commands.ServerHandshake(args));
703709
};
704710

0 commit comments

Comments
 (0)