Refine the draft timeout control from server side.
This commit is contained in:
@@ -3,8 +3,6 @@
|
|||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
#include "boost/lexical_cast.hpp"
|
#include "boost/lexical_cast.hpp"
|
||||||
#include "boost/thread.hpp"
|
|
||||||
#include "boost/date_time/posix_time/posix_time.hpp"
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@@ -115,10 +113,6 @@ bool BookListService::Handle(const std::string& http_method,
|
|||||||
std::string* response_content) {
|
std::string* response_content) {
|
||||||
if (http_method == webcc::kHttpGet) {
|
if (http_method == webcc::kHttpGet) {
|
||||||
*response_content = CreateBookListJson(g_book_store.books());
|
*response_content = CreateBookListJson(g_book_store.books());
|
||||||
|
|
||||||
// Sleep for testing timeout control.
|
|
||||||
//boost::this_thread::sleep(boost::posix_time::seconds(2));
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,9 +140,6 @@ bool BookDetailService::Handle(const std::string& http_method,
|
|||||||
|
|
||||||
*response_content = CreateBookJson(book);
|
*response_content = CreateBookJson(book);
|
||||||
|
|
||||||
// Sleep for testing timeout control.
|
|
||||||
//boost::this_thread::sleep(boost::posix_time::seconds(2));
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} else if (http_method == webcc::kHttpPost) {
|
} else if (http_method == webcc::kHttpPost) {
|
||||||
|
|||||||
@@ -27,11 +27,6 @@ int main(int argc, char* argv[]) {
|
|||||||
server.RegisterService(std::make_shared<BookDetailService>(),
|
server.RegisterService(std::make_shared<BookDetailService>(),
|
||||||
"/books/(\\d+)");
|
"/books/(\\d+)");
|
||||||
|
|
||||||
// For test purpose.
|
|
||||||
// Timeout like 60s makes more sense in a real product.
|
|
||||||
// Leave it as default (0) for no timeout control.
|
|
||||||
server.set_timeout_seconds(1);
|
|
||||||
|
|
||||||
server.Run();
|
server.Run();
|
||||||
|
|
||||||
} catch (std::exception& e) {
|
} catch (std::exception& e) {
|
||||||
|
|||||||
@@ -16,8 +16,7 @@ namespace webcc {
|
|||||||
|
|
||||||
HttpServer::HttpServer(unsigned short port, std::size_t workers)
|
HttpServer::HttpServer(unsigned short port, std::size_t workers)
|
||||||
: signals_(io_context_)
|
: signals_(io_context_)
|
||||||
, workers_(workers)
|
, workers_(workers) {
|
||||||
, timeout_seconds_(0) {
|
|
||||||
|
|
||||||
// Register to handle the signals that indicate when the server should exit.
|
// Register to handle the signals that indicate when the server should exit.
|
||||||
// It is safe to register for the same signal multiple times in a program,
|
// It is safe to register for the same signal multiple times in a program,
|
||||||
@@ -78,7 +77,7 @@ void HttpServer::DoAccept() {
|
|||||||
HttpSessionPtr session{
|
HttpSessionPtr session{
|
||||||
new HttpSession(std::move(socket), GetRequestHandler())
|
new HttpSession(std::move(socket), GetRequestHandler())
|
||||||
};
|
};
|
||||||
session->Start(timeout_seconds_);
|
session->Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
DoAccept();
|
DoAccept();
|
||||||
|
|||||||
@@ -28,10 +28,6 @@ public:
|
|||||||
|
|
||||||
virtual ~HttpServer();
|
virtual ~HttpServer();
|
||||||
|
|
||||||
void set_timeout_seconds(long seconds) {
|
|
||||||
timeout_seconds_ = seconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run the server's io_service loop.
|
// Run the server's io_service loop.
|
||||||
void Run();
|
void Run();
|
||||||
|
|
||||||
@@ -57,10 +53,6 @@ private:
|
|||||||
|
|
||||||
// Acceptor used to listen for incoming connections.
|
// Acceptor used to listen for incoming connections.
|
||||||
boost::scoped_ptr<boost::asio::ip::tcp::acceptor> acceptor_;
|
boost::scoped_ptr<boost::asio::ip::tcp::acceptor> acceptor_;
|
||||||
|
|
||||||
// Timeout in seconds for socket connection.
|
|
||||||
// Default is 0 which means no timeout.
|
|
||||||
long timeout_seconds_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webcc
|
} // namespace webcc
|
||||||
|
|||||||
@@ -22,27 +22,13 @@ HttpSession::HttpSession(boost::asio::ip::tcp::socket socket,
|
|||||||
HttpSession::~HttpSession() {
|
HttpSession::~HttpSession() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpSession::Start(long timeout_seconds) {
|
void HttpSession::Start() {
|
||||||
if (timeout_seconds > 0) {
|
|
||||||
// Create timer only necessary.
|
|
||||||
boost::asio::io_context& ioc = socket_.get_executor().context();
|
|
||||||
timer_.reset(new boost::asio::deadline_timer(ioc));
|
|
||||||
|
|
||||||
timer_->expires_from_now(boost::posix_time::seconds(timeout_seconds));
|
|
||||||
|
|
||||||
timer_->async_wait(std::bind(&HttpSession::HandleTimer,
|
|
||||||
shared_from_this(),
|
|
||||||
std::placeholders::_1));
|
|
||||||
}
|
|
||||||
|
|
||||||
DoRead();
|
DoRead();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpSession::Stop() {
|
void HttpSession::Stop() {
|
||||||
CancelTimer();
|
boost::system::error_code ec;
|
||||||
|
socket_.close(ec);
|
||||||
boost::system::error_code ignored_ec;
|
|
||||||
socket_.close(ignored_ec);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpSession::SetResponseContent(const std::string& content_type,
|
void HttpSession::SetResponseContent(const std::string& content_type,
|
||||||
@@ -78,8 +64,6 @@ void HttpSession::HandleRead(boost::system::error_code ec,
|
|||||||
if (ec) {
|
if (ec) {
|
||||||
if (ec != boost::asio::error::operation_aborted) {
|
if (ec != boost::asio::error::operation_aborted) {
|
||||||
Stop();
|
Stop();
|
||||||
} else {
|
|
||||||
CancelTimer();
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -116,15 +100,14 @@ void HttpSession::HandleWrite(boost::system::error_code ec,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!ec) {
|
if (!ec) {
|
||||||
CancelTimer();
|
#if WEBCC_DEBUG_OUTPUT
|
||||||
|
std::cout << "Response has been sent back (thread: " << thread_id << ")\n";
|
||||||
|
#endif
|
||||||
|
|
||||||
// Initiate graceful connection closure.
|
// Initiate graceful connection closure.
|
||||||
boost::system::error_code ec;
|
boost::system::error_code ec;
|
||||||
socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
|
socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
|
||||||
|
|
||||||
#if WEBCC_DEBUG_OUTPUT
|
|
||||||
std::cout << "Response has been sent back (thread: " << thread_id << ")\n";
|
|
||||||
#endif
|
|
||||||
} else {
|
} else {
|
||||||
#if WEBCC_DEBUG_OUTPUT
|
#if WEBCC_DEBUG_OUTPUT
|
||||||
std::cout << "(thread: " << thread_id << ") Sending response error: "
|
std::cout << "(thread: " << thread_id << ") Sending response error: "
|
||||||
@@ -132,37 +115,10 @@ void HttpSession::HandleWrite(boost::system::error_code ec,
|
|||||||
<< std::endl;
|
<< std::endl;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (ec == boost::asio::error::operation_aborted) {
|
if (ec != boost::asio::error::operation_aborted) {
|
||||||
CancelTimer();
|
|
||||||
} else {
|
|
||||||
Stop();
|
Stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpSession::HandleTimer(boost::system::error_code ec) {
|
|
||||||
std::cout << "HandleTimer: ";
|
|
||||||
|
|
||||||
if (!ec) {
|
|
||||||
if (socket_.is_open()) {
|
|
||||||
std::cout << "socket is open, close it.\n";
|
|
||||||
socket_.close();
|
|
||||||
} else {
|
|
||||||
std::cout << "socket is not open.\n";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (ec == boost::asio::error::operation_aborted) {
|
|
||||||
std::cout << "Timer aborted\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void HttpSession::CancelTimer() {
|
|
||||||
if (timer_) {
|
|
||||||
// The wait handler will be invoked with the operation_aborted error code.
|
|
||||||
boost::system::error_code ec;
|
|
||||||
timer_->cancel(ec);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace webcc
|
} // namespace webcc
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "boost/asio/ip/tcp.hpp" // for ip::tcp::socket
|
#include "boost/asio/ip/tcp.hpp" // for ip::tcp::socket
|
||||||
#include "boost/asio/deadline_timer.hpp"
|
|
||||||
|
|
||||||
#include "webcc/common.h"
|
#include "webcc/common.h"
|
||||||
#include "webcc/http_request.h"
|
#include "webcc/http_request.h"
|
||||||
@@ -30,8 +29,7 @@ public:
|
|||||||
return request_;
|
return request_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the session with an optional timeout.
|
void Start();
|
||||||
void Start(long timeout_seconds = 0);
|
|
||||||
|
|
||||||
void Stop();
|
void Stop();
|
||||||
|
|
||||||
@@ -54,17 +52,10 @@ private:
|
|||||||
void HandleRead(boost::system::error_code ec, std::size_t length);
|
void HandleRead(boost::system::error_code ec, std::size_t length);
|
||||||
void HandleWrite(boost::system::error_code ec, std::size_t length);
|
void HandleWrite(boost::system::error_code ec, std::size_t length);
|
||||||
|
|
||||||
void HandleTimer(boost::system::error_code ec);
|
|
||||||
|
|
||||||
void CancelTimer();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Socket for the connection.
|
// Socket for the connection.
|
||||||
boost::asio::ip::tcp::socket socket_;
|
boost::asio::ip::tcp::socket socket_;
|
||||||
|
|
||||||
// Timeout timer (optional).
|
|
||||||
std::unique_ptr<boost::asio::deadline_timer> timer_;
|
|
||||||
|
|
||||||
// The handler used to process the incoming request.
|
// The handler used to process the incoming request.
|
||||||
HttpRequestHandler* request_handler_;
|
HttpRequestHandler* request_handler_;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user