// ----------------------------------------------------------------------
// File: RedisParser.cc
// Author: Georgios Bitzes - CERN
// ----------------------------------------------------------------------
/************************************************************************
* quarkdb - a redis-like highly available key-value store *
* Copyright (C) 2016 CERN/Switzerland *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see .*
************************************************************************/
#include "RedisParser.hh"
#include "utils/Macros.hh"
#include
using namespace quarkdb;
RedisParser::RedisParser(Link *l) : reader(l) {
}
int RedisParser::purge() {
request_size = 0;
current_element = 0;
element_size = -1;
current_integer.clear();
current_request.clear();
std::string buff;
buff.resize(1024);
while(true) {
int rlen = reader.consume(1023, buff);
if(rlen <= 0) return rlen;
}
}
int RedisParser::fetch(RedisRequest &req, bool authenticated) {
if(request_size == 0) {
req.clear();
// new request to process from scratch
int retcode = readInteger('*', request_size);
if(retcode <= 0) return retcode;
element_size = -1;
current_element = 0;
if(!authenticated && request_size >= 6) {
qdb_warn("Unauthenticated client attempted to send request with " << request_size << " elements - shutting the connection down");
return -2;
}
req.resize(request_size);
}
for( ; current_element < request_size; current_element++) {
int rc = readElement(req.getPinnedBuffer(current_element), authenticated);
if(rc <= 0) return rc;
element_size = -1;
}
request_size = 0;
req.parseCommand();
if(encounteredZeroSize) {
qdb_warn("Encountered request with zero-sized string - shutting the connection down: " << req.toPrintableString());
return -2;
}
return 1;
}
int RedisParser::readInteger(char prefix, int &retval) {
std::string prev;
while(prev[0] != '\n') {
int rlen = reader.consume(1, prev);
if(rlen <= 0) return rlen;
current_integer.append(prev);
}
if(current_integer[0] != prefix) {
qdb_warn("Redis protocol error, expected an integer with preceeding " << quotes(prefix) << ", received " << quotes(current_integer[0]) << " instead (byte in decimal: " << int(current_integer[0]) << ")");
return -1;
}
if(current_integer[current_integer.size()-2] != '\r') {
qdb_warn("Redis protocol error, received \\n without preceeding \\r");
return -1;
}
current_integer.erase(current_integer.size()-2, 2);
char *endptr;
long num = strtol(current_integer.c_str()+1, &endptr, 10);
if(*endptr != '\0' || num == LONG_MIN || num == LONG_MAX) {
qdb_warn("Redis protocol error, received an invalid integer");
return -1;
}
current_integer = "";
retval = num;
return 1; // success
}
int RedisParser::readElement(PinnedBuffer &str, bool authenticated) {
if(element_size == -1) {
int retcode = readInteger('$', element_size);
if(retcode <= 0) return retcode;
if(element_size == 0) encounteredZeroSize = true;
}
if(!authenticated && element_size >= 1048576) {
qdb_warn("Unauthenticated client attempted to send request containing element with " << element_size << " bytes - shutting the connection down");
return -2;
}
return readString(element_size, str);
}
int RedisParser::readString(int nbytes, PinnedBuffer &str) {
int rlen = reader.consume(nbytes+2, str);
if(rlen <= 0) return rlen;
if(str[str.size()-2] != '\r') {
qdb_warn("Redis protocol error, expected \\r, received " << str[str.size()-2]);
return -1;
}
if(str[str.size()-1] != '\n') {
qdb_warn("Redis protocol error, expected \\n, received " << str[str.size()-1]);
return -1;
}
str.remove_suffix(2);
return rlen;
}