supports inline command protocol for command:PING/ECHO/AUTH/SELECT

This commit is contained in:
fortrue 2017-09-08 11:13:54 +08:00
parent 1569b57bd7
commit 345ff2c20f
5 changed files with 146 additions and 13 deletions

View File

@ -527,7 +527,13 @@ bool Handler::preHandleRequest(Request* req, const String& key)
directResponse(req, Response::Pong);
} else {
ResponsePtr res = ResponseAlloc::create();
res->setStr(key.data(), key.length());
if (req->isInline()) {
SString<Const::MaxKeyLen> k;
RequestParser::decodeInlineArg(k, key);
res->setStr(k.data(), k.length());
} else {
res->setStr(key.data(), key.length());
}
handleResponse(nullptr, req, res);
}
return true;
@ -1400,10 +1406,14 @@ bool Handler::permission(Request* req, const String& key, Response::GenericCode&
return true;
}
if (req->type() == Command::Auth) {
SString<Const::MaxKeyLen> pw;
if (req->isInline()) {
RequestParser::decodeInlineArg(pw, key);
}
auto m = mProxy->authority();
if (!m->hasAuth()) {
code = Response::NoPasswordSet;
} else if (auto auth = m->get(key)) {
} else if (auto auth = m->get(req->isInline() ? pw : key)) {
c->setAuth(auth);
code = Response::Ok;
} else {

View File

@ -57,6 +57,7 @@ Request::Request():
mType(Command::None),
mDone(false),
mDelivered(false),
mInline(false),
mFollowers(0),
mFollowersDone(0),
mRedirectCnt(0),
@ -70,6 +71,7 @@ Request::Request(AcceptConnection* c):
mType(Command::None),
mDone(false),
mDelivered(false),
mInline(false),
mFollowers(0),
mFollowersDone(0),
mRedirectCnt(0),
@ -82,6 +84,7 @@ Request::Request(GenericCode code):
mConn(nullptr),
mDone(false),
mDelivered(false),
mInline(false),
mFollowers(0),
mFollowersDone(0),
mRedirectCnt(0),
@ -164,6 +167,7 @@ void Request::set(const RequestParser& p, Request* leader)
mReq = p.request();
}
mKey = p.key();
mInline = p.isInline();
}
void Request::setAuth(const String& password)

View File

