From 8039c7d26c553509afe8b2dce0e9deca28957e9f Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Thu, 15 Oct 2020 17:41:55 -0400 Subject: test: Add test case for doubles --- test.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'test.c') diff --git a/test.c b/test.c index 7e7e1cb..4ec7bfa 100644 --- a/test.c +++ b/test.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "hiredis.h" #include "async.h" @@ -583,6 +584,18 @@ static void test_reply_reader(void) { ((redisReply*)reply)->element[1]->integer == 42); freeReplyObject(reply); redisReaderFree(reader); + + test("Can parse RESP3 doubles: "); + reader = redisReaderCreate(); + redisReaderFeed(reader, ",3.14159265358979323846\r\n",25); + ret = redisReaderGetReply(reader,&reply); + test_cond(ret == REDIS_OK && + ((redisReply*)reply)->type == REDIS_REPLY_DOUBLE && + fabs(((redisReply*)reply)->dval - 3.14159265358979323846) < 0.00000001 && + ((redisReply*)reply)->len == 22 && + strcmp(((redisReply*)reply)->str, "3.14159265358979323846") == 0); + freeReplyObject(reply); + redisReaderFree(reader); } static void test_free_null(void) { -- cgit v1.2.3 From 96e8ea611022e8c360f1883b81f6ec2a386b9ed3 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Thu, 15 Oct 2020 17:50:49 -0400 Subject: test: Add test cases for infinite and NaN doubles --- test.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'test.c') diff --git a/test.c b/test.c index 4ec7bfa..19cbd89 100644 --- a/test.c +++ b/test.c @@ -596,6 +596,26 @@ static void test_reply_reader(void) { strcmp(((redisReply*)reply)->str, "3.14159265358979323846") == 0); freeReplyObject(reply); redisReaderFree(reader); + + test("Correctly parses RESP3 double INFINITY: "); + reader = redisReaderCreate(); + redisReaderFeed(reader, ",inf\r\n",6); + ret = redisReaderGetReply(reader,&reply); + test_cond(ret == REDIS_OK && + ((redisReply*)reply)->type == REDIS_REPLY_DOUBLE && + isinf(((redisReply*)reply)->dval) && + ((redisReply*)reply)->dval > 0); + freeReplyObject(reply); + redisReaderFree(reader); + + test("Set error when RESP3 double is NaN: "); + reader = redisReaderCreate(); + redisReaderFeed(reader, ",nan\r\n",6); + ret = redisReaderGetReply(reader,&reply); + test_cond(ret == REDIS_ERR && + strcasecmp(reader->errstr,"Bad double value") == 0); + freeReplyObject(reply); + redisReaderFree(reader); } static void test_free_null(void) { -- cgit v1.2.3 From 790b4d3b4da0687a475e8a42724fbf6739bbd947 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Thu, 15 Oct 2020 18:17:35 -0400 Subject: test: Add test cases for RESP3 nil --- test.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'test.c') diff --git a/test.c b/test.c index 19cbd89..5deff7a 100644 --- a/test.c +++ b/test.c @@ -616,6 +616,24 @@ static void test_reply_reader(void) { strcasecmp(reader->errstr,"Bad double value") == 0); freeReplyObject(reply); redisReaderFree(reader); + + test("Can parse RESP3 nil: "); + reader = redisReaderCreate(); + redisReaderFeed(reader, "_\r\n",3); + ret = redisReaderGetReply(reader,&reply); + test_cond(ret == REDIS_OK && + ((redisReply*)reply)->type == REDIS_REPLY_NIL); + freeReplyObject(reply); + redisReaderFree(reader); + + test("Set error on invalid RESP3 nil: "); + reader = redisReaderCreate(); + redisReaderFeed(reader, "_nil\r\n",6); + ret = redisReaderGetReply(reader,&reply); + test_cond(ret == REDIS_ERR && + strcasecmp(reader->errstr,"Bad nil value") == 0); + freeReplyObject(reply); + redisReaderFree(reader); } static void test_free_null(void) { -- cgit v1.2.3 From 81c48a9821e3350191ed6a1e0a3ed575d75df35d Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Thu, 15 Oct 2020 18:18:20 -0400 Subject: test: Add test cases for RESP3 bool --- test.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'test.c') diff --git a/test.c b/test.c index 5deff7a..50055e2 100644 --- a/test.c +++ b/test.c @@ -634,6 +634,35 @@ static void test_reply_reader(void) { strcasecmp(reader->errstr,"Bad nil value") == 0); freeReplyObject(reply); redisReaderFree(reader); + + test("Can parse RESP3 bool (true): "); + reader = redisReaderCreate(); + redisReaderFeed(reader, "#t\r\n",4); + ret = redisReaderGetReply(reader,&reply); + test_cond(ret == REDIS_OK && + ((redisReply*)reply)->type == REDIS_REPLY_BOOL && + ((redisReply*)reply)->integer); + freeReplyObject(reply); + redisReaderFree(reader); + + test("Can parse RESP3 bool (false): "); + reader = redisReaderCreate(); + redisReaderFeed(reader, "#f\r\n",4); + ret = redisReaderGetReply(reader,&reply); + test_cond(ret == REDIS_OK && + ((redisReply*)reply)->type == REDIS_REPLY_BOOL && + !((redisReply*)reply)->integer); + freeReplyObject(reply); + redisReaderFree(reader); + + test("Set error on invalid RESP3 bool: "); + reader = redisReaderCreate(); + redisReaderFeed(reader, "#foobar\r\n",9); + ret = redisReaderGetReply(reader,&reply); + test_cond(ret == REDIS_ERR && + strcasecmp(reader->errstr,"Bad bool value") == 0); + freeReplyObject(reply); + redisReaderFree(reader); } static void test_free_null(void) { -- cgit v1.2.3 From 33c06dd5036014b2ded7f2ef6fb994f2f3eadbe5 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Thu, 15 Oct 2020 19:44:51 -0400 Subject: test: Add test case for RESP3 map --- test.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'test.c') diff --git a/test.c b/test.c index 50055e2..574f4bb 100644 --- a/test.c +++ b/test.c @@ -663,6 +663,26 @@ static void test_reply_reader(void) { strcasecmp(reader->errstr,"Bad bool value") == 0); freeReplyObject(reply); redisReaderFree(reader); + + test("Can parse RESP3 map: "); + reader = redisReaderCreate(); + redisReaderFeed(reader, "%2\r\n+first\r\n:123\r\n$6\r\nsecond\r\n#t\r\n",34); + ret = redisReaderGetReply(reader,&reply); + test_cond(ret == REDIS_OK && + ((redisReply*)reply)->type == REDIS_REPLY_MAP && + ((redisReply*)reply)->elements == 4 && + ((redisReply*)reply)->element[0]->type == REDIS_REPLY_STATUS && + ((redisReply*)reply)->element[0]->len == 5 && + !strcmp(((redisReply*)reply)->element[0]->str,"first") && + ((redisReply*)reply)->element[1]->type == REDIS_REPLY_INTEGER && + ((redisReply*)reply)->element[1]->integer == 123 && + ((redisReply*)reply)->element[2]->type == REDIS_REPLY_STRING && + ((redisReply*)reply)->element[2]->len == 6 && + !strcmp(((redisReply*)reply)->element[2]->str,"second") && + ((redisReply*)reply)->element[3]->type == REDIS_REPLY_BOOL && + ((redisReply*)reply)->element[3]->integer); + freeReplyObject(reply); + redisReaderFree(reader); } static void test_free_null(void) { -- cgit v1.2.3 From 0f92518847cbc7c83357a48059a273db8fe078a3 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Thu, 15 Oct 2020 20:13:25 -0400 Subject: test: Add test case for RESP3 set --- test.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'test.c') diff --git a/test.c b/test.c index 574f4bb..baa5a87 100644 --- a/test.c +++ b/test.c @@ -683,6 +683,28 @@ static void test_reply_reader(void) { ((redisReply*)reply)->element[3]->integer); freeReplyObject(reply); redisReaderFree(reader); + + test("Can parse RESP3 set: "); + reader = redisReaderCreate(); + redisReaderFeed(reader, "~5\r\n+orange\r\n$5\r\napple\r\n#f\r\n:100\r\n:999\r\n",40); + ret = redisReaderGetReply(reader,&reply); + test_cond(ret == REDIS_OK && + ((redisReply*)reply)->type == REDIS_REPLY_SET && + ((redisReply*)reply)->elements == 5 && + ((redisReply*)reply)->element[0]->type == REDIS_REPLY_STATUS && + ((redisReply*)reply)->element[0]->len == 6 && + !strcmp(((redisReply*)reply)->element[0]->str,"orange") && + ((redisReply*)reply)->element[1]->type == REDIS_REPLY_STRING && + ((redisReply*)reply)->element[1]->len == 5 && + !strcmp(((redisReply*)reply)->element[1]->str,"apple") && + ((redisReply*)reply)->element[2]->type == REDIS_REPLY_BOOL && + !((redisReply*)reply)->element[2]->integer && + ((redisReply*)reply)->element[3]->type == REDIS_REPLY_INTEGER && + ((redisReply*)reply)->element[3]->integer == 100 && + ((redisReply*)reply)->element[4]->type == REDIS_REPLY_INTEGER && + ((redisReply*)reply)->element[4]->integer == 999); + freeReplyObject(reply); + redisReaderFree(reader); } static void test_free_null(void) { -- cgit v1.2.3 From e43061156cffea910ab46dab1135e16e70774dce Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Fri, 16 Oct 2020 17:32:38 -0400 Subject: read: Additional validation and test case for RESP3 double This ensures that malformed RESP3 double messages that include an invalid null byte are not parsed as valid. --- read.c | 6 +++--- test.c | 9 +++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) (limited to 'test.c') diff --git a/read.c b/read.c index 22d6e47..01fa9dc 100644 --- a/read.c +++ b/read.c @@ -292,9 +292,9 @@ static int processLineItem(redisReader *r) { memcpy(buf,p,len); buf[len] = '\0'; - if (strcasecmp(buf,"inf") == 0) { + if (len == 3 && strcasecmp(buf,"inf") == 0) { d = INFINITY; /* Positive infinite. */ - } else if (strcasecmp(buf,"-inf") == 0) { + } else if (len == 4 && strcasecmp(buf,"-inf") == 0) { d = -INFINITY; /* Negative infinite. */ } else { d = strtod((char*)buf,&eptr); @@ -302,7 +302,7 @@ static int processLineItem(redisReader *r) { * strtod() allows other variations on infinity, NaN, * etc. We explicity handle our two allowed infinite cases * above, so strtod() should only result in finite values. */ - if (buf[0] == '\0' || eptr[0] != '\0' || !isfinite(d)) { + if (buf[0] == '\0' || eptr != &buf[len] || !isfinite(d)) { __redisReaderSetError(r,REDIS_ERR_PROTOCOL, "Bad double value"); return REDIS_ERR; diff --git a/test.c b/test.c index baa5a87..fa861b3 100644 --- a/test.c +++ b/test.c @@ -597,6 +597,15 @@ static void test_reply_reader(void) { freeReplyObject(reply); redisReaderFree(reader); + test("Set error on invalid RESP3 double: "); + reader = redisReaderCreate(); + redisReaderFeed(reader, ",3.14159\000265358979323846\r\n",26); + ret = redisReaderGetReply(reader,&reply); + test_cond(ret == REDIS_ERR && + strcasecmp(reader->errstr,"Bad double value") == 0); + freeReplyObject(reply); + redisReaderFree(reader); + test("Correctly parses RESP3 double INFINITY: "); reader = redisReaderCreate(); redisReaderFeed(reader, ",inf\r\n",6); -- cgit v1.2.3 From 83c14504257de168c45ae7730a00c930ab17cfa3 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Fri, 16 Oct 2020 18:35:58 -0400 Subject: read: Add support for the RESP3 bignum type --- hiredis.c | 4 +++- hiredis.h | 3 ++- read.c | 20 ++++++++++++++++++++ test.c | 11 +++++++++++ 4 files changed, 36 insertions(+), 2 deletions(-) (limited to 'test.c') diff --git a/hiredis.c b/hiredis.c index 5591cb9..2c1fb82 100644 --- a/hiredis.c +++ b/hiredis.c @@ -114,6 +114,7 @@ void freeReplyObject(void *reply) { case REDIS_REPLY_STRING: case REDIS_REPLY_DOUBLE: case REDIS_REPLY_VERB: + case REDIS_REPLY_BIGNUM: hi_free(r->str); break; } @@ -131,7 +132,8 @@ static void *createStringObject(const redisReadTask *task, char *str, size_t len assert(task->type == REDIS_REPLY_ERROR || task->type == REDIS_REPLY_STATUS || task->type == REDIS_REPLY_STRING || - task->type == REDIS_REPLY_VERB); + task->type == REDIS_REPLY_VERB || + task->type == REDIS_REPLY_BIGNUM); /* Copy string value */ if (task->type == REDIS_REPLY_VERB) { diff --git a/hiredis.h b/hiredis.h index a629930..e77a88a 100644 --- a/hiredis.h +++ b/hiredis.h @@ -112,7 +112,8 @@ 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 - REDIS_REPLY_VERB, and REDIS_REPLY_DOUBLE (in additional to dval). */ + REDIS_REPLY_VERB, REDIS_REPLY_DOUBLE (in additional to dval), + and REDIS_REPLY_BIGNUM. */ 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 */ diff --git a/read.c b/read.c index 89b5b5a..5e0e0b4 100644 --- a/read.c +++ b/read.c @@ -337,6 +337,22 @@ static int processLineItem(redisReader *r) { obj = r->fn->createBool(cur,bval); else obj = (void*)REDIS_REPLY_BOOL; + } else if (cur->type == REDIS_REPLY_BIGNUM) { + /* Ensure all characters are decimal digits (with possible leading + * minus sign). */ + for (int i = 0; i < len; i++) { + /* XXX Consider: Allow leading '+'? Error on leading '0's? */ + if (i == 0 && p[0] == '-') continue; + if (p[i] < '0' || p[i] > '9') { + __redisReaderSetError(r,REDIS_ERR_PROTOCOL, + "Bad bignum value"); + return REDIS_ERR; + } + } + if (r->fn && r->fn->createString) + obj = r->fn->createString(cur,p,len); + else + obj = (void*)REDIS_REPLY_BIGNUM; } else { /* Type will be error or status. */ for (int i = 0; i < len; i++) { @@ -587,6 +603,9 @@ static int processItem(redisReader *r) { case '>': cur->type = REDIS_REPLY_PUSH; break; + case '(': + cur->type = REDIS_REPLY_BIGNUM; + break; default: __redisReaderSetErrorProtocolByte(r,*p); return REDIS_ERR; @@ -605,6 +624,7 @@ static int processItem(redisReader *r) { case REDIS_REPLY_DOUBLE: case REDIS_REPLY_NIL: case REDIS_REPLY_BOOL: + case REDIS_REPLY_BIGNUM: return processLineItem(r); case REDIS_REPLY_STRING: case REDIS_REPLY_VERB: diff --git a/test.c b/test.c index fa861b3..f830695 100644 --- a/test.c +++ b/test.c @@ -714,6 +714,17 @@ static void test_reply_reader(void) { ((redisReply*)reply)->element[4]->integer == 999); freeReplyObject(reply); redisReaderFree(reader); + + test("Can parse RESP3 bignum: "); + reader = redisReaderCreate(); + redisReaderFeed(reader,"(3492890328409238509324850943850943825024385\r\n",46); + ret = redisReaderGetReply(reader,&reply); + test_cond(ret == REDIS_OK && + ((redisReply*)reply)->type == REDIS_REPLY_BIGNUM && + ((redisReply*)reply)->len == 43 && + !strcmp(((redisReply*)reply)->str,"3492890328409238509324850943850943825024385")); + freeReplyObject(reply); + redisReaderFree(reader); } static void test_free_null(void) { -- cgit v1.2.3