Fix dead loop of HttpClient on Linux; Allow to call Request() multiple times on the same HttpClient object.
This commit is contained in:
@@ -215,28 +215,33 @@ int main(int argc, char* argv[]) {
|
|||||||
PrintSeparator();
|
PrintSeparator();
|
||||||
|
|
||||||
std::list<Book> books;
|
std::list<Book> books;
|
||||||
list_client.ListBooks(&books);
|
if (list_client.ListBooks(&books)) {
|
||||||
PrintBookList(books);
|
PrintBookList(books);
|
||||||
|
}
|
||||||
|
|
||||||
PrintSeparator();
|
PrintSeparator();
|
||||||
|
|
||||||
std::string id;
|
std::string id;
|
||||||
if (!list_client.CreateBook("1984", 12.3, &id)) {
|
if (list_client.CreateBook("1984", 12.3, &id)) {
|
||||||
return 1;
|
std::cout << "Book ID: " << id << std::endl;
|
||||||
|
} else {
|
||||||
|
id = "1";
|
||||||
|
std::cout << "Book ID: " << id << " (faked)"<< std::endl;
|
||||||
}
|
}
|
||||||
std::cout << "Book ID: " << id << std::endl;
|
|
||||||
|
|
||||||
PrintSeparator();
|
PrintSeparator();
|
||||||
|
|
||||||
books.clear();
|
books.clear();
|
||||||
list_client.ListBooks(&books);
|
if (list_client.ListBooks(&books)) {
|
||||||
PrintBookList(books);
|
PrintBookList(books);
|
||||||
|
}
|
||||||
|
|
||||||
PrintSeparator();
|
PrintSeparator();
|
||||||
|
|
||||||
Book book;
|
Book book;
|
||||||
detail_client.GetBook(id, &book);
|
if (detail_client.GetBook(id, &book)) {
|
||||||
PrintBook(book);
|
PrintBook(book);
|
||||||
|
}
|
||||||
|
|
||||||
PrintSeparator();
|
PrintSeparator();
|
||||||
|
|
||||||
@@ -244,8 +249,9 @@ int main(int argc, char* argv[]) {
|
|||||||
|
|
||||||
PrintSeparator();
|
PrintSeparator();
|
||||||
|
|
||||||
detail_client.GetBook(id, &book);
|
if (detail_client.GetBook(id, &book)) {
|
||||||
PrintBook(book);
|
PrintBook(book);
|
||||||
|
}
|
||||||
|
|
||||||
PrintSeparator();
|
PrintSeparator();
|
||||||
|
|
||||||
@@ -254,8 +260,9 @@ int main(int argc, char* argv[]) {
|
|||||||
PrintSeparator();
|
PrintSeparator();
|
||||||
|
|
||||||
books.clear();
|
books.clear();
|
||||||
list_client.ListBooks(&books);
|
if (list_client.ListBooks(&books)) {
|
||||||
PrintBookList(books);
|
PrintBookList(books);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+32
-41
@@ -34,6 +34,8 @@ void HttpClient::SetTimeout(int seconds) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool HttpClient::Request(const HttpRequest& request) {
|
bool HttpClient::Request(const HttpRequest& request) {
|
||||||
|
io_context_.restart();
|
||||||
|
|
||||||
response_.reset(new HttpResponse());
|
response_.reset(new HttpResponse());
|
||||||
response_parser_.reset(new HttpResponseParser(response_.get()));
|
response_parser_.reset(new HttpResponseParser(response_.get()));
|
||||||
|
|
||||||
@@ -72,22 +74,8 @@ Error HttpClient::Connect(const HttpRequest& request) {
|
|||||||
|
|
||||||
LOG_VERB("Connect to server...");
|
LOG_VERB("Connect to server...");
|
||||||
|
|
||||||
ec = boost::asio::error::would_block;
|
// Use sync API directly since we don't need timeout control.
|
||||||
|
boost::asio::connect(socket_, endpoints, ec);
|
||||||
// ConnectHandler: void (boost::system::error_code, tcp::endpoint)
|
|
||||||
// Using |boost::lambda::var()| is identical to:
|
|
||||||
// boost::asio::async_connect(
|
|
||||||
// socket_, endpoints,
|
|
||||||
// [this, &ec](boost::system::error_code inner_ec, tcp::endpoint) {
|
|
||||||
// ec = inner_ec;
|
|
||||||
// });
|
|
||||||
boost::asio::async_connect(socket_, endpoints,
|
|
||||||
boost::lambda::var(ec) = boost::lambda::_1);
|
|
||||||
|
|
||||||
// Block until the asynchronous operation has completed.
|
|
||||||
do {
|
|
||||||
io_context_.run_one();
|
|
||||||
} while (ec == boost::asio::error::would_block);
|
|
||||||
|
|
||||||
// Determine whether a connection was successfully established.
|
// Determine whether a connection was successfully established.
|
||||||
if (ec) {
|
if (ec) {
|
||||||
@@ -98,11 +86,6 @@ Error HttpClient::Connect(const HttpRequest& request) {
|
|||||||
|
|
||||||
LOG_VERB("Socket connected.");
|
LOG_VERB("Socket connected.");
|
||||||
|
|
||||||
// ISSUE: |async_connect| reports success on failure.
|
|
||||||
// See the following bugs:
|
|
||||||
// - https://svn.boost.org/trac10/ticket/8795
|
|
||||||
// - https://svn.boost.org/trac10/ticket/8995
|
|
||||||
|
|
||||||
return kNoError;
|
return kNoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,16 +97,10 @@ Error HttpClient::SendReqeust(const HttpRequest& request) {
|
|||||||
// I find that it's almost impossible to simulate a situation in the server
|
// I find that it's almost impossible to simulate a situation in the server
|
||||||
// side to test this timeout.
|
// side to test this timeout.
|
||||||
|
|
||||||
boost::system::error_code ec = boost::asio::error::would_block;
|
boost::system::error_code ec;
|
||||||
|
|
||||||
// WriteHandler: void (boost::system::error_code, std::size_t)
|
// Use sync API directly since we don't need timeout control.
|
||||||
boost::asio::async_write(socket_, request.ToBuffers(),
|
boost::asio::write(socket_, request.ToBuffers(), ec);
|
||||||
boost::lambda::var(ec) = boost::lambda::_1);
|
|
||||||
|
|
||||||
// Block until the asynchronous operation has completed.
|
|
||||||
do {
|
|
||||||
io_context_.run_one();
|
|
||||||
} while (ec == boost::asio::error::would_block);
|
|
||||||
|
|
||||||
if (ec) {
|
if (ec) {
|
||||||
LOG_ERRO("Socket write error (%s).", ec.message().c_str());
|
LOG_ERRO("Socket write error (%s).", ec.message().c_str());
|
||||||
@@ -140,7 +117,7 @@ Error HttpClient::ReadResponse() {
|
|||||||
LOG_VERB("Read response (timeout: %ds)...", timeout_seconds_);
|
LOG_VERB("Read response (timeout: %ds)...", timeout_seconds_);
|
||||||
|
|
||||||
deadline_.expires_from_now(boost::posix_time::seconds(timeout_seconds_));
|
deadline_.expires_from_now(boost::posix_time::seconds(timeout_seconds_));
|
||||||
AsyncWaitDeadline();
|
DoWaitDeadline();
|
||||||
|
|
||||||
Error error = kNoError;
|
Error error = kNoError;
|
||||||
DoReadResponse(&error);
|
DoReadResponse(&error);
|
||||||
@@ -208,23 +185,37 @@ void HttpClient::DoReadResponse(Error* error) {
|
|||||||
} while (ec == boost::asio::error::would_block);
|
} while (ec == boost::asio::error::would_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpClient::AsyncWaitDeadline() {
|
void HttpClient::DoWaitDeadline() {
|
||||||
deadline_.async_wait(std::bind(&HttpClient::DeadlineHandler, this,
|
deadline_.async_wait(std::bind(&HttpClient::OnDeadline, this,
|
||||||
std::placeholders::_1));
|
std::placeholders::_1));
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpClient::DeadlineHandler(boost::system::error_code ec) {
|
void HttpClient::OnDeadline(boost::system::error_code ec) {
|
||||||
LOG_VERB("Deadline handler.");
|
if (stopped_) {
|
||||||
|
|
||||||
if (ec == boost::asio::error::operation_aborted) {
|
|
||||||
LOG_VERB("Deadline timer canceled.");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_WARN("HTTP client timed out.");
|
LOG_VERB("OnDeadline.");
|
||||||
timed_out_ = true;
|
|
||||||
|
|
||||||
Stop();
|
// NOTE: Can't check this:
|
||||||
|
// if (ec == boost::asio::error::operation_aborted) {
|
||||||
|
// LOG_VERB("Deadline timer canceled.");
|
||||||
|
// 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.
|
||||||
|
LOG_WARN("HTTP client timed out.");
|
||||||
|
timed_out_ = true;
|
||||||
|
Stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put the actor back to sleep.
|
||||||
|
DoWaitDeadline();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpClient::Stop() {
|
void HttpClient::Stop() {
|
||||||
|
|||||||
+2
-2
@@ -48,8 +48,8 @@ class HttpClient {
|
|||||||
|
|
||||||
void DoReadResponse(Error* error);
|
void DoReadResponse(Error* error);
|
||||||
|
|
||||||
void AsyncWaitDeadline();
|
void DoWaitDeadline();
|
||||||
void DeadlineHandler(boost::system::error_code ec);
|
void OnDeadline(boost::system::error_code ec);
|
||||||
|
|
||||||
void Stop();
|
void Stop();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user