var net = require('net');
exports.connect = function(port, host) {
return new memcache(port, host);
}
function memcache(port, host) {
this.conn = null;
this.port = port || 11211;
this.host = host || '’;
this.error = ['ERROR’, 'NOT_FOUND’, 'CLIENT_ERROR’, ‘SERVER_ERROR’];
this.crlf = "
";
this.queue = [];
this.nextData = '’;
this.init();
}
memcache.prototype = {
init : function(){
this.buffer = new Buffer(‘’);
this.cmd = null;
this.callback = null;
this.receiveTimes = 0;
this.headLength = 0;
this.bodyLength = 0;
if (this.nextData.length > 0) this.receive(new Buffer(this.nextData));
},
connect : function(callback){
if (this.conn) return callback.call(this);
var conn = net.connect(this.port, this.host),
self = this;
conn.on('connect’, function(){
self.conn = conn;
callback.call(self);
});
conn.on('data’, function(data){
self.receive(data);
});
conn.on('error’, function(e){
console.log(e);
setTimeout(function(){
self.connect(callback);
}, 10);
});
},
fixKey : function(key) {
return key.substr(0, 250).replace(/\s/g, ‘_’);
},
query : function(data, callback, cmd) {
var idx = data.indexOf(' '),
cmd = cmd || (idx == -1 ? data : data.substr(0, idx)).toLowerCase();
this.connect(function(){
this.queue.push({
cmd : cmd,
callback : callback
});
this.conn.write(data + this.crlf);
});
},
send : function(cmd, key, value, callback, lifetime, flags) {
if (typeof(callback) != ‘function’) {
lifetime = callback;
callback = null;
}
try {
val = JSON.stringify(value);
} catch(e) {
try {
val = value.toString();
} catch(e) {
val = '’;
}
}
var flags = flags || 0,
lifetime = lifetime || 0,
buffer = new Buffer(val),
bufferLength = buffer.length || 0,
query = [cmd, this.fixKey(key), flags, lifetime, bufferLength];
this.query(query.join(' ') + this.crlf + val, callback);
},
receive : function(data) {
if (this.receiveTimes == 0) {
var last = this.queue.shift();
this.cmd = last.cmd;
this.callback = last.callback;
}
this.buffer = Buffer.concat([this.buffer, data]);
this.receiveTimes++;
result = thisthis.cmd+’Handler’;
if (result === null || result === undefined) return;
try {
this.callback(result);
} catch(e) {}
this.init();
},
add : function(key, value, callback, lifetime, flags) {
this.send('add’, key, value, callback, lifetime, flags);
},
set : function(key, value, callback, lifetime, flags) {
this.send('set’, key, value, callback, lifetime, flags);
},
replace : function(key, value, callback, lifetime, flags) {
this.send('replace’, key, value, callback, lifetime, flags);
},
get : function(key, callback) {
this.query('get '+this.fixKey(key), callback);
},
delete : function(key, callback) {
this.query('delete '+this.fixKey(key), callback);
},
flush : function(callback) {
this.query('flush_all’, callback, ‘flush’);
},
stats : function(callback) {
this.query('stats’, callback);
},
version : function(callback) {
this.query('version’, callback);
},
close : function(){
var self = this;
if (this.queue.length == 0) {
this.conn.end();
this.conn.destroy();
this.conn = null;
} else {
setTimeout(function(){
self.close();
}, 10);
}
},
getLine : function(data, line){
if (Buffer.isBuffer(data)) data = data.toString();
var line = line ? parseInt(line) : 0,
data = data.split("
");
return data[line] ? data[line]+"
" : false;
},
storeHandler : function(){
var line = this.getLine(this.buffer);
this.nextData = this.buffer.slice(new Buffer(line).length);
if (line.toLowerCase().trim() == ‘stored’) {
return true;
} else {
return false;
}
},
addHandler : function(){
return this.storeHandler();
},
setHandler : function(){
return this.storeHandler();
},
replaceHandler : function(){
return this.storeHandler();
},
getHandler : function(){
if (this.receiveTimes == 1) {
//第一次开始接收数据
var line = this.getLine(this.buffer),
header = line.split(/\s+/g);
//不存在此KEY
if (header[0].toLowerCase() == ‘end’) return false;
this.headLength = new Buffer(line).length;
this.bodyLength = parseInt(header[3]);
}
//实际buffer长度
var bufferLength = this.buffer.length,
//buffer应有的长度
dataLength = this.headLength + this.bodyLength + 'END’.length + 2 * this.crlf.length;
if (bufferLength >= dataLength) {
var body = this.buffer.slice(this.headLength, this.headLength + this.bodyLength).toString();
this.nextData = this.buffer.slice(dataLength).toString();
try {
return JSON.parse(body);
} catch(e) {
return body;
}
}
return null;
},
deleteHandler : function(){
var line = this.getLine(this.buffer);
this.nextData = this.buffer.slice(new Buffer(line).length);
if (line.toLowerCase().trim() == ‘deleted’) {
return true;
} else {
return false;
}
},
flushHandler : function(){
var line = this.getLine(this.buffer);
this.nextData = this.buffer.slice(new Buffer(line).length);
if (line.toLowerCase().trim() == ‘ok’) {
return true;
} else {
return false;
}
},
statsHandler : function(){
var buffer = this.buffer.toString(),
idx = buffer.indexOf(‘END’);
if (idx != -1) {
lines = buffer.trim().split("
");
var stats = {};
for(var i=0; i<lines.length-1; i++) {
var line = lines[i].trim().split(/\s+/g);
stats[line[1]] = line[2];
}
this.nextData = new Buffer(buffer.substr(idx + 'END’.length + this.crlf.length));
return stats;
}
return null;
},
versionHandler : function(){
var line = this.getLine(this.buffer),
header = line.split(/\s+/);
this.nextData = this.buffer.slice(new Buffer(line).length);
return header[1];
}
}