From 345ff2c20f6ebdd1f7b55edb7e70b15381eceae2 Mon Sep 17 00:00:00 2001 From: fortrue Date: Fri, 8 Sep 2017 11:13:54 +0800 Subject: [PATCH] supports inline command protocol for command:PING/ECHO/AUTH/SELECT --- src/Handler.cpp | 14 ++++++- src/Request.cpp | 4 ++ src/Request.h | 5 +++ src/RequestParser.cpp | 85 ++++++++++++++++++++++++++++++++++++++----- src/RequestParser.h | 51 +++++++++++++++++++++++++- 5 files changed, 146 insertions(+), 13 deletions(-) diff --git a/src/Handler.cpp b/src/Handler.cpp index 6afb761..8b66fc8 100644 --- a/src/Handler.cpp +++ b/src/Handler.cpp @@ -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 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 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 { diff --git a/src/Request.cpp b/src/Request.cpp index 0133443..ec03b40 100644 --- a/src/Request.cpp +++ b/src/Request.cpp @@ -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) diff --git a/src/Request.h b/src/Request.h index 37696b7..4d8e431 100644 --- a/src/Request.h +++ b/src/Request.h @@ -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; diff --git a/src/RequestParser.cpp b/src/RequestParser.cpp index 7e12438..8fb7fbf 100644 --- a/src/RequestParser.cpp +++ b/src/RequestParser.cpp @@ -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; diff --git a/src/RequestParser.h b/src/RequestParser.h index 58c028c..51b5aeb 100644 --- a/src/RequestParser.h +++ b/src/RequestParser.h @@ -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 + static bool decodeInlineArg(SString& 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 +bool RequestParser::decodeInlineArg(SString& 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