Refine the parsing of response status line; add UT for utility.
This commit is contained in:
@@ -37,6 +37,9 @@ static Json::Value StringToJson(const std::string& str) {
|
|||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
static void AssertGet(webcc::ResponsePtr r) {
|
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 json = StringToJson(r->content());
|
||||||
|
|
||||||
Json::Value args = json["args"];
|
Json::Value args = json["args"];
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ set(UT_SRCS
|
|||||||
request_parser_unittest.cc
|
request_parser_unittest.cc
|
||||||
rest_service_manager_unittest.cc
|
rest_service_manager_unittest.cc
|
||||||
url_unittest.cc
|
url_unittest.cc
|
||||||
|
utility_unittest.cc
|
||||||
)
|
)
|
||||||
|
|
||||||
set(UT_TARGET_NAME webcc_unittest)
|
set(UT_TARGET_NAME webcc_unittest)
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
+2
-2
@@ -98,7 +98,7 @@ std::vector<Header>::iterator Headers::Find(const std::string& key) {
|
|||||||
static bool ParseValue(const std::string& str, const std::string& expected_key,
|
static bool ParseValue(const std::string& str, const std::string& expected_key,
|
||||||
std::string* value) {
|
std::string* value) {
|
||||||
std::string key;
|
std::string key;
|
||||||
if (!Split2(str, '=', &key, value)) {
|
if (!SplitKV(str, '=', &key, value)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,7 +182,7 @@ bool ContentDisposition::Init(const std::string& str) {
|
|||||||
std::string key;
|
std::string key;
|
||||||
std::string value;
|
std::string value;
|
||||||
for (std::size_t i = 1; i < parts.size(); ++i) {
|
for (std::size_t i = 1; i < parts.size(); ++i) {
|
||||||
if (!Split2(parts[i], '=', &key, &value)) {
|
if (!SplitKV(parts[i], '=', &key, &value)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -138,7 +138,7 @@ bool Parser::GetNextLine(std::size_t off, std::string* line, bool erase) {
|
|||||||
|
|
||||||
bool Parser::ParseHeaderLine(const std::string& line) {
|
bool Parser::ParseHeaderLine(const std::string& line) {
|
||||||
Header header;
|
Header header;
|
||||||
if (!Split2(line, ':', &header.first, &header.second)) {
|
if (!SplitKV(line, ':', &header.first, &header.second)) {
|
||||||
LOG_ERRO("Invalid header: %s", line.c_str());
|
LOG_ERRO("Invalid header: %s", line.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ bool RequestParser::ParsePartHeaders(bool* need_more_data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Header header;
|
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());
|
LOG_ERRO("Invalid part header line: %s", line.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-2
@@ -27,11 +27,19 @@ public:
|
|||||||
status_ = status;
|
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;
|
void Prepare() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int status_;
|
int status_; // Status code
|
||||||
|
std::string reason_; // Reason phrase
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webcc
|
} // namespace webcc
|
||||||
|
|||||||
@@ -16,25 +16,56 @@ void ResponseParser::Init(Response* response) {
|
|||||||
response_ = response;
|
response_ = response;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Keep the original message.
|
namespace {
|
||||||
|
|
||||||
|
void SplitStartLine(const std::string& line, std::vector<std::string>* 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) {
|
bool ResponseParser::ParseStartLine(const std::string& line) {
|
||||||
std::vector<std::string> parts;
|
std::vector<std::string> 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());
|
LOG_ERRO("Invalid HTTP response status line: %s", line.c_str());
|
||||||
return false;
|
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());
|
||||||
try {
|
|
||||||
response_->set_status(std::stoi(status_str));
|
|
||||||
} catch (const std::exception&) {
|
|
||||||
LOG_ERRO("Invalid HTTP status code: %s", status_str.c_str());
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
response_->set_status(std::stoi(parts[1]));
|
||||||
|
} catch (const std::exception&) {
|
||||||
|
LOG_ERRO("Invalid HTTP status code: %s", parts[1].c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
response_->set_reason(parts[2]);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -15,9 +15,9 @@ std::string RandomUuid() {
|
|||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Split2(const std::string& str, char token, std::string* part1,
|
bool SplitKV(const std::string& str, char delimiter,
|
||||||
std::string* part2) {
|
std::string* part1, std::string* part2) {
|
||||||
std::size_t pos = str.find(token);
|
std::size_t pos = str.find(delimiter);
|
||||||
if (pos == std::string::npos) {
|
if (pos == std::string::npos) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-3
@@ -7,9 +7,10 @@ namespace webcc {
|
|||||||
|
|
||||||
std::string RandomUuid();
|
std::string RandomUuid();
|
||||||
|
|
||||||
// Split a string to two parts by the given token.
|
// Split a key-value string.
|
||||||
bool Split2(const std::string& str, char token, std::string* part1,
|
// E.g., split "Connection: Keep-Alive".
|
||||||
std::string* part2);
|
bool SplitKV(const std::string& str, char delimiter,
|
||||||
|
std::string* key, std::string* value);
|
||||||
|
|
||||||
} // namespace webcc
|
} // namespace webcc
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user