Kea 3.3.3
client.cc
Go to the documentation of this file.
1// Copyright (C) 2018-2026 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
12#include <asiolink/tls_socket.h>
13#include <http/client.h>
14#include <http/http_log.h>
15#include <http/http_messages.h>
16#include <http/response_json.h>
20#include <util/unlock_guard.h>
21
22#include <boost/enable_shared_from_this.hpp>
23#include <boost/weak_ptr.hpp>
24
25#include <atomic>
26#include <array>
27#include <functional>
28#include <iostream>
29#include <map>
30#include <mutex>
31#include <queue>
32#include <thread>
33
34
35using namespace isc;
36using namespace isc::asiolink;
37using namespace isc::http;
38using namespace isc::util;
39using namespace boost::posix_time;
40
41namespace ph = std::placeholders;
42
43namespace {
44
48constexpr size_t MAX_LOGGED_MESSAGE_SIZE = 1024;
49
51typedef std::function<void(boost::system::error_code ec, size_t length)>
53
59class SocketCallback {
60public:
61
67 SocketCallback(SocketCallbackFunction socket_callback)
68 : callback_(socket_callback) {
69 }
70
77 void operator()(boost::system::error_code ec, size_t length = 0) {
78 if (ec.value() == boost::asio::error::operation_aborted) {
79 return;
80 }
81 callback_(ec, length);
82 }
83
84private:
85
87 SocketCallbackFunction callback_;
88
89};
90
91class ConnectionPool;
92
94typedef boost::shared_ptr<ConnectionPool> ConnectionPoolPtr;
95
111class Connection : public boost::enable_shared_from_this<Connection> {
112public:
113
121 explicit Connection(const IOServicePtr& io_service,
122 const TlsContextPtr& tls_context,
123 const ConnectionPoolPtr& conn_pool,
124 const Url& url);
125
127 ~Connection();
128
147 void doTransaction(const HttpRequestPtr& request,
148 const HttpResponsePtr& response,
149 const long request_timeout,
150 const HttpClient::RequestHandler& callback,
151 const HttpClient::ConnectHandler& connect_callback,
152 const HttpClient::HandshakeHandler& handshake_callback,
153 const HttpClient::CloseHandler& close_callback);
154
156 void close();
157
161 bool isTransactionOngoing() const {
162 return (started_);
163 }
164
168 bool isClosed() const {
169 return (closed_);
170 }
171
176 void isClosedByPeer();
177
183 bool isMySocket(int socket_fd) const;
184
200 bool checkPrematureTimeout(const uint64_t transid);
201
202private:
203
224 void doTransactionInternal(const HttpRequestPtr& request,
225 const HttpResponsePtr& response,
226 const long request_timeout,
227 const HttpClient::RequestHandler& callback,
228 const HttpClient::ConnectHandler& connect_callback,
229 const HttpClient::HandshakeHandler& handshake_callback,
230 const HttpClient::CloseHandler& close_callback);
231
235 void closeInternal();
236
243 void isClosedByPeerInternal();
244
262 bool checkPrematureTimeoutInternal(const uint64_t transid);
263
270 void resetState();
271
281 void terminate(const boost::system::error_code& ec,
282 const std::string& parsing_error = "");
283
295 void terminateInternal(const boost::system::error_code& ec,
296 const std::string& parsing_error = "");
297
304 bool runParser(const boost::system::error_code& ec, size_t length);
305
314 bool runParserInternal(const boost::system::error_code& ec, size_t length);
315
319 void scheduleTimer(const long request_timeout);
320
326 void doHandshake(const uint64_t transid);
327
333 void doSend(const uint64_t transid);
334
340 void doReceive(const uint64_t transid);
341
352 void connectCallback(HttpClient::ConnectHandler connect_callback,
353 const uint64_t transid,
354 const boost::system::error_code& ec);
355
365 void handshakeCallback(HttpClient::HandshakeHandler handshake_callback,
366 const uint64_t transid,
367 const boost::system::error_code& ec);
368
379 void sendCallback(const uint64_t transid, const boost::system::error_code& ec,
380 size_t length);
381
388 void receiveCallback(const uint64_t transid, const boost::system::error_code& ec,
389 size_t length);
390
392 void timerCallback();
393
403 void closeCallback(const bool clear = false);
404
406 IOServicePtr io_service_;
407
412 boost::weak_ptr<ConnectionPool> conn_pool_;
413
415 Url url_;
416
418 TlsContextPtr tls_context_;
419
421 std::shared_ptr<TCPSocket<SocketCallback>> tcp_socket_;
422
424 std::shared_ptr<TLSSocket<SocketCallback>> tls_socket_;
425
427 IntervalTimerPtr timer_;
428
430 HttpRequestPtr current_request_;
431
433 HttpResponsePtr current_response_;
434
436 HttpResponseParserPtr parser_;
437
439 HttpClient::RequestHandler current_callback_;
440
442 std::string buf_;
443
445 size_t position_;
446
448 std::array<char, 32768> input_buf_;
449
451 uint64_t current_transid_;
452
454 HttpClient::HandshakeHandler handshake_callback_;
455
457 HttpClient::CloseHandler close_callback_;
458
460 std::atomic<bool> started_;
461
463 std::atomic<bool> need_handshake_;
464
466 std::atomic<bool> closed_;
467
469 std::mutex mutex_;
470};
471
473typedef boost::shared_ptr<Connection> ConnectionPtr;
474
482class ConnectionPool : public boost::enable_shared_from_this<ConnectionPool> {
483public:
484
491 explicit ConnectionPool(const IOServicePtr& io_service, size_t max_url_connections)
492 : io_service_(io_service), destinations_(), pool_mutex_(),
493 max_url_connections_(max_url_connections) {
494 }
495
499 ~ConnectionPool() {
500 closeAll();
501 }
502
508 void processNextRequest(const Url& url, const TlsContextPtr& tls_context) {
509 if (MultiThreadingMgr::instance().getMode()) {
510 std::lock_guard<std::mutex> lk(pool_mutex_);
511 return (processNextRequestInternal(url, tls_context));
512 } else {
513 return (processNextRequestInternal(url, tls_context));
514 }
515 }
516
522 void postProcessNextRequest(const Url& url,
523 const TlsContextPtr& tls_context) {
524 io_service_->post(std::bind(&ConnectionPool::processNextRequest,
525 shared_from_this(), url, tls_context));
526 }
527
548 void queueRequest(const Url& url,
549 const TlsContextPtr& tls_context,
550 const HttpRequestPtr& request,
551 const HttpResponsePtr& response,
552 const long request_timeout,
553 const HttpClient::RequestHandler& request_callback,
554 const HttpClient::ConnectHandler& connect_callback,
555 const HttpClient::HandshakeHandler& handshake_callback,
556 const HttpClient::CloseHandler& close_callback) {
557 if (MultiThreadingMgr::instance().getMode()) {
558 std::lock_guard<std::mutex> lk(pool_mutex_);
559 return (queueRequestInternal(url, tls_context, request, response,
560 request_timeout, request_callback,
561 connect_callback, handshake_callback,
562 close_callback));
563 } else {
564 return (queueRequestInternal(url, tls_context, request, response,
565 request_timeout, request_callback,
566 connect_callback, handshake_callback,
567 close_callback));
568 }
569 }
570
573 void closeAll() {
574 if (MultiThreadingMgr::instance().getMode()) {
575 std::lock_guard<std::mutex> lk(pool_mutex_);
576 closeAllInternal();
577 } else {
578 closeAllInternal();
579 }
580 }
581
594 void closeIfOutOfBand(int socket_fd) {
595 if (MultiThreadingMgr::instance().getMode()) {
596 std::lock_guard<std::mutex> lk(pool_mutex_);
597 closeIfOutOfBandInternal(socket_fd);
598 } else {
599 closeIfOutOfBandInternal(socket_fd);
600 }
601 }
602
603private:
604
612 void processNextRequestInternal(const Url& url,
613 const TlsContextPtr& tls_context) {
614 // Check if there is a queue for this URL. If there is no queue, there
615 // is no request queued either.
616 DestinationPtr destination = findDestination(url, tls_context);
617 if (destination) {
618 // Remove closed connections.
619 destination->garbageCollectConnections();
620 if (!destination->queueEmpty()) {
621 // We have at least one queued request. Do we have an
622 // idle connection?
623 ConnectionPtr connection = destination->getIdleConnection();
624 if (!connection) {
625 // No idle connections.
626 if (destination->connectionsFull()) {
627 return;
628 }
629 // Room to make another connection with this destination,
630 // so make one.
631 connection.reset(new Connection(io_service_, tls_context,
632 shared_from_this(), url));
633 destination->addConnection(connection);
634 }
635
636 // Dequeue the oldest request and start a transaction for it using
637 // the idle connection.
638 RequestDescriptor desc = destination->popNextRequest();
639 connection->doTransaction(desc.request_, desc.response_,
640 desc.request_timeout_, desc.callback_,
641 desc.connect_callback_,
642 desc.handshake_callback_,
643 desc.close_callback_);
644 }
645 }
646 }
647
670 void queueRequestInternal(const Url& url,
671 const TlsContextPtr& tls_context,
672 const HttpRequestPtr& request,
673 const HttpResponsePtr& response,
674 const long request_timeout,
675 const HttpClient::RequestHandler& request_callback,
676 const HttpClient::ConnectHandler& connect_callback,
677 const HttpClient::HandshakeHandler& handshake_callback,
678 const HttpClient::CloseHandler& close_callback) {
679 ConnectionPtr connection;
680 // Find the destination for the requested URL.
681 DestinationPtr destination = findDestination(url, tls_context);
682 if (destination) {
683 // Remove closed connections.
684 destination->garbageCollectConnections();
685 // Found it, look for an idle connection.
686 connection = destination->getIdleConnection();
687 } else {
688 // Doesn't exist yet so it's a new destination.
689 destination = addDestination(url, tls_context);
690 }
691
692 if (!connection) {
693 if (destination->connectionsFull()) {
694 // All connections busy, queue it.
695 destination->pushRequest(RequestDescriptor(request, response,
696 request_timeout,
697 request_callback,
698 connect_callback,
699 handshake_callback,
700 close_callback));
701 return;
702 }
703
704 // Room to make another connection with this destination, so make one.
705 connection.reset(new Connection(io_service_, tls_context,
706 shared_from_this(), url));
707 destination->addConnection(connection);
708 }
709
710 // Use the connection to start the transaction.
711 connection->doTransaction(request, response, request_timeout, request_callback,
712 connect_callback, handshake_callback, close_callback);
713 }
714
719 void closeAllInternal() {
720 for (auto const& destination : destinations_) {
721 destination.second->closeAllConnections();
722 }
723
724 destinations_.clear();
725 }
726
741 void closeIfOutOfBandInternal(int socket_fd) {
742 for (auto const& destination : destinations_) {
743 // First we look for a connection with the socket.
744 ConnectionPtr connection = destination.second->findBySocketFd(socket_fd);
745 if (connection) {
746 if (!connection->isTransactionOngoing()) {
747 // Socket has no transaction, so any ready event is
748 // out-of-band (other end probably closed), so
749 // let's close it. Note we do not remove any queued
750 // requests, as this might somehow be occurring in
751 // between them.
752 destination.second->closeConnection(connection);
753 }
754
755 return;
756 }
757 }
758 }
759
762 struct RequestDescriptor {
776 RequestDescriptor(const HttpRequestPtr& request,
777 const HttpResponsePtr& response,
778 const long& request_timeout,
779 const HttpClient::RequestHandler& callback,
780 const HttpClient::ConnectHandler& connect_callback,
781 const HttpClient::HandshakeHandler& handshake_callback,
782 const HttpClient::CloseHandler& close_callback)
783 : request_(request), response_(response),
784 request_timeout_(request_timeout), callback_(callback),
785 connect_callback_(connect_callback),
786 handshake_callback_(handshake_callback),
787 close_callback_(close_callback) {
788 }
789
791 HttpRequestPtr request_;
792
794 HttpResponsePtr response_;
795
797 long request_timeout_;
798
801
803 HttpClient::ConnectHandler connect_callback_;
804
806 HttpClient::HandshakeHandler handshake_callback_;
807
809 HttpClient::CloseHandler close_callback_;
810 };
811
813 typedef std::pair<Url, TlsContextPtr> DestinationDescriptor;
814
816 class Destination {
817 public:
819 const size_t QUEUE_SIZE_THRESHOLD = 2048;
821 const int QUEUE_WARN_SECS = 5;
822
829 Destination(Url const& url, TlsContextPtr tls_context, size_t max_connections)
830 : url_(url), tls_context_(tls_context),
831 max_connections_(max_connections), connections_(), queue_(),
832 last_queue_warn_time_(min_date_time), last_queue_size_(0) {
833 }
834
836 ~Destination() {
837 closeAllConnections();
838 }
839
847 void addConnection(ConnectionPtr connection) {
848 if (connectionsFull()) {
849 isc_throw(BadValue, "URL: " << url_.toText()
850 << ", already at maximum connections: "
851 << max_connections_);
852 }
853
854 connections_.push_back(connection);
855 }
856
861 void closeConnection(ConnectionPtr connection) {
862 for (auto it = connections_.begin(); it != connections_.end(); ++it) {
863 if (*it == connection) {
864 (*it)->close();
865 connections_.erase(it);
866 break;
867 }
868 }
869 }
870
873 void closeAllConnections() {
874 // Flush the queue.
875 while (!queue_.empty()) {
876 queue_.pop();
877 }
878
879 for (auto const& connection : connections_) {
880 connection->close();
881 }
882
883 connections_.clear();
884 }
885
909 void garbageCollectConnections() {
910 for (auto it = connections_.begin(); it != connections_.end();) {
911 (*it)->isClosedByPeer();
912 if (!(*it)->isClosed()) {
913 ++it;
914 } else {
915 it = connections_.erase(it);
916 }
917 }
918 }
919
931 ConnectionPtr getIdleConnection() {
932 for (auto const& connection : connections_) {
933 if (!connection->isTransactionOngoing() &&
934 !connection->isClosed()) {
935 return (connection);
936 }
937 }
938
939 return (ConnectionPtr());
940 }
941
948 ConnectionPtr findBySocketFd(int socket_fd) {
949 for (auto const& connection : connections_) {
950 if (connection->isMySocket(socket_fd)) {
951 return (connection);
952 }
953 }
954
955 return (ConnectionPtr());
956 }
957
961 bool connectionsEmpty() {
962 return (connections_.empty());
963 }
964
968 bool connectionsFull() {
969 return (connections_.size() >= max_connections_);
970 }
971
975 size_t connectionCount() {
976 return (connections_.size());
977 }
978
982 size_t getMaxConnections() const {
983 return (max_connections_);
984 }
985
989 bool queueEmpty() const {
990 return (queue_.empty());
991 }
992
999 void pushRequest(RequestDescriptor const& desc) {
1000 queue_.push(desc);
1001 size_t size = queue_.size();
1002 // If the queue size is larger than the threshold and growing, issue a
1003 // periodic warning.
1004 if ((size > QUEUE_SIZE_THRESHOLD) && (size > last_queue_size_)) {
1005 ptime now = microsec_clock::universal_time();
1006 if ((now - last_queue_warn_time_) > seconds(QUEUE_WARN_SECS)) {
1008 .arg(url_.toText())
1009 .arg(size);
1010 // Remember the last time we warned.
1011 last_queue_warn_time_ = now;
1012 }
1013 }
1014
1015 // Remember the previous size.
1016 last_queue_size_ = size;
1017 }
1018
1022 RequestDescriptor popNextRequest() {
1023 if (queue_.empty()) {
1024 isc_throw(InvalidOperation, "cannot pop, queue is empty");
1025 }
1026
1027 RequestDescriptor desc = queue_.front();
1028 queue_.pop();
1029 return (desc);
1030 }
1031
1032 private:
1034 Url url_;
1035
1037 TlsContextPtr tls_context_;
1038
1040 size_t max_connections_;
1041
1043 std::list<ConnectionPtr> connections_;
1044
1046 std::queue<RequestDescriptor> queue_;
1047
1049 ptime last_queue_warn_time_;
1050
1052 size_t last_queue_size_;
1053 };
1054
1056 typedef boost::shared_ptr<Destination> DestinationPtr;
1057
1065 DestinationPtr addDestination(const Url& url,
1066 const TlsContextPtr& tls_context) {
1067 const DestinationDescriptor& desc = std::make_pair(url, tls_context);
1068 DestinationPtr destination(new Destination(url, tls_context,
1069 max_url_connections_));
1070 destinations_[desc] = destination;
1071 return (destination);
1072 }
1073
1082 DestinationPtr findDestination(const Url& url,
1083 const TlsContextPtr& tls_context) const {
1084 const DestinationDescriptor& desc = std::make_pair(url, tls_context);
1085 auto it = destinations_.find(desc);
1086 if (it != destinations_.end()) {
1087 return (it->second);
1088 }
1089
1090 return (DestinationPtr());
1091 }
1092
1104 void removeDestination(const Url& url,
1105 const TlsContextPtr& tls_context) {
1106 const DestinationDescriptor& desc = std::make_pair(url, tls_context);
1107 auto it = destinations_.find(desc);
1108 if (it != destinations_.end()) {
1109 it->second->closeAllConnections();
1110 destinations_.erase(it);
1111 }
1112 }
1113
1115 IOServicePtr io_service_;
1116
1118 std::map<DestinationDescriptor, DestinationPtr> destinations_;
1119
1121 std::mutex pool_mutex_;
1122
1124 size_t max_url_connections_;
1125};
1126
1127Connection::Connection(const IOServicePtr& io_service,
1128 const TlsContextPtr& tls_context,
1129 const ConnectionPoolPtr& conn_pool,
1130 const Url& url)
1131 : io_service_(io_service), conn_pool_(conn_pool), url_(url),
1132 tls_context_(tls_context), tcp_socket_(), tls_socket_(),
1133 timer_(new IntervalTimer(io_service)), current_request_(),
1134 current_response_(), parser_(), current_callback_(), buf_(),
1135 position_(0), input_buf_(), current_transid_(0), close_callback_(),
1136 started_(false), need_handshake_(false), closed_(false) {
1137 if (!tls_context) {
1138 tcp_socket_.reset(new asiolink::TCPSocket<SocketCallback>(io_service));
1139 } else {
1140 tls_socket_.reset(new asiolink::TLSSocket<SocketCallback>(io_service,
1141 tls_context));
1142 need_handshake_ = true;
1143 }
1144}
1145
1146Connection::~Connection() {
1147 close();
1148}
1149
1150void
1151Connection::resetState() {
1152 started_ = false;
1153 current_request_.reset();
1154 current_response_.reset();
1155 parser_.reset();
1156 current_callback_ = HttpClient::RequestHandler();
1157}
1158
1159void
1160Connection::closeCallback(const bool clear) {
1161 if (close_callback_) {
1162 try {
1163 if (tcp_socket_) {
1164 close_callback_(tcp_socket_->getNative());
1165 } else if (tls_socket_) {
1166 close_callback_(tls_socket_->getNative());
1167 } else {
1168 isc_throw(Unexpected,
1169 "internal error: can't find a socket to close");
1170 }
1171 } catch (...) {
1173 }
1174 }
1175
1176 if (clear) {
1177 close_callback_ = HttpClient::CloseHandler();
1178 }
1179}
1180
1181void
1182Connection::isClosedByPeer() {
1183 // This method applies only to idle connections.
1184 if (started_ || closed_) {
1185 return;
1186 }
1187 // This code was guarded by a lock so keep this.
1188 if (MultiThreadingMgr::instance().getMode()) {
1189 std::lock_guard<std::mutex> lk(mutex_);
1190 isClosedByPeerInternal();
1191 } else {
1192 isClosedByPeerInternal();
1193 }
1194}
1195
1196void
1197Connection::isClosedByPeerInternal() {
1198 // If the socket is open we check if it is possible to transmit
1199 // the data over this socket by reading from it with message
1200 // peeking. If the socket is not usable, we close it and then
1201 // re-open it. There is a narrow window of time between checking
1202 // the socket usability and actually transmitting the data over
1203 // this socket, when the peer may close the connection. In this
1204 // case we'll need to re-transmit but we don't handle it here.
1205 if (tcp_socket_) {
1206 if (tcp_socket_->getASIOSocket().is_open() &&
1207 !tcp_socket_->isUsable()) {
1208 closeCallback();
1209 closed_ = true;
1210 tcp_socket_->close();
1211 }
1212 } else if (tls_socket_) {
1213 if (tls_socket_->getASIOSocket().is_open() &&
1214 !tls_socket_->isUsable()) {
1215 closeCallback();
1216 closed_ = true;
1217 tls_socket_->close();
1218 }
1219 } else {
1220 isc_throw(Unexpected, "internal error: can't find the sending socket");
1221 }
1222}
1223
1224void
1225Connection::doTransaction(const HttpRequestPtr& request,
1226 const HttpResponsePtr& response,
1227 const long request_timeout,
1228 const HttpClient::RequestHandler& callback,
1229 const HttpClient::ConnectHandler& connect_callback,
1230 const HttpClient::HandshakeHandler& handshake_callback,
1231 const HttpClient::CloseHandler& close_callback) {
1232 if (MultiThreadingMgr::instance().getMode()) {
1233 std::lock_guard<std::mutex> lk(mutex_);
1234 doTransactionInternal(request, response, request_timeout,
1235 callback, connect_callback, handshake_callback,
1236 close_callback);
1237 } else {
1238 doTransactionInternal(request, response, request_timeout,
1239 callback, connect_callback, handshake_callback,
1240 close_callback);
1241 }
1242}
1243
1244void
1245Connection::doTransactionInternal(const HttpRequestPtr& request,
1246 const HttpResponsePtr& response,
1247 const long request_timeout,
1248 const HttpClient::RequestHandler& callback,
1249 const HttpClient::ConnectHandler& connect_callback,
1250 const HttpClient::HandshakeHandler& handshake_callback,
1251 const HttpClient::CloseHandler& close_callback) {
1252 try {
1253 started_ = true;
1254 current_request_ = request;
1255 current_response_ = response;
1256 parser_.reset(new HttpResponseParser(*current_response_));
1257 parser_->initModel();
1258 current_callback_ = callback;
1259 handshake_callback_ = handshake_callback;
1260 close_callback_ = close_callback;
1261
1262 // Starting new transaction. Generate new transaction id.
1263 ++current_transid_;
1264
1265 buf_ = request->toString();
1266 if (buf_.empty()) {
1267 isc_throw(Unexpected, "empty request");
1268 }
1269 position_ = 0;
1270
1273 .arg(request->toBriefString())
1274 .arg(url_.toText());
1275
1278 .arg(url_.toText())
1279 .arg(HttpMessageParserBase::logFormatHttpMessage(request->toString(),
1280 MAX_LOGGED_MESSAGE_SIZE));
1281
1282 // Setup request timer.
1283 scheduleTimer(request_timeout);
1284
1288 TCPEndpoint endpoint(url_.getStrippedHostname(),
1289 static_cast<unsigned short>(url_.getPort()));
1290 SocketCallback socket_cb(std::bind(&Connection::connectCallback, shared_from_this(),
1291 connect_callback, current_transid_,
1292 ph::_1));
1293
1294 // Establish new connection or use existing connection.
1295 if (tcp_socket_) {
1296 tcp_socket_->open(&endpoint, socket_cb);
1297 return;
1298 }
1299 if (tls_socket_) {
1300 tls_socket_->open(&endpoint, socket_cb);
1301 return;
1302 }
1303
1304 // Should never reach this point.
1305 isc_throw(Unexpected, "internal error: can't find a socket to open");
1306
1307 } catch (const std::exception& ex) {
1308 // Re-throw with the expected exception type.
1309 isc_throw(HttpClientError, ex.what());
1310 }
1311}
1312
1313void
1314Connection::close() {
1315 if (MultiThreadingMgr::instance().getMode()) {
1316 std::lock_guard<std::mutex> lk(mutex_);
1317 return (closeInternal());
1318 } else {
1319 return (closeInternal());
1320 }
1321}
1322
1323void
1324Connection::closeInternal() {
1325 // Pass in true to discard the callback.
1326 closeCallback(true);
1327
1328 closed_ = true;
1329 timer_->cancel();
1330 if (tcp_socket_) {
1331 tcp_socket_->close();
1332 }
1333 if (tls_socket_) {
1334 tls_socket_->close();
1335 }
1336
1337 resetState();
1338}
1339
1340bool
1341Connection::isMySocket(int socket_fd) const {
1342 if (tcp_socket_) {
1343 return (tcp_socket_->getNative() == socket_fd);
1344 } else if (tls_socket_) {
1345 return (tls_socket_->getNative() == socket_fd);
1346 }
1347 // Should never reach this point.
1348 std::cerr << "internal error: can't find my socket\n";
1349 return (false);
1350}
1351
1352bool
1353Connection::checkPrematureTimeout(const uint64_t transid) {
1354 if (MultiThreadingMgr::instance().getMode()) {
1355 std::lock_guard<std::mutex> lk(mutex_);
1356 return (checkPrematureTimeoutInternal(transid));
1357 } else {
1358 return (checkPrematureTimeoutInternal(transid));
1359 }
1360}
1361
1362bool
1363Connection::checkPrematureTimeoutInternal(const uint64_t transid) {
1364 // If there is no transaction but the handlers are invoked it means
1365 // that the last transaction in the queue timed out prematurely.
1366 // Also, if there is a transaction in progress but the ID of that
1367 // transaction doesn't match the one associated with the handler it,
1368 // also means that the transaction timed out prematurely.
1369 if (!isTransactionOngoing() || (transid != current_transid_)) {
1371 .arg(isTransactionOngoing())
1372 .arg(transid)
1373 .arg(current_transid_);
1374 return (true);
1375 }
1376
1377 return (false);
1378}
1379
1380void
1381Connection::terminate(const boost::system::error_code& ec,
1382 const std::string& parsing_error) {
1383 if (MultiThreadingMgr::instance().getMode()) {
1384 std::lock_guard<std::mutex> lk(mutex_);
1385 terminateInternal(ec, parsing_error);
1386 } else {
1387 terminateInternal(ec, parsing_error);
1388 }
1389}
1390
1391void
1392Connection::terminateInternal(const boost::system::error_code& ec,
1393 const std::string& parsing_error) {
1394 HttpResponsePtr response;
1395 if (isTransactionOngoing()) {
1396
1397 timer_->cancel();
1398 if (tcp_socket_) {
1399 tcp_socket_->cancel();
1400 }
1401 if (tls_socket_) {
1402 tls_socket_->cancel();
1403 }
1404
1405 if (!ec && current_response_->isFinalized()) {
1406 response = current_response_;
1407
1410 .arg(url_.toText());
1411
1414 .arg(url_.toText())
1415 .arg(parser_ ?
1416 parser_->getBufferAsString(MAX_LOGGED_MESSAGE_SIZE) :
1417 "[HttpResponseParser is null]");
1418
1419 } else {
1420 std::string err = parsing_error.empty() ? ec.message() :
1421 parsing_error;
1422
1425 .arg(url_.toText())
1426 .arg(err);
1427
1428 // Only log the details if we have received anything and tried
1429 // to parse it.
1430 if (!parsing_error.empty()) {
1433 .arg(url_.toText())
1434 .arg(parser_ ?
1435 parser_->getBufferAsString(MAX_LOGGED_MESSAGE_SIZE) :
1436 "[HttpResponseParser is null]");
1437 }
1438 }
1439
1440 try {
1441 // The callback should take care of its own exceptions but one
1442 // never knows.
1443 if (MultiThreadingMgr::instance().getMode()) {
1444 UnlockGuard<std::mutex> lock(mutex_);
1445 current_callback_(ec, response, parsing_error);
1446 } else {
1447 current_callback_(ec, response, parsing_error);
1448 }
1449 } catch (...) {
1450 }
1451
1452 // If we're not requesting connection persistence or the
1453 // connection has timed out, we should close the socket.
1454 if (!closed_ &&
1455 (!current_request_->isPersistent() ||
1456 (ec == boost::asio::error::timed_out))) {
1457 closeInternal();
1458 }
1459
1460 resetState();
1461 }
1462
1463 // Check if there are any requests queued for this destination and start
1464 // another transaction if there is at least one.
1465 ConnectionPoolPtr conn_pool = conn_pool_.lock();
1466 if (conn_pool) {
1467 conn_pool->postProcessNextRequest(url_, tls_context_);
1468 }
1469}
1470
1471void
1472Connection::scheduleTimer(const long request_timeout) {
1473 if (request_timeout > 0) {
1474 timer_->setup(std::bind(&Connection::timerCallback, this), request_timeout,
1476 }
1477}
1478
1479void
1480Connection::doHandshake(const uint64_t transid) {
1481 // Skip the handshake if it is not needed.
1482 if (!need_handshake_) {
1483 doSend(transid);
1484 return;
1485 }
1486
1487 SocketCallback socket_cb(std::bind(&Connection::handshakeCallback,
1488 shared_from_this(),
1489 handshake_callback_,
1490 transid,
1491 ph::_1));
1492 try {
1493 tls_socket_->handshake(socket_cb);
1494
1495 } catch (...) {
1496 terminate(boost::asio::error::not_connected);
1497 }
1498}
1499
1500void
1501Connection::doSend(const uint64_t transid) {
1502 SocketCallback socket_cb(std::bind(&Connection::sendCallback,
1503 shared_from_this(),
1504 transid,
1505 ph::_1,
1506 ph::_2));
1507 try {
1508 // doSend is called only when there is something to send so
1509 // remaining is guaranteed to be greater than 0 and position_
1510 // to point inside the buffer.
1511 size_t remaining = buf_.size() - position_;
1512 if (tcp_socket_) {
1513 tcp_socket_->asyncSend(&buf_[position_], remaining, socket_cb);
1514 return;
1515 }
1516
1517 if (tls_socket_) {
1518 tls_socket_->asyncSend(&buf_[position_], remaining, socket_cb);
1519 return;
1520 }
1521
1522 // Should never reach this point.
1523 std::cerr << "internal error: can't find a socket to send to\n";
1524 isc_throw(Unexpected,
1525 "internal error: can't find a socket to send to");
1526 } catch (...) {
1527 terminate(boost::asio::error::not_connected);
1528 }
1529}
1530
1531void
1532Connection::doReceive(const uint64_t transid) {
1533 TCPEndpoint endpoint;
1534 SocketCallback socket_cb(std::bind(&Connection::receiveCallback,
1535 shared_from_this(),
1536 transid,
1537 ph::_1,
1538 ph::_2));
1539 try {
1540 if (tcp_socket_) {
1541 tcp_socket_->asyncReceive(static_cast<void*>(input_buf_.data()),
1542 input_buf_.size(), 0,
1543 &endpoint, socket_cb);
1544 return;
1545 }
1546 if (tls_socket_) {
1547 tls_socket_->asyncReceive(static_cast<void*>(input_buf_.data()),
1548 input_buf_.size(), 0,
1549 &endpoint, socket_cb);
1550 return;
1551 }
1552 // Should never reach this point.
1553 std::cerr << "internal error: can't find a socket to receive from\n";
1554 isc_throw(Unexpected,
1555 "internal error: can't find a socket to receive from");
1556
1557 } catch (...) {
1558 terminate(boost::asio::error::not_connected);
1559 }
1560}
1561
1562void
1563Connection::connectCallback(HttpClient::ConnectHandler connect_callback,
1564 const uint64_t transid,
1565 const boost::system::error_code& ec) {
1566 if (checkPrematureTimeout(transid)) {
1567 return;
1568 }
1569
1570 // Run user defined connect callback if specified.
1571 if (connect_callback) {
1572 // If the user defined callback indicates that the connection
1573 // should not be continued.
1574 if (tcp_socket_) {
1575 if (!connect_callback(ec, tcp_socket_->getNative())) {
1576 return;
1577 }
1578 } else if (tls_socket_) {
1579 if (!connect_callback(ec, tls_socket_->getNative())) {
1580 return;
1581 }
1582 } else {
1583 // Should never reach this point.
1584 std::cerr << "internal error: can't find a socket to connect\n";
1585 }
1586 }
1587
1588 if (ec && (ec.value() == boost::asio::error::operation_aborted)) {
1589 return;
1590
1591 // In some cases the "in progress" status code may be returned. It doesn't
1592 // indicate an error. Sending the request over the socket is expected to
1593 // be successful. Getting such status appears to be highly dependent on
1594 // the operating system.
1595 } else if (ec &&
1596 (ec.value() != boost::asio::error::in_progress) &&
1597 (ec.value() != boost::asio::error::already_connected)) {
1598 terminate(ec);
1599
1600 } else {
1601 // Start the TLS handshake asynchronously.
1602 doHandshake(transid);
1603 }
1604}
1605
1606void
1607Connection::handshakeCallback(HttpClient::ConnectHandler handshake_callback,
1608 const uint64_t transid,
1609 const boost::system::error_code& ec) {
1610 need_handshake_ = false;
1611 if (checkPrematureTimeout(transid)) {
1612 return;
1613 }
1614
1615 // Run user defined handshake callback if specified.
1616 if (handshake_callback) {
1617 // If the user defined callback indicates that the connection
1618 // should not be continued.
1619 if (tls_socket_) {
1620 if (!handshake_callback(ec, tls_socket_->getNative())) {
1621 return;
1622 }
1623 } else {
1624 // Should never reach this point.
1625 std::cerr << "internal error: can't find TLS socket\n";
1626 }
1627 }
1628
1629 if (ec && (ec.value() == boost::asio::error::operation_aborted)) {
1630 return;
1631 } else if (ec) {
1632 terminate(ec);
1633
1634 } else {
1635 // Start sending the request asynchronously.
1636 doSend(transid);
1637 }
1638}
1639
1640void
1641Connection::sendCallback(const uint64_t transid,
1642 const boost::system::error_code& ec,
1643 size_t length) {
1644 if (checkPrematureTimeout(transid)) {
1645 return;
1646 }
1647
1648 if (ec) {
1649 if (ec.value() == boost::asio::error::operation_aborted) {
1650 return;
1651
1652 // EAGAIN and EWOULDBLOCK don't really indicate an error. The length
1653 // should be 0 in this case but let's be sure.
1654 } else if ((ec.value() == boost::asio::error::would_block) ||
1655 (ec.value() == boost::asio::error::try_again)) {
1656 length = 0;
1657
1658 } else {
1659 // Any other error should cause the transaction to terminate.
1660 terminate(ec);
1661 return;
1662 }
1663 }
1664
1665 // Sending is in progress, so push back the timeout.
1666 scheduleTimer(timer_->getInterval());
1667
1668 // If any data have been sent, remove it from the buffer and only leave the
1669 // portion that still has to be sent.
1670 if (length >= buf_.size() - position_) {
1671 position_ = buf_.size();
1672 } else {
1673 position_ += length;
1674 }
1675
1676 // If there is no more data to be sent, start receiving a response. Otherwise,
1677 // continue sending.
1678 if (position_ == buf_.size()) {
1679 doReceive(transid);
1680
1681 } else {
1682 doSend(transid);
1683 }
1684}
1685
1686void
1687Connection::receiveCallback(const uint64_t transid,
1688 const boost::system::error_code& ec,
1689 size_t length) {
1690 if (checkPrematureTimeout(transid)) {
1691 return;
1692 }
1693
1694 if (ec) {
1695 if (ec.value() == boost::asio::error::operation_aborted) {
1696 return;
1697 }
1698 // EAGAIN and EWOULDBLOCK don't indicate an error in this case. All
1699 // other errors should terminate the transaction.
1700 if ((ec.value() != boost::asio::error::try_again) &&
1701 (ec.value() != boost::asio::error::would_block)) {
1702 terminate(ec);
1703 return;
1704
1705 } else {
1706 // For EAGAIN and EWOULDBLOCK the length should be 0 anyway,
1707 // but let's make sure.
1708 length = 0;
1709 }
1710 }
1711
1712 // Receiving is in progress, so push back the timeout.
1713 scheduleTimer(timer_->getInterval());
1714
1715 if (runParser(ec, length)) {
1716 doReceive(transid);
1717 }
1718}
1719
1720bool
1721Connection::runParser(const boost::system::error_code& ec, size_t length) {
1722 if (MultiThreadingMgr::instance().getMode()) {
1723 std::lock_guard<std::mutex> lk(mutex_);
1724 return (runParserInternal(ec, length));
1725 } else {
1726 return (runParserInternal(ec, length));
1727 }
1728}
1729
1730bool
1731Connection::runParserInternal(const boost::system::error_code& ec,
1732 size_t length) {
1733 // If we have received any data, let's feed the parser with it.
1734 if (length != 0) {
1735 parser_->postBuffer(static_cast<void*>(input_buf_.data()), length);
1736 parser_->poll();
1737 }
1738
1739 // If the parser still needs data, let's schedule another receive.
1740 if (parser_->needData()) {
1741 return (true);
1742
1743 } else if (parser_->httpParseOk()) {
1744 // No more data needed and parsing has been successful so far. Let's
1745 // try to finalize the response parsing.
1746 try {
1747 current_response_->finalize();
1748 terminateInternal(ec);
1749
1750 } catch (const std::exception& ex) {
1751 // If there is an error here, we need to return the error message.
1752 terminateInternal(ec, ex.what());
1753 }
1754
1755 } else {
1756 // Parsing was unsuccessful. Let's pass the error message held in the
1757 // parser.
1758 terminateInternal(ec, parser_->getErrorMessage());
1759 }
1760
1761 return (false);
1762}
1763
1764void
1765Connection::timerCallback() {
1766 // Request timeout occurred.
1767 terminate(boost::asio::error::timed_out);
1768}
1769
1770}
1771
1772namespace isc {
1773namespace http {
1774
1777public:
1801 HttpClientImpl(const IOServicePtr& io_service, size_t thread_pool_size = 0,
1802 bool defer_thread_start = false)
1803 : thread_pool_size_(thread_pool_size), thread_pool_() {
1804 if (thread_pool_size_ > 0) {
1805 // Create our own private IOService.
1806 thread_io_service_.reset(new IOService());
1807
1808 // Create the connection pool. Note that we use the thread_pool_size
1809 // as the maximum connections per URL value.
1810 conn_pool_.reset(new ConnectionPool(thread_io_service_, thread_pool_size_));
1811
1812 // Create the thread pool.
1813 thread_pool_.reset(new IoServiceThreadPool(thread_io_service_, thread_pool_size_,
1814 defer_thread_start));
1815
1817 .arg(thread_pool_size_);
1818 } else {
1819 // Single-threaded mode: use the caller's IOService,
1820 // one connection per URL.
1821 conn_pool_.reset(new ConnectionPool(io_service, 1));
1822 }
1823 }
1824
1829 stop();
1830 }
1831
1838 if (thread_pool_) {
1839 thread_pool_->checkPausePermissions();
1840 }
1841 }
1842
1844 void start() {
1845 if (thread_pool_) {
1846 thread_pool_->run();
1847 }
1848 }
1849
1852 void stop() {
1853 // Close all the connections.
1854 conn_pool_->closeAll();
1855
1856 // Stop the thread pool.
1857 if (thread_pool_) {
1858 thread_pool_->stop();
1859 }
1860
1861 if (thread_io_service_) {
1862 thread_io_service_->stopAndPoll();
1863 thread_io_service_->stop();
1864 }
1865 }
1866
1871 void pause() {
1872 if (!thread_pool_) {
1873 isc_throw(InvalidOperation, "HttpClient::pause - no thread pool");
1874 }
1875
1876 // Pause the thread pool.
1877 thread_pool_->pause();
1878 }
1879
1884 void resume() {
1885 if (!thread_pool_) {
1886 isc_throw(InvalidOperation, "HttpClient::resume - no thread pool");
1887 }
1888
1889 // Resume running the thread pool.
1890 thread_pool_->run();
1891 }
1892
1897 bool isRunning() {
1898 if (thread_pool_) {
1899 return (thread_pool_->isRunning());
1900 }
1901
1902 return (false);
1903 }
1904
1909 bool isStopped() {
1910 if (thread_pool_) {
1911 return (thread_pool_->isStopped());
1912 }
1913
1914 return (false);
1915 }
1916
1921 bool isPaused() {
1922 if (thread_pool_) {
1923 return (thread_pool_->isPaused());
1924 }
1925
1926 return (false);
1927 }
1928
1934 return (thread_io_service_);
1935 };
1936
1941 return (thread_pool_size_);
1942 }
1943
1947 uint16_t getThreadCount() {
1948 if (!thread_pool_) {
1949 return (0);
1950 }
1951 return (thread_pool_->getThreadCount());
1952 }
1953
1955 ConnectionPoolPtr conn_pool_;
1956
1957private:
1958
1960 size_t thread_pool_size_;
1961
1963 asiolink::IOServicePtr thread_io_service_;
1964
1967 IoServiceThreadPoolPtr thread_pool_;
1968};
1969
1970HttpClient::HttpClient(const IOServicePtr& io_service, bool multi_threading_enabled,
1971 size_t thread_pool_size, bool defer_thread_start) {
1972 if (!multi_threading_enabled && thread_pool_size) {
1974 "HttpClient thread_pool_size must be zero "
1975 "when Kea core multi-threading is disabled");
1976 }
1977
1978 impl_.reset(new HttpClientImpl(io_service, thread_pool_size,
1979 defer_thread_start));
1980}
1981
1983 impl_->stop();
1984}
1985
1986void
1988 const TlsContextPtr& tls_context,
1989 const HttpRequestPtr& request,
1990 const HttpResponsePtr& response,
1991 const HttpClient::RequestHandler& request_callback,
1992 const HttpClient::RequestTimeout& request_timeout,
1993 const HttpClient::ConnectHandler& connect_callback,
1994 const HttpClient::HandshakeHandler& handshake_callback,
1995 const HttpClient::CloseHandler& close_callback) {
1996 if (!url.isValid()) {
1997 isc_throw(HttpClientError, "invalid URL specified for the HTTP client");
1998 }
1999
2000 if ((url.getScheme() == Url::Scheme::HTTPS) && !tls_context) {
2001 isc_throw(HttpClientError, "HTTPS URL scheme but no TLS context");
2002 }
2003
2004 if (!request) {
2005 isc_throw(HttpClientError, "HTTP request must not be null");
2006 }
2007
2008 if (!response) {
2009 isc_throw(HttpClientError, "HTTP response must not be null");
2010 }
2011
2012 if (!request_callback) {
2013 isc_throw(HttpClientError, "callback for HTTP transaction must not be null");
2014 }
2015
2016 impl_->conn_pool_->queueRequest(url, tls_context, request, response,
2017 request_timeout.value_,
2018 request_callback, connect_callback,
2019 handshake_callback, close_callback);
2020}
2021
2022void
2024 return (impl_->conn_pool_->closeIfOutOfBand(socket_fd));
2025}
2026
2027void
2029 impl_->start();
2030}
2031
2032void
2034 impl_->checkPermissions();
2035}
2036
2037void
2039 impl_->pause();
2040}
2041
2042void
2044 impl_->resume();
2045}
2046
2047void
2049 impl_->stop();
2050}
2051
2052const IOServicePtr
2054 return (impl_->getThreadIOService());
2055}
2056
2057uint16_t
2059 return (impl_->getThreadPoolSize());
2060}
2061
2062uint16_t
2064 return (impl_->getThreadCount());
2065}
2066
2067bool
2069 return (impl_->isRunning());
2070}
2071
2072bool
2074 return (impl_->isStopped());
2075}
2076
2077bool
2079 return (impl_->isPaused());
2080}
2081
2082} // end of namespace isc::http
2083} // end of namespace isc
A generic exception that is thrown if a function is called in a prohibited way.
A generic error raised by the HttpClient class.
HttpClient implementation.
Definition client.cc:1776
ConnectionPoolPtr conn_pool_
Holds a pointer to the connection pool.
Definition client.cc:1955
uint16_t getThreadCount()
Fetches the number of threads in the pool.
Definition client.cc:1947
~HttpClientImpl()
Destructor.
Definition client.cc:1828
void pause()
Pauses the client's thread pool.
Definition client.cc:1871
uint16_t getThreadPoolSize()
Fetches the maximum size of the thread pool.
Definition client.cc:1940
void start()
Starts running the client's thread pool, if multi-threaded.
Definition client.cc:1844
void stop()
Close all connections, and if multi-threaded, stops the client's thread pool.
Definition client.cc:1852
asiolink::IOServicePtr getThreadIOService()
Fetches the internal IOService used in multi-threaded mode.
Definition client.cc:1933
void checkPermissions()
Check if the current thread can perform thread pool state transition.
Definition client.cc:1837
bool isPaused()
Indicates if the thread pool is paused.
Definition client.cc:1921
void resume()
Resumes running the client's thread pool.
Definition client.cc:1884
HttpClientImpl(const IOServicePtr &io_service, size_t thread_pool_size=0, bool defer_thread_start=false)
Constructor.
Definition client.cc:1801
bool isStopped()
Indicates if the thread pool is stopped.
Definition client.cc:1909
bool isRunning()
Indicates if the thread pool is running.
Definition client.cc:1897
uint16_t getThreadCount() const
Fetches the number of threads in the pool.
Definition client.cc:2063
bool isRunning()
Indicates if the thread pool is running.
Definition client.cc:2068
HttpClient(const asiolink::IOServicePtr &io_service, bool multi_threading_enabled, size_t thread_pool_size=0, bool defer_thread_start=false)
Constructor.
Definition client.cc:1970
std::function< void(const boost::system::error_code &, const HttpResponsePtr &, const std::string &)> RequestHandler
Callback type used in call to HttpClient::asyncSendRequest.
void stop()
Halts client-side IO activity.
Definition client.cc:2048
bool isPaused()
Indicates if the thread pool is paused.
Definition client.cc:2078
void pause()
Pauses the client's thread pool.
Definition client.cc:2038
std::function< void(const int)> CloseHandler
Optional handler invoked when client closes the connection to the server.
const asiolink::IOServicePtr getThreadIOService() const
Fetches a pointer to the internal IOService used to drive the thread-pool in multi-threaded mode.
Definition client.cc:2053
void start()
Starts running the client's thread pool, if multi-threaded.
Definition client.cc:2028
std::function< bool(const boost::system::error_code &, const int)> ConnectHandler
Optional handler invoked when client connects to the server.
uint16_t getThreadPoolSize() const
Fetches the maximum size of the thread pool.
Definition client.cc:2058
std::function< bool(const boost::system::error_code &, const int)> HandshakeHandler
Optional handler invoked when client performs the TLS handshake with the server.
void closeIfOutOfBand(int socket_fd)
Closes a connection if it has an out-of-band socket event.
Definition client.cc:2023
~HttpClient()
Destructor.
Definition client.cc:1982
void resume()
Resumes running the client's thread pool.
Definition client.cc:2043
void asyncSendRequest(const Url &url, const asiolink::TlsContextPtr &tls_context, const HttpRequestPtr &request, const HttpResponsePtr &response, const RequestHandler &request_callback, const RequestTimeout &request_timeout=RequestTimeout(10000), const ConnectHandler &connect_callback=ConnectHandler(), const HandshakeHandler &handshake_callback=HandshakeHandler(), const CloseHandler &close_callback=CloseHandler())
Queues new asynchronous HTTP request for a given URL.
Definition client.cc:1987
bool isStopped()
Indicates if the thread pool is stopped.
Definition client.cc:2073
void checkPermissions()
Check if the current thread can perform thread pool state transition.
Definition client.cc:2033
static std::string logFormatHttpMessage(const std::string &message, const size_t limit=0)
Formats provided HTTP message for logging.
Represents an URL.
Definition url.h:22
Scheme getScheme() const
Returns parsed scheme.
Definition url.cc:31
bool isValid() const
Checks if the URL is valid.
Definition url.h:49
static MultiThreadingMgr & instance()
Returns a single instance of Multi Threading Manager.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition macros.h:32
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition macros.h:26
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition macros.h:14
const isc::log::MessageID HTTP_CLIENT_MT_STARTED
const isc::log::MessageID HTTP_CONNECTION_CLOSE_CALLBACK_FAILED
const isc::log::MessageID HTTP_BAD_SERVER_RESPONSE_RECEIVED
isc::log::Logger http_logger("http")
Defines the logger used within libkea-http library.
Definition http_log.h:18
const isc::log::MessageID HTTP_SERVER_RESPONSE_RECEIVED
boost::shared_ptr< HttpResponseParser > HttpResponseParserPtr
Pointer to the HttpResponseParser.
const isc::log::MessageID HTTP_CLIENT_REQUEST_SEND
boost::shared_ptr< HttpResponse > HttpResponsePtr
Pointer to the HttpResponse object.
Definition response.h:82
const isc::log::MessageID HTTP_BAD_SERVER_RESPONSE_RECEIVED_DETAILS
const isc::log::MessageID HTTP_CLIENT_REQUEST_SEND_DETAILS
const isc::log::MessageID HTTP_CLIENT_QUEUE_SIZE_GROWING
const isc::log::MessageID HTTP_SERVER_RESPONSE_RECEIVED_DETAILS
boost::shared_ptr< HttpRequest > HttpRequestPtr
Pointer to the HttpRequest object.
Definition request.h:30
const isc::log::MessageID HTTP_PREMATURE_CONNECTION_TIMEOUT_OCCURRED
const int DBGLVL_TRACE_BASIC
Trace basic operations.
const int DBGLVL_TRACE_DETAIL_DATA
Trace data associated with detailed operations.
const int DBGLVL_TRACE_BASIC_DATA
Trace data associated with the basic operations.
const int DBGLVL_TRACE_DETAIL
Trace detailed operations.
std::function< void(boost::system::error_code ec, size_t length)> SocketCallbackFunction
Type of the function implementing a callback invoked by the SocketCallback functor.
Defines the logger used by the top-level component of kea-lfc.
HTTP request/response timeout value.
long value_
Timeout value specified.