//------------------------------------------------------------------------------ // File: MessageParser.cc // Author: Georgios Bitzes - CERN //------------------------------------------------------------------------------ /************************************************************************ * qclient - A simple redis C++ client with support for redirects * * 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 "MessageParser.hh" #include "qclient/pubsub/Message.hh" #include namespace qclient { //------------------------------------------------------------------------------ // Return true if reply is the given string. //------------------------------------------------------------------------------ static bool doesMatchString(const redisReply *reply, const std::string &str) { if(reply->type != REDIS_REPLY_STRING) { return false; } if( (size_t)reply->len != str.size()) { return false; } if(str.compare(0, str.size(), reply->str) != 0) { return false; } return true; } //------------------------------------------------------------------------------ // Return true if given reply is a string, and extract it. //------------------------------------------------------------------------------ static bool extractString(const redisReply *reply, std::string &out) { if(reply->type != REDIS_REPLY_STRING) { return false; } out = std::string(reply->str, reply->len); return true; } //------------------------------------------------------------------------------ // Return true if given reply is an integer, and extract it. //------------------------------------------------------------------------------ static bool extractInteger(const redisReply *reply, int &out) { if(reply->type != REDIS_REPLY_INTEGER) { return false; } out = reply->integer; return true; } //------------------------------------------------------------------------------ // Given a redisReplyPtr from the server, determine if this is a pub-sub // message, and if so, parse its contents. //------------------------------------------------------------------------------ bool MessageParser::parse(redisReplyPtr &&reply, Message &out) { out.clear(); if(!reply) { //-------------------------------------------------------------------------- // Invalid parameter //-------------------------------------------------------------------------- return false; } size_t baseIdx = 0; if(reply->type == REDIS_REPLY_ARRAY) { //-------------------------------------------------------------------------- // Array type, base index starts from 0 //-------------------------------------------------------------------------- baseIdx = 0; } else if(reply->type == REDIS_REPLY_PUSH) { //-------------------------------------------------------------------------- // Push type, ensure first element is pubsub //-------------------------------------------------------------------------- if(strncmp(reply->str, "pubsub", reply->len) != 0) { return false; } baseIdx = 1; } else { //-------------------------------------------------------------------------- // Nope, can't parse //-------------------------------------------------------------------------- return false; } //---------------------------------------------------------------------------- // Is this a kMessage? //---------------------------------------------------------------------------- if(doesMatchString(reply->element[baseIdx], "message")) { if(reply->elements != baseIdx+3) return false; out.messageType = MessageType::kMessage; if(!extractString(reply->element[baseIdx+1], out.channel)) return false; if(!extractString(reply->element[baseIdx+2], out.payload)) return false; return true; } //---------------------------------------------------------------------------- // Is this a kPatternMessage? //---------------------------------------------------------------------------- if(doesMatchString(reply->element[baseIdx], "pmessage")) { if(reply->elements != baseIdx+4) return false; out.messageType = MessageType::kPatternMessage; if(!extractString(reply->element[baseIdx+1], out.pattern)) return false; if(!extractString(reply->element[baseIdx+2], out.channel)) return false; if(!extractString(reply->element[baseIdx+3], out.payload)) return false; return true; } //---------------------------------------------------------------------------- // Is this a kSubscribe? //---------------------------------------------------------------------------- if(doesMatchString(reply->element[baseIdx], "subscribe")) { if(reply->elements != baseIdx+3) return false; out.messageType = MessageType::kSubscribe; if(!extractString(reply->element[baseIdx+1], out.channel)) return false; if(!extractInteger(reply->element[baseIdx+2], out.activeSubscriptions)) return false; return true; } //---------------------------------------------------------------------------- // Is this a kPatternSubscribe? //---------------------------------------------------------------------------- if(doesMatchString(reply->element[baseIdx], "psubscribe")) { if(reply->elements != baseIdx+3) return false; out.messageType = MessageType::kPatternSubscribe; if(!extractString(reply->element[baseIdx+1], out.pattern)) return false; if(!extractInteger(reply->element[baseIdx+2], out.activeSubscriptions)) return false; return true; } //---------------------------------------------------------------------------- // Is this a kUnsubscribe? //---------------------------------------------------------------------------- if(doesMatchString(reply->element[baseIdx], "unsubscribe")) { if(reply->elements != baseIdx+3) return false; out.messageType = MessageType::kUnsubscribe; if(!extractString(reply->element[baseIdx+1], out.channel)) return false; if(!extractInteger(reply->element[baseIdx+2], out.activeSubscriptions)) return false; return true; } //---------------------------------------------------------------------------- // Is this a kPatternUnsubscribe? //---------------------------------------------------------------------------- if(doesMatchString(reply->element[baseIdx], "punsubscribe")) { if(reply->elements != baseIdx+3) return false; out.messageType = MessageType::kPatternUnsubscribe; if(!extractString(reply->element[baseIdx+1], out.pattern)) return false; if(!extractInteger(reply->element[baseIdx+2], out.activeSubscriptions)) return false; return true; } return false; } }