diff --git a/src/rest/parser.cpp b/src/rest/parser.cpp index 9401e4d0d5b..8b5f7cd6cd5 100644 --- a/src/rest/parser.cpp +++ b/src/rest/parser.cpp @@ -34,105 +34,143 @@ namespace otbr { namespace rest { -static int OnUrl(http_parser *parser, const char *at, size_t len) +Parser::Parser(Request *aRequest) +{ + mState.mRequest = aRequest; + + mParser.data = &mState; +} + +void Parser::Init(void) +{ + mSettings.on_message_begin = Parser::OnMessageBegin; + mSettings.on_url = Parser::OnUrl; + mSettings.on_status = Parser::OnHandlerData; + mSettings.on_header_field = Parser::OnHeaderField; + mSettings.on_header_value = Parser::OnHeaderData; + mSettings.on_body = Parser::OnBody; + mSettings.on_headers_complete = Parser::OnHeaderComplete; + mSettings.on_message_complete = Parser::OnMessageComplete; + http_parser_init(&mParser, HTTP_REQUEST); +} + +void Parser::Process(const char *aBuf, size_t aLength) { - Request *request = reinterpret_cast(parser->data); + http_parser_execute(&mParser, &mSettings, aBuf, aLength); +} + +int Parser::OnUrl(http_parser *parser, const char *at, size_t len) +{ + State *state = reinterpret_cast(parser->data); if (len > 0) { - request->SetUrl(at, len); + state->mUrl.append(at, len); } return 0; } -static int OnBody(http_parser *parser, const char *at, size_t len) +int Parser::OnBody(http_parser *parser, const char *at, size_t len) { - Request *request = reinterpret_cast(parser->data); + State *state = reinterpret_cast(parser->data); if (len > 0) { - request->SetBody(at, len); + state->mRequest->SetBody(at, len); } return 0; } -static int OnMessageComplete(http_parser *parser) +int Parser::OnMessageComplete(http_parser *parser) { - Request *request = reinterpret_cast(parser->data); + State *state = reinterpret_cast(parser->data); + + http_parser_url urlParser; + http_parser_url_init(&urlParser); + http_parser_parse_url(state->mUrl.c_str(), state->mUrl.length(), 1, &urlParser); + + if (urlParser.field_set & (1 << UF_PATH)) + { + std::string path = state->mUrl.substr(urlParser.field_data[UF_PATH].off, urlParser.field_data[UF_PATH].len); + state->mRequest->SetUrlPath(path); + } - request->SetReadComplete(); + if (urlParser.field_set & (1 << UF_QUERY)) + { + uint16_t offset = urlParser.field_data[UF_QUERY].off; + uint16_t end = offset + urlParser.field_data[UF_QUERY].len; + + while (offset < end) + { + std::string::size_type next = state->mUrl.find('&', offset); + if (next == std::string::npos) + { + next = end; + } + + std::string::size_type split = state->mUrl.find('=', offset); + if (split != std::string::npos && static_cast(split) < next) + { + std::string query = state->mUrl.substr(offset, split - offset); + std::string value = state->mUrl.substr(split + 1, next - split - 1); + + state->mRequest->AddQueryField(query, value); + } + + offset = static_cast(next + 1); + } + } + + state->mRequest->SetReadComplete(); return 0; } -static int OnMessageBegin(http_parser *parser) +int Parser::OnMessageBegin(http_parser *parser) { - Request *request = reinterpret_cast(parser->data); - request->ResetReadComplete(); + State *state = reinterpret_cast(parser->data); + state->mRequest->ResetReadComplete(); return 0; } -static int OnHeaderComplete(http_parser *parser) +int Parser::OnHeaderComplete(http_parser *parser) { - Request *request = reinterpret_cast(parser->data); - request->SetMethod(parser->method); + State *state = reinterpret_cast(parser->data); + state->mRequest->SetMethod(parser->method); return 0; } -static int OnHandlerData(http_parser *, const char *, size_t) +int Parser::OnHandlerData(http_parser *, const char *, size_t) { return 0; } -static int OnHeaderField(http_parser *parser, const char *at, size_t len) +int Parser::OnHeaderField(http_parser *parser, const char *at, size_t len) { - Request *request = reinterpret_cast(parser->data); + State *state = reinterpret_cast(parser->data); if (len > 0) { - request->SetNextHeaderField(at, len); + state->mNextHeaderField = std::string(at, len); } return 0; } -static int OnHeaderData(http_parser *parser, const char *at, size_t len) +int Parser::OnHeaderData(http_parser *parser, const char *at, size_t len) { - Request *request = reinterpret_cast(parser->data); + State *state = reinterpret_cast(parser->data); if (len > 0) { - request->SetHeaderValue(at, len); + state->mRequest->AddHeaderField(state->mNextHeaderField, std::string(at, len)); } return 0; } -Parser::Parser(Request *aRequest) -{ - mParser.data = aRequest; -} - -void Parser::Init(void) -{ - mSettings.on_message_begin = OnMessageBegin; - mSettings.on_url = OnUrl; - mSettings.on_status = OnHandlerData; - mSettings.on_header_field = OnHeaderField; - mSettings.on_header_value = OnHeaderData; - mSettings.on_body = OnBody; - mSettings.on_headers_complete = OnHeaderComplete; - mSettings.on_message_complete = OnMessageComplete; - http_parser_init(&mParser, HTTP_REQUEST); -} - -void Parser::Process(const char *aBuf, size_t aLength) -{ - http_parser_execute(&mParser, &mSettings, aBuf, aLength); -} - } // namespace rest } // namespace otbr diff --git a/src/rest/parser.hpp b/src/rest/parser.hpp index b79d79a83c5..0dc43894deb 100644 --- a/src/rest/parser.hpp +++ b/src/rest/parser.hpp @@ -80,8 +80,25 @@ class Parser void Process(const char *aBuf, size_t aLength); private: + struct State + { + Request *mRequest; + std::string mUrl; + std::string mNextHeaderField; + }; + + static int OnUrl(http_parser *parser, const char *at, size_t len); + static int OnBody(http_parser *parser, const char *at, size_t len); + static int OnMessageComplete(http_parser *parser); + static int OnMessageBegin(http_parser *parser); + static int OnHeaderComplete(http_parser *parser); + static int OnHandlerData(http_parser *, const char *, size_t); + static int OnHeaderField(http_parser *parser, const char *at, size_t len); + static int OnHeaderData(http_parser *parser, const char *at, size_t len); + http_parser mParser; http_parser_settings mSettings; + State mState; }; } // namespace rest diff --git a/src/rest/request.cpp b/src/rest/request.cpp index 50a84bed76c..368c8883269 100644 --- a/src/rest/request.cpp +++ b/src/rest/request.cpp @@ -37,9 +37,9 @@ Request::Request(void) { } -void Request::SetUrl(const char *aString, size_t aLength) +void Request::SetUrlPath(std::string aPath) { - mUrl += std::string(aString, aLength); + mUrlPath = aPath; } void Request::SetBody(const char *aString, size_t aLength) @@ -57,14 +57,14 @@ void Request::SetMethod(int32_t aMethod) mMethod = aMethod; } -void Request::SetNextHeaderField(const char *aString, size_t aLength) +void Request::AddHeaderField(std::string aField, std::string aValue) { - mNextHeaderField = StringUtils::ToLowercase(std::string(aString, aLength)); + mHeaders[aField] = aValue; } -void Request::SetHeaderValue(const char *aString, size_t aLength) +void Request::AddQueryField(std::string aField, std::string aValue) { - mHeaders[mNextHeaderField] = std::string(aString, aLength); + mQueryParameters[aField] = aValue; } HttpMethod Request::GetMethod() const @@ -77,25 +77,9 @@ std::string Request::GetBody() const return mBody; } -std::string Request::GetUrl(void) const +std::string Request::GetUrlPath(void) const { - std::string url = mUrl; - - size_t urlEnd = url.find("?"); - - if (urlEnd != std::string::npos) - { - url = url.substr(0, urlEnd); - } - while (!url.empty() && url[url.size() - 1] == '/') - { - url.pop_back(); - } - - VerifyOrExit(url.size() > 0, url = "/"); - -exit: - return url; + return mUrlPath; } std::string Request::GetHeaderValue(const std::string aHeaderField) const @@ -105,6 +89,13 @@ std::string Request::GetHeaderValue(const std::string aHeaderField) const return (it == mHeaders.end()) ? "" : it->second; } +std::string Request::GetQueryValue(const std::string aQueryName) const +{ + auto it = mQueryParameters.find(aQueryName); + + return (it == mQueryParameters.end()) ? "" : it->second; +} + void Request::SetReadComplete(void) { mComplete = true; diff --git a/src/rest/request.hpp b/src/rest/request.hpp index ceb92a5f2e4..388e2b03b87 100644 --- a/src/rest/request.hpp +++ b/src/rest/request.hpp @@ -59,13 +59,12 @@ class Request Request(void); /** - * This method sets the Url field of a request. + * This method sets the Url Path field of a request. * - * @param[in] aString A pointer points to url string. - * @param[in] aLength Length of the url string + * @param[in] aPath The url path * */ - void SetUrl(const char *aString, size_t aLength); + void SetUrlPath(std::string aPath); /** * This method sets the body field of a request. @@ -95,20 +94,19 @@ class Request /** * This method sets the next header field of a request. * - * @param[in] aString A pointer points to body string. - * @param[in] aLength Length of the body string + * @param[in] aField The field name. + * @param[in] aValue The value of the field. * */ - void SetNextHeaderField(const char *aString, size_t aLength); + void AddHeaderField(std::string aField, std::string aValue); /** - * This method sets the header value of the previously set header of a request. - * - * @param[in] aString A pointer points to body string. - * @param[in] aLength Length of the body string + * This method adds a query field to the request. * + * @param[in] aField The field name. + * @param[in] aValue The value of the field. */ - void SetHeaderValue(const char *aString, size_t aLength); + void AddQueryField(std::string aField, std::string aValue); /** * This method labels the request as complete which means it no longer need to be parsed one more time . @@ -141,7 +139,7 @@ class Request * * @returns A string contains the url of this request. */ - std::string GetUrl(void) const; + std::string GetUrlPath(void) const; /** * This method returns the specified header field for this request. @@ -151,6 +149,14 @@ class Request */ std::string GetHeaderValue(const std::string aHeaderField) const; + /** + * This method returns the specified query for this request. + * + * @param aQueryName A query name. + * @return A string containing the value of the query or an empty string if the query could not be found. + */ + std::string GetQueryValue(const std::string aQueryName) const; + /** * This method indicates whether this request is parsed completely. * @@ -161,10 +167,10 @@ class Request private: int32_t mMethod; size_t mContentLength; - std::string mUrl; + std::string mUrlPath; std::string mBody; - std::string mNextHeaderField; std::map mHeaders; + std::map mQueryParameters; bool mComplete; }; diff --git a/src/rest/resource.cpp b/src/rest/resource.cpp index a60e9d94836..6cdfd8bbcc1 100644 --- a/src/rest/resource.cpp +++ b/src/rest/resource.cpp @@ -151,7 +151,7 @@ void Resource::Init(void) void Resource::Handle(Request &aRequest, Response &aResponse) const { - std::string url = aRequest.GetUrl(); + std::string url = aRequest.GetUrlPath(); auto it = mResourceMap.find(url); if (it != mResourceMap.end()) @@ -167,7 +167,7 @@ void Resource::Handle(Request &aRequest, Response &aResponse) const void Resource::HandleCallback(Request &aRequest, Response &aResponse) { - std::string url = aRequest.GetUrl(); + std::string url = aRequest.GetUrlPath(); auto it = mResourceCallbackMap.find(url); if (it != mResourceCallbackMap.end())