918 lines
25 KiB
C
918 lines
25 KiB
C
|
// Copyright 2015-2021 The NATS Authors
|
||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
// you may not use this file except in compliance with the License.
|
||
|
// You may obtain a copy of the License at
|
||
|
//
|
||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||
|
//
|
||
|
// Unless required by applicable law or agreed to in writing, software
|
||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
// See the License for the specific language governing permissions and
|
||
|
// limitations under the License.
|
||
|
|
||
|
#include "natsp.h"
|
||
|
|
||
|
// Do this after including natsp.h in order to have some of the
|
||
|
// GNU specific flag set first.
|
||
|
#include <string.h>
|
||
|
#include <ctype.h>
|
||
|
|
||
|
#include "mem.h"
|
||
|
#include "msg.h"
|
||
|
|
||
|
int
|
||
|
natsMsgHeader_encodedLen(natsMsg *msg)
|
||
|
{
|
||
|
natsStrHashIter iter;
|
||
|
char *key = NULL;
|
||
|
void *p = NULL;
|
||
|
int hl = 0;
|
||
|
|
||
|
// Special case: if needsLift is true, it means that this is a message
|
||
|
// that was received but never lifted (so for sure no header was added,
|
||
|
// modified or removed. So return the current len of the encoded headers.
|
||
|
if (natsMsg_needsLift(msg))
|
||
|
return msg->hdrLen;
|
||
|
|
||
|
// Here it could be that a message was created, some headers added but
|
||
|
// then all were removed before the send. Returning 0 here means that
|
||
|
// the publish will send PUB instead of HPUB. We could choose to return
|
||
|
// hdrLinePreLen + _CRLF_LEN_ + _CRLF_LEN_ instead, which means that
|
||
|
// we would encode "NATS/1.0\r\n\r\n" only.
|
||
|
if (msg->headers == NULL)
|
||
|
return 0;
|
||
|
|
||
|
hl = HDR_LINE_LEN;
|
||
|
natsStrHashIter_Init(&iter, msg->headers);
|
||
|
while (natsStrHashIter_Next(&iter, &key, &p))
|
||
|
{
|
||
|
natsHeaderValue *v = (natsHeaderValue*) p;
|
||
|
natsHeaderValue *c;
|
||
|
|
||
|
for (c = v; c != NULL; c = c->next)
|
||
|
{
|
||
|
hl += (int) strlen(key) + 2; // 2 for ": "
|
||
|
hl += (int) strlen(c->value) + _CRLF_LEN_;
|
||
|
}
|
||
|
}
|
||
|
natsStrHashIter_Done(&iter);
|
||
|
hl += _CRLF_LEN_;
|
||
|
|
||
|
return hl;
|
||
|
}
|
||
|
|
||
|
natsStatus
|
||
|
natsMsgHeader_encode(natsBuffer *buf, natsMsg *msg)
|
||
|
{
|
||
|
natsStrHashIter iter;
|
||
|
natsStatus s = NATS_OK;
|
||
|
char *key = NULL;
|
||
|
void *p = NULL;
|
||
|
|
||
|
// See explanation in natsMsgHeader_encodedLen()
|
||
|
if (natsMsg_needsLift(msg))
|
||
|
{
|
||
|
s = natsBuf_Append(buf, (const char*) msg->hdr, msg->hdrLen);
|
||
|
return NATS_UPDATE_ERR_STACK(s);
|
||
|
}
|
||
|
|
||
|
// Based on decision in natsMsgHeader_encodedLen(),
|
||
|
// getting here with NULL headers is likely a bug.
|
||
|
if (msg->headers == NULL)
|
||
|
return nats_setError(NATS_ERR, "%s", "trying to encode headers while there is none");
|
||
|
|
||
|
s = natsBuf_Append(buf, HDR_LINE, HDR_LINE_LEN);
|
||
|
if (s == NATS_OK)
|
||
|
{
|
||
|
natsStrHashIter_Init(&iter, msg->headers);
|
||
|
while ((s == NATS_OK) && natsStrHashIter_Next(&iter, &key, &p))
|
||
|
{
|
||
|
natsHeaderValue *v = (natsHeaderValue*) p;
|
||
|
natsHeaderValue *c;
|
||
|
|
||
|
for (c = v; (s == NATS_OK) && (c != NULL); c = c->next)
|
||
|
{
|
||
|
s = natsBuf_Append(buf, (const char*) key, (int) strlen(key));
|
||
|
if (s == NATS_OK)
|
||
|
s = natsBuf_Append(buf, ": ", 2);
|
||
|
if (s == NATS_OK)
|
||
|
{
|
||
|
int vl = (int) strlen(c->value);
|
||
|
int pos = natsBuf_Len(buf);
|
||
|
|
||
|
s = natsBuf_Append(buf, (const char*) c->value, vl);
|
||
|
if (s == NATS_OK)
|
||
|
{
|
||
|
char *ch = natsBuf_Data(buf)+pos;
|
||
|
int i;
|
||
|
|
||
|
for (i=0; i<vl; i++)
|
||
|
{
|
||
|
if ((*ch == '\r') || (*ch == '\n'))
|
||
|
*ch = ' ';
|
||
|
ch++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (s == NATS_OK)
|
||
|
s = natsBuf_Append(buf, _CRLF_, _CRLF_LEN_);
|
||
|
}
|
||
|
}
|
||
|
natsStrHashIter_Done(&iter);
|
||
|
}
|
||
|
if (s == NATS_OK)
|
||
|
s = natsBuf_Append(buf, _CRLF_, _CRLF_LEN_);
|
||
|
|
||
|
return NATS_UPDATE_ERR_STACK(s);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
natsHeaderValue_free(natsHeaderValue *v, bool all)
|
||
|
{
|
||
|
if (v == NULL)
|
||
|
return;
|
||
|
|
||
|
if (v->needFree && (v->value != NULL))
|
||
|
NATS_FREE(v->value);
|
||
|
|
||
|
if (all && v->next != NULL)
|
||
|
natsHeaderValue_free(v->next, all);
|
||
|
|
||
|
NATS_FREE(v);
|
||
|
}
|
||
|
|
||
|
natsStatus
|
||
|
natsHeaderValue_create(natsHeaderValue **retV, const char *value, bool makeCopy)
|
||
|
{
|
||
|
natsStatus s = NATS_OK;
|
||
|
char *cv = (char*) value;
|
||
|
natsHeaderValue *v = NULL;
|
||
|
|
||
|
*retV = NULL;
|
||
|
|
||
|
v = NATS_MALLOC(sizeof(natsHeaderValue));
|
||
|
if (v == NULL)
|
||
|
return nats_setDefaultError(NATS_NO_MEMORY);
|
||
|
|
||
|
if (makeCopy && value != NULL)
|
||
|
{
|
||
|
DUP_STRING(s, cv, value);
|
||
|
if (s != NATS_OK)
|
||
|
{
|
||
|
NATS_FREE(v);
|
||
|
return NATS_UPDATE_ERR_STACK(s);
|
||
|
}
|
||
|
}
|
||
|
v->value = cv;
|
||
|
v->needFree = makeCopy;
|
||
|
v->next = NULL;
|
||
|
*retV = v;
|
||
|
|
||
|
return NATS_OK;
|
||
|
}
|
||
|
|
||
|
static natsStatus
|
||
|
_checkMsgAndKey(natsMsg *msg, const char *key)
|
||
|
{
|
||
|
if (msg == NULL)
|
||
|
return nats_setError(NATS_INVALID_ARG, "%s", "message cannot be NULL");
|
||
|
|
||
|
if ((key == NULL) || (key[0] == '\0'))
|
||
|
return nats_setError(NATS_INVALID_ARG, "%s", "key cannot be NULL nor empty");
|
||
|
|
||
|
return NATS_OK;
|
||
|
}
|
||
|
|
||
|
static char*
|
||
|
_moveToLF(char *end, char *ptr)
|
||
|
{
|
||
|
while (ptr != end)
|
||
|
{
|
||
|
if ((*ptr == '\r') && (*(ptr+1) == '\n'))
|
||
|
return ++ptr;
|
||
|
else
|
||
|
ptr++;
|
||
|
}
|
||
|
return ptr;
|
||
|
}
|
||
|
|
||
|
static natsStatus
|
||
|
_processKeyValue(int line, natsMsg *msg, char *endPtr, char **pPtr, char **lastKey)
|
||
|
{
|
||
|
natsStatus s = NATS_OK;
|
||
|
char *ptr = *pPtr;
|
||
|
char *col = NULL;
|
||
|
char *key = NULL;
|
||
|
char *val = NULL;
|
||
|
natsHeaderValue *v = NULL;
|
||
|
bool ml = false;
|
||
|
char *start;
|
||
|
char *endval;
|
||
|
|
||
|
start = ptr;
|
||
|
if (*ptr == '\r')
|
||
|
{
|
||
|
if ((++ptr == endPtr) || ((*ptr == '\n') && (++ptr == endPtr)))
|
||
|
{
|
||
|
*pPtr = ptr;
|
||
|
return NATS_OK;
|
||
|
}
|
||
|
return nats_setError(NATS_PROTOCOL_ERROR, "invalid start of a key: %s", start);
|
||
|
}
|
||
|
if (isspace((unsigned char) *ptr))
|
||
|
{
|
||
|
if (line == 0)
|
||
|
return nats_setError(NATS_PROTOCOL_ERROR, "key cannot start with a space: %s", ptr);
|
||
|
|
||
|
key = *lastKey;
|
||
|
ml = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
col = strchr((const char*) ptr, (int) ':');
|
||
|
if (col == NULL)
|
||
|
return nats_setError(NATS_PROTOCOL_ERROR, "column delimiter not found: %s", ptr);
|
||
|
|
||
|
// Replace column char with \0 to terminate the key string.
|
||
|
key = ptr;
|
||
|
ptr = col+1;
|
||
|
(*col) = '\0';
|
||
|
}
|
||
|
|
||
|
while ((ptr != endPtr) && (isspace((unsigned char) *ptr)))
|
||
|
ptr++;
|
||
|
|
||
|
if (ptr == endPtr)
|
||
|
return nats_setError(NATS_PROTOCOL_ERROR, "no value found for key %s", key);
|
||
|
|
||
|
val = ptr;
|
||
|
// Now find the \r\n for this value
|
||
|
ptr = _moveToLF(endPtr, ptr);
|
||
|
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)))
|
||
|
endval--;
|
||
|
endval++;
|
||
|
*(endval) = '\0';
|
||
|
|
||
|
if (ml)
|
||
|
{
|
||
|
char *newValue = NULL;
|
||
|
|
||
|
natsHeaderValue *cur = natsStrHash_Get(msg->headers, key);
|
||
|
if (cur == NULL)
|
||
|
return nats_setError(NATS_PROTOCOL_ERROR, "unable to process folding lines for key %s", key);
|
||
|
|
||
|
for (; cur->next != NULL; )
|
||
|
cur = cur->next;
|
||
|
|
||
|
if (nats_asprintf(&newValue, "%s %s", cur->value, val) == -1)
|
||
|
return nats_setDefaultError(NATS_NO_MEMORY);
|
||
|
|
||
|
if (cur->needFree)
|
||
|
NATS_FREE(cur->value);
|
||
|
|
||
|
cur->value = newValue;
|
||
|
cur->needFree = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
s = natsHeaderValue_create(&v, (const char*) val, false);
|
||
|
if (s == NATS_OK)
|
||
|
{
|
||
|
natsHeaderValue *cur = natsStrHash_Get(msg->headers, key);
|
||
|
if (cur != NULL)
|
||
|
{
|
||
|
for (; cur->next != NULL; )
|
||
|
cur = cur->next;
|
||
|
|
||
|
cur->next = v;
|
||
|
}
|
||
|
else
|
||
|
s = natsStrHash_Set(msg->headers, (char*) key, false, (void*) v, NULL);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (s == NATS_OK)
|
||
|
{
|
||
|
ptr++;
|
||
|
*pPtr = ptr;
|
||
|
*lastKey = key;
|
||
|
}
|
||
|
return NATS_UPDATE_ERR_STACK(s);
|
||
|
}
|
||
|
|
||
|
static natsStatus
|
||
|
_liftHeaders(natsMsg *msg, bool setOrAdd)
|
||
|
{
|
||
|
natsStatus s = NATS_OK;
|
||
|
char *ptr = NULL;
|
||
|
char *sts = NULL;
|
||
|
char *endPtr = NULL;
|
||
|
char *lk = NULL;
|
||
|
int i;
|
||
|
|
||
|
// If there is no header map and needsLift is false, and this is not
|
||
|
// an action to set or add a header, then simply return.
|
||
|
if (!setOrAdd && (msg->headers == NULL) && !natsMsg_needsLift(msg))
|
||
|
return NATS_OK;
|
||
|
|
||
|
// For set or add operations, possibly create the headers map.
|
||
|
if (msg->headers == NULL)
|
||
|
{
|
||
|
s = natsStrHash_Create(&(msg->headers), 4);
|
||
|
if (s != NATS_OK)
|
||
|
return NATS_UPDATE_ERR_STACK(s);
|
||
|
}
|
||
|
|
||
|
// In all cases, if there is no need to lift, we are done.
|
||
|
if (!natsMsg_needsLift(msg))
|
||
|
return NATS_OK;
|
||
|
|
||
|
// If hdrLen is less than what we need for NATS/1.0\r\n, then
|
||
|
// clearly this is a bad header.
|
||
|
if ((msg->hdrLen < HDR_LINE_LEN) || (strstr(msg->hdr, HDR_LINE_PRE) != msg->hdr))
|
||
|
return nats_setError(NATS_PROTOCOL_ERROR, "header prefix missing: %s", msg->hdr);
|
||
|
|
||
|
endPtr = (char*) (msg->hdr + msg->hdrLen);
|
||
|
|
||
|
sts = (char*) (msg->hdr + HDR_LINE_PRE_LEN);
|
||
|
while ((sts != endPtr) && (*sts == ' '))
|
||
|
sts++;
|
||
|
|
||
|
ptr = sts;
|
||
|
ptr = _moveToLF(endPtr, ptr);
|
||
|
if (ptr != endPtr)
|
||
|
{
|
||
|
char *stsEnd = ptr;
|
||
|
|
||
|
ptr++;
|
||
|
while ((stsEnd != sts) && (*stsEnd != '\r'))
|
||
|
stsEnd--;
|
||
|
|
||
|
// Terminate the status.
|
||
|
*stsEnd = '\0';
|
||
|
}
|
||
|
|
||
|
if (ptr == endPtr)
|
||
|
return nats_setError(NATS_PROTOCOL_ERROR, "early termination of headers: %s", msg->hdr);
|
||
|
|
||
|
for (i=0; ((s == NATS_OK) && (ptr != endPtr)); i++)
|
||
|
s = _processKeyValue(i, msg, endPtr, &ptr, &lk);
|
||
|
|
||
|
if (s == NATS_OK)
|
||
|
{
|
||
|
// At this point we have had no protocol error lifting the header
|
||
|
// so we clear this flag so that we don't attempt to lift again.
|
||
|
natsMsg_clearNeedsLift(msg);
|
||
|
|
||
|
// Furthermore, we need the flag to be cleared should we need to
|
||
|
// add the no responders header (otherwise we would recursively
|
||
|
// try to lift headers).
|
||
|
// If adding the field fails, it is likely due to memory issue,
|
||
|
// so it is fine to keep "needsLift" as false.
|
||
|
|
||
|
// Check if we have an inlined status.
|
||
|
if ((sts != NULL) && (*sts != '\0'))
|
||
|
{
|
||
|
// There could be a description...
|
||
|
if (strlen(sts) > HDR_STATUS_LEN)
|
||
|
{
|
||
|
char *desc = (char*) (sts + HDR_STATUS_LEN);
|
||
|
char descb = 0;
|
||
|
|
||
|
// Save byte that starts the description
|
||
|
descb = *desc;
|
||
|
// Replace with '\0' to end the status.
|
||
|
*desc = '\0';
|
||
|
|
||
|
// Set status value (this will make a copy)
|
||
|
s = natsMsgHeader_Set(msg, STATUS_HDR, (const char*) sts);
|
||
|
if (s == NATS_OK)
|
||
|
{
|
||
|
char *desce = NULL;
|
||
|
|
||
|
// Restore character of starting description
|
||
|
*desc = descb;
|
||
|
// Trim left spaces
|
||
|
while ((*desc != '\0') && isspace((unsigned char) *desc))
|
||
|
desc++;
|
||
|
|
||
|
// If we are not at the end of description
|
||
|
if (*desc != '\0')
|
||
|
{
|
||
|
// Go to end of description and walk back to trim right.
|
||
|
desce = (char*) (desc + (int) strlen(desc) - 1);
|
||
|
while ((desce != desc) && isspace((unsigned char) *desce))
|
||
|
{
|
||
|
*desce = '\0';
|
||
|
desce--;
|
||
|
}
|
||
|
}
|
||
|
// If there is a description, set the value (this will make a copy)
|
||
|
if (*desc != '\0')
|
||
|
s = natsMsgHeader_Set(msg, DESCRIPTION_HDR, (const char*) desc);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
s = natsMsgHeader_Set(msg, STATUS_HDR, (const char*) sts);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NATS_UPDATE_ERR_STACK(s);
|
||
|
}
|
||
|
|
||
|
natsStatus
|
||
|
natsMsgHeader_Set(natsMsg *msg, const char *key, const char *value)
|
||
|
{
|
||
|
natsStatus s = NATS_OK;
|
||
|
|
||
|
if ((s = _checkMsgAndKey(msg, key)) != NATS_OK)
|
||
|
return NATS_UPDATE_ERR_STACK(s);
|
||
|
|
||
|
if ((s = _liftHeaders(msg, true)) != NATS_OK)
|
||
|
return NATS_UPDATE_ERR_STACK(s);
|
||
|
|
||
|
if (s == NATS_OK)
|
||
|
{
|
||
|
natsHeaderValue *v = NULL;
|
||
|
|
||
|
s = natsHeaderValue_create(&v, value, true);
|
||
|
if (s == NATS_OK)
|
||
|
{
|
||
|
void *p = NULL;
|
||
|
|
||
|
s = natsStrHash_Set(msg->headers, (char*) key, true, (void*) v, &p);
|
||
|
if (s != NATS_OK)
|
||
|
natsHeaderValue_free(v, false);
|
||
|
else if (p != NULL)
|
||
|
{
|
||
|
natsHeaderValue *old = (natsHeaderValue*) p;
|
||
|
natsHeaderValue_free(old, true);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return NATS_UPDATE_ERR_STACK(s);
|
||
|
}
|
||
|
|
||
|
natsStatus
|
||
|
natsMsgHeader_Add(natsMsg *msg, const char *key, const char *value)
|
||
|
{
|
||
|
natsStatus s = NATS_OK;
|
||
|
|
||
|
if ((s = _checkMsgAndKey(msg, key)) != NATS_OK)
|
||
|
return NATS_UPDATE_ERR_STACK(s);
|
||
|
|
||
|
if ((s = _liftHeaders(msg, true)) != NATS_OK)
|
||
|
return NATS_UPDATE_ERR_STACK(s);
|
||
|
|
||
|
if (s == NATS_OK)
|
||
|
{
|
||
|
natsHeaderValue *v = NULL;
|
||
|
|
||
|
s = natsHeaderValue_create(&v, value, true);
|
||
|
if (s == NATS_OK)
|
||
|
{
|
||
|
natsHeaderValue *cur = natsStrHash_Get(msg->headers, (char*) key);
|
||
|
if (cur != NULL)
|
||
|
{
|
||
|
for (; cur->next != NULL; )
|
||
|
cur = cur->next;
|
||
|
|
||
|
cur->next = v;
|
||
|
}
|
||
|
else
|
||
|
s = natsStrHash_Set(msg->headers, (char*) key, true, (void*) v, NULL);
|
||
|
}
|
||
|
}
|
||
|
return NATS_UPDATE_ERR_STACK(s);
|
||
|
}
|
||
|
|
||
|
natsStatus
|
||
|
natsMsgHeader_Get(natsMsg *msg, const char *key, const char **value)
|
||
|
{
|
||
|
natsStatus s = NATS_OK;
|
||
|
natsHeaderValue *v = NULL;
|
||
|
|
||
|
if ((s = _checkMsgAndKey(msg, key)) != NATS_OK)
|
||
|
return NATS_UPDATE_ERR_STACK(s);
|
||
|
|
||
|
if (value == NULL)
|
||
|
return nats_setError(NATS_INVALID_ARG, "%s", "value cannot be NULL");
|
||
|
|
||
|
*value = NULL;
|
||
|
|
||
|
if ((s = _liftHeaders(msg, false)) != NATS_OK)
|
||
|
return NATS_UPDATE_ERR_STACK(s);
|
||
|
|
||
|
if ((msg->headers == NULL) || natsStrHash_Count(msg->headers) == 0)
|
||
|
return NATS_NOT_FOUND; // normal error, so don't update error stack
|
||
|
|
||
|
v = natsStrHash_Get(msg->headers, (char*) key);
|
||
|
if (v == NULL)
|
||
|
return NATS_NOT_FOUND; // normal error, so don't update error stack
|
||
|
|
||
|
*value = (const char*) v->value;
|
||
|
|
||
|
return NATS_UPDATE_ERR_STACK(s);
|
||
|
}
|
||
|
|
||
|
natsStatus
|
||
|
natsMsgHeader_Values(natsMsg *msg, const char *key, const char* **values, int *count)
|
||
|
{
|
||
|
natsStatus s = NATS_OK;
|
||
|
int c = 0;
|
||
|
natsHeaderValue *cur = NULL;
|
||
|
const char* *strs = NULL;
|
||
|
natsHeaderValue *v = NULL;
|
||
|
|
||
|
if ((s = _checkMsgAndKey(msg, key)) != NATS_OK)
|
||
|
return NATS_UPDATE_ERR_STACK(s);
|
||
|
|
||
|
if ((values == NULL) || (count == NULL))
|
||
|
return nats_setError(NATS_INVALID_ARG, "%s", "value or count cannot be NULL");
|
||
|
|
||
|
*values = NULL;
|
||
|
*count = 0;
|
||
|
|
||
|
if ((s = _liftHeaders(msg, false)) != NATS_OK)
|
||
|
return NATS_UPDATE_ERR_STACK(s);
|
||
|
|
||
|
if ((msg->headers == NULL) || natsStrHash_Count(msg->headers) == 0)
|
||
|
return NATS_NOT_FOUND; // normal error, so don't update error stack
|
||
|
|
||
|
v = natsStrHash_Get(msg->headers, (char*) key);
|
||
|
if (v == NULL)
|
||
|
return NATS_NOT_FOUND; // normal error, so don't update error stack
|
||
|
|
||
|
for (cur=v; cur != NULL; cur = cur->next)
|
||
|
c++;
|
||
|
|
||
|
strs = NATS_CALLOC(c, sizeof(char*));
|
||
|
if (strs == NULL)
|
||
|
s = nats_setDefaultError(NATS_NO_MEMORY);
|
||
|
else
|
||
|
{
|
||
|
int i = 0;
|
||
|
|
||
|
for (cur=v; cur != NULL; cur = cur->next)
|
||
|
strs[i++] = (const char*) cur->value;
|
||
|
|
||
|
*values = strs;
|
||
|
*count = c;
|
||
|
}
|
||
|
|
||
|
return NATS_UPDATE_ERR_STACK(s);
|
||
|
}
|
||
|
|
||
|
natsStatus
|
||
|
natsMsgHeader_Keys(natsMsg *msg, const char* **keys, int *count)
|
||
|
{
|
||
|
natsStatus s = NATS_OK;
|
||
|
const char* *strs = NULL;
|
||
|
int c = 0;
|
||
|
|
||
|
if (msg == NULL)
|
||
|
return nats_setError(NATS_INVALID_ARG, "%s", "message cannot be NULL");
|
||
|
|
||
|
if ((keys == NULL) || (count == NULL))
|
||
|
return nats_setError(NATS_INVALID_ARG, "%s", "keys or count cannot be NULL");
|
||
|
|
||
|
*keys = NULL;
|
||
|
*count = 0;
|
||
|
|
||
|
if ((s = _liftHeaders(msg, false)) != NATS_OK)
|
||
|
return NATS_UPDATE_ERR_STACK(s);
|
||
|
|
||
|
if ((msg->headers == NULL) || (c = natsStrHash_Count(msg->headers)) == 0)
|
||
|
return NATS_NOT_FOUND; // normal error, so don't update error stack
|
||
|
|
||
|
strs = NATS_CALLOC(c, sizeof(char*));
|
||
|
if (strs == NULL)
|
||
|
s = nats_setDefaultError(NATS_NO_MEMORY);
|
||
|
else
|
||
|
{
|
||
|
natsStrHashIter iter;
|
||
|
char *hk = NULL;
|
||
|
int i;
|
||
|
|
||
|
natsStrHashIter_Init(&iter, msg->headers);
|
||
|
for (i=0; natsStrHashIter_Next(&iter, &hk, NULL); i++)
|
||
|
{
|
||
|
strs[i] = (const char*) hk;
|
||
|
}
|
||
|
natsStrHashIter_Done(&iter);
|
||
|
|
||
|
*keys = strs;
|
||
|
*count = c;
|
||
|
}
|
||
|
return NATS_UPDATE_ERR_STACK(s);
|
||
|
}
|
||
|
|
||
|
natsStatus
|
||
|
natsMsgHeader_Delete(natsMsg *msg, const char *key)
|
||
|
{
|
||
|
natsStatus s = NATS_OK;
|
||
|
natsHeaderValue *v = NULL;
|
||
|
|
||
|
if ((s = _checkMsgAndKey(msg, key)) != NATS_OK)
|
||
|
return NATS_UPDATE_ERR_STACK(s);
|
||
|
|
||
|
if ((s = _liftHeaders(msg, false)) != NATS_OK)
|
||
|
return NATS_UPDATE_ERR_STACK(s);
|
||
|
|
||
|
if ((msg->headers == NULL) || natsStrHash_Count(msg->headers) == 0)
|
||
|
return NATS_NOT_FOUND; // normal error, so don't update error stack
|
||
|
|
||
|
v = natsStrHash_Remove(msg->headers, (char*) key);
|
||
|
if (v == NULL)
|
||
|
return NATS_NOT_FOUND; // normal error, so don't update error stack
|
||
|
|
||
|
natsHeaderValue_free(v, true);
|
||
|
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
natsMsg_freeHeaders(natsMsg *msg)
|
||
|
{
|
||
|
natsStrHashIter iter;
|
||
|
void *p = NULL;
|
||
|
|
||
|
if (msg->headers == NULL)
|
||
|
return;
|
||
|
|
||
|
natsStrHashIter_Init(&iter, msg->headers);
|
||
|
for (;natsStrHashIter_Next(&iter, NULL, &p);)
|
||
|
{
|
||
|
natsHeaderValue *v = (natsHeaderValue *)p;
|
||
|
natsHeaderValue_free(v, true);
|
||
|
}
|
||
|
natsStrHash_Destroy(msg->headers);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
natsMsg_free(void *object)
|
||
|
{
|
||
|
natsMsg *msg;
|
||
|
|
||
|
if (object == NULL)
|
||
|
return;
|
||
|
|
||
|
msg = (natsMsg*) object;
|
||
|
natsMsg_freeHeaders(msg);
|
||
|
|
||
|
NATS_FREE(msg);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
natsMsg_Destroy(natsMsg *msg)
|
||
|
{
|
||
|
if (msg == NULL)
|
||
|
return;
|
||
|
|
||
|
if (natsMsg_isNoDestroy(msg))
|
||
|
return;
|
||
|
|
||
|
if (natsGC_collect((natsGCItem *) msg))
|
||
|
return;
|
||
|
|
||
|
natsMsg_free((void*) msg);
|
||
|
}
|
||
|
|
||
|
const char*
|
||
|
natsMsg_GetSubject(const natsMsg *msg)
|
||
|
{
|
||
|
if (msg == NULL)
|
||
|
return NULL;
|
||
|
|
||
|
return (const char*) msg->subject;
|
||
|
}
|
||
|
|
||
|
const char*
|
||
|
natsMsg_GetReply(const natsMsg *msg)
|
||
|
{
|
||
|
if (msg == NULL)
|
||
|
return NULL;
|
||
|
|
||
|
return (const char*) msg->reply;
|
||
|
}
|
||
|
|
||
|
const char*
|
||
|
natsMsg_GetData(const natsMsg *msg)
|
||
|
{
|
||
|
if (msg == NULL)
|
||
|
return NULL;
|
||
|
|
||
|
return (const char*) msg->data;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
natsMsg_GetDataLength(const natsMsg *msg)
|
||
|
{
|
||
|
if (msg == NULL)
|
||
|
return 0;
|
||
|
|
||
|
return msg->dataLen;
|
||
|
}
|
||
|
|
||
|
uint64_t
|
||
|
natsMsg_GetSequence(natsMsg *msg)
|
||
|
{
|
||
|
if (msg == NULL)
|
||
|
return 0;
|
||
|
|
||
|
return msg->seq;
|
||
|
}
|
||
|
|
||
|
int64_t
|
||
|
natsMsg_GetTime(natsMsg *msg)
|
||
|
{
|
||
|
if (msg == NULL)
|
||
|
return 0;
|
||
|
|
||
|
return msg->time;
|
||
|
}
|
||
|
|
||
|
natsStatus
|
||
|
natsMsg_createWithPadding(natsMsg **newMsg,
|
||
|
const char *subject, int subjLen,
|
||
|
const char *reply, int replyLen,
|
||
|
const char *buf, int bufLen, int bufPaddingSize, int hdrLen)
|
||
|
{
|
||
|
natsMsg *msg = NULL;
|
||
|
char *ptr = NULL;
|
||
|
int bufSize = 0;
|
||
|
int dataLen = bufLen;
|
||
|
bool hasHdrs = (hdrLen > 0 ? true : false);
|
||
|
// Make payload a null-terminated string and add at least one zero byte to the end
|
||
|
int padLen = (bufPaddingSize > 0 ? bufPaddingSize : 1);
|
||
|
|
||
|
bufSize = subjLen;
|
||
|
bufSize += 1;
|
||
|
if (replyLen > 0)
|
||
|
{
|
||
|
bufSize += replyLen;
|
||
|
bufSize += 1;
|
||
|
}
|
||
|
bufSize += bufLen;
|
||
|
bufSize += padLen;
|
||
|
if (hasHdrs)
|
||
|
bufSize++;
|
||
|
|
||
|
msg = NATS_MALLOC(sizeof(natsMsg) + bufSize);
|
||
|
if (msg == NULL)
|
||
|
return nats_setDefaultError(NATS_NO_MEMORY);
|
||
|
|
||
|
// To be safe, we could 'memset' the message up to sizeof(natsMsg),
|
||
|
// but since we are explicitly initializing most of the fields, we save
|
||
|
// on that call, but we need to make sure what we initialize all fields!!!
|
||
|
|
||
|
// That being said, we memset the 'gc' structure to protect us in case
|
||
|
// some fields are later added to this 'external' structure and we forget
|
||
|
// about updating this initialization code.
|
||
|
memset(&(msg->gc), 0, sizeof(natsGCItem));
|
||
|
|
||
|
msg->hdr = NULL;
|
||
|
msg->hdrLen = 0;
|
||
|
msg->flags = 0;
|
||
|
msg->headers = NULL;
|
||
|
msg->sub = NULL;
|
||
|
msg->next = NULL;
|
||
|
msg->seq = 0;
|
||
|
msg->time = 0;
|
||
|
|
||
|
ptr = (char*) (((char*) &(msg->next)) + sizeof(msg->next));
|
||
|
|
||
|
msg->subject = (const char*) ptr;
|
||
|
memcpy(ptr, subject, subjLen);
|
||
|
ptr += subjLen;
|
||
|
*(ptr++) = '\0';
|
||
|
|
||
|
if (replyLen > 0)
|
||
|
{
|
||
|
msg->reply = (const char*) ptr;
|
||
|
memcpy(ptr, reply, replyLen);
|
||
|
ptr += replyLen;
|
||
|
*(ptr++) = '\0';
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
msg->reply = NULL;
|
||
|
}
|
||
|
|
||
|
if (hasHdrs)
|
||
|
{
|
||
|
msg->hdr = ptr;
|
||
|
if (buf != NULL)
|
||
|
{
|
||
|
memcpy(ptr, buf, hdrLen);
|
||
|
buf += hdrLen;
|
||
|
}
|
||
|
ptr += hdrLen;
|
||
|
*(ptr++) = '\0';
|
||
|
|
||
|
msg->hdrLen = hdrLen;
|
||
|
natsMsg_setNeedsLift(msg);
|
||
|
dataLen -= hdrLen;
|
||
|
}
|
||
|
msg->data = (const char*) ptr;
|
||
|
msg->dataLen = dataLen;
|
||
|
if (buf != NULL)
|
||
|
memcpy(ptr, buf, dataLen);
|
||
|
ptr += dataLen;
|
||
|
memset(ptr, 0, padLen);
|
||
|
// This is essentially to match server's view of a message size
|
||
|
// when sending messages to pull consumers and keeping track
|
||
|
// of size in regards to a max_bytes setting.
|
||
|
msg->wsz = subjLen + replyLen + bufLen;
|
||
|
|
||
|
// Setting the callback will trigger garbage collection when
|
||
|
// natsMsg_Destroy() is invoked.
|
||
|
msg->gc.freeCb = natsMsg_free;
|
||
|
|
||
|
*newMsg = msg;
|
||
|
|
||
|
return NATS_OK;
|
||
|
}
|
||
|
|
||
|
natsStatus
|
||
|
natsMsg_create(natsMsg **newMsg,
|
||
|
const char *subject, int subjLen,
|
||
|
const char *reply, int replyLen,
|
||
|
const char *buf, int bufLen, int hdrLen)
|
||
|
{
|
||
|
return natsMsg_createWithPadding(newMsg, subject, subjLen, reply, replyLen,
|
||
|
buf, bufLen, 0, hdrLen);
|
||
|
}
|
||
|
|
||
|
// Used internally to initialize a message structure, generally defined on the stack,
|
||
|
// that will then be passed as a reference to publish functions.
|
||
|
void
|
||
|
natsMsg_init(natsMsg *msg, const char *subj, const char *data, int dataLen)
|
||
|
{
|
||
|
memset(msg, 0, sizeof(natsMsg));
|
||
|
msg->subject = subj;
|
||
|
msg->data = data;
|
||
|
msg->dataLen = dataLen;
|
||
|
}
|
||
|
|
||
|
natsStatus
|
||
|
natsMsg_Create(natsMsg **newMsg, const char *subj, const char *reply,
|
||
|
const char *data, int dataLen)
|
||
|
{
|
||
|
natsStatus s = NATS_OK;
|
||
|
|
||
|
if ((subj == NULL)
|
||
|
|| (subj[0] == '\0')
|
||
|
|| ((reply != NULL) && (reply[0] == '\0')))
|
||
|
{
|
||
|
return nats_setDefaultError(NATS_INVALID_ARG);
|
||
|
}
|
||
|
|
||
|
s = natsMsg_create(newMsg,
|
||
|
subj, (int) strlen(subj),
|
||
|
reply, (reply == NULL ? 0 : (int) strlen(reply)),
|
||
|
data, dataLen, -1);
|
||
|
|
||
|
return NATS_UPDATE_ERR_STACK(s);
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
natsMsg_IsNoResponders(natsMsg *m)
|
||
|
{
|
||
|
const char *val = NULL;
|
||
|
|
||
|
// To be a "no responders" message, it has to be of 0 length,
|
||
|
// and have a "Status" header with "503" as a value.
|
||
|
return ((m != NULL)
|
||
|
&& (natsMsg_GetDataLength(m) == 0)
|
||
|
&& (natsMsgHeader_Get(m, STATUS_HDR, &val) == NATS_OK)
|
||
|
&& (val != NULL)
|
||
|
&& (strncmp(val, NO_RESP_STATUS, HDR_STATUS_LEN) == 0));
|
||
|
}
|
||
|
|
||
|
void
|
||
|
natsMsgList_Destroy(natsMsgList *list)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
if ((list == NULL) || (list->Msgs == NULL))
|
||
|
return;
|
||
|
|
||
|
for (i=0; i < list->Count; i++)
|
||
|
natsMsg_Destroy(list->Msgs[i]);
|
||
|
NATS_FREE(list->Msgs);
|
||
|
list->Msgs = NULL;
|
||
|
list->Count = 0;
|
||
|
}
|