complete redesign: use nodejs on server instead of php - documentation to be updated

This commit is contained in:
Marc Wäckerlin
2016-01-10 23:17:30 +00:00
parent 1c3765955b
commit a56d3118de
1376 changed files with 183732 additions and 1253 deletions

View File

@@ -0,0 +1,41 @@
var Sequence = require('./Sequence');
var Util = require('util');
var Packets = require('../packets');
var Auth = require('../Auth');
module.exports = ChangeUser;
Util.inherits(ChangeUser, Sequence);
function ChangeUser(options, callback) {
Sequence.call(this, options, callback);
this._user = options.user;
this._password = options.password;
this._database = options.database;
this._charsetNumber = options.charsetNumber;
this._currentConfig = options.currentConfig;
}
ChangeUser.prototype.start = function(handshakeInitializationPacket) {
var scrambleBuff = handshakeInitializationPacket.scrambleBuff();
scrambleBuff = Auth.token(this._password, scrambleBuff);
var packet = new Packets.ComChangeUserPacket({
user : this._user,
scrambleBuff : scrambleBuff,
database : this._database,
charsetNumber : this._charsetNumber
});
this._currentConfig.user = this._user;
this._currentConfig.password = this._password;
this._currentConfig.database = this._database;
this._currentConfig.charsetNumber = this._charsetNumber;
this.emit('packet', packet);
};
ChangeUser.prototype['ErrorPacket'] = function(packet) {
var err = this._packetToError(packet);
err.fatal = true;
this.end(err);
};

View File

@@ -0,0 +1,104 @@
var Sequence = require('./Sequence');
var Util = require('util');
var Packets = require('../packets');
var Auth = require('../Auth');
var ClientConstants = require('../constants/client');
module.exports = Handshake;
Util.inherits(Handshake, Sequence);
function Handshake(options, callback) {
Sequence.call(this, options, callback);
options = options || {};
this._config = options.config;
this._handshakeInitializationPacket = null;
}
Handshake.prototype.determinePacket = function(firstByte) {
if (firstByte === 0xff) {
return Packets.ErrorPacket;
}
if (!this._handshakeInitializationPacket) {
return Packets.HandshakeInitializationPacket;
}
if (firstByte === 0xfe) {
return Packets.UseOldPasswordPacket;
}
};
Handshake.prototype['HandshakeInitializationPacket'] = function(packet) {
this._handshakeInitializationPacket = packet;
this._config.protocol41 = packet.protocol41;
var serverSSLSupport = packet.serverCapabilities1 & ClientConstants.CLIENT_SSL;
if (this._config.ssl) {
if (!serverSSLSupport) {
var err = new Error('Server does not support secure connnection');
err.code = 'HANDSHAKE_NO_SSL_SUPPORT';
err.fatal = true;
this.end(err);
return;
}
this._config.clientFlags |= ClientConstants.CLIENT_SSL;
this.emit('packet', new Packets.SSLRequestPacket({
clientFlags : this._config.clientFlags,
maxPacketSize : this._config.maxPacketSize,
charsetNumber : this._config.charsetNumber
}));
this.emit('start-tls');
} else {
this._sendCredentials();
}
};
Handshake.prototype._tlsUpgradeCompleteHandler = function() {
this._sendCredentials();
};
Handshake.prototype._sendCredentials = function(serverHello) {
var packet = this._handshakeInitializationPacket;
this.emit('packet', new Packets.ClientAuthenticationPacket({
clientFlags : this._config.clientFlags,
maxPacketSize : this._config.maxPacketSize,
charsetNumber : this._config.charsetNumber,
user : this._config.user,
scrambleBuff : (packet.protocol41)
? Auth.token(this._config.password, packet.scrambleBuff())
: Auth.scramble323(packet.scrambleBuff(), this._config.password),
database : this._config.database,
protocol41 : packet.protocol41
}));
};
Handshake.prototype['UseOldPasswordPacket'] = function(packet) {
if (!this._config.insecureAuth) {
var err = new Error(
'MySQL server is requesting the old and insecure pre-4.1 auth mechanism.' +
'Upgrade the user password or use the {insecureAuth: true} option.'
);
err.code = 'HANDSHAKE_INSECURE_AUTH';
err.fatal = true;
this.end(err);
return;
}
this.emit('packet', new Packets.OldPasswordPacket({
scrambleBuff : Auth.scramble323(this._handshakeInitializationPacket.scrambleBuff(), this._config.password)
}));
};
Handshake.prototype['ErrorPacket'] = function(packet) {
var err = this._packetToError(packet, true);
err.fatal = true;
this.end(err);
};

View File

@@ -0,0 +1,19 @@
var Sequence = require('./Sequence');
var Util = require('util');
var Packets = require('../packets');
module.exports = Ping;
Util.inherits(Ping, Sequence);
function Ping(options, callback) {
if (!callback && typeof options === 'function') {
callback = options;
options = {};
}
Sequence.call(this, options, callback);
}
Ping.prototype.start = function() {
this.emit('packet', new Packets.ComPingPacket);
};

