nats.zig/deps/nats.c/src/parser.c
torque 79a45fd2e3
git subrepo clone (merge) --branch=v3.6.1 https://github.com/nats-io/nats.c.git deps/nats.c
subrepo:
  subdir:   "deps/nats.c"
  merged:   "66cec7f"
upstream:
  origin:   "https://github.com/nats-io/nats.c.git"
  branch:   "v3.6.1"
  commit:   "66cec7f"
git-subrepo:
  version:  "0.4.6"
  commit:   "b8b46501e"
2023-08-15 00:21:33 -07:00

941 lines
28 KiB
C

// Copyright 2015-2020 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 <string.h>
#include <stdio.h>
#include "natsp.h"
#include "conn.h"
#include "util.h"
#include "mem.h"
// cloneMsgArg is used when the split buffer scenario has the pubArg in the existing read buffer, but
// we need to hold onto it into the next read.
static natsStatus
_cloneMsgArg(natsConnection *nc)
{
natsStatus s;
int subjLen = natsBuf_Len(nc->ps->ma.subject);
s = natsBuf_InitWithBackend(&(nc->ps->argBufRec),
nc->ps->scratch,
0,
sizeof(nc->ps->scratch));
if (s == NATS_OK)
{
nc->ps->argBuf = &(nc->ps->argBufRec);
s = natsBuf_Append(nc->ps->argBuf,
natsBuf_Data(nc->ps->ma.subject),
natsBuf_Len(nc->ps->ma.subject));
if (s == NATS_OK)
{
natsBuf_Destroy(nc->ps->ma.subject);
nc->ps->ma.subject = NULL;
s = natsBuf_InitWithBackend(&(nc->ps->ma.subjectRec),
nc->ps->scratch,
subjLen,
subjLen);
if (s == NATS_OK)
nc->ps->ma.subject = &(nc->ps->ma.subjectRec);
}
}
if ((s == NATS_OK) && (nc->ps->ma.reply != NULL))
{
s = natsBuf_Append(nc->ps->argBuf,
natsBuf_Data(nc->ps->ma.reply),
natsBuf_Len(nc->ps->ma.reply));
if (s == NATS_OK)
{
int replyLen = natsBuf_Len(nc->ps->ma.reply);
natsBuf_Destroy(nc->ps->ma.reply);
nc->ps->ma.reply = NULL;
s = natsBuf_InitWithBackend(&(nc->ps->ma.replyRec),
nc->ps->scratch + subjLen,
replyLen,
replyLen);
if (s == NATS_OK)
nc->ps->ma.reply = &(nc->ps->ma.replyRec);
}
}
return s;
}
struct slice
{
char *start;
int len;
};
static natsStatus
_processMsgArgs(natsConnection *nc, char *buf, int bufLen)
{
natsStatus s = NATS_OK;
int start = -1;
int index = 0;
int i;
char b;
struct slice slices[5];
char errTxt[256];
int indexLimit = 3;
int minArgs = 3;
int maxArgs = 4;
bool hasHeaders = (nc->ps->hdr >= 0 ? true : false);
// If headers, the content should be:
// <subject> <sid> [reply] <hdr size> <overall size>
// otherwise:
// <subject> <sid> [reply] <overall size>
if (hasHeaders)
{
indexLimit = 4;
minArgs = 4;
maxArgs = 5;
}
for (i = 0; i < bufLen; i++)
{
b = buf[i];
if (((b == ' ') || (b == '\t') || (b == '\r') || (b == '\n')))
{
if (start >=0)
{
if (index > indexLimit)
{
s = NATS_PROTOCOL_ERROR;
break;
}
slices[index].start = buf + start;
slices[index].len = i - start;
index++;
start = -1;
}
}
else if (start < 0)
{
start = i;
}
}
if ((s == NATS_OK) && (start >= 0))
{
if (index > indexLimit)
{
s = NATS_PROTOCOL_ERROR;
}
else
{
slices[index].start = buf + start;
slices[index].len = i - start;
index++;
}
}
if ((s == NATS_OK) && ((index == minArgs) || (index == maxArgs)))
{
int maSizeIndex = index-1; // position of size is always last.
int hdrSizeIndex = index-2; // position of hdr size is always before last.
s = natsBuf_InitWithBackend(&(nc->ps->ma.subjectRec),
slices[0].start,
slices[0].len,
slices[0].len);
if (s == NATS_OK)
{
nc->ps->ma.subject = &(nc->ps->ma.subjectRec);
nc->ps->ma.sid = nats_ParseInt64(slices[1].start, slices[1].len);
if (index == minArgs)
{
nc->ps->ma.reply = NULL;
}
else
{
s = natsBuf_InitWithBackend(&(nc->ps->ma.replyRec),
slices[2].start,
slices[2].len,
slices[2].len);
if (s == NATS_OK)
{
nc->ps->ma.reply = &(nc->ps->ma.replyRec);
}
}
}
if (s == NATS_OK)
{
if (hasHeaders)
{
nc->ps->ma.hdr = (int) nats_ParseInt64(slices[hdrSizeIndex].start,
slices[hdrSizeIndex].len);
}
nc->ps->ma.size = (int) nats_ParseInt64(slices[maSizeIndex].start,
slices[maSizeIndex].len);
}
}
else
{
snprintf(errTxt, sizeof(errTxt), "%s", "processMsgArgs Parse Error: wrong number of arguments");
s = NATS_PROTOCOL_ERROR;
}
if (nc->ps->ma.sid < 0)
{
snprintf(errTxt, sizeof(errTxt), "processMsgArgs Bad or Missing Sid: '%.*s'",
bufLen, buf);
s = NATS_PROTOCOL_ERROR;
}
if (nc->ps->ma.size < 0)
{
snprintf(errTxt, sizeof(errTxt), "processMsgArgs Bad or Missing Size: '%.*s'",
bufLen, buf);
s = NATS_PROTOCOL_ERROR;
}
if (hasHeaders && ((nc->ps->ma.hdr < 0) || (nc->ps->ma.hdr > nc->ps->ma.size)))
{
snprintf(errTxt, sizeof(errTxt), "processMsgArgs Bad or Missing Header Size: '%.*s'",
bufLen, buf);
s = NATS_PROTOCOL_ERROR;
}
if (s != NATS_OK)
{
natsConn_Lock(nc);
snprintf(nc->errStr, sizeof(nc->errStr), "%s", errTxt);
nc->err = s;
natsConn_Unlock(nc);
}
return s;
}
// parse is the fast protocol parser engine.
natsStatus
natsParser_Parse(natsConnection *nc, char* buf, int bufLen)
{
natsStatus s = NATS_OK;
int i;
char b;
for (i = 0; (s == NATS_OK) && (i < bufLen); i++)
{
b = buf[i];
switch (nc->ps->state)
{
case OP_START:
{
switch (b)
{
case 'M':
case 'm':
nc->ps->state = OP_M;
nc->ps->hdr = -1;
nc->ps->ma.hdr = -1;
break;
case 'H':
case 'h':
nc->ps->state = OP_H;
nc->ps->hdr = 0;
nc->ps->ma.hdr = 0;
break;
case 'P':
case 'p':
nc->ps->state = OP_P;
break;
case '+':
nc->ps->state = OP_PLUS;
break;
case '-':
nc->ps->state = OP_MINUS;
break;
case 'I':
case 'i':
nc->ps->state = OP_I;
break;
default:
goto parseErr;
}
break;
}
case OP_H:
{
switch (b)
{
case 'M':
case 'm':
nc->ps->state = OP_M;
break;
default:
goto parseErr;
}
break;
}
case OP_M:
{
switch (b)
{
case 'S':
case 's':
nc->ps->state = OP_MS;
break;
default:
goto parseErr;
}
break;
}
case OP_MS:
{
switch (b)
{
case 'G':
case 'g':
nc->ps->state = OP_MSG;
break;
default:
goto parseErr;
}
break;
}
case OP_MSG:
{
switch (b)
{
case ' ':
case '\t':
nc->ps->state = OP_MSG_SPC;
break;
default:
goto parseErr;
}
break;
}
case OP_MSG_SPC:
{
switch (b)
{
case ' ':
case '\t':
continue;
default:
nc->ps->state = MSG_ARG;
nc->ps->afterSpace = i;
break;
}
break;
}
case MSG_ARG:
{
switch (b)
{
case '\r':
nc->ps->drop = 1;
break;
case '\n':
{
char *start = NULL;
int len = 0;
if (nc->ps->argBuf != NULL)
{
start = natsBuf_Data(nc->ps->argBuf);
len = natsBuf_Len(nc->ps->argBuf);
}
else
{
start = buf + nc->ps->afterSpace;
len = (i - nc->ps->drop) - nc->ps->afterSpace;
}
s = _processMsgArgs(nc, start, len);
if (s == NATS_OK)
{
nc->ps->drop = 0;
nc->ps->afterSpace = i+1;
nc->ps->state = MSG_PAYLOAD;
// jump ahead with the index. If this overruns
// what is left we fall out and process split
// buffer.
i = nc->ps->afterSpace + nc->ps->ma.size - 1;
}
break;
}
default:
{
if (nc->ps->argBuf != NULL)
s = natsBuf_AppendByte(nc->ps->argBuf, b);
break;
}
}
break;
}
case MSG_PAYLOAD:
{
bool done = false;
if (nc->ps->msgBuf != NULL)
{
if (natsBuf_Len(nc->ps->msgBuf) >= nc->ps->ma.size)
{
s = natsConn_processMsg(nc,
natsBuf_Data(nc->ps->msgBuf),
natsBuf_Len(nc->ps->msgBuf));
done = true;
}
else
{
// copy as much as we can to the buffer and skip ahead.
int toCopy = nc->ps->ma.size - natsBuf_Len(nc->ps->msgBuf);
int avail = bufLen - i;
if (avail < toCopy)
toCopy = avail;
if (toCopy > 0)
{
s = natsBuf_Append(nc->ps->msgBuf, buf+i, toCopy);
if (s == NATS_OK)
i += toCopy - 1;
}
else
{
s = natsBuf_AppendByte(nc->ps->msgBuf, b);
}
}
}
else if (i-nc->ps->afterSpace >= nc->ps->ma.size)
{
char *start = NULL;
int len = 0;
start = buf + nc->ps->afterSpace;
len = (i - nc->ps->afterSpace);
s = natsConn_processMsg(nc, start, len);
done = true;
}
if (done)
{
natsBuf_Destroy(nc->ps->argBuf);
nc->ps->argBuf = NULL;
natsBuf_Destroy(nc->ps->msgBuf);
nc->ps->msgBuf = NULL;
nc->ps->state = MSG_END;
}
break;
}
case MSG_END:
{
switch (b)
{
case '\n':
nc->ps->drop = 0;
nc->ps->afterSpace = i+1;
nc->ps->state = OP_START;
break;
default:
continue;
}
break;
}
case OP_PLUS:
{
switch (b)
{
case 'O':
case 'o':
nc->ps->state = OP_PLUS_O;
break;
default:
goto parseErr;
}
break;
}
case OP_PLUS_O:
{
switch (b)
{
case 'K':
case 'k':
nc->ps->state = OP_PLUS_OK;
break;
default:
goto parseErr;
}
break;
}
case OP_PLUS_OK:
{
switch (b)
{
case '\n':
natsConn_processOK(nc);
nc->ps->drop = 0;
nc->ps->state = OP_START;
break;
}
break;
}
case OP_MINUS:
{
switch (b)
{
case 'E':
case 'e':
nc->ps->state = OP_MINUS_E;
break;
default:
goto parseErr;
}
break;
}
case OP_MINUS_E:
{
switch (b)
{
case 'R':
case 'r':
nc->ps->state = OP_MINUS_ER;
break;
default:
goto parseErr;
}
break;
}
case OP_MINUS_ER:
{
switch (b)
{
case 'R':
case 'r':
nc->ps->state = OP_MINUS_ERR;
break;
default:
goto parseErr;
}
break;
}
case OP_MINUS_ERR:
{
switch (b)
{
case ' ':
case '\t':
nc->ps->state = OP_MINUS_ERR_SPC;
break;
default:
goto parseErr;
}
break;
}
case OP_MINUS_ERR_SPC:
{
switch (b)
{
case ' ':
case '\t':
continue;
default:
nc->ps->state = MINUS_ERR_ARG;
nc->ps->afterSpace = i;
break;
}
break;
}
case MINUS_ERR_ARG:
{
switch (b)
{
case '\r':
nc->ps->drop = 1;
break;
case '\n':
{
char *start = NULL;
int len = 0;
if (nc->ps->argBuf != NULL)
{
start = natsBuf_Data(nc->ps->argBuf);
len = natsBuf_Len(nc->ps->argBuf);
}
else
{
start = buf + nc->ps->afterSpace;
len = (i - nc->ps->drop) - nc->ps->afterSpace;
}
natsConn_processErr(nc, start, len);
nc->ps->drop = 0;
nc->ps->afterSpace = i+1;
nc->ps->state = OP_START;
if (nc->ps->argBuf != NULL)
{
natsBuf_Destroy(nc->ps->argBuf);
nc->ps->argBuf = NULL;
}
break;
}
default:
{
if (nc->ps->argBuf != NULL)
s = natsBuf_AppendByte(nc->ps->argBuf, b);
break;
}
}
break;
}
case OP_P:
{
switch (b)
{
case 'I':
case 'i':
nc->ps->state = OP_PI;
break;
case 'O':
case 'o':
nc->ps->state = OP_PO;
break;
default:
goto parseErr;
}
break;
}
case OP_PO:
{
switch (b)
{
case 'N':
case 'n':
nc->ps->state = OP_PON;
break;
default:
goto parseErr;
}
break;
}
case OP_PON:
{
switch (b)
{
case 'G':
case 'g':
nc->ps->state = OP_PONG;
break;
default:
goto parseErr;
}
break;
}
case OP_PONG:
{
switch (b)
{
case '\n':
natsConn_processPong(nc);
nc->ps->drop = 0;
nc->ps->afterSpace = i+1;
nc->ps->state = OP_START;
break;
}
break;
}
case OP_PI:
{
switch (b)
{
case 'N':
case 'n':
nc->ps->state = OP_PIN;
break;
default:
goto parseErr;
}
break;
}
case OP_PIN:
{
switch (b)
{
case 'G':
case 'g':
nc->ps->state = OP_PING;
break;
default:
goto parseErr;
}
break;
}
case OP_PING:
{
switch (b)
{
case '\n':
natsConn_processPing(nc);
nc->ps->drop = 0;
nc->ps->afterSpace = i+1;
nc->ps->state = OP_START;
break;
}
break;
}
case OP_I:
{
switch (b)
{
case 'N':
case 'n':
nc->ps->state = OP_IN;
break;
default:
goto parseErr;
}
break;
}
case OP_IN:
{
switch (b)
{
case 'F':
case 'f':
nc->ps->state = OP_INF;
break;
default:
goto parseErr;
}
break;
}
case OP_INF:
{
switch (b)
{
case 'O':
case 'o':
nc->ps->state = OP_INFO;
break;
default:
goto parseErr;
}
break;
}
case OP_INFO:
{
switch (b)
{
case ' ':
case '\t':
nc->ps->state = OP_INFO_SPC;
break;
default:
goto parseErr;
}
break;
}
case OP_INFO_SPC:
{
switch (b)
{
case ' ':
case '\t':
continue;
default:
nc->ps->state = INFO_ARG;
nc->ps->afterSpace = i;
break;
}
break;
}
case INFO_ARG:
{
switch (b)
{
case '\r':
nc->ps->drop = 1;
break;
case '\n':
{
char *start = NULL;
int len = 0;
if (nc->ps->argBuf != NULL)
{
start = natsBuf_Data(nc->ps->argBuf);
len = natsBuf_Len(nc->ps->argBuf);
}
else
{
start = buf + nc->ps->afterSpace;
len = (i - nc->ps->drop) - nc->ps->afterSpace;
}
natsConn_processAsyncINFO(nc, start, len);
nc->ps->drop = 0;
nc->ps->afterSpace = i+1;
nc->ps->state = OP_START;
if (nc->ps->argBuf != NULL)
{
natsBuf_Destroy(nc->ps->argBuf);
nc->ps->argBuf = NULL;
}
break;
}
default:
{
if (nc->ps->argBuf != NULL)
s = natsBuf_AppendByte(nc->ps->argBuf, b);
break;
}
}
break;
}
default:
goto parseErr;
}
}
// Check for split buffer scenarios
if ((s == NATS_OK)
&& ((nc->ps->state == MSG_ARG)
|| (nc->ps->state == MINUS_ERR_ARG)
|| (nc->ps->state == INFO_ARG))
&& (nc->ps->argBuf == NULL))
{
s = natsBuf_InitWithBackend(&(nc->ps->argBufRec),
nc->ps->scratch,
0,
sizeof(nc->ps->scratch));
if (s == NATS_OK)
{
nc->ps->argBuf = &(nc->ps->argBufRec);
s = natsBuf_Append(nc->ps->argBuf,
buf + nc->ps->afterSpace,
(i - nc->ps->drop) - nc->ps->afterSpace);
}
}
// Check for split msg
if ((s == NATS_OK)
&& (nc->ps->state == MSG_PAYLOAD) && (nc->ps->msgBuf == NULL))
{
// We need to clone the msgArg if it is still referencing the
// read buffer and we are not able to process the msg.
if (nc->ps->argBuf == NULL)
s = _cloneMsgArg(nc);
if (s == NATS_OK)
{
int remainingInScratch;
int toCopy;
#ifdef _WIN32
// Suppresses the warning that nc->ps->argBuf may be NULL.
// If nc->ps->argBuf is NULL above, then _cloneMsgArg() will set it. If 's'
// is NATS_OK here, then nc->ps->argBuf can't be NULL.
#pragma warning(suppress: 6011)
#endif
// If we will overflow the scratch buffer, just create a
// new buffer to hold the split message.
remainingInScratch = sizeof(nc->ps->scratch) - natsBuf_Len(nc->ps->argBuf);
toCopy = bufLen - nc->ps->afterSpace;
if (nc->ps->ma.size > remainingInScratch)
{
s = natsBuf_Create(&(nc->ps->msgBuf), nc->ps->ma.size);
}
else
{
s = natsBuf_InitWithBackend(&(nc->ps->msgBufRec),
nc->ps->scratch + natsBuf_Len(nc->ps->argBuf),
0, remainingInScratch);
if (s == NATS_OK)
nc->ps->msgBuf = &(nc->ps->msgBufRec);
}
if (s == NATS_OK)
s = natsBuf_Append(nc->ps->msgBuf,
buf + nc->ps->afterSpace,
toCopy);
}
}
if (s != NATS_OK)
{
// Let's clear all our pointers...
natsBuf_Destroy(nc->ps->argBuf);
nc->ps->argBuf = NULL;
natsBuf_Destroy(nc->ps->msgBuf);
nc->ps->msgBuf = NULL;
natsBuf_Destroy(nc->ps->ma.subject);
nc->ps->ma.subject = NULL;
natsBuf_Destroy(nc->ps->ma.reply);
nc->ps->ma.reply = NULL;
}
return s;
parseErr:
if (s == NATS_OK)
s = NATS_PROTOCOL_ERROR;
natsMutex_Lock(nc->mu);
snprintf(nc->errStr, sizeof(nc->errStr),
"Parse Error [%u]: '%.*s'",
nc->ps->state,
bufLen - i,
buf + i);
natsMutex_Unlock(nc->mu);
return s;
}
natsStatus
natsParser_Create(natsParser **newParser)
{
natsParser *parser = (natsParser *) NATS_CALLOC(1, sizeof(natsParser));
if (parser == NULL)
return NATS_NO_MEMORY;
*newParser = parser;
return NATS_OK;
}
void
natsParser_Destroy(natsParser *parser)
{
if (parser == NULL)
return;
natsBuf_Cleanup(&(parser->ma.subjectRec));
natsBuf_Cleanup(&(parser->ma.replyRec));
natsBuf_Cleanup(&(parser->argBufRec));
natsBuf_Cleanup(&(parser->msgBufRec));
NATS_FREE(parser);
}