Rework the client error handling
This commit is contained in:
@@ -31,8 +31,8 @@ int main() {
|
|||||||
// Print the response content data.
|
// Print the response content data.
|
||||||
std::cout << r->content() << std::endl;
|
std::cout << r->content() << std::endl;
|
||||||
|
|
||||||
} catch (const webcc::Exception& e) {
|
} catch (const webcc::Error& error) {
|
||||||
std::cout << e.what() << std::endl;
|
std::cout << error << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
+14
-14
@@ -73,8 +73,8 @@ TEST(ClientTest, Get_RequestFunc) {
|
|||||||
|
|
||||||
AssertGet(r);
|
AssertGet(r);
|
||||||
|
|
||||||
} catch (const webcc::Exception& e) {
|
} catch (const webcc::Error& error) {
|
||||||
std::cerr << e.what() << std::endl;
|
std::cerr << error << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,8 +88,8 @@ TEST(ClientTest, Get_Shortcut) {
|
|||||||
|
|
||||||
AssertGet(r);
|
AssertGet(r);
|
||||||
|
|
||||||
} catch (const webcc::Exception& e) {
|
} catch (const webcc::Error& error) {
|
||||||
std::cerr << e.what() << std::endl;
|
std::cerr << error << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,8 +110,8 @@ TEST(ClientTest, Get_SSL) {
|
|||||||
|
|
||||||
AssertGet(r);
|
AssertGet(r);
|
||||||
|
|
||||||
} catch (const webcc::Exception& e) {
|
} catch (const webcc::Error& error) {
|
||||||
std::cerr << e.what() << std::endl;
|
std::cerr << error << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // WEBCC_ENABLE_SSL
|
#endif // WEBCC_ENABLE_SSL
|
||||||
@@ -131,8 +131,8 @@ TEST(ClientTest, Compression_Gzip) {
|
|||||||
|
|
||||||
EXPECT_EQ(true, json["gzipped"].asBool());
|
EXPECT_EQ(true, json["gzipped"].asBool());
|
||||||
|
|
||||||
} catch (const webcc::Exception& e) {
|
} catch (const webcc::Error& error) {
|
||||||
std::cerr << e.what() << std::endl;
|
std::cerr << error << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,8 +147,8 @@ TEST(ClientTest, Compression_Deflate) {
|
|||||||
|
|
||||||
EXPECT_EQ(true, json["deflated"].asBool());
|
EXPECT_EQ(true, json["deflated"].asBool());
|
||||||
|
|
||||||
} catch (const webcc::Exception& e) {
|
} catch (const webcc::Error& error) {
|
||||||
std::cerr << e.what() << std::endl;
|
std::cerr << error << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,8 +201,8 @@ TEST(ClientTest, KeepAlive) {
|
|||||||
|
|
||||||
EXPECT_TRUE(iequals(r->GetHeader("Connection"), "Keep-alive"));
|
EXPECT_TRUE(iequals(r->GetHeader("Connection"), "Keep-alive"));
|
||||||
|
|
||||||
} catch (const webcc::Exception& e) {
|
} catch (const webcc::Error& error) {
|
||||||
std::cerr << e.what() << std::endl;
|
std::cerr << error << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,8 +226,8 @@ TEST(ClientTest, GetImageJpeg) {
|
|||||||
|
|
||||||
// TODO: Verify the response is a valid JPEG image.
|
// TODO: Verify the response is a valid JPEG image.
|
||||||
|
|
||||||
} catch (const webcc::Exception& e) {
|
} catch (const webcc::Error& error) {
|
||||||
std::cerr << e.what() << std::endl;
|
std::cerr << error << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,8 +58,8 @@ int main() {
|
|||||||
|
|
||||||
#endif // WEBCC_ENABLE_SSL
|
#endif // WEBCC_ENABLE_SSL
|
||||||
|
|
||||||
} catch (const webcc::Exception& e) {
|
} catch (const webcc::Error& error) {
|
||||||
std::cout << "Exception: " << e.what() << std::endl;
|
std::cout << error << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -58,8 +58,8 @@ int main(int argc, char* argv[]) {
|
|||||||
|
|
||||||
std::cout << r->status() << std::endl;
|
std::cout << r->status() << std::endl;
|
||||||
|
|
||||||
} catch (const webcc::Exception& e) {
|
} catch (const webcc::Error& error) {
|
||||||
std::cout << "Exception: " << e.what() << std::endl;
|
std::cout << error << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ int main(int argc, char* argv[]) {
|
|||||||
server.Run();
|
server.Run();
|
||||||
|
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
std::cerr << "Exception: " << e.what() << std::endl;
|
std::cerr << e.what() << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -67,8 +67,8 @@ void ListEvents(webcc::ClientSession& session) {
|
|||||||
|
|
||||||
PRINT_JSON_STRING(r->content());
|
PRINT_JSON_STRING(r->content());
|
||||||
|
|
||||||
} catch (const webcc::Exception& e) {
|
} catch (const webcc::Error& error) {
|
||||||
std::cout << e.what() << std::endl;
|
std::cout << error << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,8 +81,8 @@ void ListUserFollowers(webcc::ClientSession& session, const std::string& user) {
|
|||||||
|
|
||||||
PRINT_JSON_STRING(r->content());
|
PRINT_JSON_STRING(r->content());
|
||||||
|
|
||||||
} catch (const webcc::Exception& e) {
|
} catch (const webcc::Error& error) {
|
||||||
std::cout << e.what() << std::endl;
|
std::cout << error << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,8 +100,8 @@ void ListAuthUserFollowers(webcc::ClientSession& session,
|
|||||||
|
|
||||||
PRINT_JSON_STRING(r->content());
|
PRINT_JSON_STRING(r->content());
|
||||||
|
|
||||||
} catch (const webcc::Exception& e) {
|
} catch (const webcc::Error& error) {
|
||||||
std::cout << e.what() << std::endl;
|
std::cout << error << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,8 +124,8 @@ void CreateAuthorization(webcc::ClientSession& session,
|
|||||||
|
|
||||||
std::cout << r->content() << std::endl;
|
std::cout << r->content() << std::endl;
|
||||||
|
|
||||||
} catch (const webcc::Exception& e) {
|
} catch (const webcc::Error& error) {
|
||||||
std::cout << e.what() << std::endl;
|
std::cout << error << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -74,8 +74,8 @@ public:
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} catch (const webcc::Exception& e) {
|
} catch (const webcc::Error& error) {
|
||||||
std::cerr << e.what() << std::endl;
|
std::cerr << error << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -97,8 +97,8 @@ public:
|
|||||||
|
|
||||||
return !id->empty();
|
return !id->empty();
|
||||||
|
|
||||||
} catch (const webcc::Exception& e) {
|
} catch (const webcc::Error& error) {
|
||||||
std::cerr << e.what() << std::endl;
|
std::cerr << error << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,8 +122,8 @@ public:
|
|||||||
|
|
||||||
return JsonStringToBook(r->content(), book);
|
return JsonStringToBook(r->content(), book);
|
||||||
|
|
||||||
} catch (const webcc::Exception& e) {
|
} catch (const webcc::Error& error) {
|
||||||
std::cerr << e.what() << std::endl;
|
std::cerr << error << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -143,8 +143,8 @@ public:
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} catch (const webcc::Exception& e) {
|
} catch (const webcc::Error& error) {
|
||||||
std::cerr << e.what() << std::endl;
|
std::cerr << error << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,8 +159,8 @@ public:
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} catch (const webcc::Exception& e) {
|
} catch (const webcc::Error& error) {
|
||||||
std::cerr << e.what() << std::endl;
|
std::cerr << error << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -238,7 +238,7 @@ int main(int argc, char* argv[]) {
|
|||||||
server.Run();
|
server.Run();
|
||||||
|
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
std::cerr << "Exception: " << e.what() << std::endl;
|
std::cerr << e.what() << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+56
-60
@@ -12,43 +12,30 @@ Client::Client()
|
|||||||
buffer_size_(kBufferSize),
|
buffer_size_(kBufferSize),
|
||||||
timeout_(kMaxReadSeconds),
|
timeout_(kMaxReadSeconds),
|
||||||
closed_(false),
|
closed_(false),
|
||||||
timer_canceled_(false),
|
timer_canceled_(false) {
|
||||||
timed_out_(false),
|
|
||||||
error_(kNoError) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Client::Request(RequestPtr request, bool connect) {
|
Error Client::Request(RequestPtr request, bool connect) {
|
||||||
io_context_.restart();
|
Restart();
|
||||||
|
|
||||||
response_.reset(new Response{});
|
|
||||||
response_parser_.Init(response_.get());
|
|
||||||
|
|
||||||
closed_ = false;
|
|
||||||
timer_canceled_ = false;
|
|
||||||
timed_out_ = false;
|
|
||||||
error_ = kNoError;
|
|
||||||
|
|
||||||
if (buffer_.size() != buffer_size_) {
|
|
||||||
LOG_VERB("Resize buffer: %u -> %u.", buffer_.size(), buffer_size_);
|
|
||||||
buffer_.resize(buffer_size_);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (connect) {
|
if (connect) {
|
||||||
// No existing socket connection was specified, create a new one.
|
// No existing socket connection was specified, create a new one.
|
||||||
if ((error_ = Connect(request)) != kNoError) {
|
Connect(request);
|
||||||
return false;
|
|
||||||
|
if (error_) {
|
||||||
|
return error_;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((error_ = WriteReqeust(request)) != kNoError) {
|
WriteReqeust(request);
|
||||||
return false;
|
|
||||||
|
if (error_) {
|
||||||
|
return error_;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((error_ = ReadResponse()) != kNoError) {
|
ReadResponse();
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return error_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::Close() {
|
void Client::Close() {
|
||||||
@@ -68,22 +55,38 @@ void Client::Close() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Error Client::Connect(RequestPtr request) {
|
void Client::Restart() {
|
||||||
if (request->url().scheme() == "https") {
|
io_context_.restart();
|
||||||
#if WEBCC_ENABLE_SSL
|
|
||||||
socket_.reset(new SslSocket{ io_context_, ssl_verify_ });
|
response_.reset(new Response{});
|
||||||
return DoConnect(request, "443");
|
response_parser_.Init(response_.get());
|
||||||
#else
|
|
||||||
LOG_ERRO("SSL/HTTPS support is not enabled.");
|
closed_ = false;
|
||||||
return kSchemaError;
|
timer_canceled_ = false;
|
||||||
#endif // WEBCC_ENABLE_SSL
|
error_ = Error{};
|
||||||
} else {
|
|
||||||
socket_.reset(new Socket{ io_context_ });
|
if (buffer_.size() != buffer_size_) {
|
||||||
return DoConnect(request, "80");
|
LOG_VERB("Resize buffer: %u -> %u.", buffer_.size(), buffer_size_);
|
||||||
|
buffer_.resize(buffer_size_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Error Client::DoConnect(RequestPtr request, const std::string& default_port) {
|
void Client::Connect(RequestPtr request) {
|
||||||
|
if (request->url().scheme() == "https") {
|
||||||
|
#if WEBCC_ENABLE_SSL
|
||||||
|
socket_.reset(new SslSocket{ io_context_, ssl_verify_ });
|
||||||
|
DoConnect(request, "443");
|
||||||
|
#else
|
||||||
|
LOG_ERRO("SSL/HTTPS support is not enabled.");
|
||||||
|
return kSyntaxError;
|
||||||
|
#endif // WEBCC_ENABLE_SSL
|
||||||
|
} else {
|
||||||
|
socket_.reset(new Socket{ io_context_ });
|
||||||
|
DoConnect(request, "80");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::DoConnect(RequestPtr request, const std::string& default_port) {
|
||||||
tcp::resolver resolver(io_context_);
|
tcp::resolver resolver(io_context_);
|
||||||
|
|
||||||
std::string port = request->port(default_port);
|
std::string port = request->port(default_port);
|
||||||
@@ -94,7 +97,7 @@ Error Client::DoConnect(RequestPtr request, const std::string& default_port) {
|
|||||||
if (ec) {
|
if (ec) {
|
||||||
LOG_ERRO("Host resolve error (%s): %s, %s.", ec.message().c_str(),
|
LOG_ERRO("Host resolve error (%s): %s, %s.", ec.message().c_str(),
|
||||||
request->host().c_str(), port.c_str());
|
request->host().c_str(), port.c_str());
|
||||||
return kHostResolveError;
|
error_.Set(Error::kResolveError, "Host resolve error");
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_VERB("Connect to server...");
|
LOG_VERB("Connect to server...");
|
||||||
@@ -106,15 +109,14 @@ Error Client::DoConnect(RequestPtr request, const std::string& default_port) {
|
|||||||
if (ec) {
|
if (ec) {
|
||||||
LOG_ERRO("Socket connect error (%s).", ec.message().c_str());
|
LOG_ERRO("Socket connect error (%s).", ec.message().c_str());
|
||||||
Close();
|
Close();
|
||||||
return kEndpointConnectError;
|
// TODO: Handshake error
|
||||||
|
error_.Set(Error::kConnectError, "Endpoint connect error");
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_VERB("Socket connected.");
|
LOG_VERB("Socket connected.");
|
||||||
|
|
||||||
return kNoError;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Error Client::WriteReqeust(RequestPtr request) {
|
void Client::WriteReqeust(RequestPtr request) {
|
||||||
LOG_VERB("HTTP request:\n%s", request->Dump(4, "> ").c_str());
|
LOG_VERB("HTTP request:\n%s", request->Dump(4, "> ").c_str());
|
||||||
|
|
||||||
// NOTE:
|
// NOTE:
|
||||||
@@ -130,35 +132,29 @@ Error Client::WriteReqeust(RequestPtr request) {
|
|||||||
if (ec) {
|
if (ec) {
|
||||||
LOG_ERRO("Socket write error (%s).", ec.message().c_str());
|
LOG_ERRO("Socket write error (%s).", ec.message().c_str());
|
||||||
Close();
|
Close();
|
||||||
return kSocketWriteError;
|
error_.Set(Error::kSocketWriteError, "Socket write error");
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_INFO("Request sent.");
|
LOG_INFO("Request sent.");
|
||||||
|
|
||||||
return kNoError;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Error Client::ReadResponse() {
|
void Client::ReadResponse() {
|
||||||
LOG_VERB("Read response (timeout: %ds)...", timeout_);
|
LOG_VERB("Read response (timeout: %ds)...", timeout_);
|
||||||
|
|
||||||
timer_.expires_after(std::chrono::seconds(timeout_));
|
timer_.expires_after(std::chrono::seconds(timeout_));
|
||||||
|
|
||||||
DoWaitTimer();
|
DoWaitTimer();
|
||||||
|
DoReadResponse();
|
||||||
|
|
||||||
Error error = kNoError;
|
if (!error_) {
|
||||||
DoReadResponse(&error);
|
|
||||||
|
|
||||||
if (error == kNoError) {
|
|
||||||
LOG_VERB("HTTP response:\n%s", response_->Dump(4, "> ").c_str());
|
LOG_VERB("HTTP response:\n%s", response_->Dump(4, "> ").c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::DoReadResponse(Error* error) {
|
void Client::DoReadResponse() {
|
||||||
boost::system::error_code ec = boost::asio::error::would_block;
|
boost::system::error_code ec = boost::asio::error::would_block;
|
||||||
|
|
||||||
auto handler = [this, &ec, error](boost::system::error_code inner_ec,
|
auto handler = [this, &ec](boost::system::error_code inner_ec,
|
||||||
std::size_t length) {
|
std::size_t length) {
|
||||||
ec = inner_ec;
|
ec = inner_ec;
|
||||||
|
|
||||||
@@ -170,7 +166,7 @@ void Client::DoReadResponse(Error* error) {
|
|||||||
// TODO: Is it necessary to check `length == 0`?
|
// TODO: Is it necessary to check `length == 0`?
|
||||||
if (ec || length == 0) {
|
if (ec || length == 0) {
|
||||||
Close();
|
Close();
|
||||||
*error = kSocketReadError;
|
error_.Set(Error::kSocketReadError, "Socket read error");
|
||||||
LOG_ERRO("Socket read error (%s).", ec.message().c_str());
|
LOG_ERRO("Socket read error (%s).", ec.message().c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -180,7 +176,7 @@ void Client::DoReadResponse(Error* error) {
|
|||||||
// Parse the response piece just read.
|
// Parse the response piece just read.
|
||||||
if (!response_parser_.Parse(buffer_.data(), length)) {
|
if (!response_parser_.Parse(buffer_.data(), length)) {
|
||||||
Close();
|
Close();
|
||||||
*error = kHttpError;
|
error_.Set(Error::kParseError, "HTTP parse error");
|
||||||
LOG_ERRO("Failed to parse HTTP response.");
|
LOG_ERRO("Failed to parse HTTP response.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -203,7 +199,7 @@ void Client::DoReadResponse(Error* error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!closed_) {
|
if (!closed_) {
|
||||||
DoReadResponse(error);
|
DoReadResponse();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -238,7 +234,7 @@ void Client::OnTimer(boost::system::error_code ec) {
|
|||||||
// The deadline has passed. The socket is closed so that any outstanding
|
// The deadline has passed. The socket is closed so that any outstanding
|
||||||
// asynchronous operations are canceled.
|
// asynchronous operations are canceled.
|
||||||
LOG_WARN("HTTP client timed out.");
|
LOG_WARN("HTTP client timed out.");
|
||||||
timed_out_ = true;
|
error_.set_timeout(true);
|
||||||
Close();
|
Close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-15
@@ -29,7 +29,7 @@ class Client {
|
|||||||
public:
|
public:
|
||||||
Client();
|
Client();
|
||||||
|
|
||||||
virtual ~Client() = default;
|
~Client() = default;
|
||||||
|
|
||||||
Client(const Client&) = delete;
|
Client(const Client&) = delete;
|
||||||
Client& operator=(const Client&) = delete;
|
Client& operator=(const Client&) = delete;
|
||||||
@@ -52,7 +52,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Connect to server, send request, wait until response is received.
|
// Connect to server, send request, wait until response is received.
|
||||||
bool Request(RequestPtr request, bool connect = true);
|
Error Request(RequestPtr request, bool connect = true);
|
||||||
|
|
||||||
// Close the socket.
|
// Close the socket.
|
||||||
void Close();
|
void Close();
|
||||||
@@ -61,20 +61,18 @@ public:
|
|||||||
|
|
||||||
bool closed() const { return closed_; }
|
bool closed() const { return closed_; }
|
||||||
|
|
||||||
bool timed_out() const { return timed_out_; }
|
|
||||||
|
|
||||||
Error error() const { return error_; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Error Connect(RequestPtr request);
|
void Restart();
|
||||||
|
|
||||||
Error DoConnect(RequestPtr request, const std::string& default_port);
|
void Connect(RequestPtr request);
|
||||||
|
|
||||||
Error WriteReqeust(RequestPtr request);
|
void DoConnect(RequestPtr request, const std::string& default_port);
|
||||||
|
|
||||||
Error ReadResponse();
|
void WriteReqeust(RequestPtr request);
|
||||||
|
|
||||||
void DoReadResponse(Error* error);
|
void ReadResponse();
|
||||||
|
|
||||||
|
void DoReadResponse();
|
||||||
|
|
||||||
void DoWaitTimer();
|
void DoWaitTimer();
|
||||||
void OnTimer(boost::system::error_code ec);
|
void OnTimer(boost::system::error_code ec);
|
||||||
@@ -113,10 +111,6 @@ private:
|
|||||||
// Deadline timer canceled.
|
// Deadline timer canceled.
|
||||||
bool timer_canceled_;
|
bool timer_canceled_;
|
||||||
|
|
||||||
// Timeout occurred.
|
|
||||||
bool timed_out_;
|
|
||||||
|
|
||||||
// Error code.
|
|
||||||
Error error_;
|
Error error_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -143,11 +143,11 @@ void ClientSession::InitHeaders() {
|
|||||||
// content coding.
|
// content coding.
|
||||||
//
|
//
|
||||||
// A note about "deflate":
|
// A note about "deflate":
|
||||||
// (https://www.zlib.net/zlib_faq.html#faq39)
|
|
||||||
// "gzip" is the gzip format, and "deflate" is the zlib format. They should
|
// "gzip" is the gzip format, and "deflate" is the zlib format. They should
|
||||||
// probably have called the second one "zlib" instead to avoid confusion with
|
// probably have called the second one "zlib" instead to avoid confusion with
|
||||||
// the raw deflate compressed data format.
|
// the raw deflate compressed data format.
|
||||||
// Simply put, "deflate" is not recommended for HTTP 1.1 encoding.
|
// Simply put, "deflate" is not recommended for HTTP 1.1 encoding.
|
||||||
|
// (https://www.zlib.net/zlib_faq.html#faq39)
|
||||||
|
|
||||||
#if WEBCC_ENABLE_GZIP
|
#if WEBCC_ENABLE_GZIP
|
||||||
headers_.Set(kAcceptEncoding, "gzip, deflate");
|
headers_.Set(kAcceptEncoding, "gzip, deflate");
|
||||||
@@ -161,7 +161,7 @@ void ClientSession::InitHeaders() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ResponsePtr ClientSession::Send(RequestPtr request) {
|
ResponsePtr ClientSession::Send(RequestPtr request) {
|
||||||
const ClientPool::Key key{request->url()};
|
const ClientPool::Key key{ request->url() };
|
||||||
|
|
||||||
// Reuse a pooled connection.
|
// Reuse a pooled connection.
|
||||||
bool reuse = false;
|
bool reuse = false;
|
||||||
@@ -179,22 +179,22 @@ ResponsePtr ClientSession::Send(RequestPtr request) {
|
|||||||
client->set_buffer_size(buffer_size_);
|
client->set_buffer_size(buffer_size_);
|
||||||
client->set_timeout(timeout_);
|
client->set_timeout(timeout_);
|
||||||
|
|
||||||
bool ok = client->Request(request, !reuse);
|
Error error = client->Request(request, !reuse);
|
||||||
|
|
||||||
if (!ok) {
|
if (error) {
|
||||||
if (reuse && client->error() == kSocketWriteError) {
|
if (reuse && error.code() == Error::kSocketWriteError) {
|
||||||
LOG_WARN("Cannot send request with the reused connection. "
|
LOG_WARN("Cannot send request with the reused connection. "
|
||||||
"The server must have closed it, reconnect and try again.");
|
"The server must have closed it, reconnect and try again.");
|
||||||
ok = client->Request(request, true);
|
error = client->Request(request, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ok) {
|
if (error) {
|
||||||
if (reuse) {
|
|
||||||
// Remove the failed connection from pool.
|
// Remove the failed connection from pool.
|
||||||
|
if (reuse) {
|
||||||
pool_.Remove(key);
|
pool_.Remove(key);
|
||||||
}
|
}
|
||||||
throw Exception(client->error(), "", client->timed_out());
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update connection pool.
|
// Update connection pool.
|
||||||
|
|||||||
+1
-1
@@ -204,7 +204,7 @@ FormPart::FormPart(const std::string& name, const Path& path,
|
|||||||
const std::string& media_type)
|
const std::string& media_type)
|
||||||
: name_(name), media_type_(media_type) {
|
: name_(name), media_type_(media_type) {
|
||||||
if (!ReadFile(path, &data_)) {
|
if (!ReadFile(path, &data_)) {
|
||||||
throw Exception(kFileIOError, "Cannot read the file.");
|
throw Error{ Error::kFileError, "Cannot read the file." };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine file name from file path.
|
// Determine file name from file path.
|
||||||
|
|||||||
+7
-32
@@ -1,9 +1,8 @@
|
|||||||
#include "webcc/globals.h"
|
#include "webcc/globals.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#include "webcc/version.h"
|
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@@ -47,37 +46,13 @@ std::string FromExtension(const std::string& extension,
|
|||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
const char* DescribeError(Error error) {
|
std::ostream& operator<<(std::ostream& os, const Error& error) {
|
||||||
switch (error) {
|
os << std::to_string(static_cast<int>(error.code()));
|
||||||
case kSchemaError:
|
os << ": " << error.message();
|
||||||
return "Schema error";
|
if (error.timeout()) {
|
||||||
case kHostResolveError:
|
os << " (timeout)";
|
||||||
return "Host resolve error";
|
|
||||||
case kEndpointConnectError:
|
|
||||||
return "Endpoint connect error";
|
|
||||||
case kHandshakeError:
|
|
||||||
return "Handshake error";
|
|
||||||
case kSocketReadError:
|
|
||||||
return "Socket read error";
|
|
||||||
case kSocketWriteError:
|
|
||||||
return "Socket write error";
|
|
||||||
case kHttpError:
|
|
||||||
return "HTTP error";
|
|
||||||
case kFileIOError:
|
|
||||||
return "File IO error";
|
|
||||||
default:
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Exception::Exception(Error error, const std::string& details, bool timeout)
|
|
||||||
: error_(error), msg_(DescribeError(error)), timeout_(timeout) {
|
|
||||||
if (!details.empty()) {
|
|
||||||
msg_ += " (" + details + ")";
|
|
||||||
}
|
|
||||||
if (timeout) {
|
|
||||||
msg_ += " (timeout)";
|
|
||||||
}
|
}
|
||||||
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace webcc
|
} // namespace webcc
|
||||||
|
|||||||
+31
-37
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
|
#include <iosfwd>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "webcc/config.h"
|
#include "webcc/config.h"
|
||||||
@@ -149,56 +150,49 @@ enum class ContentEncoding {
|
|||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
// Client side error codes.
|
// Error (or exception) for the client.
|
||||||
enum Error {
|
class Error {
|
||||||
kNoError = 0, // i.e., OK
|
public:
|
||||||
|
enum Code {
|
||||||
kSchemaError, // TODO
|
kOK = 0,
|
||||||
|
kSyntaxError,
|
||||||
kHostResolveError,
|
kResolveError,
|
||||||
kEndpointConnectError,
|
kConnectError,
|
||||||
kHandshakeError, // HTTPS handshake
|
kHandshakeError,
|
||||||
kSocketReadError,
|
kSocketReadError,
|
||||||
kSocketWriteError,
|
kSocketWriteError,
|
||||||
|
kParseError,
|
||||||
|
kFileError,
|
||||||
|
};
|
||||||
|
|
||||||
// HTTP error.
|
|
||||||
// E.g., failed to parse HTTP response (invalid content length, etc.).
|
|
||||||
kHttpError,
|
|
||||||
|
|
||||||
// File read/write error.
|
|
||||||
kFileIOError,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Return a descriptive message for the given error code.
|
|
||||||
const char* DescribeError(Error error);
|
|
||||||
|
|
||||||
class Exception : public std::exception {
|
|
||||||
public:
|
public:
|
||||||
explicit Exception(Error error, const std::string& details = "",
|
Error(Code code = kOK, const std::string& message = "")
|
||||||
bool timeout = false);
|
: code_(code), message_(message), timeout_(false) {
|
||||||
|
|
||||||
Error error() const {
|
|
||||||
return error_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note that `noexcept` is required by GCC.
|
Code code() const { return code_; }
|
||||||
const char* what() const WEBCC_NOEXCEPT override{
|
|
||||||
return msg_.c_str();
|
const std::string& message() const { return message_; }
|
||||||
|
|
||||||
|
void Set(Code code, const std::string& message) {
|
||||||
|
code_ = code;
|
||||||
|
message_ = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool timeout() const {
|
bool timeout() const { return timeout_; }
|
||||||
return timeout_;
|
|
||||||
}
|
void set_timeout(bool timeout) { timeout_ = timeout; }
|
||||||
|
|
||||||
|
operator bool() const { return code_ != kOK; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Error error_;
|
Code code_;
|
||||||
|
std::string message_;
|
||||||
std::string msg_;
|
|
||||||
|
|
||||||
// If the error was caused by timeout or not.
|
|
||||||
bool timeout_;
|
bool timeout_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, const Error& error);
|
||||||
|
|
||||||
} // namespace webcc
|
} // namespace webcc
|
||||||
|
|
||||||
#endif // WEBCC_GLOBALS_H_
|
#endif // WEBCC_GLOBALS_H_
|
||||||
|
|||||||
+1
-1
@@ -81,7 +81,7 @@ void Request::CreateStartLine() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (url_.host().empty()) {
|
if (url_.host().empty()) {
|
||||||
throw Exception(kSchemaError, "Invalid request: host is missing.");
|
throw Error{ Error::kSyntaxError, "Host is missing" };
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string target = "/" + url_.path();
|
std::string target = "/" + url_.path();
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ bool RestServiceManager::AddService(RestServicePtr service,
|
|||||||
item.url_regex.assign(url, flags);
|
item.url_regex.assign(url, flags);
|
||||||
service_items_.push_back(std::move(item));
|
service_items_.push_back(std::move(item));
|
||||||
return true;
|
return true;
|
||||||
} catch (std::regex_error& e) {
|
} catch (const std::regex_error& e) {
|
||||||
LOG_ERRO("URL is not a valid regular expression: %s", e.what());
|
LOG_ERRO("URL is not a valid regular expression: %s", e.what());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user