Add UT for http parser.
This commit is contained in:
+4
-4
@@ -123,10 +123,10 @@ if(WIN32)
|
|||||||
# For including CMake generated zconf.h.
|
# For including CMake generated zconf.h.
|
||||||
include_directories(${PROJECT_BINARY_DIR}/third_party/src/zlib)
|
include_directories(${PROJECT_BINARY_DIR}/third_party/src/zlib)
|
||||||
else()
|
else()
|
||||||
find_package(ZLIB REQUIRED)
|
find_package(ZLIB REQUIRED)
|
||||||
if(ZLIB_FOUND)
|
if(ZLIB_FOUND)
|
||||||
include_directories(${ZLIB_INCLUDE_DIRS})
|
include_directories(${ZLIB_INCLUDE_DIRS})
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# SOAP support needs pugixml to parse and create XML.
|
# SOAP support needs pugixml to parse and create XML.
|
||||||
|
|||||||
+28
-28
@@ -4,33 +4,33 @@
|
|||||||
set(EXAMPLE_LIBS webcc ${Boost_LIBRARIES} "${CMAKE_THREAD_LIBS_INIT}")
|
set(EXAMPLE_LIBS webcc ${Boost_LIBRARIES} "${CMAKE_THREAD_LIBS_INIT}")
|
||||||
|
|
||||||
if(WEBCC_ENABLE_SSL)
|
if(WEBCC_ENABLE_SSL)
|
||||||
set(EXAMPLE_LIBS ${EXAMPLE_LIBS} ${OPENSSL_LIBRARIES})
|
set(EXAMPLE_LIBS ${EXAMPLE_LIBS} ${OPENSSL_LIBRARIES})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
set(EXAMPLE_LIBS ${EXAMPLE_LIBS} zlibstatic crypt32)
|
set(EXAMPLE_LIBS ${EXAMPLE_LIBS} zlibstatic crypt32)
|
||||||
else()
|
else()
|
||||||
set(EXAMPLE_LIBS ${EXAMPLE_LIBS} ${ZLIB_LIBRARIES})
|
set(EXAMPLE_LIBS ${EXAMPLE_LIBS} ${ZLIB_LIBRARIES})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
# Add `-ldl` for Linux to avoid "undefined reference to `dlopen'".
|
# Add `-ldl` for Linux to avoid "undefined reference to `dlopen'".
|
||||||
set(EXAMPLE_LIBS ${EXAMPLE_LIBS} ${CMAKE_DL_LIBS})
|
set(EXAMPLE_LIBS ${EXAMPLE_LIBS} ${CMAKE_DL_LIBS})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(REST_BOOK_SRCS
|
set(REST_BOOK_SRCS
|
||||||
common/book.cc
|
common/book.cc
|
||||||
common/book.h
|
common/book.h
|
||||||
common/book_json.cc
|
common/book_json.cc
|
||||||
common/book_json.h
|
common/book_json.h
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(http_client http_client.cc)
|
add_executable(http_client http_client.cc)
|
||||||
target_link_libraries(http_client ${EXAMPLE_LIBS})
|
target_link_libraries(http_client ${EXAMPLE_LIBS})
|
||||||
|
|
||||||
if(WEBCC_ENABLE_SSL)
|
if(WEBCC_ENABLE_SSL)
|
||||||
add_executable(github_client github_client.cc)
|
add_executable(github_client github_client.cc)
|
||||||
target_link_libraries(github_client ${EXAMPLE_LIBS} jsoncpp)
|
target_link_libraries(github_client ${EXAMPLE_LIBS} jsoncpp)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_executable(file_upload_client file_upload_client.cc)
|
add_executable(file_upload_client file_upload_client.cc)
|
||||||
@@ -46,24 +46,24 @@ add_executable(rest_book_client rest_book_client.cc ${REST_BOOK_SRCS})
|
|||||||
target_link_libraries(rest_book_client ${EXAMPLE_LIBS} jsoncpp)
|
target_link_libraries(rest_book_client ${EXAMPLE_LIBS} jsoncpp)
|
||||||
|
|
||||||
if(WEBCC_ENABLE_SOAP)
|
if(WEBCC_ENABLE_SOAP)
|
||||||
add_executable(soap_calc_server soap_calc_server.cc)
|
add_executable(soap_calc_server soap_calc_server.cc)
|
||||||
add_executable(soap_calc_client soap_calc_client.cc)
|
add_executable(soap_calc_client soap_calc_client.cc)
|
||||||
add_executable(soap_calc_client_parasoft soap_calc_client_parasoft.cc)
|
add_executable(soap_calc_client_parasoft soap_calc_client_parasoft.cc)
|
||||||
|
|
||||||
target_link_libraries(soap_calc_server ${EXAMPLE_LIBS} pugixml)
|
target_link_libraries(soap_calc_server ${EXAMPLE_LIBS} pugixml)
|
||||||
target_link_libraries(soap_calc_client ${EXAMPLE_LIBS} pugixml)
|
target_link_libraries(soap_calc_client ${EXAMPLE_LIBS} pugixml)
|
||||||
target_link_libraries(soap_calc_client_parasoft ${EXAMPLE_LIBS} pugixml)
|
target_link_libraries(soap_calc_client_parasoft ${EXAMPLE_LIBS} pugixml)
|
||||||
|
|
||||||
set(SOAP_BOOK_SRCS
|
set(SOAP_BOOK_SRCS
|
||||||
common/book.cc
|
common/book.cc
|
||||||
common/book.h
|
common/book.h
|
||||||
common/book_xml.cc
|
common/book_xml.cc
|
||||||
common/book_xml.h
|
common/book_xml.h
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(soap_book_server soap_book_server.cc ${SOAP_BOOK_SRCS})
|
add_executable(soap_book_server soap_book_server.cc ${SOAP_BOOK_SRCS})
|
||||||
add_executable(soap_book_client soap_book_client.cc ${SOAP_BOOK_SRCS})
|
add_executable(soap_book_client soap_book_client.cc ${SOAP_BOOK_SRCS})
|
||||||
|
|
||||||
target_link_libraries(soap_book_server ${EXAMPLE_LIBS} pugixml)
|
target_link_libraries(soap_book_server ${EXAMPLE_LIBS} pugixml)
|
||||||
target_link_libraries(soap_book_client ${EXAMPLE_LIBS} pugixml)
|
target_link_libraries(soap_book_client ${EXAMPLE_LIBS} pugixml)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
+5
-5
@@ -4,18 +4,18 @@
|
|||||||
set(TEST_LIBS webcc ${Boost_LIBRARIES} "${CMAKE_THREAD_LIBS_INIT}")
|
set(TEST_LIBS webcc ${Boost_LIBRARIES} "${CMAKE_THREAD_LIBS_INIT}")
|
||||||
|
|
||||||
if(WEBCC_ENABLE_SSL)
|
if(WEBCC_ENABLE_SSL)
|
||||||
set(TEST_LIBS ${TEST_LIBS} ${OPENSSL_LIBRARIES})
|
set(TEST_LIBS ${TEST_LIBS} ${OPENSSL_LIBRARIES})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
set(TEST_LIBS ${TEST_LIBS} zlibstatic crypt32)
|
set(TEST_LIBS ${TEST_LIBS} zlibstatic crypt32)
|
||||||
else()
|
else()
|
||||||
set(TEST_LIBS ${TEST_LIBS} ${ZLIB_LIBRARIES})
|
set(TEST_LIBS ${TEST_LIBS} ${ZLIB_LIBRARIES})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
# Add `-ldl` for Linux to avoid "undefined reference to `dlopen'".
|
# Add `-ldl` for Linux to avoid "undefined reference to `dlopen'".
|
||||||
set(TEST_LIBS ${TEST_LIBS} ${CMAKE_DL_LIBS})
|
set(TEST_LIBS ${TEST_LIBS} ${CMAKE_DL_LIBS})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_executable(test_logger test_logger.cc)
|
add_executable(test_logger test_logger.cc)
|
||||||
|
|||||||
+22
-4
@@ -1,14 +1,32 @@
|
|||||||
# Unit test
|
# Unit test
|
||||||
set(UT_SRCS
|
set(UT_SRCS
|
||||||
base64_test.cc
|
base64_test.cc
|
||||||
rest_service_manager_test.cc
|
http_parser_test.cc
|
||||||
|
rest_service_manager_test.cc
|
||||||
url_test.cc
|
url_test.cc
|
||||||
)
|
)
|
||||||
|
|
||||||
set(UT_TARGET_NAME webcc_unittest)
|
set(UT_TARGET_NAME webcc_unittest)
|
||||||
|
|
||||||
|
# Common libraries to link.
|
||||||
|
set(TEST_LIBS webcc ${Boost_LIBRARIES} "${CMAKE_THREAD_LIBS_INIT}")
|
||||||
|
|
||||||
|
if(WEBCC_ENABLE_SSL)
|
||||||
|
set(TEST_LIBS ${TEST_LIBS} ${OPENSSL_LIBRARIES})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
set(TEST_LIBS ${TEST_LIBS} zlibstatic crypt32)
|
||||||
|
else()
|
||||||
|
set(TEST_LIBS ${TEST_LIBS} ${ZLIB_LIBRARIES})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(UNIX)
|
||||||
|
# Add `-ldl` for Linux to avoid "undefined reference to `dlopen'".
|
||||||
|
set(TEST_LIBS ${TEST_LIBS} ${CMAKE_DL_LIBS})
|
||||||
|
endif()
|
||||||
|
|
||||||
add_executable(${UT_TARGET_NAME} ${UT_SRCS})
|
add_executable(${UT_TARGET_NAME} ${UT_SRCS})
|
||||||
target_link_libraries(${UT_TARGET_NAME} webcc gtest ${Boost_LIBRARIES})
|
target_link_libraries(${UT_TARGET_NAME} webcc gtest ${TEST_LIBS})
|
||||||
target_link_libraries(${UT_TARGET_NAME} "${CMAKE_THREAD_LIBS_INIT}")
|
|
||||||
|
|
||||||
add_test(${UT_TARGET_NAME} ${UT_TARGET_NAME})
|
add_test(${UT_TARGET_NAME} ${UT_TARGET_NAME})
|
||||||
|
|||||||
@@ -0,0 +1,147 @@
|
|||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
#include "webcc/http_request.h"
|
||||||
|
#include "webcc/http_request_parser.h"
|
||||||
|
#include "webcc/http_response.h"
|
||||||
|
#include "webcc/http_response_parser.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// HTTP GET request parser test fixture.
|
||||||
|
class GetRequestParserTest : public testing::Test {
|
||||||
|
protected:
|
||||||
|
GetRequestParserTest() : parser_(&request_) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetUp() override {
|
||||||
|
payload_ =
|
||||||
|
"GET /get HTTP/1.1\r\n"
|
||||||
|
"Accept: application/json\r\n"
|
||||||
|
"Connection: Close\r\n"
|
||||||
|
"Host: httpbin.org\r\n\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckResult() {
|
||||||
|
EXPECT_EQ("GET", request_.method());
|
||||||
|
EXPECT_EQ("httpbin.org", request_.GetHeader("Host"));
|
||||||
|
EXPECT_EQ("application/json", request_.GetHeader("Accept"));
|
||||||
|
EXPECT_EQ("Close", request_.GetHeader("Connection"));
|
||||||
|
|
||||||
|
EXPECT_EQ("", request_.content());
|
||||||
|
EXPECT_EQ(0, request_.content_length());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string payload_;
|
||||||
|
|
||||||
|
webcc::HttpRequest request_;
|
||||||
|
webcc::HttpRequestParser parser_;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(GetRequestParserTest, ParseFullDataOnce) {
|
||||||
|
bool ok = parser_.Parse(payload_.data(), payload_.size());
|
||||||
|
|
||||||
|
EXPECT_TRUE(ok);
|
||||||
|
EXPECT_TRUE(parser_.finished());
|
||||||
|
|
||||||
|
CheckResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse byte by byte.
|
||||||
|
TEST_F(GetRequestParserTest, ParseByteWise) {
|
||||||
|
for (std::size_t i = 0; i < payload_.size(); ++i) {
|
||||||
|
bool ok = parser_.Parse(payload_.data() + i, 1);
|
||||||
|
EXPECT_TRUE(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_TRUE(parser_.finished());
|
||||||
|
|
||||||
|
CheckResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse line by line.
|
||||||
|
TEST_F(GetRequestParserTest, ParseLineWise) {
|
||||||
|
for (std::size_t i = 0; i < payload_.size();) {
|
||||||
|
std::size_t j = payload_.find('\n', i);
|
||||||
|
|
||||||
|
if (j != std::string::npos) {
|
||||||
|
bool ok = parser_.Parse(payload_.data() + i, j - i + 1);
|
||||||
|
EXPECT_TRUE(ok);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = j + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_TRUE(parser_.finished());
|
||||||
|
|
||||||
|
CheckResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// HTTP POST request parser test fixture.
|
||||||
|
class PostRequestParserTest : public testing::Test {
|
||||||
|
protected:
|
||||||
|
PostRequestParserTest() : parser_(&request_) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetUp() override {
|
||||||
|
data_ =
|
||||||
|
"{\n"
|
||||||
|
" 'note': 'Webcc test',\n"
|
||||||
|
" 'scopes': ['public_repo', 'repo', 'repo:status', 'user']\n"
|
||||||
|
"}";
|
||||||
|
|
||||||
|
payload_ =
|
||||||
|
"POST /authorizations HTTP/1.1\r\n"
|
||||||
|
"Content-Type: application/json; charset=utf-8\r\n"
|
||||||
|
"Content-Length: " + std::to_string(data_.size()) + "\r\n"
|
||||||
|
"Accept: application/json\r\n"
|
||||||
|
"Connection: Close\r\n"
|
||||||
|
"Host: api.github.com\r\n\r\n";
|
||||||
|
|
||||||
|
payload_ += data_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckResult() {
|
||||||
|
EXPECT_EQ("POST", request_.method());
|
||||||
|
EXPECT_EQ("api.github.com", request_.GetHeader("Host"));
|
||||||
|
EXPECT_EQ("application/json", request_.GetHeader("Accept"));
|
||||||
|
EXPECT_EQ("Close", request_.GetHeader("Connection"));
|
||||||
|
EXPECT_EQ("application/json; charset=utf-8", request_.GetHeader("Content-Type"));
|
||||||
|
EXPECT_EQ(std::to_string(data_.size()), request_.GetHeader("Content-Length"));
|
||||||
|
|
||||||
|
EXPECT_EQ(data_, request_.content());
|
||||||
|
EXPECT_EQ(data_.size(), request_.content_length());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string payload_;
|
||||||
|
std::string data_;
|
||||||
|
|
||||||
|
webcc::HttpRequest request_;
|
||||||
|
webcc::HttpRequestParser parser_;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(PostRequestParserTest, ParseFullDataOnce) {
|
||||||
|
bool ok = parser_.Parse(payload_.data(), payload_.size());
|
||||||
|
|
||||||
|
EXPECT_TRUE(ok);
|
||||||
|
EXPECT_TRUE(parser_.finished());
|
||||||
|
|
||||||
|
CheckResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse byte by byte.
|
||||||
|
TEST_F(PostRequestParserTest, ParseByteWise) {
|
||||||
|
for (std::size_t i = 0; i < payload_.size(); ++i) {
|
||||||
|
bool ok = parser_.Parse(payload_.data() + i, 1);
|
||||||
|
EXPECT_TRUE(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_TRUE(parser_.finished());
|
||||||
|
|
||||||
|
CheckResult();
|
||||||
|
}
|
||||||
+22
-6
@@ -28,9 +28,10 @@ std::ostream& operator<<(std::ostream& os, const HttpMessage& message) {
|
|||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
bool HttpMessage::IsConnectionKeepAlive() const {
|
bool HttpMessage::IsConnectionKeepAlive() const {
|
||||||
|
using http::headers::kConnection;
|
||||||
|
|
||||||
bool existed = false;
|
bool existed = false;
|
||||||
const std::string& connection =
|
const std::string& connection = GetHeader(kConnection, &existed);
|
||||||
GetHeader(http::headers::kConnection, &existed);
|
|
||||||
|
|
||||||
if (!existed) {
|
if (!existed) {
|
||||||
// Keep-Alive is by default for HTTP/1.1.
|
// Keep-Alive is by default for HTTP/1.1.
|
||||||
@@ -45,7 +46,9 @@ bool HttpMessage::IsConnectionKeepAlive() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
http::ContentEncoding HttpMessage::GetContentEncoding() const {
|
http::ContentEncoding HttpMessage::GetContentEncoding() const {
|
||||||
const std::string& encoding = GetHeader(http::headers::kContentEncoding);
|
using http::headers::kContentEncoding;
|
||||||
|
|
||||||
|
const std::string& encoding = GetHeader(kContentEncoding);
|
||||||
if (encoding == "gzip") {
|
if (encoding == "gzip") {
|
||||||
return http::ContentEncoding::kGzip;
|
return http::ContentEncoding::kGzip;
|
||||||
}
|
}
|
||||||
@@ -64,11 +67,12 @@ bool HttpMessage::AcceptEncodingGzip() const {
|
|||||||
// See: https://tools.ietf.org/html/rfc7231#section-3.1.1.1
|
// See: https://tools.ietf.org/html/rfc7231#section-3.1.1.1
|
||||||
void HttpMessage::SetContentType(const std::string& media_type,
|
void HttpMessage::SetContentType(const std::string& media_type,
|
||||||
const std::string& charset) {
|
const std::string& charset) {
|
||||||
|
using http::headers::kContentType;
|
||||||
|
|
||||||
if (charset.empty()) {
|
if (charset.empty()) {
|
||||||
SetHeader(http::headers::kContentType, media_type);
|
SetHeader(kContentType, media_type);
|
||||||
} else {
|
} else {
|
||||||
SetHeader(http::headers::kContentType,
|
SetHeader(kContentType, media_type + ";charset=" + charset);
|
||||||
media_type + ";charset=" + charset);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,6 +107,18 @@ void HttpMessage::Prepare() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HttpMessage::CopyPayload(std::ostream& os) const {
|
||||||
|
for (const boost::asio::const_buffer& b : payload_) {
|
||||||
|
os.write(static_cast<const char*>(b.data()), b.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpMessage::CopyPayload(std::string* str) const {
|
||||||
|
std::stringstream ss;
|
||||||
|
CopyPayload(ss);
|
||||||
|
*str = ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
void HttpMessage::Dump(std::ostream& os, std::size_t indent,
|
void HttpMessage::Dump(std::ostream& os, std::size_t indent,
|
||||||
const std::string& prefix) const {
|
const std::string& prefix) const {
|
||||||
std::string indent_str;
|
std::string indent_str;
|
||||||
|
|||||||
@@ -88,6 +88,12 @@ public:
|
|||||||
return payload_;
|
return payload_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy the exact payload to the given output stream.
|
||||||
|
void CopyPayload(std::ostream& os) const;
|
||||||
|
|
||||||
|
// Copy the exact payload to the given string.
|
||||||
|
void CopyPayload(std::string* str) const;
|
||||||
|
|
||||||
// Dump to output stream.
|
// Dump to output stream.
|
||||||
void Dump(std::ostream& os, std::size_t indent = 0,
|
void Dump(std::ostream& os, std::size_t indent = 0,
|
||||||
const std::string& prefix = "") const;
|
const std::string& prefix = "") const;
|
||||||
|
|||||||
@@ -152,8 +152,8 @@ bool HttpParser::ParseHeaderLine(const std::string& line) {
|
|||||||
|
|
||||||
LOG_INFO("Content length: %u.", content_length_);
|
LOG_INFO("Content length: %u.", content_length_);
|
||||||
|
|
||||||
|
// Reserve memory to avoid frequent reallocation when append.
|
||||||
try {
|
try {
|
||||||
// Reserve memory to avoid frequent reallocation when append.
|
|
||||||
content_.reserve(content_length_);
|
content_.reserve(content_length_);
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
LOG_ERRO("Failed to reserve content memory: %s.", e.what());
|
LOG_ERRO("Failed to reserve content memory: %s.", e.what());
|
||||||
|
|||||||
Reference in New Issue
Block a user