diff --git a/example/rest_book_client/main.cc b/example/rest_book_client/main.cc index 4cfae18..72ebe9e 100644 --- a/example/rest_book_client/main.cc +++ b/example/rest_book_client/main.cc @@ -29,7 +29,7 @@ class BookClientBase { BookClientBase(const std::string& host, const std::string& port, int timeout_seconds) : rest_client_(host, port) { - rest_client_.set_timeout_seconds(timeout_seconds); + rest_client_.SetTimeout(timeout_seconds); } virtual ~BookClientBase() = default; diff --git a/example/soap_book_client/book_client.h b/example/soap_book_client/book_client.h index 203a83d..92da8b5 100644 --- a/example/soap_book_client/book_client.h +++ b/example/soap_book_client/book_client.h @@ -4,6 +4,7 @@ #include #include +#include "pugixml/pugixml.hpp" #include "webcc/soap_client.h" #include "example/common/book.h" diff --git a/example/soap_calc_client/main.cc b/example/soap_calc_client/main.cc index b60e288..8ef54d3 100644 --- a/example/soap_calc_client/main.cc +++ b/example/soap_calc_client/main.cc @@ -9,7 +9,7 @@ class CalcClient { public: CalcClient(const std::string& host, const std::string& port) : soap_client_(host, port) { - soap_client_.set_timeout_seconds(5); + soap_client_.SetTimeout(5); soap_client_.set_url("/calculator"); soap_client_.set_service_ns({ diff --git a/example/soap_calc_client_parasoft/main.cc b/example/soap_calc_client_parasoft/main.cc index e912395..47638ca 100644 --- a/example/soap_calc_client_parasoft/main.cc +++ b/example/soap_calc_client_parasoft/main.cc @@ -9,7 +9,7 @@ class CalcClient { public: CalcClient(const std::string& host, const std::string& port) : soap_client_(host, port) { - soap_client_.set_timeout_seconds(5); + soap_client_.SetTimeout(5); soap_client_.set_url("/glue/calculator"); soap_client_.set_service_ns({ diff --git a/webcc/http_async_client.cc b/webcc/http_async_client.cc index 04b68ad..3cb87c5 100644 --- a/webcc/http_async_client.cc +++ b/webcc/http_async_client.cc @@ -11,7 +11,7 @@ namespace webcc { HttpAsyncClient::HttpAsyncClient(boost::asio::io_context& io_context) : socket_(io_context), - resolver_(new tcp::resolver(io_context)), + resolver_(io_context), buffer_(kBufferSize), deadline_(io_context), timeout_seconds_(kMaxReadSeconds), @@ -34,11 +34,11 @@ void HttpAsyncClient::Request(std::shared_ptr request, request_ = request; response_handler_ = response_handler; - resolver_->async_resolve(tcp::v4(), request->host(), request->port(kHttpPort), - std::bind(&HttpAsyncClient::ResolveHandler, - shared_from_this(), - std::placeholders::_1, - std::placeholders::_2)); + resolver_.async_resolve(tcp::v4(), request->host(), request->port(kHttpPort), + std::bind(&HttpAsyncClient::ResolveHandler, + shared_from_this(), + std::placeholders::_1, + std::placeholders::_2)); } void HttpAsyncClient::Stop() { @@ -65,11 +65,8 @@ void HttpAsyncClient::ResolveHandler(boost::system::error_code ec, request_->host().c_str(), request_->port().c_str()); response_handler_(response_, kHostResolveError, timed_out_); } else { - // Start the connect actor. - endpoints_ = endpoints; - // ConnectHandler: void(boost::system::error_code, tcp::endpoint) - boost::asio::async_connect(socket_, endpoints_, + boost::asio::async_connect(socket_, endpoints, std::bind(&HttpAsyncClient::ConnectHandler, shared_from_this(), std::placeholders::_1, diff --git a/webcc/http_async_client.h b/webcc/http_async_client.h index ec74a5e..e4e4a6f 100644 --- a/webcc/http_async_client.h +++ b/webcc/http_async_client.h @@ -19,6 +19,10 @@ namespace webcc { // Response handler/callback. typedef std::function HttpResponseHandler; +// HTTP client session in asynchronous mode. +// A request will return without waiting for the response, the callback handler +// will be invoked when the response is received or timeout occurs. +// Don't use the same HttpAsyncClient object in multiple threads. class HttpAsyncClient : public std::enable_shared_from_this { public: explicit HttpAsyncClient(boost::asio::io_context& io_context); @@ -55,9 +59,8 @@ class HttpAsyncClient : public std::enable_shared_from_this { void AsyncWaitDeadline(); void DeadlineHandler(boost::system::error_code ec); + tcp::resolver resolver_; tcp::socket socket_; - std::unique_ptr resolver_; - tcp::resolver::results_type endpoints_; std::shared_ptr request_; std::vector buffer_; diff --git a/webcc/http_client.cc b/webcc/http_client.cc index 192a82a..656b647 100644 --- a/webcc/http_client.cc +++ b/webcc/http_client.cc @@ -27,11 +27,19 @@ HttpClient::HttpClient() error_(kNoError) { } +void HttpClient::SetTimeout(int seconds) { + if (seconds > 0) { + timeout_seconds_ = seconds; + } +} + bool HttpClient::Request(const HttpRequest& request) { response_.reset(new HttpResponse()); response_parser_.reset(new HttpResponseParser(response_.get())); - stopped_ = timed_out_ = false; + stopped_ = false; + timed_out_ = false; + error_ = kNoError; if ((error_ = Connect(request)) != kNoError) { return false; diff --git a/webcc/http_client.h b/webcc/http_client.h index 061e521..cc0de64 100644 --- a/webcc/http_client.h +++ b/webcc/http_client.h @@ -16,6 +16,9 @@ namespace webcc { +// HTTP client session in synchronous mode. +// A request will not return until the response is received or timeout occurs. +// Don't use the same HttpClient object in multiple threads. class HttpClient { public: HttpClient(); @@ -23,10 +26,9 @@ class HttpClient { DELETE_COPY_AND_ASSIGN(HttpClient); - void set_timeout_seconds(int timeout_seconds) { - assert(timeout_seconds > 0); - timeout_seconds_ = timeout_seconds; - } + // Set the timeout seconds for reading response. + // The |seconds| is only effective when greater than 0. + void SetTimeout(int seconds); // Connect to server, send request, wait until response is received. bool Request(const HttpRequest& request); diff --git a/webcc/rest_client.cc b/webcc/rest_client.cc index 747adab..db8f599 100644 --- a/webcc/rest_client.cc +++ b/webcc/rest_client.cc @@ -1,14 +1,11 @@ #include "webcc/rest_client.h" -#include "webcc/http_client.h" #include "webcc/http_request.h" namespace webcc { RestClient::RestClient(const std::string& host, const std::string& port) - : host_(host), port_(port), - timeout_seconds_(0), timed_out_(false), - error_(kNoError) { + : host_(host), port_(port) { if (port_.empty()) { std::size_t i = host_.find_last_of(':'); if (i != std::string::npos) { @@ -20,11 +17,6 @@ RestClient::RestClient(const std::string& host, const std::string& port) bool RestClient::Request(const std::string& method, const std::string& url, std::string&& content) { - response_.reset(); - - error_ = kNoError; - timed_out_ = false; - HttpRequest http_request; http_request.set_method(method); @@ -38,19 +30,10 @@ bool RestClient::Request(const std::string& method, const std::string& url, http_request.UpdateStartLine(); - HttpClient http_client; - - if (timeout_seconds_ > 0) { - http_client.set_timeout_seconds(timeout_seconds_); - } - - if (!http_client.Request(http_request)) { - error_ = http_client.error(); - timed_out_ = http_client.timed_out(); + if (!http_client_.Request(http_request)) { return false; } - response_ = http_client.response(); return true; } diff --git a/webcc/rest_client.h b/webcc/rest_client.h index 11dabeb..f20a6b1 100644 --- a/webcc/rest_client.h +++ b/webcc/rest_client.h @@ -6,6 +6,7 @@ #include // for move() #include "webcc/globals.h" +#include "webcc/http_client.h" #include "webcc/http_response.h" namespace webcc { @@ -14,20 +15,23 @@ class RestClient { public: // If |port| is empty, |host| will be checked to see if it contains port or // not (separated by ':'). - RestClient(const std::string& host, const std::string& port = ""); + explicit RestClient(const std::string& host, const std::string& port = ""); ~RestClient() = default; DELETE_COPY_AND_ASSIGN(RestClient); - void set_timeout_seconds(int timeout_seconds) { - timeout_seconds_ = timeout_seconds; + void SetTimeout(int seconds) { + http_client_.SetTimeout(seconds); } + // NOTE: + // The return value of the following methods (Get, Post, etc.) only indicates + // if the socket communication is successful or not. Check error() and + // timed_out() for more information if it's failed. Check response_status() + // instead for the HTTP status code. + // HTTP GET request. - // The return value indicates if the socket communication is successful - // or not. If it's failed, check error() and timed_out() for more details. - // For HTTP status, check response_status() instead. inline bool Get(const std::string& url) { return Request(kHttpGet, url, ""); } @@ -52,21 +56,27 @@ class RestClient { return Request(kHttpDelete, url, ""); } - HttpResponsePtr response() const { return response_; } + HttpResponsePtr response() const { + return http_client_.response(); + } int response_status() const { - assert(response_); - return response_->status(); + assert(response()); + return response()->status(); } const std::string& response_content() const { - assert(response_); - return response_->content(); + assert(response()); + return response()->content(); } - bool timed_out() const { return timed_out_; } + bool timed_out() const { + return http_client_.timed_out(); + } - Error error() const { return error_; } + Error error() const { + return http_client_.error(); + } private: bool Request(const std::string& method, const std::string& url, @@ -75,15 +85,7 @@ class RestClient { std::string host_; std::string port_; - // Timeout in seconds; only effective when > 0. - int timeout_seconds_; - - HttpResponsePtr response_; - - // If the error was caused by timeout or not. - bool timed_out_; - - Error error_; + HttpClient http_client_; }; } // namespace webcc diff --git a/webcc/soap_client.cc b/webcc/soap_client.cc index 4bc50a2..6080f50 100644 --- a/webcc/soap_client.cc +++ b/webcc/soap_client.cc @@ -3,8 +3,6 @@ #include #include // for move() -#include "webcc/http_client.h" -#include "webcc/soap_globals.h" #include "webcc/soap_request.h" #include "webcc/soap_response.h" @@ -13,8 +11,7 @@ namespace webcc { SoapClient::SoapClient(const std::string& host, const std::string& port) : host_(host), port_(port), soapenv_ns_(kSoapEnvNamespace), - format_raw_(true), timeout_seconds_(0), timed_out_(false), - error_(kNoError) { + format_raw_(true), error_(kNoError) { if (port_.empty()) { std::size_t i = host_.find_last_of(':'); if (i != std::string::npos) { @@ -31,6 +28,8 @@ bool SoapClient::Request(const std::string& operation, assert(!url_.empty() && !host_.empty()); assert(!result_name_.empty()); + error_ = kNoError; + SoapRequest soap_request; soap_request.set_soapenv_ns(soapenv_ns_); @@ -55,22 +54,15 @@ bool SoapClient::Request(const std::string& operation, http_request.SetHeader(kSoapAction, operation); http_request.UpdateStartLine(); - HttpClient http_client; - - if (timeout_seconds_ > 0) { - http_client.set_timeout_seconds(timeout_seconds_); - } - - if (!http_client.Request(http_request)) { - error_ = http_client.error(); - timed_out_ = http_client.timed_out(); + if (!http_client_.Request(http_request)) { + error_ = http_client_.error(); return false; } SoapResponse soap_response; soap_response.set_result_name(result_name_); - if (!soap_response.FromXml(http_client.response()->content())) { + if (!soap_response.FromXml(http_client_.response()->content())) { error_ = kXmlError; return false; } diff --git a/webcc/soap_client.h b/webcc/soap_client.h index c90d431..8ed3067 100644 --- a/webcc/soap_client.h +++ b/webcc/soap_client.h @@ -4,7 +4,8 @@ #include #include -#include "webcc/soap_message.h" +#include "webcc/http_client.h" +#include "webcc/soap_globals.h" #include "webcc/soap_parameter.h" namespace webcc { @@ -13,14 +14,14 @@ class SoapClient { public: // If |port| is empty, |host| will be checked to see if it contains port or // not (separated by ':'). - SoapClient(const std::string& host, const std::string& port = ""); + explicit SoapClient(const std::string& host, const std::string& port = ""); ~SoapClient() = default; DELETE_COPY_AND_ASSIGN(SoapClient); - void set_timeout_seconds(int timeout_seconds) { - timeout_seconds_ = timeout_seconds; + void SetTimeout(int seconds) { + http_client_.SetTimeout(seconds); } void set_url(const std::string& url) { url_ = url; } @@ -33,7 +34,9 @@ class SoapClient { result_name_ = result_name; } - void set_format_raw(bool format_raw) { format_raw_ = format_raw; } + void set_format_raw(bool format_raw) { + format_raw_ = format_raw; + } void set_indent_str(const std::string& indent_str) { indent_str_ = indent_str; @@ -43,7 +46,9 @@ class SoapClient { std::vector&& parameters, std::string* result); - bool timed_out() const { return timed_out_; } + bool timed_out() const { + return http_client_.timed_out(); + } Error error() const { return error_; } @@ -68,11 +73,7 @@ class SoapClient { // Applicable when |format_raw_| is false. std::string indent_str_; - // Timeout in seconds; only effective when > 0. - int timeout_seconds_; - - // If the error was caused by timeout or not. - bool timed_out_; + HttpClient http_client_; Error error_; }; diff --git a/webcc/soap_globals.cc b/webcc/soap_globals.cc index 7ba5cc3..377d5f1 100644 --- a/webcc/soap_globals.cc +++ b/webcc/soap_globals.cc @@ -10,4 +10,9 @@ const std::string kSoapAction = "SOAPAction"; // See: https://www.w3.org/TR/2007/REC-soap12-part0-20070427/#L26854 const std::string kTextXmlUtf8 = "text/xml; charset=utf-8"; +const SoapNamespace kSoapEnvNamespace{ + "soap", + "http://schemas.xmlsoap.org/soap/envelope/" +}; + } // namespace webcc diff --git a/webcc/soap_globals.h b/webcc/soap_globals.h index 166f2be..5d4195b 100644 --- a/webcc/soap_globals.h +++ b/webcc/soap_globals.h @@ -12,6 +12,22 @@ extern const std::string kSoapAction; extern const std::string kTextXmlUtf8; +// ----------------------------------------------------------------------------- + +// XML namespace name/url pair. +// E.g., { "soap", "http://schemas.xmlsoap.org/soap/envelope/" } +struct SoapNamespace { + std::string name; + std::string url; + + bool IsValid() const { + return !name.empty() && !url.empty(); + } +}; + +// CSoap's default namespace for SOAP Envelope. +extern const SoapNamespace kSoapEnvNamespace; + } // namespace webcc #endif // WEBCC_SOAP_GLOBALS_H_ diff --git a/webcc/soap_message.cc b/webcc/soap_message.cc index b5e928e..5ff29c4 100644 --- a/webcc/soap_message.cc +++ b/webcc/soap_message.cc @@ -6,11 +6,6 @@ namespace webcc { -const SoapNamespace kSoapEnvNamespace{ - "soap", - "http://schemas.xmlsoap.org/soap/envelope/" -}; - void SoapMessage::ToXml(bool format_raw, const std::string& indent, std::string* xml_string) { assert(soapenv_ns_.IsValid() && service_ns_.IsValid() && !operation_.empty()); diff --git a/webcc/soap_message.h b/webcc/soap_message.h index 71dfa8c..a2fc235 100644 --- a/webcc/soap_message.h +++ b/webcc/soap_message.h @@ -5,28 +5,14 @@ #include "pugixml/pugixml.hpp" -#include "webcc/globals.h" +#include "webcc/soap_globals.h" namespace webcc { -// XML namespace name/url pair. -// E.g., { "soap", "http://schemas.xmlsoap.org/soap/envelope/" } -struct SoapNamespace { - std::string name; - std::string url; - - bool IsValid() const { - return !name.empty() && !url.empty(); - } -}; - -// CSoap's default namespace for SOAP Envelope. -extern const SoapNamespace kSoapEnvNamespace; - // Base class for SOAP request and response. class SoapMessage { public: - virtual ~SoapMessage() {} + virtual ~SoapMessage() = default; // E.g., set as kSoapEnvNamespace. void set_soapenv_ns(const SoapNamespace& soapenv_ns) {