/* * Copyright (C) 2019 pengjian.uestc @ gmail.com */ /* * This file is part of Scylla. * * Scylla is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Scylla is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Scylla. If not, see . */ #include #include #include #include #include "bytes.hh" #include "redis/request.hh" #include using namespace seastar; using namespace redis; %%{ machine redis_resp_protocol; access _fsm_; action mark { g.mark_start(p); } action start_blob { g.mark_start(p); _size_left = _arg_size; } action start_command { g.mark_start(p); _size_left = _arg_size; } action advance_blob { auto len = std::min(static_cast(pe - p), _size_left); _size_left -= len; p += len; if (_size_left == 0) { _req._args.push_back(str()); p--; fret; } p--; } action advance_command { auto len = std::min(static_cast(pe - p), _size_left); _size_left -= len; p += len; if (_size_left == 0) { _req._command = str(); p--; fret; } p--; } crlf = '\r\n'; u32 = digit+ >{ _u32 = 0;} ${ _u32 *= 10; _u32 += fc - '0';}; args_count = '*' u32 crlf ${_req._args_count = _u32 - 1;}; blob := any+ >start_blob $advance_blob; command := any+ >start_command $advance_command; arg = '$' u32 crlf ${ _arg_size = _u32;}; main := (args_count (arg @{fcall command; } crlf) (arg @{fcall blob; } crlf)+) ${_req._state = request_state::ok;} >eof{_req._state = request_state::eof;}; prepush { prepush(); } postpop { postpop(); } }%% class redis_protocol_parser : public ragel_parser_base { %% write data nofinal noprefix; public: redis::request _req; uint32_t _u32; uint32_t _arg_size; uint32_t _size_left; public: virtual void init() { init_base(); _req._state = request_state::error; _req._args.clear(); _req._args_count = 0; _size_left = 0; _arg_size = 0; %% write init; } char* parse(char* p, char* pe, char* eof) { sstring_builder::guard g(_builder, p, pe); auto str = [this, &g, &p] { g.mark_end(p); auto s = get_str(); return to_bytes(s); }; %% write exec; if (_req._state != request_state::error) { return p; } if (p != pe) { p = pe; return p; } return nullptr; } bool eof() const { return _req._state == request_state::eof; } redis::request& get_request() { std::transform(_req._command.begin(), _req._command.end(), _req._command.begin(), ::tolower); return _req; } };