14 #include <http/http_messages.h> 17 #include <boost/bind.hpp> 18 #include <boost/enable_shared_from_this.hpp> 19 #include <boost/weak_ptr.hpp> 35 constexpr
size_t MAX_LOGGED_MESSAGE_SIZE = 1024;
38 typedef boost::function<void(boost::system::error_code ec,
size_t length)>
39 SocketCallbackFunction;
46 class SocketCallback {
54 SocketCallback(SocketCallbackFunction socket_callback)
55 : callback_(socket_callback) {
64 void operator()(boost::system::error_code ec,
size_t length = 0) {
65 if (ec.value() == boost::asio::error::operation_aborted) {
68 callback_(ec, length);
74 SocketCallbackFunction callback_;
81 typedef boost::shared_ptr<ConnectionPool> ConnectionPoolPtr;
98 class Connection :
public boost::enable_shared_from_this<Connection> {
107 explicit Connection(
IOService& io_service,
const ConnectionPoolPtr& conn_pool,
128 const long request_timeout,
const HttpClient::RequestHandler& callback,
129 const HttpClient::ConnectHandler& connect_callback);
137 bool isTransactionOngoing()
const;
156 void terminate(
const boost::system::error_code& ec,
157 const std::string& parsing_error =
"");
162 void scheduleTimer(
const long request_timeout);
182 void connectCallback(HttpClient::ConnectHandler connect_callback,
183 const boost::system::error_code& ec);
194 void sendCallback(
const boost::system::error_code& ec,
size_t length);
201 void receiveCallback(
const boost::system::error_code& ec,
size_t length);
204 void timerCallback();
210 boost::weak_ptr<ConnectionPool> conn_pool_;
231 HttpClient::RequestHandler current_callback_;
237 std::array<char, 32768> input_buf_;
241 typedef boost::shared_ptr<Connection> ConnectionPtr;
250 class ConnectionPool :
public boost::enable_shared_from_this<ConnectionPool> {
257 explicit ConnectionPool(
IOService& io_service)
258 : io_service_(io_service), conns_(), queue_() {
281 bool getNextRequest(
const Url& url,
284 long& request_timeout,
285 HttpClient::RequestHandler& callback,
286 HttpClient::ConnectHandler& connect_callback) {
289 auto it = queue_.find(url);
290 if (it != queue_.end()) {
292 if (!it->second.empty()) {
293 RequestDescriptor desc = it->second.front();
295 request = desc.request_;
296 response = desc.response_;
297 request_timeout = desc.request_timeout_,
298 callback = desc.callback_;
299 connect_callback = desc.connect_callback_;
322 void queueRequest(
const Url& url,
325 const long request_timeout,
326 const HttpClient::RequestHandler& request_callback,
327 const HttpClient::ConnectHandler& connect_callback) {
328 auto it = conns_.find(url);
329 if (it != conns_.end()) {
330 ConnectionPtr conn = it->second;
332 if (conn->isTransactionOngoing()) {
334 queue_[url].push(RequestDescriptor(request, response,
341 conn->doTransaction(request, response, request_timeout,
342 request_callback, connect_callback);
348 ConnectionPtr conn(
new Connection(io_service_, shared_from_this(),
350 conn->doTransaction(request, response, request_timeout, request_callback,
360 void closeConnection(
const Url& url) {
362 auto conns_it = conns_.find(url);
363 if (conns_it != conns_.end()) {
364 conns_it->second->close();
365 conns_.erase(conns_it);
369 auto queue_it = queue_.find(url);
370 if (queue_it != queue_.end()) {
371 queue_.erase(queue_it);
378 for (
auto conns_it = conns_.begin(); conns_it != conns_.end();
380 conns_it->second->close();
393 std::map<Url, ConnectionPtr> conns_;
397 struct RequestDescriptor {
409 const long request_timeout,
410 const HttpClient::RequestHandler& callback,
411 const HttpClient::ConnectHandler& connect_callback)
412 : request_(request), response_(response),
413 request_timeout_(request_timeout),
415 connect_callback_(connect_callback) {
423 long request_timeout_;
425 HttpClient::RequestHandler callback_;
427 HttpClient::ConnectHandler connect_callback_;
431 std::map<Url, std::queue<RequestDescriptor> > queue_;
434 Connection::Connection(
IOService& io_service,
435 const ConnectionPoolPtr& conn_pool,
437 : conn_pool_(conn_pool), url_(url), socket_(io_service), timer_(io_service),
438 current_request_(), current_response_(), parser_(), current_callback_(),
439 buf_(), input_buf_() {
442 Connection::~Connection() {
447 Connection::resetState() {
448 current_request_.reset();
449 current_response_.reset();
451 current_callback_ = HttpClient::RequestHandler();
457 const long request_timeout,
458 const HttpClient::RequestHandler& callback,
459 const HttpClient::ConnectHandler& connect_callback) {
461 current_request_ = request;
462 current_response_ = response;
463 parser_.reset(
new HttpResponseParser(*current_response_));
464 parser_->initModel();
465 current_callback_ = callback;
467 buf_ = request->toString();
475 if (socket_.getASIOSocket().is_open() && !socket_.isUsable()) {
480 HTTP_CLIENT_REQUEST_SEND)
481 .arg(request->toBriefString())
485 HTTP_CLIENT_REQUEST_SEND_DETAILS)
487 .arg(HttpMessageParserBase::logFormatHttpMessage(request->toString(),
488 MAX_LOGGED_MESSAGE_SIZE));
491 scheduleTimer(request_timeout);
497 static_cast<unsigned short>(url_.getPort()));
498 SocketCallback socket_cb(boost::bind(&Connection::connectCallback, shared_from_this(),
499 connect_callback, _1));
502 socket_.open(&endpoint, socket_cb);
504 }
catch (
const std::exception& ex) {
511 Connection::close() {
518 Connection::isTransactionOngoing()
const {
519 return (static_cast<bool>(current_request_));
523 Connection::terminate(
const boost::system::error_code& ec,
524 const std::string& parsing_error) {
531 if (!ec && current_response_->isFinalized()) {
532 response = current_response_;
535 HTTP_SERVER_RESPONSE_RECEIVED)
539 HTTP_SERVER_RESPONSE_RECEIVED_DETAILS)
541 .arg(parser_->getBufferAsString(MAX_LOGGED_MESSAGE_SIZE));
544 std::string err = parsing_error.empty() ? ec.message() : parsing_error;
547 HTTP_BAD_SERVER_RESPONSE_RECEIVED)
553 if (!parsing_error.empty()) {
555 HTTP_BAD_SERVER_RESPONSE_RECEIVED_DETAILS)
557 .arg(parser_->getBufferAsString());
565 current_callback_(ec, response, parsing_error);
572 if (!current_request_->isPersistent()) {
581 long request_timeout;
582 HttpClient::RequestHandler callback;
583 HttpClient::ConnectHandler connect_callback;
584 ConnectionPoolPtr conn_pool = conn_pool_.lock();
585 if (conn_pool && conn_pool->getNextRequest(url_, request, response, request_timeout,
586 callback, connect_callback)) {
587 doTransaction(request, response, request_timeout, callback, connect_callback);
592 Connection::scheduleTimer(
const long request_timeout) {
593 if (request_timeout > 0) {
594 timer_.setup(boost::bind(&Connection::timerCallback,
this), request_timeout,
600 Connection::doSend() {
601 SocketCallback socket_cb(boost::bind(&Connection::sendCallback, shared_from_this(),
604 socket_.asyncSend(&buf_[0], buf_.size(), socket_cb);
607 terminate(boost::asio::error::not_connected);
612 Connection::doReceive() {
614 SocketCallback socket_cb(boost::bind(&Connection::receiveCallback, shared_from_this(),
618 socket_.asyncReceive(static_cast<void*>(input_buf_.data()), input_buf_.size(), 0,
619 &endpoint, socket_cb);
621 terminate(boost::asio::error::not_connected);
626 Connection::connectCallback(HttpClient::ConnectHandler connect_callback,
627 const boost::system::error_code& ec) {
629 if (connect_callback) {
632 if (!connect_callback(ec)) {
642 (ec.value() != boost::asio::error::in_progress) &&
643 (ec.value() != boost::asio::error::already_connected)) {
653 Connection::sendCallback(
const boost::system::error_code& ec,
size_t length) {
657 if ((ec.value() == boost::asio::error::would_block) ||
658 (ec.value() == boost::asio::error::try_again)) {
669 scheduleTimer(timer_.getInterval());
674 buf_.erase(0, length);
688 Connection::receiveCallback(
const boost::system::error_code& ec,
size_t length) {
692 if ((ec.value() != boost::asio::error::try_again) &&
693 (ec.value() != boost::asio::error::would_block)) {
705 scheduleTimer(timer_.getInterval());
709 parser_->postBuffer(static_cast<void*>(input_buf_.data()), length);
714 if (parser_->needData()) {
717 }
else if (parser_->httpParseOk()) {
721 current_response_->finalize();
724 }
catch (
const std::exception& ex) {
726 terminate(ec, ex.what());
732 terminate(ec, parser_->getErrorMessage());
737 Connection::timerCallback() {
739 terminate(boost::asio::error::timed_out);
755 : conn_pool_(new ConnectionPool(io_service)) {
785 if (!request_callback) {
789 impl_->conn_pool_->queueRequest(url, request, response, request_timeout.
value_,
790 request_callback, connect_callback);
795 impl_->conn_pool_->closeAll();
void stop()
Closes all connections.
const int DBGLVL_TRACE_BASIC
Trace basic operations.
HttpClientImpl(IOService &io_service)
Constructor.
long value_
Timeout value specified.
std::function< void(const boost::system::error_code &, const HttpResponsePtr &, const std::string &)> RequestHandler
Callback type used in call to HttpClient::asyncSendRequest.
HTTP request/response timeout value.
std::function< bool(const boost::system::error_code &)> ConnectHandler
Optional handler invoked when client connects to the server.
The IOService class is a wrapper for the ASIO io_service class.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
The TCPEndpoint class is a concrete derived class of IOEndpoint that represents an endpoint of a TCP ...
ConnectionPoolPtr conn_pool_
Holds a pointer to the connection pool.
The IntervalTimer class is a wrapper for the ASIO boost::asio::deadline_timer class.
void asyncSendRequest(const Url &url, const HttpRequestPtr &request, const HttpResponsePtr &response, const RequestHandler &request_callback, const RequestTimeout &request_timeout=RequestTimeout(10000), const ConnectHandler &connect_callback=ConnectHandler())
Queues new asynchronous HTTP request.
boost::shared_ptr< HttpResponse > HttpResponsePtr
Pointer to the HttpResponse object.
bool isValid() const
Checks if the URL is valid.
const int DBGLVL_TRACE_DETAIL_DATA
Trace data associated with detailed operations.
Defines the logger used by the top-level component of kea-dhcp-ddns.
boost::shared_ptr< HttpResponseParser > HttpResponseParserPtr
Pointer to the HttpResponseParser.
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
isc::log::Logger http_logger("http")
Defines the logger used within libkea-http library.
boost::shared_ptr< HttpRequest > HttpRequestPtr
Pointer to the HttpRequest object.
const int DBGLVL_TRACE_DETAIL
Trace detailed operations.
A generic error raised by the HttpClient class.
const int DBGLVL_TRACE_BASIC_DATA
Trace data associated with the basic operations.
HttpClient implementation.