Optimize memory allocation for client buffer; add VLD to detect memory on Windows.
This commit is contained in:
+17
-6
@@ -6,6 +6,13 @@ option(WEBCC_ENABLE_SOAP "Enable SOAP support (need pugixml)?" ON)
|
||||
option(WEBCC_BUILD_UNITTEST "Build unit test?" ON)
|
||||
option(WEBCC_BUILD_EXAMPLE "Build examples?" ON)
|
||||
|
||||
if(WIN32)
|
||||
option(WEBCC_ENABLE_VLD "Enable VLD (Visual Leak Detector)?" OFF)
|
||||
if(WEBCC_ENABLE_VLD)
|
||||
add_definitions(-DWEBCC_ENABLE_VLD)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(WEBCC_LOG_LEVEL "VERB" CACHE STRING "Log level (VERB, INFO, WARN, ERRO or FATA)")
|
||||
|
||||
if(WEBCC_ENABLE_LOG)
|
||||
@@ -96,13 +103,17 @@ endif()
|
||||
# For including its own headers as "webcc/http_client.h".
|
||||
include_directories(${PROJECT_SOURCE_DIR}/src)
|
||||
|
||||
if(WIN32)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/third_party/win32/include)
|
||||
link_directories(${PROJECT_SOURCE_DIR}/third_party/win32/lib)
|
||||
endif()
|
||||
|
||||
# SOAP support needs pugixml to parse and create XML.
|
||||
if(WEBCC_ENABLE_SOAP)
|
||||
add_subdirectory(${PROJECT_SOURCE_DIR}/third_party/pugixml)
|
||||
add_subdirectory(${PROJECT_SOURCE_DIR}/third_party/src/pugixml)
|
||||
|
||||
# For including pugixml as "pugixml/pugixml.hpp".
|
||||
# TODO: Remove "pugixml" prefix?
|
||||
include_directories(${PROJECT_SOURCE_DIR}/third_party)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/third_party/src)
|
||||
endif()
|
||||
|
||||
add_subdirectory(src/webcc)
|
||||
@@ -112,8 +123,8 @@ if(WEBCC_BUILD_EXAMPLE)
|
||||
add_subdirectory(${PROJECT_SOURCE_DIR}/example/http/async_client)
|
||||
|
||||
# REST example needs jsoncpp to parse and create JSON.
|
||||
add_subdirectory(${PROJECT_SOURCE_DIR}/third_party/jsoncpp)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/third_party/jsoncpp)
|
||||
add_subdirectory(${PROJECT_SOURCE_DIR}/third_party/src/jsoncpp)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/third_party/src/jsoncpp)
|
||||
|
||||
add_subdirectory(${PROJECT_SOURCE_DIR}/example/rest/book_server)
|
||||
add_subdirectory(${PROJECT_SOURCE_DIR}/example/rest/book_client)
|
||||
@@ -126,6 +137,6 @@ if(WEBCC_BUILD_EXAMPLE)
|
||||
endif()
|
||||
|
||||
if(WEBCC_BUILD_UNITTEST)
|
||||
add_subdirectory(third_party/gtest)
|
||||
add_subdirectory(third_party/src/gtest)
|
||||
add_subdirectory(unittest)
|
||||
endif()
|
||||
|
||||
@@ -40,7 +40,7 @@ void Test(boost::asio::io_context& ioc) {
|
||||
}
|
||||
|
||||
int main() {
|
||||
LOG_INIT("", webcc::LOG_CONSOLE);
|
||||
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
|
||||
|
||||
boost::asio::io_context ioc;
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ void Test() {
|
||||
}
|
||||
|
||||
int main() {
|
||||
LOG_INIT("", webcc::LOG_CONSOLE);
|
||||
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
|
||||
|
||||
Test();
|
||||
Test();
|
||||
|
||||
@@ -101,7 +101,7 @@ int main(int argc, char* argv[]) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
LOG_INIT("", webcc::LOG_CONSOLE);
|
||||
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
|
||||
|
||||
std::string host = argv[1];
|
||||
std::string port = argv[2];
|
||||
|
||||
@@ -5,6 +5,19 @@
|
||||
#include "webcc/logger.h"
|
||||
#include "webcc/rest_client.h"
|
||||
|
||||
// In order to run with VLD, please copy the following files to the example
|
||||
// output folder from "third_party\win32\bin":
|
||||
// - dbghelp.dll
|
||||
// - Microsoft.DTfW.DHL.manifest
|
||||
// - vld_x86.dll
|
||||
#if (defined(WIN32) || defined(_WIN64))
|
||||
#if defined(_DEBUG) && defined(WEBCC_ENABLE_VLD)
|
||||
#pragma message ("< include vld.h >")
|
||||
#include "vld/vld.h"
|
||||
#pragma comment(lib, "vld")
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Write a JSON object to string.
|
||||
@@ -160,7 +173,7 @@ int main(int argc, char* argv[]) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
LOG_INIT("", webcc::LOG_CONSOLE);
|
||||
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
|
||||
|
||||
std::string host = argv[1];
|
||||
std::string port = argv[2];
|
||||
|
||||
@@ -5,6 +5,19 @@
|
||||
|
||||
#include "book_services.h"
|
||||
|
||||
// In order to run with VLD, please copy the following files to the example
|
||||
// output folder from "third_party\win32\bin":
|
||||
// - dbghelp.dll
|
||||
// - Microsoft.DTfW.DHL.manifest
|
||||
// - vld_x86.dll
|
||||
#if (defined(WIN32) || defined(_WIN64))
|
||||
#if defined(_DEBUG) && defined(WEBCC_ENABLE_VLD)
|
||||
#pragma message ("< include vld.h >")
|
||||
#include "vld/vld.h"
|
||||
#pragma comment(lib, "vld")
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void Help(const char* argv0) {
|
||||
std::cout << "Usage: " << argv0 << " <port> [seconds]" << std::endl;
|
||||
std::cout << "If |seconds| is provided, the server will sleep these seconds "
|
||||
@@ -21,7 +34,7 @@ int main(int argc, char* argv[]) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
LOG_INIT("", webcc::LOG_FILE | webcc::LOG_CONSOLE | webcc::LOG_OVERWRITE);
|
||||
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
|
||||
|
||||
unsigned short port = std::atoi(argv[1]);
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "calc_client.h"
|
||||
|
||||
int main() {
|
||||
LOG_INIT("", webcc::LOG_CONSOLE);
|
||||
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
|
||||
|
||||
CalcClient calc;
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ int main(int argc, char* argv[]) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
LOG_INIT("", webcc::LOG_CONSOLE);
|
||||
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
|
||||
|
||||
unsigned short port = std::atoi(argv[1]);
|
||||
|
||||
|
||||
@@ -13,8 +13,12 @@
|
||||
|
||||
namespace webcc {
|
||||
|
||||
extern void AdjustBufferSize(std::size_t content_length,
|
||||
std::vector<char>* buffer);
|
||||
|
||||
AsyncHttpClient::AsyncHttpClient(boost::asio::io_context& io_context)
|
||||
: socket_(io_context),
|
||||
buffer_(kBufferSize),
|
||||
timeout_seconds_(kMaxReceiveSeconds),
|
||||
deadline_(io_context) {
|
||||
resolver_.reset(new tcp::resolver(io_context));
|
||||
@@ -165,21 +169,36 @@ void AsyncHttpClient::ReadHandler(boost::system::error_code ec,
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_VERB("Socket async read handler.");
|
||||
|
||||
if (ec || length == 0) {
|
||||
Stop();
|
||||
LOG_ERRO("Socket read error.");
|
||||
response_handler_(response_, kSocketReadError, timed_out_);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO("Read data, length: %d.", length);
|
||||
|
||||
bool content_length_parsed = response_parser_->content_length_parsed();
|
||||
|
||||
// Parse the response piece just read.
|
||||
// If the content has been fully received, |finished()| will be true.
|
||||
if (!response_parser_->Parse(buffer_.data(), length)) {
|
||||
Stop();
|
||||
LOG_ERRO("Failed to parse HTTP response.");
|
||||
response_handler_(response_, kHttpError, timed_out_);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!content_length_parsed &&
|
||||
response_parser_->content_length_parsed()) {
|
||||
// Content length just has been parsed.
|
||||
AdjustBufferSize(response_parser_->content_length(), &buffer_);
|
||||
}
|
||||
|
||||
if (response_parser_->finished()) {
|
||||
LOG_INFO("Finished to read and parse HTTP response.");
|
||||
LOG_VERB("HTTP response:\n%s", response_->Dump(4, "> ").c_str());
|
||||
Stop();
|
||||
response_handler_(response_, kNoError, timed_out_);
|
||||
@@ -199,11 +218,8 @@ void AsyncHttpClient::CheckDeadline() {
|
||||
// The deadline has passed.
|
||||
// The socket is closed so that any outstanding asynchronous operations
|
||||
// are canceled.
|
||||
boost::system::error_code ignored_ec;
|
||||
socket_.close(ignored_ec);
|
||||
|
||||
deadline_.expires_at(boost::posix_time::pos_infin);
|
||||
|
||||
LOG_WARN("HTTP client timed out.");
|
||||
Stop();
|
||||
timed_out_ = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#ifndef WEBCC_ASYNC_HTTP_CLIENT_H_
|
||||
#define WEBCC_ASYNC_HTTP_CLIENT_H_
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "boost/asio/deadline_timer.hpp"
|
||||
#include "boost/asio/io_context.hpp"
|
||||
@@ -67,7 +67,7 @@ class AsyncHttpClient : public std::enable_shared_from_this<AsyncHttpClient> {
|
||||
std::unique_ptr<tcp::resolver> resolver_;
|
||||
tcp::resolver::results_type endpoints_;
|
||||
|
||||
std::array<char, kBufferSize> buffer_;
|
||||
std::vector<char> buffer_;
|
||||
|
||||
std::unique_ptr<HttpResponseParser> response_parser_;
|
||||
|
||||
|
||||
+1
-2
@@ -17,8 +17,7 @@ namespace webcc {
|
||||
// -----------------------------------------------------------------------------
|
||||
// Constants
|
||||
|
||||
// Buffer size for sending HTTP request and receiving HTTP response.
|
||||
// TODO: Configurable
|
||||
// Default buffer size for socket reading.
|
||||
const std::size_t kBufferSize = 1024;
|
||||
|
||||
const std::size_t kInvalidLength = static_cast<std::size_t>(-1);
|
||||
|
||||
+63
-10
@@ -19,20 +19,43 @@
|
||||
|
||||
namespace webcc {
|
||||
|
||||
// Adjust buffer size according to content length.
|
||||
// This is to avoid reading too many times.
|
||||
// Also used by AsyncHttpClient.
|
||||
void AdjustBufferSize(std::size_t content_length, std::vector<char>* buffer) {
|
||||
const std::size_t kMaxTimes = 10;
|
||||
|
||||
// According to test, a client never read more than 200000 bytes a time.
|
||||
// So it doesn't make sense to set any larger size, e.g., 1MB.
|
||||
const std::size_t kMaxBufferSize = 200000;
|
||||
|
||||
LOG_INFO("Adjust buffer size according to content length.");
|
||||
|
||||
std::size_t min_buffer_size = content_length / kMaxTimes;
|
||||
if (min_buffer_size > buffer->size()) {
|
||||
buffer->resize(std::min(min_buffer_size, kMaxBufferSize));
|
||||
LOG_INFO("Resize read buffer to %d.", buffer->size());
|
||||
} else {
|
||||
LOG_INFO("Keep the current buffer size (%d).", buffer->size());
|
||||
}
|
||||
}
|
||||
|
||||
HttpClient::HttpClient()
|
||||
: socket_(io_context_),
|
||||
buffer_(kBufferSize),
|
||||
timeout_seconds_(kMaxReceiveSeconds),
|
||||
deadline_(io_context_) {
|
||||
deadline_.expires_at(boost::posix_time::pos_infin);
|
||||
}
|
||||
|
||||
bool HttpClient::Request(const HttpRequest& request) {
|
||||
response_.reset(new HttpResponse());
|
||||
response_parser_.reset(new HttpResponseParser(response_.get()));
|
||||
|
||||
stopped_ = false;
|
||||
timed_out_ = false;
|
||||
|
||||
// Start the persistent actor that checks for deadline expiry.
|
||||
deadline_.expires_at(boost::posix_time::pos_infin);
|
||||
CheckDeadline();
|
||||
|
||||
if ((error_ = Connect(request)) != kNoError) {
|
||||
@@ -50,6 +73,15 @@ bool HttpClient::Request(const HttpRequest& request) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void HttpClient::Stop() {
|
||||
stopped_ = true;
|
||||
|
||||
boost::system::error_code ignored_ec;
|
||||
socket_.close(ignored_ec);
|
||||
|
||||
deadline_.cancel();
|
||||
}
|
||||
|
||||
Error HttpClient::Connect(const HttpRequest& request) {
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
@@ -88,6 +120,7 @@ Error HttpClient::Connect(const HttpRequest& request) {
|
||||
// check whether the socket is still open before deciding if we succeeded
|
||||
// or failed.
|
||||
if (ec || !socket_.is_open()) {
|
||||
Stop();
|
||||
if (!ec) {
|
||||
timed_out_ = true;
|
||||
}
|
||||
@@ -114,6 +147,7 @@ Error HttpClient::SendReqeust(const HttpRequest& request) {
|
||||
} while (ec == boost::asio::error::would_block);
|
||||
|
||||
if (ec) {
|
||||
Stop();
|
||||
return kSocketWriteError;
|
||||
}
|
||||
|
||||
@@ -121,6 +155,8 @@ Error HttpClient::SendReqeust(const HttpRequest& request) {
|
||||
}
|
||||
|
||||
Error HttpClient::ReadResponse() {
|
||||
deadline_.expires_from_now(boost::posix_time::seconds(timeout_seconds_));
|
||||
|
||||
Error error = kNoError;
|
||||
DoReadResponse(&error);
|
||||
|
||||
@@ -132,8 +168,6 @@ Error HttpClient::ReadResponse() {
|
||||
}
|
||||
|
||||
void HttpClient::DoReadResponse(Error* error) {
|
||||
deadline_.expires_from_now(boost::posix_time::seconds(timeout_seconds_));
|
||||
|
||||
boost::system::error_code ec = boost::asio::error::would_block;
|
||||
|
||||
socket_.async_read_some(
|
||||
@@ -142,24 +176,42 @@ void HttpClient::DoReadResponse(Error* error) {
|
||||
std::size_t length) {
|
||||
ec = inner_ec;
|
||||
|
||||
LOG_VERB("Socket async read handler.");
|
||||
|
||||
if (stopped_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (inner_ec || length == 0) {
|
||||
Stop();
|
||||
*error = kSocketReadError;
|
||||
LOG_ERRO("Socket read error.");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO("Read data, length: %d.", length);
|
||||
|
||||
bool content_length_parsed = response_parser_->content_length_parsed();
|
||||
|
||||
// Parse the response piece just read.
|
||||
// If the content has been fully received, next time flag "finished_"
|
||||
// will be set.
|
||||
if (!response_parser_->Parse(buffer_.data(), length)) {
|
||||
Stop();
|
||||
*error = kHttpError;
|
||||
LOG_ERRO("Failed to parse HTTP response.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!content_length_parsed &&
|
||||
response_parser_->content_length_parsed()) {
|
||||
// Content length just has been parsed.
|
||||
AdjustBufferSize(response_parser_->content_length(), &buffer_);
|
||||
}
|
||||
|
||||
if (response_parser_->finished()) {
|
||||
// Stop trying to read once all content has been received,
|
||||
// because some servers will block extra call to read_some().
|
||||
Stop();
|
||||
LOG_INFO("Finished to read and parse HTTP response.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -173,16 +225,17 @@ void HttpClient::DoReadResponse(Error* error) {
|
||||
}
|
||||
|
||||
void HttpClient::CheckDeadline() {
|
||||
if (stopped_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (deadline_.expires_at() <=
|
||||
boost::asio::deadline_timer::traits_type::now()) {
|
||||
// The deadline has passed.
|
||||
// The socket is closed so that any outstanding asynchronous operations
|
||||
// are canceled.
|
||||
boost::system::error_code ignored_ec;
|
||||
socket_.close(ignored_ec);
|
||||
|
||||
deadline_.expires_at(boost::posix_time::pos_infin);
|
||||
|
||||
LOG_WARN("HTTP client timed out.");
|
||||
Stop();
|
||||
timed_out_ = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#ifndef WEBCC_HTTP_CLIENT_H_
|
||||
#define WEBCC_HTTP_CLIENT_H_
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "boost/asio/deadline_timer.hpp"
|
||||
#include "boost/asio/io_context.hpp"
|
||||
@@ -24,6 +25,7 @@ class HttpClient {
|
||||
DELETE_COPY_AND_ASSIGN(HttpClient);
|
||||
|
||||
void set_timeout_seconds(int timeout_seconds) {
|
||||
assert(timeout_seconds > 0);
|
||||
timeout_seconds_ = timeout_seconds;
|
||||
}
|
||||
|
||||
@@ -37,6 +39,9 @@ class HttpClient {
|
||||
bool Request(const HttpRequest& request);
|
||||
|
||||
private:
|
||||
// Terminate all the actors to shut down the connection.
|
||||
void Stop();
|
||||
|
||||
Error Connect(const HttpRequest& request);
|
||||
|
||||
Error SendReqeust(const HttpRequest& request);
|
||||
@@ -51,13 +56,15 @@ class HttpClient {
|
||||
|
||||
boost::asio::ip::tcp::socket socket_;
|
||||
|
||||
std::array<char, kBufferSize> buffer_;
|
||||
std::vector<char> buffer_;
|
||||
|
||||
HttpResponsePtr response_;
|
||||
std::unique_ptr<HttpResponseParser> response_parser_;
|
||||
|
||||
Error error_ = kNoError;
|
||||
|
||||
bool stopped_ = false;
|
||||
|
||||
// If the error was caused by timeout or not.
|
||||
bool timed_out_ = false;
|
||||
|
||||
|
||||
@@ -12,8 +12,9 @@
|
||||
namespace webcc {
|
||||
|
||||
HttpConnection::HttpConnection(boost::asio::ip::tcp::socket socket,
|
||||
HttpRequestHandler* handler)
|
||||
HttpRequestHandler* handler)
|
||||
: socket_(std::move(socket)),
|
||||
buffer_(kBufferSize),
|
||||
request_handler_(handler),
|
||||
request_parser_(&request_) {
|
||||
}
|
||||
@@ -28,7 +29,7 @@ void HttpConnection::Close() {
|
||||
}
|
||||
|
||||
void HttpConnection::SetResponseContent(std::string&& content,
|
||||
const std::string& content_type) {
|
||||
const std::string& content_type) {
|
||||
response_.SetContent(std::move(content));
|
||||
response_.SetContentType(content_type);
|
||||
}
|
||||
@@ -47,7 +48,7 @@ void HttpConnection::AsyncRead() {
|
||||
}
|
||||
|
||||
void HttpConnection::ReadHandler(boost::system::error_code ec,
|
||||
std::size_t length) {
|
||||
std::size_t length) {
|
||||
if (ec) {
|
||||
if (ec != boost::asio::error::operation_aborted) {
|
||||
Close();
|
||||
@@ -88,7 +89,7 @@ void HttpConnection::AsyncWrite() {
|
||||
// io_context.run), even though DoWrite() is invoked by worker threads. This is
|
||||
// ensured by Asio.
|
||||
void HttpConnection::WriteHandler(boost::system::error_code ec,
|
||||
std::size_t length) {
|
||||
std::size_t length) {
|
||||
if (!ec) {
|
||||
LOG_INFO("Response has been sent back.");
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#ifndef WEBCC_HTTP_CONNECTION_H_
|
||||
#define WEBCC_HTTP_CONNECTION_H_
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "boost/asio/ip/tcp.hpp" // for ip::tcp::socket
|
||||
|
||||
@@ -51,12 +51,12 @@ class HttpConnection : public std::enable_shared_from_this<HttpConnection> {
|
||||
// Socket for the connection.
|
||||
boost::asio::ip::tcp::socket socket_;
|
||||
|
||||
// Buffer for incoming data.
|
||||
std::vector<char> buffer_;
|
||||
|
||||
// The handler used to process the incoming request.
|
||||
HttpRequestHandler* request_handler_;
|
||||
|
||||
// Buffer for incoming data.
|
||||
std::array<char, kBufferSize> buffer_;
|
||||
|
||||
// The incoming request.
|
||||
HttpRequest request_;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "boost/lexical_cast.hpp"
|
||||
|
||||
#include "webcc/http_message.h"
|
||||
#include "webcc/logger.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
@@ -16,10 +17,10 @@ HttpParser::HttpParser(HttpMessage* message)
|
||||
finished_(false) {
|
||||
}
|
||||
|
||||
bool HttpParser::Parse(const char* data, std::size_t len) {
|
||||
bool HttpParser::Parse(const char* data, std::size_t length) {
|
||||
if (header_parsed_) {
|
||||
// Add the data to the content.
|
||||
AppendContent(data, len);
|
||||
AppendContent(data, length);
|
||||
|
||||
if (IsContentFull()) {
|
||||
// All content has been read.
|
||||
@@ -29,7 +30,7 @@ bool HttpParser::Parse(const char* data, std::size_t len) {
|
||||
return true;
|
||||
}
|
||||
|
||||
pending_data_.append(data, len);
|
||||
pending_data_.append(data, length);
|
||||
std::size_t off = 0;
|
||||
|
||||
while (true) {
|
||||
@@ -64,9 +65,10 @@ bool HttpParser::Parse(const char* data, std::size_t len) {
|
||||
|
||||
if (header_parsed_) {
|
||||
// Headers just ended.
|
||||
LOG_INFO("HTTP headers parsed.");
|
||||
|
||||
if (!content_length_parsed_) {
|
||||
// No Content-Length, no content. (TODO: Support chucked data)
|
||||
// No Content-Length, no content.
|
||||
Finish();
|
||||
return true;
|
||||
} else {
|
||||
@@ -110,7 +112,16 @@ void HttpParser::ParseContentLength(const std::string& line) {
|
||||
|
||||
try {
|
||||
content_length_ = boost::lexical_cast<std::size_t>(value);
|
||||
LOG_INFO("Content length: %d", content_length_);
|
||||
|
||||
// Reserve memory to avoid frequent reallocation when append.
|
||||
try {
|
||||
content_.reserve(content_length_);
|
||||
} catch (std::exception& e) {
|
||||
LOG_ERRO("Failed to reserve content memory: %s.", e.what());
|
||||
}
|
||||
} catch (boost::bad_lexical_cast&) {
|
||||
LOG_ERRO("Invalid content length: %s.", value.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,12 @@ class HttpParser {
|
||||
|
||||
DELETE_COPY_AND_ASSIGN(HttpParser);
|
||||
|
||||
bool finished() const {
|
||||
return finished_;
|
||||
}
|
||||
bool finished() const { return finished_; }
|
||||
|
||||
bool Parse(const char* data, std::size_t len);
|
||||
bool content_length_parsed() const { return content_length_parsed_; }
|
||||
std::size_t content_length() const { return content_length_; }
|
||||
|
||||
bool Parse(const char* data, std::size_t length);
|
||||
|
||||
protected:
|
||||
virtual bool ParseStartLine(const std::string& line) = 0;
|
||||
|
||||
+6
-3
@@ -30,6 +30,10 @@ enum LogMode {
|
||||
LOG_OVERWRITE = 8, // Overwrite any existing log file.
|
||||
};
|
||||
|
||||
// Commonly used modes.
|
||||
const int LOG_CONSOLE_FILE_APPEND = LOG_CONSOLE | LOG_FILE;
|
||||
const int LOG_CONSOLE_FILE_OVERWRITE = LOG_CONSOLE | LOG_FILE | LOG_OVERWRITE;
|
||||
|
||||
// Initialize logger.
|
||||
// If |dir| is empty, log file will be generated in current directory.
|
||||
void LogInit(const std::string& dir, int modes);
|
||||
@@ -39,8 +43,7 @@ void LogWrite(int level, const char* file, int line, const char* format, ...);
|
||||
} // namespace webcc
|
||||
|
||||
// Initialize the logger with a level.
|
||||
// E.g., LOG_INIT(FLUSH)
|
||||
#define LOG_INIT(dir, modes) webcc::LogInit(dir, modes);
|
||||
#define WEBCC_LOG_INIT(dir, modes) webcc::LogInit(dir, modes);
|
||||
|
||||
#if (defined(WIN32) || defined(_WIN64))
|
||||
|
||||
@@ -126,7 +129,7 @@ void LogWrite(int level, const char* file, int line, const char* format, ...);
|
||||
|
||||
#else // WEBCC_ENABLE_LOG == 0
|
||||
|
||||
#define LOG_INIT(modes)
|
||||
#define WEBCC_LOG_INIT(modes)
|
||||
|
||||
#if (defined(WIN32) || defined(_WIN64))
|
||||
#define LOG_VERB(format, ...)
|
||||
|
||||
@@ -25,7 +25,10 @@ bool RestClient::Request(const std::string& method,
|
||||
request.Build();
|
||||
|
||||
HttpClient http_client;
|
||||
http_client.set_timeout_seconds(timeout_seconds_);
|
||||
|
||||
if (timeout_seconds_ > 0) {
|
||||
http_client.set_timeout_seconds(timeout_seconds_);
|
||||
}
|
||||
|
||||
if (!http_client.Request(request)) {
|
||||
error_ = http_client.error();
|
||||
|
||||
@@ -63,8 +63,8 @@ class RestClient {
|
||||
std::string host_;
|
||||
std::string port_;
|
||||
|
||||
// -1 means default timeout (normally 30s) will be used.
|
||||
int timeout_seconds_ = -1;
|
||||
// Timeout in seconds; only effective when > 0.
|
||||
int timeout_seconds_ = 0;
|
||||
|
||||
HttpResponsePtr response_;
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ Error SoapClient::Call(const std::string& operation,
|
||||
|
||||
HttpClient http_client;
|
||||
|
||||
if (timeout_seconds_ != -1) {
|
||||
if (timeout_seconds_ > 0) {
|
||||
http_client.set_timeout_seconds(timeout_seconds_);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,8 +26,8 @@ class SoapClient {
|
||||
std::vector<Parameter>&& parameters,
|
||||
std::string* result);
|
||||
|
||||
// -1 means default timeout (normally 30s) will be used.
|
||||
int timeout_seconds_ = -1;
|
||||
// Timeout in seconds; only effective when > 0.
|
||||
int timeout_seconds_ = 0;
|
||||
|
||||
// If the error was caused by timeout or not.
|
||||
bool timed_out_ = false;
|
||||
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
Pugixml is not a Git submodule because its Git repo is not well structured. It does have a CMakeLists.txt but it's difficult to integrate.
|
||||
|
||||
TODO: Use Git submodule for gtest?
|
||||
Vendored
Vendored
Vendored
Vendored
Vendored
Vendored
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<!-- $Id -->
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<noInheritable />
|
||||
<assemblyIdentity type="win32" name="Microsoft.DTfW.DHL" version="6.11.1.404" processorArchitecture="x86" />
|
||||
<file name="dbghelp.dll" />
|
||||
</assembly>
|
||||
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
+307
@@ -0,0 +1,307 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Visual Leak Detector - Import Library Header
|
||||
// Copyright (c) 2005-2013 VLD Team
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
//
|
||||
// See COPYING.txt for the full terms of the GNU Lesser General Public License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vld_def.h"
|
||||
|
||||
#if defined _DEBUG || defined VLD_FORCE_ENABLE
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#pragma comment(lib, "vld.lib")
|
||||
|
||||
// Force a symbolic reference to the global VisualLeakDetector class object from
|
||||
// the DLL. This ensures that the DLL is loaded and linked with the program,
|
||||
// even if no code otherwise imports any of the DLL's exports.
|
||||
#pragma comment(linker, "/include:__imp_?g_vld@@3VVisualLeakDetector@@A")
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Visual Leak Detector APIs
|
||||
//
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
// VLDDisable - Disables Visual Leak Detector's memory leak detection at
|
||||
// runtime. If memory leak detection is already disabled, then calling this
|
||||
// function has no effect.
|
||||
//
|
||||
// Note: In multithreaded programs, this function operates on a per-thread
|
||||
// basis. In other words, if you call this function from one thread, then
|
||||
// memory leak detection is only disabled for that thread. If memory leak
|
||||
// detection is enabled for other threads, then it will remain enabled for
|
||||
// those other threads. It was designed to work this way to insulate you,
|
||||
// the programmer, from having to ensure thread synchronization when calling
|
||||
// VLDEnable() and VLDDisable(). Without this, calling these two functions
|
||||
// unsynchronized could result in unpredictable and unintended behavior.
|
||||
// But this also means that if you want to disable memory leak detection
|
||||
// process-wide, then you need to call this function from every thread in
|
||||
// the process.
|
||||
//
|
||||
// Return Value:
|
||||
//
|
||||
// None.
|
||||
//
|
||||
__declspec(dllimport) void VLDDisable ();
|
||||
|
||||
// VLDEnable - Enables Visual Leak Detector's memory leak detection at runtime.
|
||||
// If memory leak detection is already enabled, which it is by default, then
|
||||
// calling this function has no effect.
|
||||
//
|
||||
// Note: In multithreaded programs, this function operates on a per-thread
|
||||
// basis. In other words, if you call this function from one thread, then
|
||||
// memory leak detection is only enabled for that thread. If memory leak
|
||||
// detection is disabled for other threads, then it will remain disabled for
|
||||
// those other threads. It was designed to work this way to insulate you,
|
||||
// the programmer, from having to ensure thread synchronization when calling
|
||||
// VLDEnable() and VLDDisable(). Without this, calling these two functions
|
||||
// unsynchronized could result in unpredictable and unintended behavior.
|
||||
// But this also means that if you want to enable memory leak detection
|
||||
// process-wide, then you need to call this function from every thread in
|
||||
// the process.
|
||||
//
|
||||
// Return Value:
|
||||
//
|
||||
// None.
|
||||
//
|
||||
__declspec(dllimport) void VLDEnable ();
|
||||
|
||||
// VLDRestore - Restore Visual Leak Detector's previous state.
|
||||
//
|
||||
// Return Value:
|
||||
//
|
||||
// None.
|
||||
//
|
||||
__declspec(dllimport) void VLDRestore ();
|
||||
|
||||
// VLDGlobalDisable - Disables Visual Leak Detector's memory leak detection at
|
||||
// runtime in all threads. If memory leak detection is already disabled,
|
||||
// then calling this function has no effect.
|
||||
//
|
||||
// Return Value:
|
||||
//
|
||||
// None.
|
||||
//
|
||||
__declspec(dllimport) void VLDGlobalDisable ();
|
||||
|
||||
// VLDGlobalEnable - Enables Visual Leak Detector's memory leak detection
|
||||
// at runtime in all threads. If memory leak detection is already enabled,
|
||||
// which it is by default, then calling this function has no effect.
|
||||
//
|
||||
// Return Value:
|
||||
//
|
||||
// None.
|
||||
//
|
||||
__declspec(dllimport) void VLDGlobalEnable ();
|
||||
|
||||
// VLDReportLeaks - Report leaks up to the execution point.
|
||||
//
|
||||
// Return Value:
|
||||
//
|
||||
// None.
|
||||
//
|
||||
__declspec(dllimport) UINT VLDReportLeaks ();
|
||||
|
||||
// VLDGetLeaksCount - Return memory leaks count to the execution point.
|
||||
//
|
||||
// Return Value:
|
||||
//
|
||||
// None.
|
||||
//
|
||||
__declspec(dllimport) UINT VLDGetLeaksCount ();
|
||||
|
||||
// VLDMarkAllLeaksAsReported - Mark all leaks as reported.
|
||||
//
|
||||
// Return Value:
|
||||
//
|
||||
// None.
|
||||
//
|
||||
__declspec(dllimport) void VLDMarkAllLeaksAsReported ();
|
||||
|
||||
|
||||
// VLDRefreshModules - Look for recently loaded DLLs and patch them if necessary.
|
||||
//
|
||||
// Return Value:
|
||||
//
|
||||
// None.
|
||||
//
|
||||
__declspec(dllimport) void VLDRefreshModules();
|
||||
|
||||
|
||||
// VLDEnableModule - Enable Memory leak checking on the specified module.
|
||||
//
|
||||
// module: module handle.
|
||||
//
|
||||
// Return Value:
|
||||
//
|
||||
// None.
|
||||
//
|
||||
|
||||
__declspec(dllimport) void VLDEnableModule(HMODULE module);
|
||||
|
||||
|
||||
// VLDDisableModule - Disable Memory leak checking on the specified module.
|
||||
//
|
||||
// module: module handle.
|
||||
//
|
||||
// Return Value:
|
||||
//
|
||||
// None.
|
||||
//
|
||||
__declspec(dllimport) void VLDDisableModule(HMODULE module);
|
||||
|
||||
// VLDGetOptions - Return all current options.
|
||||
//
|
||||
// Return Value:
|
||||
//
|
||||
// Mask of current options.
|
||||
//
|
||||
__declspec(dllimport) UINT VLDGetOptions();
|
||||
|
||||
// VLDGetReportFilename - Return current report filename.
|
||||
//
|
||||
// filename: current report filename (max characters - MAX_PATH).
|
||||
//
|
||||
// Return Value:
|
||||
//
|
||||
// None.
|
||||
//
|
||||
__declspec(dllimport) void VLDGetReportFilename(WCHAR *filename);
|
||||
|
||||
// VLDSetOptions - Update the report options via function call rather than INI file.
|
||||
//
|
||||
// option_mask: Only the following flags are checked
|
||||
// VLD_OPT_AGGREGATE_DUPLICATES
|
||||
// VLD_OPT_MODULE_LIST_INCLUDE
|
||||
// VLD_OPT_SAFE_STACK_WALK
|
||||
// VLD_OPT_SLOW_DEBUGGER_DUMP
|
||||
// VLD_OPT_TRACE_INTERNAL_FRAMES
|
||||
// VLD_OPT_START_DISABLED
|
||||
// VLD_OPT_SKIP_HEAPFREE_LEAKS
|
||||
// VLD_OPT_VALIDATE_HEAPFREE
|
||||
//
|
||||
// maxDataDump: maximum number of user-data bytes to dump for each leaked block.
|
||||
//
|
||||
// maxTraceFrames: maximum number of frames per stack trace for each leaked block.
|
||||
//
|
||||
// Return Value:
|
||||
//
|
||||
// None.
|
||||
//
|
||||
__declspec(dllimport) void VLDSetOptions(UINT option_mask, SIZE_T maxDataDump, UINT maxTraceFrames);
|
||||
|
||||
// VLDSetModulesList - Set list of modules included/excluded in leak detection
|
||||
// depending on parameter "includeModules".
|
||||
//
|
||||
// modules: list of modules to be forcefully included/excluded in leak detection.
|
||||
//
|
||||
// includeModules: include or exclude that modules.
|
||||
//
|
||||
// Return Value:
|
||||
//
|
||||
// None.
|
||||
//
|
||||
__declspec(dllimport) void VLDSetModulesList(CONST WCHAR *modules, BOOL includeModules);
|
||||
|
||||
// VLDGetModulesList - Return current list of included/excluded modules
|
||||
// depending on flag VLD_OPT_TRACE_INTERNAL_FRAMES.
|
||||
//
|
||||
// modules: destination string for list of included/excluded modules (maximum length 512 characters).
|
||||
//
|
||||
// size: maximum string size.
|
||||
//
|
||||
// Return Value:
|
||||
//
|
||||
// BOOL: TRUE if include modules, otherwise FALSE.
|
||||
//
|
||||
__declspec(dllimport) BOOL VLDGetModulesList(WCHAR *modules, UINT size);
|
||||
|
||||
// VLDSetReportOptions - Update the report options via function call rather than INI file.
|
||||
//
|
||||
// Only the following flags are checked
|
||||
// VLD_OPT_REPORT_TO_DEBUGGER
|
||||
// VLD_OPT_REPORT_TO_FILE
|
||||
// VLD_OPT_REPORT_TO_STDOUT
|
||||
// VLD_OPT_UNICODE_REPORT
|
||||
//
|
||||
// filename is optional and can be NULL.
|
||||
//
|
||||
// Return Value:
|
||||
//
|
||||
// None.
|
||||
//
|
||||
__declspec(dllimport) void VLDSetReportOptions(UINT option_mask, CONST WCHAR *filename);
|
||||
|
||||
// VLDSetReportHook - Installs or uninstalls a client-defined reporting function by hooking it
|
||||
// into the C run-time debug reporting process (debug version only).
|
||||
//
|
||||
// mode: The action to take: VLD_RPTHOOK_INSTALL or VLD_RPTHOOK_REMOVE.
|
||||
//
|
||||
// pfnNewHook: Report hook to install or remove.
|
||||
//
|
||||
// Return Value:
|
||||
//
|
||||
// int: 0 if success.
|
||||
//
|
||||
__declspec(dllimport) int VLDSetReportHook(int mode, VLD_REPORT_HOOK pfnNewHook);
|
||||
|
||||
// VLDResolveCallstacks - Performs symbol resolution for all saved extent CallStack's that have
|
||||
// been tracked by Visual Leak Detector. This function is necessary for applications that
|
||||
// dynamically load and unload modules, and through which memory leaks might be included.
|
||||
// If this is NOT called, stack traces may have stack frames with no symbol information. This
|
||||
// happens because the symbol API's cannot look up symbols for a binary / module that has been unloaded
|
||||
// from the process.
|
||||
//
|
||||
// Return Value:
|
||||
//
|
||||
// None.
|
||||
//
|
||||
__declspec(dllexport) void VLDResolveCallstacks();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif // __cplusplus
|
||||
|
||||
#else // !_DEBUG
|
||||
|
||||
#define VLDEnable()
|
||||
#define VLDDisable()
|
||||
#define VLDRestore()
|
||||
#define VLDReportLeaks() 0
|
||||
#define VLDGetLeaksCount() 0
|
||||
#define VLDMarkAllLeaksAsReported()
|
||||
#define VLDRefreshModules()
|
||||
#define VLDEnableModule(a)
|
||||
#define VLDDisableModule(b)
|
||||
#define VLDGetOptions() 0
|
||||
#define VLDGetReportFilename(a)
|
||||
#define VLDSetOptions(a, b, c)
|
||||
#define VLDSetReportHook(a, b)
|
||||
#define VLDSetModulesList(a)
|
||||
#define VLDGetModulesList(a, b) FALSE
|
||||
#define VLDSetReportOptions(a, b)
|
||||
|
||||
#endif // _DEBUG
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Visual Leak Detector - Import Library Header
|
||||
// Copyright (c) 2005-2013 VLD Team
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
//
|
||||
// See COPYING.txt for the full terms of the GNU Lesser General Public License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#define VLD_OPT_AGGREGATE_DUPLICATES 0x0001 // If set, aggregate duplicate leaks in the leak report.
|
||||
#define VLD_OPT_MODULE_LIST_INCLUDE 0x0002 // If set, modules in the module list are included, all others are excluded.
|
||||
#define VLD_OPT_REPORT_TO_DEBUGGER 0x0004 // If set, the memory leak report is sent to the debugger.
|
||||
#define VLD_OPT_REPORT_TO_FILE 0x0008 // If set, the memory leak report is sent to a file.
|
||||
#define VLD_OPT_SAFE_STACK_WALK 0x0010 // If set, the stack is walked using the "safe" method (StackWalk64).
|
||||
#define VLD_OPT_SELF_TEST 0x0020 // If set, perform a self-test to verify memory leak self-checking.
|
||||
#define VLD_OPT_SLOW_DEBUGGER_DUMP 0x0040 // If set, inserts a slight delay between sending output to the debugger.
|
||||
#define VLD_OPT_START_DISABLED 0x0080 // If set, memory leak detection will initially disabled.
|
||||
#define VLD_OPT_TRACE_INTERNAL_FRAMES 0x0100 // If set, include useless frames (e.g. internal to VLD) in call stacks.
|
||||
#define VLD_OPT_UNICODE_REPORT 0x0200 // If set, the leak report will be encoded UTF-16 instead of ASCII.
|
||||
#define VLD_OPT_VLDOFF 0x0400 // If set, VLD will be completely deactivated. It will not attach to any modules.
|
||||
#define VLD_OPT_REPORT_TO_STDOUT 0x0800 // If set, the memory leak report is sent to stdout.
|
||||
#define VLD_OPT_SKIP_HEAPFREE_LEAKS 0x1000 // If set, VLD skip HeapFree memory leaks.
|
||||
#define VLD_OPT_VALIDATE_HEAPFREE 0x2000 // If set, VLD verifies and reports heap consistency for HeapFree calls.
|
||||
#define VLD_OPT_RELEASE_CRT_RUNTIME 0x4000 // If set, VLD treat CRT runtime as release version (use only with define VLD_FORCE_ENABLE).
|
||||
|
||||
#define VLD_RPTHOOK_INSTALL 0
|
||||
#define VLD_RPTHOOK_REMOVE 1
|
||||
|
||||
typedef int (__cdecl * VLD_REPORT_HOOK)(int reportType, wchar_t *message, int *returnValue);
|
||||
Vendored
BIN
Binary file not shown.
Reference in New Issue
Block a user