View File

@@ -0,0 +1,219 @@
var Sequence = require('./Sequence');
var Util = require('util');
var Packets = require('../packets');
var ResultSet = require('../ResultSet');
var ServerStatus = require('../constants/server_status');
var fs = require('fs');
var Readable = require('readable-stream');
module.exports = Query;
Util.inherits(Query, Sequence);
function Query(options, callback) {
Sequence.call(this, options, callback);
this.sql = options.sql;
this.values = options.values;
this.typeCast = (options.typeCast === undefined)
? true
: options.typeCast;
this.nestTables = options.nestTables || false;
this._resultSet = null;
this._results = [];
this._fields = [];
this._index = 0;
this._loadError = null;
}
Query.prototype.start = function() {
this.emit('packet', new Packets.ComQueryPacket(this.sql));
};
Query.prototype.determinePacket = function(firstByte, parser) {
if (firstByte === 0) {
// If we have a resultSet and got one eofPacket
if (this._resultSet && this._resultSet.eofPackets.length === 1) {
// Then this is a RowDataPacket with an empty string in the first column.
// See: https://github.com/felixge/node-mysql/issues/222
} else if (this._resultSet && this._resultSet.resultSetHeaderPacket
&& this._resultSet.resultSetHeaderPacket.fieldCount !== null) {
return Packets.FieldPacket;
} else {
return;
}
}
if (firstByte === 255) {
return;
}
// EofPacket's are 5 bytes in mysql >= 4.1
// This is the only / best way to differentiate their firstByte from a 9
// byte length coded binary.
if (firstByte === 0xfe && parser.packetLength() < 9) {
return Packets.EofPacket;
}
if (!this._resultSet) {
return Packets.ResultSetHeaderPacket;
}
return (this._resultSet.eofPackets.length === 0)
? Packets.FieldPacket
: Packets.RowDataPacket;
};
Query.prototype['OkPacket'] = function(packet) {
// try...finally for exception safety
try {
if (!this._callback) {
this.emit('result', packet, this._index);
} else {
this._results.push(packet);
this._fields.push(undefined);
}
} finally {
this._index++;
this._resultSet = null;
this._handleFinalResultPacket(packet);
}
};
Query.prototype['ErrorPacket'] = function(packet) {
var err = this._packetToError(packet);
var results = (this._results.length > 0)
? this._results
: undefined;
var fields = (this._fields.length > 0)
? this._fields
: undefined;
err.index = this._index;
this.end(err, results, fields);
};
Query.prototype['ResultSetHeaderPacket'] = function(packet) {
this._resultSet = new ResultSet(packet);
// used by LOAD DATA LOCAL INFILE queries
if (packet.fieldCount === null) {
this._sendLocalDataFile(packet.extra);
}
};
Query.prototype['FieldPacket'] = function(packet) {
this._resultSet.fieldPackets.push(packet);
};
Query.prototype['EofPacket'] = function(packet) {
this._resultSet.eofPackets.push(packet);
if (this._resultSet.eofPackets.length === 1 && !this._callback) {
this.emit('fields', this._resultSet.fieldPackets, this._index);
}
if (this._resultSet.eofPackets.length !== 2) {
return;
}
if (this._callback) {
this._results.push(this._resultSet.rows);
this._fields.push(this._resultSet.fieldPackets);
}
this._index++;
this._resultSet = null;
this._handleFinalResultPacket(packet);
};
Query.prototype._handleFinalResultPacket = function(packet) {
if (packet.serverStatus & ServerStatus.SERVER_MORE_RESULTS_EXISTS) {
return;
}
var results = (this._results.length > 1)
? this._results
: this._results[0];
var fields = (this._fields.length > 1)
? this._fields
: this._fields[0];
this.end(this._loadError, results, fields);
};
Query.prototype['RowDataPacket'] = function(packet, parser, connection) {
packet.parse(parser, this._resultSet.fieldPackets, this.typeCast, this.nestTables, connection);
if (this._callback) {
this._resultSet.rows.push(packet);
} else {
this.emit('result', packet, this._index);
}
};
Query.prototype._sendLocalDataFile = function(path) {
var self = this;
var localStream = fs.createReadStream(path, {
'flag': 'r',
'encoding': null,
'autoClose': true
});
this.on('pause', function () {
localStream.pause();
});
this.on('resume', function () {
localStream.resume();
});
localStream.on('data', function (data) {
self.emit('packet', new Packets.LocalDataFilePacket(data));
});
localStream.on('error', function (err) {
self._loadError = err;
localStream.emit('end');
});
localStream.on('end', function () {
self.emit('packet', new Packets.EmptyPacket());
});
};
Query.prototype.stream = function(options) {
var self = this,
stream;
options = options || {};
options.objectMode = true;
stream = new Readable(options);
stream._read = function() {
self._connection && self._connection.resume();
};
this.on('result',function(row,i) {
if (!stream.push(row)) self._connection.pause();
stream.emit('result',row,i); // replicate old emitter
});
this.on('error',function(err) {
stream.emit('error',err); // Pass on any errors
});
this.on('end', function() {
stream.emit('close'); // notify readers that query has completed
stream.push(null); // pushing null, indicating EOF
});
this.on('fields',function(fields,i) {
stream.emit('fields',fields,i); // replicate old emitter
});
return stream;
};

