Skip to content
This repository was archived by the owner on Feb 13, 2022. It is now read-only.
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 89 additions & 19 deletions lib/memcache.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,31 @@

var tcp = require('net'),
util = require('util');
var crlf = "\r\n";

var crlf = "\r\n";
var crlf_len = crlf.length;

var endTag = 'END\r\n';
var endTag_len = endTag.length;

var fullEndTag = '\r\nEND\r\n';
var fullEndTag_len = fullEndTag.length;

var error_replies = ['ERROR', 'NOT_FOUND', 'CLIENT_ERROR', 'SERVER_ERROR'];

isArray = Array.isArray || function(obj) {
return toString.call(obj) == '[object Array]';
};

trimLeft = /^\s+/
trimRight = /\s+$/
trim = String.prototype.trim || function(text) {
return text == null ?
"" :
text.toString().replace( trimLeft, "" ).replace( trimRight, "" )
}


var Client = exports.Client = function(port, host) {
this.port = port || 11211;
this.host = host || 'localhost';
Expand All @@ -53,22 +72,22 @@ Client.prototype.connect = function () {
this.setNoDelay();
self.emit("connect");
self.dispatchHandles();
});
});

this.conn.addListener("data", function (data) {
self.buffer += data;
// util.debug(data);
self.recieves += 1;
self.handle_received_data();
});

this.conn.addListener("end", function () {
if (self.conn && self.conn.readyState) {
self.conn.end();
self.conn = null;
}
});

this.conn.addListener("close", function () {
self.conn = null;
self.emit("close");
Expand All @@ -88,7 +107,7 @@ Client.prototype.connect = function () {

Client.prototype.addHandler = function(callback) {
this.handles.push(callback);

if (this.conn.readyState == 'open') {
this.dispatchHandles();
}
Expand Down Expand Up @@ -118,7 +137,22 @@ Client.prototype.close = function() {
};

Client.prototype.get = function(key, callback) {
return this.query('get ' + key, 'get', callback);
// allow for multi get calls
var keyIsArray = false;
if ( isArray(key) ) {
keyIsArray = true;
// replace whitespace with length > 2 with single space
key = key.join(' ').replace(/\s{2,}/, " ");
key = trim.call(key);
}

key = trim.call(key);

if (keyIsArray || (key.indexOf(" ") != -1)) {
return this.query('get ' + key, 'get_multi', callback);
} else {
return this.query('get ' + key, 'get', callback);
}
};


Expand Down Expand Up @@ -163,7 +197,7 @@ Client.prototype.cas = function(key, value, unique, callback, lifetime, flags) {

Client.prototype.del = function(key, callback){
util.error("mc.del() is deprecated - use mc.delete() instead");
return this.delete(key, callback);
return this.delete(key, callback);
};

Client.prototype.delete = function(key, callback){
Expand Down Expand Up @@ -211,7 +245,7 @@ Client.prototype.stats = function(type, callback){
}

Client.prototype.handle_received_data = function(){

while (this.buffer.length > 0){

var result = this.determine_reply_handler(this.buffer);
Expand Down Expand Up @@ -247,7 +281,7 @@ Client.prototype.determine_reply_handler = function (buffer){
if (crlf_at == -1){
return null;
}

// determine errors
for (var error_idx in error_replies){
var error_indicator = error_replies[error_idx];
Expand All @@ -268,26 +302,62 @@ Client.prototype.determine_reply_handler = function (buffer){
Client.prototype.handle_get = function(buffer) {
var next_result_at = 0;
var result_value = null;
var end_indicator_len = 3;
var result_len = 0;
if (buffer.indexOf('END') == 0) {
return [result_value, end_indicator_len + crlf_len];
} else if (buffer.indexOf('VALUE') == 0 && buffer.indexOf('END') != -1) {

if (buffer.indexOf(endTag) == 0) {
return [result_value, endTag_len];
} else if (buffer.indexOf('VALUE') == 0 && buffer.indexOf(fullEndTag) != -1) {
first_line_len = buffer.indexOf(crlf) + crlf_len;
var end_indicator_start = buffer.indexOf('END');
result_len = end_indicator_start - first_line_len - crlf_len;
result_value = buffer.substr(first_line_len, result_len);
return [result_value, first_line_len + parseInt(result_len, 10) + crlf_len + end_indicator_len + crlf_len]
return [result_value, first_line_len + parseInt(result_len, 10) + fullEndTag_len]
} else {
var first_line_len = buffer.indexOf(crlf) + crlf_len;
var result_len = buffer.substr(0, first_line_len).split(' ')[3];
result_value = buffer.substr(first_line_len, result_len);

return [result_value, first_line_len + parseInt(result_len ) + crlf_len + end_indicator_len + crlf_len];

return [result_value, first_line_len + parseInt(result_len, 10) + fullEndTag_len];
}
};


Client.prototype.handle_get_multi = function(buffer) {

if (buffer.indexOf(endTag) == 0) {
// received nothing. return a lonely result
return [{}, endTag_len, null];
}

var end_tag_pos = buffer.indexOf(fullEndTag);
if ( (buffer.indexOf('VALUE ') == 0) && (end_tag_pos != -1) ) {
// got valid stuff!

// split by new lines to get...
// ["Value KEY1 X Y", "RESULT #1","VALUE KEY2 X Y", "RESULT #2", ...., "END", ""]
results = buffer.split(crlf);

results = results.slice(0, -2); // remove ["END", ""]
resultsLength = results.length;

// ret[key] = value for key and value within arr
var ret = {};
var resultKey;
for (var i = 0; i < resultsLength; i += 2) {
resultKey = results[i].split(' ')[1]; // retrieve key name
ret[resultKey] = results[i + 1]; // retrieve value and store
}

return [ret, end_tag_pos + fullEndTag_len, null]
} else {

// still has more to receive...
// return null to avoid work... no need to parse results to break right afterwards
return null;
}
};


Client.prototype.handle_stats = function(buffer){

// special case - no stats at all
Expand Down