@ -129,6 +129,10 @@ public:
{
return mDelivered;
}
bool isInline() const
{
return mInline;
}
void setDelivered()
{
mDelivered = true;
@ -163,6 +167,7 @@ private:
ResponsePtr mRes;
bool mDone;
bool mDelivered;
bool mInline;
Segment mHead; //for multi key command mget/mset/del...
Segment mReq;
Segment mKey;

View File

@ -25,6 +25,8 @@ void RequestParser::reset()
mStatus = Normal;
mState = Idle;
mInline = false;
mEscape = false;
mQuote = '\0';
mArgNum = 0;
mArgCnt = 0;
mArgLen = 0;
@ -92,9 +94,11 @@ RequestParser::Status RequestParser::parse(Buffer* buf, int& pos, bool split)
mState = InlineBegin;
mInline = true;
}
/* NO break */
case InlineBegin:
if (ch == '\r') {
error = __LINE__;
if (ch == '\n') {
mState = Idle;
//error = __LINE__;
} else if (!isspace(ch)) {
mReq.begin().buf = buf;
mReq.begin().pos = pos;
@ -107,7 +111,13 @@ RequestParser::Status RequestParser::parse(Buffer* buf, int& pos, bool split)
if (isspace(ch)) {
mCmd[mArgLen < Const::MaxCmdLen ? mArgLen : Const::MaxCmdLen - 1] = '\0';
parseCmd();
mState = ch == '\r' ? InlineLF : InlineArg;
mArgCnt = 1;
if (ch == '\n') {
mArgNum = 1;
mState = Finished;
goto Done;
}
mState = InlineArgBegin;
} else {
if (mArgLen < Const::MaxCmdLen) {
mCmd[mArgLen] = tolower(ch);
@ -115,15 +125,64 @@ RequestParser::Status RequestParser::parse(Buffer* buf, int& pos, bool split)
++mArgLen;
}
break;
case InlineArg:
if (ch == '\r') {
mState = InlineLF;
}
break;
case InlineLF:
case InlineArgBegin:
if (ch == '\n') {
mArgNum = mArgCnt;
mState = Finished;
goto Done;
} else if (isspace(ch)) {
break;
}
if (mArgCnt == 1) {
mKey.begin().buf = buf;
mKey.begin().pos = pos;
}
mState = InlineArg;
/* NO break */
case InlineArg:
if (mEscape) {
mEscape = false;
} else if (mQuote) {
if (ch == mQuote) {
mState = InlineArgEnd;
} else if (ch == '\\') {
mEscape = true;
} else if (ch == '\n') {
error = __LINE__;
}
} else {
if (isspace(ch)) {
if (mArgCnt == 1) {
mKey.end().buf = buf;
mKey.end().pos = pos;
}
++mArgCnt;
if (ch == '\n') {
mArgNum = mArgCnt;
mState = Finished;
goto Done;
} else {
mState = InlineArgBegin;
}
} else if (ch == '\'' || ch == '"') {
mQuote = ch;
}
}
break;
case InlineArgEnd:
if (isspace(ch)) {
if (mArgCnt == 1) {
mKey.end().buf = buf;
mKey.end().pos = pos;
}
++mArgCnt;
if (ch == '\n') {
mArgNum = mArgCnt;
mState = Finished;
goto Done;
} else {
mState = InlineArgBegin;
}
} else {
error = __LINE__;
}
@ -405,7 +464,13 @@ void RequestParser::parseCmd()
mType = c->type;
mCommand = c;
if (mInline) {
if (mType != Command::Ping) {
switch (mType) {
case Command::Ping:
case Command::Echo:
case Command::Auth:
case Command::Select:
break;
default:
mStatus = CmdError;
logNotice("unsupport command %s in inline command protocol", c->name);
return;

View File

@ -19,8 +19,9 @@ public:
Idle, // * or inline command
InlineBegin,
InlineCmd,
InlineLF,
InlineArgBegin,
InlineArg,
InlineArgEnd,
ArgNum, // 2
ArgNumLF, // \r\n
CmdTag,
@ -63,6 +64,8 @@ public:
~RequestParser();
Status parse(Buffer* buf, int& pos, bool split);
void reset();
template<int Size>
static bool decodeInlineArg(SString<Size>& dst, const String& src);
bool isIdle() const
{
return mState == Idle;
@ -116,6 +119,8 @@ private:
Status mStatus;
State mState;
bool mInline;
bool mEscape;
char mQuote;
int mArgNum;
int mArgCnt;
int mArgLen;
@ -123,4 +128,48 @@ private:
int mByteCnt;
};
template<int Size>
bool RequestParser::decodeInlineArg(SString<Size>& dst, const String& src)
{
bool ret = true;
bool escape = false;
char quote = '\0';
const char* p = src.data();
for (int i = 0; i < src.length(); ++i, ++p) {
char c = *p;
if (escape) {
if (quote == '"') {
switch (c) {
case 'n': c = '\n'; break;
case 'r': c = '\r'; break;
case 't': c = '\t'; break;
case 'b': c = '\b'; break;
case 'a': c = '\a'; break;
default: break;
}
} else {
if (!dst.append('\\')) {
ret = false;
}
}
escape = false;
} else if (quote) {
if (c == '\\') {
escape = true;
continue;
} else if (c == quote) {
quote = '\0';
continue;
}
} else if (c == '"' || c == '\'') {
quote = c;
continue;
}
if (!dst.append(c)) {
ret = false;
}
}
return ret;
}
#endif