diff --git a/src/msg.c b/src/msg.c index b2b0b1015..5e145a3de 100644 --- a/src/msg.c +++ b/src/msg.c @@ -53,7 +53,7 @@ natsMsgHeader_encodedLen(natsMsg *msg) for (c = v; c != NULL; c = c->next) { hl += (int) strlen(key) + 2; // 2 for ": " - hl += (int) strlen(c->value) + _CRLF_LEN_; + hl += (int) (c->value == NULL ? 0 : strlen(c->value)) + _CRLF_LEN_; } } natsStrHashIter_Done(&iter); @@ -98,7 +98,7 @@ natsMsgHeader_encode(natsBuffer *buf, natsMsg *msg) s = natsBuf_Append(buf, ": ", 2); if (s == NATS_OK) { - int vl = (int) strlen(c->value); + int vl = (c->value == NULL ? 0 : (int) strlen(c->value)); int pos = natsBuf_Len(buf); s = natsBuf_Append(buf, (const char*) c->value, vl); @@ -209,6 +209,8 @@ _processKeyValue(int line, natsMsg *msg, char *endPtr, char **pPtr, char **lastK bool ml = false; char *start; char *endval; + bool isNullVal = false; + bool tmpCarRet = false; start = ptr; if (*ptr == '\r') @@ -243,7 +245,19 @@ _processKeyValue(int line, natsMsg *msg, char *endPtr, char **pPtr, char **lastK } while ((ptr != endPtr) && (isspace((unsigned char) *ptr))) + { + if (tmpCarRet && (*ptr == '\n')) + { + // Value was NULL, or only whitespaces. Set pointer to before + // \r\n for further processing. + isNullVal = true; + ptr--; + break; + } + + tmpCarRet = *ptr == '\r'; ptr++; + } if (ptr == endPtr) return nats_setError(NATS_PROTOCOL_ERROR, "no value found for key %s", key); @@ -254,16 +268,26 @@ _processKeyValue(int line, natsMsg *msg, char *endPtr, char **pPtr, char **lastK if (ptr == endPtr) return nats_setError(NATS_PROTOCOL_ERROR, "no CRLF found for value of key %s", key); - // Trim right spaces and set to \0 to terminate the value string. - endval = ptr; - // Backtrack to \r and any space characters. Make sure we don't go - // past the beginning of the value pointer. - endval--; - if (*endval == '\r') - endval--; - while ((endval != val) && (isspace((unsigned char) *endval))) + if (isNullVal) + { + // For NULL or all-whitespace values, set the end value to the \r + // at the end. + endval = val; + } + else + { + // Trim right spaces and set to \0 to terminate the value string. + endval = ptr; + // Backtrack to \r and any space characters. Make sure we don't go + // past the beginning of the value pointer. endval--; - endval++; + if (*endval == '\r') + endval--; + while ((endval != val) && (isspace((unsigned char)*endval))) + endval--; + endval++; + } + *(endval) = '\0'; if (ml) diff --git a/test/test.c b/test/test.c index 0c1a73088..4df6d7e86 100644 --- a/test/test.c +++ b/test/test.c @@ -5129,6 +5129,12 @@ void test_HeadersLift(void) snprintf(buf, sizeof(buf), "%sk: a\r\n bc\r\n def\r\n\r\n", HDR_LINE); _testHeader("Multiline values: ", buf, NATS_OK, "", "k", "a bc def"); + snprintf(buf, sizeof(buf), "%sk:\r\n\r\n", HDR_LINE); + _testHeader("No value: ", buf, NATS_OK, "no value found for key", "k", ""); + + snprintf(buf, sizeof(buf), "%sk: \r\n\r\n", HDR_LINE); + _testHeader("No value (extra spaces): ", buf, NATS_OK, "no value found for key", "k", ""); + snprintf(buf, sizeof(buf), "%s", "NATS\r\nk:v\r\n\r\n"); _testHeader("NATS header missing: ", buf, NATS_PROTOCOL_ERROR, "header prefix missing", NULL, NULL); @@ -5144,12 +5150,6 @@ void test_HeadersLift(void) snprintf(buf, sizeof(buf), "%sk\r\n\r\n", HDR_LINE); _testHeader("Column missing: ", buf, NATS_PROTOCOL_ERROR, "column delimiter not found", NULL, NULL); - snprintf(buf, sizeof(buf), "%sk:\r\n\r\n", HDR_LINE); - _testHeader("No value: ", buf, NATS_PROTOCOL_ERROR, "no value found for key", NULL, NULL); - - snprintf(buf, sizeof(buf), "%sk: \r\n\r\n", HDR_LINE); - _testHeader("No value (extra spaces): ", buf, NATS_PROTOCOL_ERROR, "no value found for key", NULL, NULL); - // Check status description in header line prefix... snprintf(buf, sizeof(buf), "%s 503\r\n\r\n", HDR_LINE_PRE); @@ -20072,12 +20072,14 @@ void test_HeadersNotSupported(void) void test_HeadersBasic(void) { natsStatus s; - natsConnection *nc = NULL; - natsPid pid = NATS_INVALID_PID; - natsMsg *msg = NULL; - natsMsg *rmsg = NULL; - natsSubscription *sub = NULL; - const char *val = NULL; + natsConnection *nc = NULL; + natsPid pid = NATS_INVALID_PID; + natsMsg *msg = NULL; + natsMsg *rmsg = NULL; + natsSubscription *sub = NULL; + const char *val = NULL; + int count = 0; + const char **values = NULL; if (!serverVersionAtLeast(2, 2, 0)) { @@ -20109,6 +20111,24 @@ void test_HeadersBasic(void) IFOK(s, natsMsgHeader_Set(msg, "Headers", "Hello Headers!")) testCond(s == NATS_OK); + test("Add NULL header: "); + IFOK(s, natsMsgHeader_Add(msg, "NULL header", NULL)) + testCond(s == NATS_OK); + + test("Add empty header: "); + IFOK(s, natsMsgHeader_Add(msg, "Empty header", "")) + testCond(s == NATS_OK); + + test("Add whitespace header: "); + IFOK(s, natsMsgHeader_Add(msg, "Whitespace header", " ")) + testCond(s == NATS_OK); + + test("Add NULL, empty, and whitespace values under same header: "); + IFOK(s, natsMsgHeader_Add(msg, "Special-Headers", NULL)) + IFOK(s, natsMsgHeader_Add(msg, "Special-Headers", "")) + IFOK(s, natsMsgHeader_Add(msg, "Special-Headers", " ")) + testCond(s == NATS_OK); + test("Publish with headers ok: "); s = natsConnection_PublishMsg(nc, msg); testCond(s == NATS_OK); @@ -20134,6 +20154,31 @@ void test_HeadersBasic(void) && (natsMsg_GetDataLength(rmsg) == 4) && (strncmp(natsMsg_GetData(msg), "body", 4) == 0)); + test("Check NULL header: "); + s = natsMsgHeader_Get(rmsg, "NULL header", &val); + testCond((s == NATS_OK) + && (val != NULL) && *val == '\0'); + + test("Check empty header: "); + s = natsMsgHeader_Get(rmsg, "Empty header", &val); + testCond((s == NATS_OK) + && (val != NULL) && *val == '\0'); + + test("Check whitespace header: "); + s = natsMsgHeader_Get(rmsg, "Whitespace header", &val); + testCond((s == NATS_OK) + && (val != NULL) && *val == '\0'); + + test("Check getting values from special header: "); + s = natsMsgHeader_Values(rmsg, "Special-Headers", &values, &count); + testCond((s == NATS_OK) && (count == 3) + && (values != NULL) + && (*values[0] == '\0') + && (*values[1] == '\0') + && (*values[2] == '\0')); + free(values); + values = NULL; + natsMsg_Destroy(rmsg); rmsg = NULL; test("Value with CRLFs replaced with spaces: ");