From 2035c1f55299f9a73c1a5d6cee660a10b523a742 Mon Sep 17 00:00:00 2001 From: Chunting Gu Date: Fri, 31 May 2019 13:43:44 +0800 Subject: [PATCH] Refine the parsing of response status line; add UT for utility. --- autotest/client_autotest.cc | 3 +++ unittest/CMakeLists.txt | 1 + unittest/utility_unittest.cc | 16 ++++++++++++++ webcc/common.cc | 4 ++-- webcc/parser.cc | 2 +- webcc/request_parser.cc | 2 +- webcc/response.h | 12 ++++++++-- webcc/response_parser.cc | 43 +++++++++++++++++++++++++++++++----- webcc/utility.cc | 6 ++--- webcc/utility.h | 7 +++--- 10 files changed, 78 insertions(+), 18 deletions(-) create mode 100644 unittest/utility_unittest.cc diff --git a/autotest/client_autotest.cc b/autotest/client_autotest.cc index 92d73ec..ce44193 100644 --- a/autotest/client_autotest.cc +++ b/autotest/client_autotest.cc @@ -37,6 +37,9 @@ static Json::Value StringToJson(const std::string& str) { // ----------------------------------------------------------------------------- static void AssertGet(webcc::ResponsePtr r) { + EXPECT_EQ(webcc::Status::kOK, r->status()); + EXPECT_EQ("OK", r->reason()); + Json::Value json = StringToJson(r->content()); Json::Value args = json["args"]; diff --git a/unittest/CMakeLists.txt b/unittest/CMakeLists.txt index fb4db64..742cdc4 100644 --- a/unittest/CMakeLists.txt +++ b/unittest/CMakeLists.txt @@ -5,6 +5,7 @@ set(UT_SRCS request_parser_unittest.cc rest_service_manager_unittest.cc url_unittest.cc + utility_unittest.cc ) set(UT_TARGET_NAME webcc_unittest) diff --git a/unittest/utility_unittest.cc b/unittest/utility_unittest.cc new file mode 100644 index 0000000..4e16d83 --- /dev/null +++ b/unittest/utility_unittest.cc @@ -0,0 +1,16 @@ +#include "gtest/gtest.h" + +#include "webcc/utility.h" + +TEST(UtilityTest, SplitKV) { + const std::string str = "Connection: Keep-Alive"; + + std::string key; + std::string value; + + bool ok = webcc::SplitKV(str, ':', &key, &value); + + EXPECT_EQ(true, ok); + EXPECT_EQ("Connection", key); + EXPECT_EQ("Keep-Alive", value); +} diff --git a/webcc/common.cc b/webcc/common.cc index 3c79922..c754672 100644 --- a/webcc/common.cc +++ b/webcc/common.cc @@ -98,7 +98,7 @@ std::vector
::iterator Headers::Find(const std::string& key) { static bool ParseValue(const std::string& str, const std::string& expected_key, std::string* value) { std::string key; - if (!Split2(str, '=', &key, value)) { + if (!SplitKV(str, '=', &key, value)) { return false; } @@ -182,7 +182,7 @@ bool ContentDisposition::Init(const std::string& str) { std::string key; std::string value; for (std::size_t i = 1; i < parts.size(); ++i) { - if (!Split2(parts[i], '=', &key, &value)) { + if (!SplitKV(parts[i], '=', &key, &value)) { return false; } diff --git a/webcc/parser.cc b/webcc/parser.cc index 7ac320b..8c5c575 100644 --- a/webcc/parser.cc +++ b/webcc/parser.cc @@ -138,7 +138,7 @@ bool Parser::GetNextLine(std::size_t off, std::string* line, bool erase) { bool Parser::ParseHeaderLine(const std::string& line) { Header header; - if (!Split2(line, ':', &header.first, &header.second)) { + if (!SplitKV(line, ':', &header.first, &header.second)) { LOG_ERRO("Invalid header: %s", line.c_str()); return false; } diff --git a/webcc/request_parser.cc b/webcc/request_parser.cc index e050254..9571a0d 100644 --- a/webcc/request_parser.cc +++ b/webcc/request_parser.cc @@ -167,7 +167,7 @@ bool RequestParser::ParsePartHeaders(bool* need_more_data) { } Header header; - if (!Split2(line, ':', &header.first, &header.second)) { + if (!SplitKV(line, ':', &header.first, &header.second)) { LOG_ERRO("Invalid part header line: %s", line.c_str()); return false; } diff --git a/webcc/response.h b/webcc/response.h index 861e97c..bc22887 100644 --- a/webcc/response.h +++ b/webcc/response.h @@ -27,11 +27,19 @@ public: status_ = status; } - // Set start line according to status code. + const std::string& reason() const { + return reason_; + } + + void set_reason(const std::string& reason) { + reason_ = reason; + } + void Prepare() override; private: - int status_; + int status_; // Status code + std::string reason_; // Reason phrase }; } // namespace webcc diff --git a/webcc/response_parser.cc b/webcc/response_parser.cc index 76e4e64..3e1be1d 100644 --- a/webcc/response_parser.cc +++ b/webcc/response_parser.cc @@ -16,25 +16,56 @@ void ResponseParser::Init(Response* response) { response_ = response; } -// TODO: Keep the original message. +namespace { + +void SplitStartLine(const std::string& line, std::vector* parts) { + const char SPACE = ' '; + + std::size_t off = 0; + std::size_t pos = 0; + + for (std::size_t i = 0; i < 2; ++i) { + pos = line.find(SPACE, off); + if (pos == std::string::npos) { + break; + } + + parts->push_back(line.substr(off, pos - off)); + off = pos + 1; + + for (; off < line.size() && line[off] == SPACE; ++off) {} + } + + if (off < line.size()) { + parts->push_back(line.substr(off)); + } +} + +} // namespace + bool ResponseParser::ParseStartLine(const std::string& line) { std::vector parts; - boost::split(parts, line, boost::is_any_of(" "), boost::token_compress_on); + SplitStartLine(line, &parts); - if (parts.size() < 3) { + if (parts.size() != 3) { LOG_ERRO("Invalid HTTP response status line: %s", line.c_str()); return false; } - std::string& status_str = parts[1]; + if (!boost::starts_with(parts[0], "HTTP/1.")) { + LOG_ERRO("Invalid HTTP version: %s", parts[0].c_str()); + return false; + } try { - response_->set_status(std::stoi(status_str)); + response_->set_status(std::stoi(parts[1])); } catch (const std::exception&) { - LOG_ERRO("Invalid HTTP status code: %s", status_str.c_str()); + LOG_ERRO("Invalid HTTP status code: %s", parts[1].c_str()); return false; } + response_->set_reason(parts[2]); + return true; } diff --git a/webcc/utility.cc b/webcc/utility.cc index a172a21..0d97c7c 100644 --- a/webcc/utility.cc +++ b/webcc/utility.cc @@ -15,9 +15,9 @@ std::string RandomUuid() { return ss.str(); } -bool Split2(const std::string& str, char token, std::string* part1, - std::string* part2) { - std::size_t pos = str.find(token); +bool SplitKV(const std::string& str, char delimiter, + std::string* part1, std::string* part2) { + std::size_t pos = str.find(delimiter); if (pos == std::string::npos) { return false; } diff --git a/webcc/utility.h b/webcc/utility.h index 2b06d68..37283e0 100644 --- a/webcc/utility.h +++ b/webcc/utility.h @@ -7,9 +7,10 @@ namespace webcc { std::string RandomUuid(); -// Split a string to two parts by the given token. -bool Split2(const std::string& str, char token, std::string* part1, - std::string* part2); +// Split a key-value string. +// E.g., split "Connection: Keep-Alive". +bool SplitKV(const std::string& str, char delimiter, + std::string* key, std::string* value); } // namespace webcc