From 5c9f49e2123c5df7148939a70b80cd72e4e59646 Mon Sep 17 00:00:00 2001 From: Michael Grunder Date: Tue, 19 May 2020 12:56:02 -0700 Subject: Resp3 verbatim string support (#805) Pull RESP3 verbatim string handling from Redis Fixes #802 --- hiredis.c | 34 ++++++++++++++++++++++++---------- hiredis.h | 4 +++- read.c | 14 +++++++++++++- read.h | 2 +- test.c | 11 +++++++++++ 5 files changed, 52 insertions(+), 13 deletions(-) diff --git a/hiredis.c b/hiredis.c index 8e438f2..deb65a9 100644 --- a/hiredis.c +++ b/hiredis.c @@ -107,6 +107,7 @@ void freeReplyObject(void *reply) { case REDIS_REPLY_STATUS: case REDIS_REPLY_STRING: case REDIS_REPLY_DOUBLE: + case REDIS_REPLY_VERB: free(r->str); break; } @@ -121,21 +122,34 @@ static void *createStringObject(const redisReadTask *task, char *str, size_t len if (r == NULL) return NULL; - buf = malloc(len+1); - if (buf == NULL) { - freeReplyObject(r); - return NULL; - } - assert(task->type == REDIS_REPLY_ERROR || task->type == REDIS_REPLY_STATUS || - task->type == REDIS_REPLY_STRING); + task->type == REDIS_REPLY_STRING || + task->type == REDIS_REPLY_VERB); /* Copy string value */ - memcpy(buf,str,len); - buf[len] = '\0'; + if (task->type == REDIS_REPLY_VERB) { + buf = malloc(len-4+1); /* Skip 4 bytes of verbatim type header. */ + if (buf == NULL) { + freeReplyObject(r); + return NULL; + } + memcpy(r->vtype,str,3); + r->vtype[3] = '\0'; + memcpy(buf,str+4,len-4); + buf[len-4] = '\0'; + r->len = len - 4; + } else { + buf = malloc(len+1); + if (buf == NULL) { + freeReplyObject(r); + return NULL; + } + memcpy(buf,str,len); + buf[len] = '\0'; + r->len = len; + } r->str = buf; - r->len = len; if (task->parent) { parent = task->parent->obj; diff --git a/hiredis.h b/hiredis.h index 6733199..9282671 100644 --- a/hiredis.h +++ b/hiredis.h @@ -102,7 +102,9 @@ typedef struct redisReply { double dval; /* The double when type is REDIS_REPLY_DOUBLE */ size_t len; /* Length of string */ char *str; /* Used for REDIS_REPLY_ERROR, REDIS_REPLY_STRING - and REDIS_REPLY_DOUBLE (in additional to dval). */ + REDIS_REPLY_VERB, and REDIS_REPLY_DOUBLE (in additional to dval). */ + char vtype[4]; /* Used for REDIS_REPLY_VERB, contains the null + terminated 3 character content type, such as "txt". */ size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */ struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */ } redisReply; diff --git a/read.c b/read.c index f6dfef0..835eb65 100644 --- a/read.c +++ b/read.c @@ -383,10 +383,18 @@ static int processBulkItem(redisReader *r) { /* Only continue when the buffer contains the entire bulk item. */ bytelen += len+2; /* include \r\n */ if (r->pos+bytelen <= r->len) { + if ((cur->type == REDIS_REPLY_VERB && len < 4) || + (cur->type == REDIS_REPLY_VERB && s[5] != ':')) + { + __redisReaderSetError(r,REDIS_ERR_PROTOCOL, + "Verbatim string 4 bytes of content type are " + "missing or incorrectly encoded."); + return REDIS_ERR; + } if (r->fn && r->fn->createString) obj = r->fn->createString(cur,s+2,len); else - obj = (void*)REDIS_REPLY_STRING; + obj = (void*)(long)cur->type; success = 1; } } @@ -551,6 +559,9 @@ static int processItem(redisReader *r) { case '#': cur->type = REDIS_REPLY_BOOL; break; + case '=': + cur->type = REDIS_REPLY_VERB; + break; default: __redisReaderSetErrorProtocolByte(r,*p); return REDIS_ERR; @@ -571,6 +582,7 @@ static int processItem(redisReader *r) { case REDIS_REPLY_BOOL: return processLineItem(r); case REDIS_REPLY_STRING: + case REDIS_REPLY_VERB: return processBulkItem(r); case REDIS_REPLY_ARRAY: case REDIS_REPLY_MAP: diff --git a/read.h b/read.h index 6eff14c..788c401 100644 --- a/read.h +++ b/read.h @@ -56,12 +56,12 @@ #define REDIS_REPLY_ERROR 6 #define REDIS_REPLY_DOUBLE 7 #define REDIS_REPLY_BOOL 8 -#define REDIS_REPLY_VERB 9 #define REDIS_REPLY_MAP 9 #define REDIS_REPLY_SET 10 #define REDIS_REPLY_ATTR 11 #define REDIS_REPLY_PUSH 12 #define REDIS_REPLY_BIGNUM 13 +#define REDIS_REPLY_VERB 14 #define REDIS_READER_MAX_BUF (1024*16) /* Default max unused reader buffer. */ diff --git a/test.c b/test.c index d8b9555..8e45e78 100644 --- a/test.c +++ b/test.c @@ -477,6 +477,17 @@ static void test_reply_reader(void) { ((redisReply*)reply)->elements == 0); freeReplyObject(reply); redisReaderFree(reader); + + /* RESP3 verbatim strings (GitHub issue #802) */ + test("Can parse RESP3 verbatim strings: "); + reader = redisReaderCreate(); + redisReaderFeed(reader,(char*)"=10\r\ntxt:LOLWUT\r\n",17); + ret = redisReaderGetReply(reader,&reply); + test_cond(ret == REDIS_OK && + ((redisReply*)reply)->type == REDIS_REPLY_VERB && + !memcmp(((redisReply*)reply)->str,"LOLWUT", 6)); + freeReplyObject(reply); + redisReaderFree(reader); } static void test_free_null(void) { -- cgit v1.2.3