From 3647407b8b58f03b59713db2e8ccf008a46b9e1e Mon Sep 17 00:00:00 2001 From: Chunting Gu Date: Fri, 8 Mar 2019 11:06:19 +0800 Subject: [PATCH] Throw exception from inside HttpClientSession.Request(). --- example/http_client/main.cc | 35 +++++++++-------- webcc/globals.cc | 12 ++++++ webcc/globals.h | 75 +++++++++++++++++++++++------------- webcc/http_client_session.cc | 14 +++---- webcc/http_client_session.h | 3 +- webcc/http_message.cc | 2 +- webcc/http_parser.cc | 4 +- webcc/http_request.cc | 2 +- 8 files changed, 89 insertions(+), 58 deletions(-) diff --git a/example/http_client/main.cc b/example/http_client/main.cc index f7b96d0..79422a7 100644 --- a/example/http_client/main.cc +++ b/example/http_client/main.cc @@ -37,34 +37,35 @@ int main() { // - constructor: HttpRequestArgs{ "GET" } // - move constructor: auto args = ... - //auto args = HttpRequestArgs{"GET"}. - // url("http://httpbin.org/get"). - // parameters({ "key1", "value1", "key2", "value2" }). - // headers({ "Accept", "application/json" }). - // buffer_size(1000); + auto args = HttpRequestArgs{"GET"}. + url("http://httpbin.org/get"). + parameters({ "key1", "value1", "key2", "value2" }). + headers({ "Accept", "application/json" }). + buffer_size(1000); - //r = session.Request(std::move(args)); + r = session.Request(std::move(args)); // --------------------------------------------------------------------------- // Use pre-defined wrappers. - //r = session.Get("http://httpbin.org/get", - // { "key1", "value1", "key2", "value2" }, - // { "Accept", "application/json" }, - // HttpRequestArgs{}.buffer_size(1000)); + r = session.Get("http://httpbin.org/get", + { "key1", "value1", "key2", "value2" }, + { "Accept", "application/json" }, + HttpRequestArgs{}.buffer_size(1000)); // --------------------------------------------------------------------------- // HTTPS is auto-detected from the URL schema. - r = session.Post("https://httpbin.org/post", "{ 'key': 'value' }", true, - { "Accept", "application/json" }, - HttpRequestArgs{}.ssl_verify(false).buffer_size(1000)); + try { + r = session.Post("httpt://httpbin.org/post", "{ 'key': 'value' }", true, + {"Accept", "application/json"}, + HttpRequestArgs{}.ssl_verify(false).buffer_size(1000)); - if (r) { - std::cout << r->content() << std::endl; - } + std::cout << r->status() << std::endl << r->content() << std::endl; - GetBoostOrgLicense(session); + } catch (const webcc::Exception& e) { + std::cout << "Exception: " << e.what() << std::endl; + } return 0; } diff --git a/webcc/globals.cc b/webcc/globals.cc index ec9b726..a41fe96 100644 --- a/webcc/globals.cc +++ b/webcc/globals.cc @@ -8,6 +8,8 @@ namespace webcc { const char* DescribeError(Error error) { switch (error) { + case kSchemaError: + return "Schema error"; case kHostResolveError: return "Host resolve error"; case kEndpointConnectError: @@ -29,4 +31,14 @@ const char* DescribeError(Error error) { } } +Exception::Exception(Error error, bool timeout, const std::string& details) + : error_(error), timeout_(timeout), msg_(DescribeError(error)) { + if (timeout) { + msg_ += " (timeout)"; + } + if (!details.empty()) { + msg_ += " (" + details + ")"; + } +} + } // namespace webcc diff --git a/webcc/globals.h b/webcc/globals.h index 657355c..82b2e6b 100644 --- a/webcc/globals.h +++ b/webcc/globals.h @@ -33,9 +33,8 @@ namespace webcc { // ----------------------------------------------------------------------------- -// Constants -const char* const CRLF = "\r\n"; +const char* const kCRLF = "\r\n"; // Default buffer size for socket reading. const std::size_t kBufferSize = 1024; @@ -54,30 +53,7 @@ const std::size_t kMaxDumpSize = 2048; const char* const kPort80 = "80"; const char* const kPort443 = "443"; -// Client side error codes. -enum Error { - kNoError = 0, // i.e., OK - - kHostResolveError, - kEndpointConnectError, - kHandshakeError, // HTTPS handshake - kSocketReadError, - kSocketWriteError, - - // HTTP error. - // E.g., failed to parse HTTP response (invalid content length, etc.). - kHttpError, - - // Server error. - // E.g., HTTP status 500 + SOAP Fault element. - kServerError, - - // XML parsing error. - kXmlError, -}; - -// Return a descriptive message for the given error code. -const char* DescribeError(Error error); +// ----------------------------------------------------------------------------- // HTTP headers. namespace http { @@ -146,6 +122,53 @@ const char* const kUtf8 = "utf-8"; } // namespace http +// ----------------------------------------------------------------------------- + +// Client side error codes. +enum Error { + kNoError = 0, // i.e., OK + + kSchemaError, + + kHostResolveError, + kEndpointConnectError, + kHandshakeError, // HTTPS handshake + kSocketReadError, + kSocketWriteError, + + // HTTP error. + // E.g., failed to parse HTTP response (invalid content length, etc.). + kHttpError, + + // Server error. + // E.g., HTTP status 500 + SOAP Fault element. + kServerError, + + // XML parsing error. + kXmlError, +}; + +// Return a descriptive message for the given error code. +const char* DescribeError(Error error); + +class Exception : public std::exception { +public: + explicit Exception(Error error = kNoError, bool timeout = false, + const std::string& details = ""); + + const char* what() const override { + return msg_.c_str(); + } + +private: + Error error_; + + // If the error was caused by timeout or not. + bool timeout_; + + std::string msg_; +}; + } // namespace webcc #endif // WEBCC_GLOBALS_H_ diff --git a/webcc/http_client_session.cc b/webcc/http_client_session.cc index 3d35f5a..6e85896 100644 --- a/webcc/http_client_session.cc +++ b/webcc/http_client_session.cc @@ -45,8 +45,6 @@ HttpResponsePtr HttpClientSession::Request(HttpRequestArgs&& args) { request.Prepare(); - // TODO: - std::shared_ptr impl; if (request.url().scheme() == "http") { @@ -54,17 +52,15 @@ HttpResponsePtr HttpClientSession::Request(HttpRequestArgs&& args) { } else if (request.url().scheme() == "https") { impl.reset(new HttpSslClient{args.ssl_verify_}); } else { - return HttpResponsePtr{}; + throw Exception(kSchemaError, false, + "unknown schema: " + request.url().scheme()); } - if (impl) { - if (!impl->Request(request, args.buffer_size_)) { - return HttpResponsePtr{}; - } - return impl->response(); + if (!impl->Request(request, args.buffer_size_)) { + throw Exception(impl->error(), impl->timed_out()); } - return HttpResponsePtr{}; + return impl->response(); } HttpResponsePtr HttpClientSession::Get(const std::string& url, diff --git a/webcc/http_client_session.h b/webcc/http_client_session.h index c48c2b2..999239f 100644 --- a/webcc/http_client_session.h +++ b/webcc/http_client_session.h @@ -34,8 +34,7 @@ public: std::vector&& headers = {}, HttpRequestArgs&& args = HttpRequestArgs()); - HttpResponsePtr Post(const std::string& url, - std::string&& data, bool json, + HttpResponsePtr Post(const std::string& url, std::string&& data, bool json, std::vector&& headers = {}, HttpRequestArgs&& args = HttpRequestArgs()); diff --git a/webcc/http_message.cc b/webcc/http_message.cc index fada240..6a6a294 100644 --- a/webcc/http_message.cc +++ b/webcc/http_message.cc @@ -126,7 +126,7 @@ void HttpMessage::Dump(std::ostream& os, std::size_t indent, } else { // Split by EOL to achieve more readability. std::vector splitted; - boost::split(splitted, content_, boost::is_any_of(CRLF)); + boost::split(splitted, content_, boost::is_any_of(kCRLF)); std::size_t size = 0; diff --git a/webcc/http_parser.cc b/webcc/http_parser.cc index a3abb0a..b9364b8 100644 --- a/webcc/http_parser.cc +++ b/webcc/http_parser.cc @@ -81,7 +81,7 @@ bool HttpParser::ParseHeaders() { if (!start_line_parsed_) { start_line_parsed_ = true; - message_->set_start_line(line + CRLF); + message_->set_start_line(line + kCRLF); if (!ParseStartLine(line)) { return false; } @@ -98,7 +98,7 @@ bool HttpParser::ParseHeaders() { bool HttpParser::NextPendingLine(std::size_t off, std::string* line, bool remove) { - std::size_t pos = pending_data_.find(CRLF, off); + std::size_t pos = pending_data_.find(kCRLF, off); if (pos == std::string::npos) { return false; diff --git a/webcc/http_request.cc b/webcc/http_request.cc index 6a6b363..32e1d03 100644 --- a/webcc/http_request.cc +++ b/webcc/http_request.cc @@ -20,7 +20,7 @@ bool HttpRequest::Prepare() { start_line_ += " "; start_line_ += target; start_line_ += " HTTP/1.1"; - start_line_ += CRLF; + start_line_ += kCRLF; if (url_.port().empty()) { SetHeader(http::headers::kHost, url_.host());