View File

@@ -0,0 +1,18 @@
var Sequence = require('./Sequence');
var Util = require('util');
var Packets = require('../packets');
module.exports = Quit;
Util.inherits(Quit, Sequence);
function Quit(options, callback) {
if (!callback && typeof options === 'function') {
callback = options;
options = {};
}
Sequence.call(this, options, callback);
}
Quit.prototype.start = function() {
this.emit('packet', new Packets.ComQuitPacket);
};

View File

@@ -0,0 +1,117 @@
var Util = require('util');
var EventEmitter = require('events').EventEmitter;
var Packets = require('../packets');
var ErrorConstants = require('../constants/errors');
var listenerCount = EventEmitter.listenerCount
|| function(emitter, type){ return emitter.listeners(type).length; };
module.exports = Sequence;
Util.inherits(Sequence, EventEmitter);
function Sequence(options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
}
EventEmitter.call(this);
options = options || {};
this._callback = callback;
this._callSite = null;
this._ended = false;
this._timeout = options.timeout;
// For Timers
this._idleNext = null;
this._idlePrev = null;
this._idleStart = null;
this._idleTimeout = undefined;
this._repeat = null;
}
Sequence.determinePacket = function(byte) {
switch (byte) {
case 0x00: return Packets.OkPacket;
case 0xfe: return Packets.EofPacket;
case 0xff: return Packets.ErrorPacket;
}
};
Sequence.prototype.hasErrorHandler = function() {
return Boolean(this._callback) || listenerCount(this, 'error') > 1;
};
Sequence.prototype._packetToError = function(packet) {
var code = ErrorConstants[packet.errno] || 'UNKNOWN_CODE_PLEASE_REPORT';
var err = new Error(code + ': ' + packet.message);
err.code = code;
err.errno = packet.errno;
err.sqlState = packet.sqlState;
return err;
};
Sequence.prototype._addLongStackTrace = function _addLongStackTrace(err) {
if (!this._callSite || !this._callSite.stack) {
return;
}
var delimiter = '\n --------------------\n';
if (err.stack.indexOf(delimiter) > -1) {
return;
}
err.stack += delimiter + this._callSite.stack.replace(/.+\n/, '');
};
Sequence.prototype.end = function(err) {
if (this._ended) {
return;
}
this._ended = true;
if (err) {
this._addLongStackTrace(err);
}
// Without this we are leaking memory. This problem was introduced in
// 8189925374e7ce3819bbe88b64c7b15abac96b16. I suspect that the error object
// causes a cyclic reference that the GC does not detect properly, but I was
// unable to produce a standalone version of this leak. This would be a great
// challenge for somebody interested in difficult problems : )!
this._callSite = null;
// try...finally for exception safety
try {
if (err) {
this.emit('error', err);
}
} finally {
try {
if (this._callback) {
this._callback.apply(this, arguments);
}
} finally {
this.emit('end');
}
}
};
Sequence.prototype['OkPacket'] = function(packet) {
this.end(null, packet);
};
Sequence.prototype['ErrorPacket'] = function(packet) {
this.end(this._packetToError(packet));
};
// Implemented by child classes
Sequence.prototype.start = function() {};
Sequence.prototype._onTimeout = function _onTimeout() {
this.emit('timeout');
};

View File

@@ -0,0 +1,28 @@
var Sequence = require('./Sequence');
var Util = require('util');
var Packets = require('../packets');
module.exports = Statistics;
Util.inherits(Statistics, Sequence);
function Statistics(options, callback) {
if (!callback && typeof options === 'function') {
callback = options;
options = {};
}
Sequence.call(this, options, callback);
}
Statistics.prototype.start = function() {
this.emit('packet', new Packets.ComStatisticsPacket);
};
Statistics.prototype['StatisticsPacket'] = function (packet) {
this.end(null, packet);
};
Statistics.prototype.determinePacket = function(firstByte, parser) {
if (firstByte === 0x55) {
return Packets.StatisticsPacket;
}
};

View File

@@ -0,0 +1,7 @@
exports.ChangeUser = require('./ChangeUser');
exports.Handshake = require('./Handshake');
exports.Ping = require('./Ping');
exports.Query = require('./Query');
exports.Quit = require('./Quit');
exports.Sequence = require('./Sequence');
exports.Statistics = require('./Statistics');