From 5853f39ac30a02a635cd86408eaa9d84982808d3 Mon Sep 17 00:00:00 2001 From: Chunting Gu Date: Tue, 19 Mar 2019 16:19:37 +0800 Subject: [PATCH] Stop deadline timer once start to read response. --- examples/rest_book_client.cc | 113 ++++++++++++++++------------------- webcc/http_client.cc | 25 +++++--- webcc/http_client.h | 18 +++--- webcc/http_client_session.cc | 4 +- webcc/http_request_args.h | 5 -- 5 files changed, 83 insertions(+), 82 deletions(-) diff --git a/examples/rest_book_client.cc b/examples/rest_book_client.cc index e37566c..eca6791 100644 --- a/examples/rest_book_client.cc +++ b/examples/rest_book_client.cc @@ -21,29 +21,13 @@ class BookClientBase { public: - BookClientBase(const std::string& url, int timeout_seconds) : url_(url) { - //session_.SetTimeout(timeout_seconds); - - //session_.set_content_type("application/json"); - session_.set_charset("utf-8"); + BookClientBase(webcc::HttpClientSession& session, const std::string& url) + : session_(session), url_(url) { } virtual ~BookClientBase() = default; protected: - // Helper function to make a request. - //webcc::HttpRequestPtr MakeRequest(const std::string& method, - // const std::string& url, - // std::string&& content = "") { - // auto request = webcc::HttpRequest::New(method, url, host_, port_); - // request->AcceptAppJson(); - // if (!content.empty()) { - // request->SetContentInAppJsonUtf8(JsonToString(content), true); - // } - // request->Prepare(); - // return request; - //} - // Check HTTP response status. bool CheckStatus(webcc::HttpResponsePtr response, int expected_status) { int status = response->status(); @@ -58,15 +42,15 @@ protected: protected: std::string url_; - webcc::HttpClientSession session_; + webcc::HttpClientSession& session_; }; // ----------------------------------------------------------------------------- class BookListClient : public BookClientBase { public: - BookListClient(const std::string& url, int timeout_seconds) - : BookClientBase(url, timeout_seconds) { + BookListClient(webcc::HttpClientSession& session, const std::string& url) + : BookClientBase(session, url) { } bool ListBooks(std::list* books) { @@ -124,13 +108,13 @@ public: class BookDetailClient : public BookClientBase { public: - BookDetailClient(const std::string& url, int timeout_seconds) - : BookClientBase(url, timeout_seconds) { + BookDetailClient(webcc::HttpClientSession& session, const std::string& url) + : BookClientBase(session, url) { } bool GetBook(const std::string& id, Book* book) { try { - auto r = session_.Get(url_ + "/books" + id); + auto r = session_.Get(url_ + "/books/" + id); if (!CheckStatus(r, webcc::http::Status::kOK)) { return false; @@ -151,7 +135,7 @@ public: json["price"] = price; try { - auto r = session_.Put(url_ + "/books" + id, JsonToString(json), true); + auto r = session_.Put(url_ + "/books/" + id, JsonToString(json), true); if (!CheckStatus(r, webcc::http::Status::kOK)) { return false; @@ -218,13 +202,22 @@ int main(int argc, char* argv[]) { std::string url = argv[1]; - int timeout_seconds = -1; + int timeout = 0; if (argc > 2) { - timeout_seconds = std::atoi(argv[2]); + timeout = std::atoi(argv[2]); } - BookListClient list_client(url, timeout_seconds); - BookDetailClient detail_client(url, timeout_seconds); + // Share the same session. + webcc::HttpClientSession session; + + // Session-level settings. + session.set_timeout(timeout); + // TODO + //session.set_content_type("application/json"); + //session.set_charset("utf-8"); + + BookListClient list_client(session, url); + BookDetailClient detail_client(session, url); PrintSeparator(); @@ -233,50 +226,50 @@ int main(int argc, char* argv[]) { PrintBookList(books); } - //PrintSeparator(); + PrintSeparator(); - //std::string id; - //if (list_client.CreateBook("1984", 12.3, &id)) { - // std::cout << "Book ID: " << id << std::endl; - //} else { - // id = "1"; - // std::cout << "Book ID: " << id << " (faked)"<< std::endl; - //} + std::string id; + if (list_client.CreateBook("1984", 12.3, &id)) { + std::cout << "Book ID: " << id << std::endl; + } else { + id = "1"; + std::cout << "Book ID: " << id << " (faked)"<< std::endl; + } - //PrintSeparator(); + PrintSeparator(); - //books.clear(); - //if (list_client.ListBooks(&books)) { - // PrintBookList(books); - //} + books.clear(); + if (list_client.ListBooks(&books)) { + PrintBookList(books); + } - //PrintSeparator(); + PrintSeparator(); - //Book book; - //if (detail_client.GetBook(id, &book)) { - // PrintBook(book); - //} + Book book; + if (detail_client.GetBook(id, &book)) { + PrintBook(book); + } - //PrintSeparator(); + PrintSeparator(); - //detail_client.UpdateBook(id, "1Q84", 32.1); + detail_client.UpdateBook(id, "1Q84", 32.1); - //PrintSeparator(); + PrintSeparator(); - //if (detail_client.GetBook(id, &book)) { - // PrintBook(book); - //} + if (detail_client.GetBook(id, &book)) { + PrintBook(book); + } - //PrintSeparator(); + PrintSeparator(); - //detail_client.DeleteBook(id); + detail_client.DeleteBook(id); - //PrintSeparator(); + PrintSeparator(); - //books.clear(); - //if (list_client.ListBooks(&books)) { - // PrintBookList(books); - //} + books.clear(); + if (list_client.ListBooks(&books)) { + PrintBookList(books); + } return 0; } diff --git a/webcc/http_client.cc b/webcc/http_client.cc index 79ffb98..f5e9811 100644 --- a/webcc/http_client.cc +++ b/webcc/http_client.cc @@ -9,12 +9,13 @@ using boost::asio::ip::tcp; namespace webcc { -HttpClient::HttpClient(std::size_t buffer_size, bool ssl_verify) - : buffer_size_(buffer_size == 0 ? kBufferSize : buffer_size), - timer_(io_context_), +HttpClient::HttpClient(bool ssl_verify, std::size_t buffer_size) + : timer_(io_context_), ssl_verify_(ssl_verify), + buffer_size_(buffer_size == 0 ? kBufferSize : buffer_size), timeout_(kMaxReadSeconds), closed_(false), + timer_canceled_(false), timed_out_(false), error_(kNoError) { } @@ -26,6 +27,7 @@ bool HttpClient::Request(const HttpRequest& request, bool connect) { response_parser_.reset(new HttpResponseParser(response_.get())); closed_ = false; + timer_canceled_ = false; timed_out_ = false; error_ = kNoError; @@ -160,8 +162,10 @@ void HttpClient::DoReadResponse(Error* error) { LOG_VERB("Socket async read handler."); + // Stop the deadline timer once the read has started. + CancelTimer(); + if (ec || length == 0) { - StopTimer(); Close(); *error = kSocketReadError; LOG_ERRO("Socket read error (%s).", ec.message().c_str()); @@ -172,7 +176,7 @@ void HttpClient::DoReadResponse(Error* error) { // Parse the response piece just read. if (!response_parser_->Parse(buffer_.data(), length)) { - StopTimer(); + //CancelTimer(); Close(); *error = kHttpError; LOG_ERRO("Failed to parse HTTP response."); @@ -183,7 +187,7 @@ void HttpClient::DoReadResponse(Error* error) { // Stop trying to read once all content has been received, because // some servers will block extra call to read_some(). - StopTimer(); + //CancelTimer(); if (response_->IsConnectionKeepAlive()) { // Close the timer but keep the socket connection. @@ -243,10 +247,15 @@ void HttpClient::OnTimer(boost::system::error_code ec) { DoWaitTimer(); } -void HttpClient::StopTimer() { - // Cancel any asynchronous operations that are waiting on the timer. +void HttpClient::CancelTimer() { + if (timer_canceled_) { + return; + } + LOG_INFO("Cancel deadline timer..."); timer_.cancel(); + + timer_canceled_ = true; } } // namespace webcc diff --git a/webcc/http_client.h b/webcc/http_client.h index 57d3bda..5d1cdba 100644 --- a/webcc/http_client.h +++ b/webcc/http_client.h @@ -27,7 +27,7 @@ typedef std::shared_ptr HttpClientPtr; // Please don't use the same client object in multiple threads. class HttpClient { public: - explicit HttpClient(std::size_t buffer_size = 0, bool ssl_verify = true); + explicit HttpClient(bool ssl_verify = true, std::size_t buffer_size = 0); virtual ~HttpClient() = default; @@ -87,7 +87,8 @@ private: void DoWaitTimer(); void OnTimer(boost::system::error_code ec); - void StopTimer(); + // Cancel any async-operations waiting on the timer. + void CancelTimer(); private: boost::asio::io_context io_context_; @@ -104,14 +105,13 @@ private: // The buffer for reading response. std::vector buffer_; + // Verify the certificate of the peer or not (for HTTPS). + bool ssl_verify_; + // The size of the buffer for reading response. // Set 0 for using default value (e.g., 1024). std::size_t buffer_size_; - // Verify the certificate of the peer (remote server) or not. - // HTTPS only. - bool ssl_verify_; - // Maximum seconds to wait before the client cancels the operation. // Only for reading response from server. int timeout_; @@ -119,9 +119,13 @@ private: // Connection closed. bool closed_; - // If the error was caused by timeout or not. + // Deadline timer canceled. + bool timer_canceled_; + + // Timeout occurred. bool timed_out_; + // Error code. Error error_; }; diff --git a/webcc/http_client_session.cc b/webcc/http_client_session.cc index f279633..76ef40d 100644 --- a/webcc/http_client_session.cc +++ b/webcc/http_client_session.cc @@ -42,7 +42,7 @@ HttpResponsePtr HttpClientSession::Request(HttpRequestArgs&& args) { assert(args.parameters_.size() % 2 == 0); assert(args.headers_.size() % 2 == 0); - HttpRequest request{ args.method_, args.url_ }; + HttpRequest request{args.method_, args.url_}; for (std::size_t i = 1; i < args.parameters_.size(); i += 2) { request.AddParameter(args.parameters_[i - 1], args.parameters_[i]); @@ -83,7 +83,7 @@ HttpResponsePtr HttpClientSession::Request(HttpRequestArgs&& args) { HttpClientPtr client = pool_.Get(key); if (!client) { - client.reset(new HttpClient{buffer_size, ssl_verify}); + client.reset(new HttpClient{ssl_verify, buffer_size}); reuse = false; } else { client->set_buffer_size(buffer_size); diff --git a/webcc/http_request_args.h b/webcc/http_request_args.h index 59db072..8479b87 100644 --- a/webcc/http_request_args.h +++ b/webcc/http_request_args.h @@ -38,11 +38,6 @@ public: return std::move(*this); } - HttpRequestArgs&& url(std::string&& url) { - url_ = std::move(url); - return std::move(*this); - } - HttpRequestArgs&& parameters(const std::vector& parameters) { parameters_ = parameters; return std::move(*this);