mirror of
https://github.com/joyieldInc/predixy.git
synced 2026-02-05 09:52:24 +08:00
261 lines
7.3 KiB
C++
261 lines
7.3 KiB
C++
/*
|
|
* predixy - A high performance and full features proxy for redis.
|
|
* Copyright (C) 2017 Joyield, Inc. <joyield.com@gmail.com>
|
|
* All rights reserved.
|
|
*/
|
|
|
|
#include "String.h"
|
|
#include "ResponseParser.h"
|
|
|
|
ResponseParser::ResponseParser()
|
|
{
|
|
reset();
|
|
}
|
|
|
|
ResponseParser::~ResponseParser()
|
|
{
|
|
}
|
|
|
|
void ResponseParser::reset()
|
|
{
|
|
mType = Reply::None;
|
|
mRes.clear();
|
|
mState = Idle;
|
|
mInteger = 0;
|
|
mStringLen = 0;
|
|
mStringCnt = 0;
|
|
mDepth = 0;
|
|
mSign = false;
|
|
}
|
|
|
|
ResponseParser::Status ResponseParser::parse(Buffer* buf, int& pos)
|
|
{
|
|
FuncCallTimer();
|
|
char* cursor = buf->data() + pos;
|
|
char* end = buf->tail();
|
|
int error = 0;
|
|
while (cursor < end && !error) {
|
|
char ch = *cursor;
|
|
switch (mState) {
|
|
case Idle:
|
|
mRes.begin().buf = buf;
|
|
mRes.begin().pos = cursor - buf->data();
|
|
mRes.rewind();
|
|
switch (ch) {
|
|
case '+':
|
|
mType = Reply::Status;
|
|
mState = StatusHead;
|
|
break;
|
|
case '-':
|
|
mType = Reply::Error;
|
|
mState = ErrorHead;
|
|
break;
|
|
case ':':
|
|
mType = Reply::Integer;
|
|
mState = IntegerHead;
|
|
break;
|
|
case '$':
|
|
mType = Reply::String;
|
|
mState = StringHead;
|
|
break;
|
|
case '*':
|
|
mType = Reply::Array;
|
|
mState = ArrayHead;
|
|
break;
|
|
default:
|
|
error = __LINE__;
|
|
break;
|
|
}
|
|
break;
|
|
case StatusHead:
|
|
case ErrorHead:
|
|
if (ch == '\r') {
|
|
mState = HeadLF;
|
|
}
|
|
break;
|
|
case IntegerHead:
|
|
case StringHead:
|
|
case ArrayHead:
|
|
if (ch >= '0' && ch <= '9') {
|
|
mInteger = mInteger * 10 + ch - '0';
|
|
} else if (ch == '\r') {
|
|
if (mSign) {
|
|
mInteger = -mInteger;
|
|
}
|
|
mState = HeadLF;
|
|
} else if (ch == '-' && !mSign) {
|
|
mSign = true;
|
|
} else {
|
|
error = __LINE__;
|
|
}
|
|
break;
|
|
case HeadLF:
|
|
if (ch != '\n') {
|
|
error = __LINE__;
|
|
break;
|
|
}
|
|
switch (mType) {
|
|
case Reply::Status:
|
|
case Reply::Error:
|
|
case Reply::Integer:
|
|
goto Done;
|
|
case Reply::String:
|
|
if (mInteger < 0) {
|
|
goto Done;
|
|
}
|
|
mStringLen = mInteger;
|
|
mStringCnt = 0;
|
|
mState = StringBody;
|
|
break;
|
|
case Reply::Array:
|
|
if (mInteger <= 0) {
|
|
goto Done;
|
|
}
|
|
mDepth = 0;
|
|
mArrayNum[mDepth] = mInteger;
|
|
mElementCnt[mDepth++] = 0;
|
|
mState = ArrayBody;
|
|
break;
|
|
default:
|
|
error = __LINE__;
|
|
break;
|
|
}
|
|
break;
|
|
case StringBody:
|
|
if (mStringCnt == mStringLen) {
|
|
ch == '\r' ? mState = StringBodyLF : error = __LINE__;
|
|
} else {
|
|
if (mStringCnt + (end - cursor) >= mStringLen) {
|
|
cursor += mStringLen - mStringCnt - 1;
|
|
mStringCnt = mStringLen;
|
|
} else {
|
|
mStringCnt += end - cursor;
|
|
cursor = end - 1;
|
|
}
|
|
}
|
|
break;
|
|
case StringBodyLF:
|
|
if (ch == '\n') {
|
|
goto Done;
|
|
}
|
|
error = __LINE__;
|
|
break;
|
|
case ArrayBody:
|
|
switch (ch) {
|
|
case '+':
|
|
case '-':
|
|
case ':':
|
|
mState = LineBody;
|
|
break;
|
|
case '$':
|
|
mSign = false;
|
|
mInteger = 0;
|
|
mState = SubStringLen;
|
|
break;
|
|
case '*':
|
|
mInteger = 0;
|
|
mState = SubArrayLen;
|
|
break;
|
|
default:
|
|
error = __LINE__;
|
|
break;
|
|
}
|
|
break;
|
|
case LineBody:
|
|
if (ch == '\r') {
|
|
mState = ElementLF;
|
|
}
|
|
break;
|
|
case SubStringLen:
|
|
if (ch >= '0' && ch <= '9') {
|
|
mInteger = mInteger * 10 + ch - '0';
|
|
} else if (ch == '\r') {
|
|
mState = mSign ? ElementLF : SubStringLenLF;
|
|
} else if (ch == '-' && !mSign) {
|
|
mSign = true;
|
|
} else {
|
|
error = __LINE__;
|
|
}
|
|
break;
|
|
case SubArrayLen:
|
|
if (ch >= '0' && ch <= '9') {
|
|
mInteger = mInteger * 10 + ch - '0';
|
|
} else if (ch == '\r') {
|
|
mState = mInteger > 0 ? SubArrayLenLF : ElementLF;
|
|
} else {
|
|
error = __LINE__;
|
|
}
|
|
break;
|
|
case SubStringLenLF:
|
|
mStringLen = mInteger;
|
|
mStringCnt = 0;
|
|
ch == '\n' ? mState = SubStringBody : error = __LINE__;
|
|
break;
|
|
case SubArrayLenLF:
|
|
if (ch == '\n') {
|
|
if (mDepth < MaxArrayDepth) {
|
|
mArrayNum[mDepth] = mInteger;
|
|
mElementCnt[mDepth++] = 0;
|
|
mState = ArrayBody;
|
|
} else {
|
|
logError("response parse error:array too depth");
|
|
error = __LINE__;
|
|
}
|
|
} else {
|
|
error = __LINE__;
|
|
}
|
|
break;
|
|
case SubStringBody:
|
|
if (mStringCnt + (end - cursor) > mStringLen) {
|
|
cursor += mStringLen - mStringCnt;
|
|
*cursor == '\r' ? mState = ElementLF : error = __LINE__;
|
|
} else {
|
|
mStringCnt += end - cursor;
|
|
cursor = end - 1;
|
|
}
|
|
break;
|
|
case ElementLF:
|
|
if (ch != '\n') {
|
|
error = __LINE__;
|
|
break;
|
|
}
|
|
mState = ArrayBody;
|
|
while (true) {
|
|
if (++mElementCnt[mDepth - 1] == mArrayNum[mDepth - 1]) {
|
|
if (--mDepth == 0) {
|
|
goto Done;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
error = __LINE__;
|
|
break;
|
|
}
|
|
++cursor;
|
|
}
|
|
if (error) {
|
|
SString<64> bufHex;
|
|
bufHex.printHex(buf->data() + pos, buf->length() - pos);
|
|
SString<16> errHex;
|
|
errHex.printHex(cursor - 1, end - cursor + 1);
|
|
logError("response parse error %d state %d buf:%s errpos %d err:%s",
|
|
error, mState, bufHex.data(),
|
|
cursor - 1 - buf->data() - pos, errHex.data());
|
|
return ParseError;
|
|
}
|
|
pos = cursor + 1 - buf->data();
|
|
mRes.end().buf = buf;
|
|
mRes.end().pos = pos;
|
|
return (mState > HeadLF) ? Partial : Normal;
|
|
Done:
|
|
mState = Finished;
|
|
pos = cursor + 1 - buf->data();
|
|
mRes.end().buf = buf;
|
|
mRes.end().pos = pos;
|
|
return Complete;
|
|
}
|
|
|