Kea 2.5.8
client.cc
Go to the documentation of this file.
1// Copyright (C) 2018-2024 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)>
52SocketCallbackFunction;
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 std::array<char, 32768> input_buf_;
446
448 uint64_t current_transid_;
449
451 HttpClient::HandshakeHandler handshake_callback_;
452
454 HttpClient::CloseHandler close_callback_;
455
457 std::atomic<bool> started_;
458
460 std::atomic<bool> need_handshake_;
461
463 std::atomic<bool> closed_;
464
466 std::mutex mutex_;
467};
468
470typedef boost::shared_ptr<Connection> ConnectionPtr;
471
479class ConnectionPool : public boost::enable_shared_from_this<ConnectionPool> {
480public:
481
488 explicit ConnectionPool(const IOServicePtr& io_service, size_t max_url_connections)
489 : io_service_(io_service), destinations_(), pool_mutex_(),
490 max_url_connections_(max_url_connections) {
491 }
492
496 ~ConnectionPool() {
497 closeAll();
498 }
499
505 void processNextRequest(const Url& url, const TlsContextPtr& tls_context) {
506 if (MultiThreadingMgr::instance().getMode()) {
507 std::lock_guard<std::mutex> lk(pool_mutex_);
508 return (processNextRequestInternal(url, tls_context));
509 } else {
510 return (processNextRequestInternal(url, tls_context));
511 }
512 }
513
519 void postProcessNextRequest(const Url& url,
520 const TlsContextPtr& tls_context) {
521 io_service_->post(std::bind(&ConnectionPool::processNextRequest,
522 shared_from_this(), url, tls_context));
523 }
524
545 void queueRequest(const Url& url,
546 const TlsContextPtr& tls_context,
547 const HttpRequestPtr& request,
548 const HttpResponsePtr& response,
549 const long request_timeout,
550 const HttpClient::RequestHandler& request_callback,
551 const HttpClient::ConnectHandler& connect_callback,
552 const HttpClient::HandshakeHandler& handshake_callback,
553 const HttpClient::CloseHandler& close_callback) {
554 if (MultiThreadingMgr::instance().getMode()) {
555 std::lock_guard<std::mutex> lk(pool_mutex_);
556 return (queueRequestInternal(url, tls_context, request, response,
557 request_timeout, request_callback,
558 connect_callback, handshake_callback,
559 close_callback));
560 } else {
561 return (queueRequestInternal(url, tls_context, request, response,
562 request_timeout, request_callback,
563 connect_callback, handshake_callback,
564 close_callback));
565 }
566 }
567
570 void closeAll() {
571 if (MultiThreadingMgr::instance().getMode()) {
572 std::lock_guard<std::mutex> lk(pool_mutex_);
573 closeAllInternal();
574 } else {
575 closeAllInternal();
576 }
577 }
578
591 void closeIfOutOfBand(int socket_fd) {
592 if (MultiThreadingMgr::instance().getMode()) {
593 std::lock_guard<std::mutex> lk(pool_mutex_);
594 closeIfOutOfBandInternal(socket_fd);
595 } else {
596 closeIfOutOfBandInternal(socket_fd);
597 }
598 }
599
600private:
601
609 void processNextRequestInternal(const Url& url,
610 const TlsContextPtr& tls_context) {
611 // Check if there is a queue for this URL. If there is no queue, there
612 // is no request queued either.
613 DestinationPtr destination = findDestination(url, tls_context);
614 if (destination) {
615 // Remove closed connections.
616 destination->garbageCollectConnections();
617 if (!destination->queueEmpty()) {
618 // We have at least one queued request. Do we have an
619 // idle connection?
620 ConnectionPtr connection = destination->getIdleConnection();
621 if (!connection) {
622 // No idle connections.
623 if (destination->connectionsFull()) {
624 return;
625 }
626 // Room to make another connection with this destination,
627 // so make one.
628 connection.reset(new Connection(io_service_, tls_context,
629 shared_from_this(), url));
630 destination->addConnection(connection);
631 }
632
633 // Dequeue the oldest request and start a transaction for it using
634 // the idle connection.
635 RequestDescriptor desc = destination->popNextRequest();
636 connection->doTransaction(desc.request_, desc.response_,
637 desc.request_timeout_, desc.callback_,
638 desc.connect_callback_,
639 desc.handshake_callback_,
640 desc.close_callback_);
641 }
642 }
643 }
644
667 void queueRequestInternal(const Url& url,
668 const TlsContextPtr& tls_context,
669 const HttpRequestPtr& request,
670 const HttpResponsePtr& response,
671 const long request_timeout,
672 const HttpClient::RequestHandler& request_callback,
673 const HttpClient::ConnectHandler& connect_callback,
674 const HttpClient::HandshakeHandler& handshake_callback,
675 const HttpClient::CloseHandler& close_callback) {
676 ConnectionPtr connection;
677 // Find the destination for the requested URL.
678 DestinationPtr destination = findDestination(url, tls_context);
679 if (destination) {
680 // Remove closed connections.
681 destination->garbageCollectConnections();
682 // Found it, look for an idle connection.
683 connection = destination->getIdleConnection();
684 } else {
685 // Doesn't exist yet so it's a new destination.
686 destination = addDestination(url, tls_context);
687 }
688
689 if (!connection) {
690 if (destination->connectionsFull()) {
691 // All connections busy, queue it.
692 destination->pushRequest(RequestDescriptor(request, response,
693 request_timeout,
694 request_callback,
695 connect_callback,
696 handshake_callback,
697 close_callback));
698 return;
699 }
700
701 // Room to make another connection with this destination, so make one.
702 connection.reset(new Connection(io_service_, tls_context,
703 shared_from_this(), url));
704 destination->addConnection(connection);
705 }
706
707 // Use the connection to start the transaction.
708 connection->doTransaction(request, response, request_timeout, request_callback,
709 connect_callback, handshake_callback, close_callback);
710 }
711
716 void closeAllInternal() {
717 for (auto const& destination : destinations_) {
718 destination.second->closeAllConnections();
719 }
720
721 destinations_.clear();
722 }
723
738 void closeIfOutOfBandInternal(int socket_fd) {
739 for (auto const& destination : destinations_) {
740 // First we look for a connection with the socket.
741 ConnectionPtr connection = destination.second->findBySocketFd(socket_fd);
742 if (connection) {
743 if (!connection->isTransactionOngoing()) {
744 // Socket has no transaction, so any ready event is
745 // out-of-band (other end probably closed), so
746 // let's close it. Note we do not remove any queued
747 // requests, as this might somehow be occurring in
748 // between them.
749 destination.second->closeConnection(connection);
750 }
751
752 return;
753 }
754 }
755 }
756
759 struct RequestDescriptor {
773 RequestDescriptor(const HttpRequestPtr& request,
774 const HttpResponsePtr& response,
775 const long& request_timeout,
776 const HttpClient::RequestHandler& callback,
777 const HttpClient::ConnectHandler& connect_callback,
778 const HttpClient::HandshakeHandler& handshake_callback,
779 const HttpClient::CloseHandler& close_callback)
780 : request_(request), response_(response),
781 request_timeout_(request_timeout), callback_(callback),
782 connect_callback_(connect_callback),
783 handshake_callback_(handshake_callback),
784 close_callback_(close_callback) {
785 }
786
788 HttpRequestPtr request_;
789
791 HttpResponsePtr response_;
792
794 long request_timeout_;
795
798
800 HttpClient::ConnectHandler connect_callback_;
801
803 HttpClient::HandshakeHandler handshake_callback_;
804
806 HttpClient::CloseHandler close_callback_;
807 };
808
810 typedef std::pair<Url, TlsContextPtr> DestinationDescriptor;
811
813 class Destination {
814 public:
816 const size_t QUEUE_SIZE_THRESHOLD = 2048;
818 const int QUEUE_WARN_SECS = 5;
819
826 Destination(Url const& url, TlsContextPtr tls_context, size_t max_connections)
827 : url_(url), tls_context_(tls_context),
828 max_connections_(max_connections), connections_(), queue_(),
829 last_queue_warn_time_(min_date_time), last_queue_size_(0) {
830 }
831
833 ~Destination() {
834 closeAllConnections();
835 }
836
844 void addConnection(ConnectionPtr connection) {
845 if (connectionsFull()) {
846 isc_throw(BadValue, "URL: " << url_.toText()
847 << ", already at maximum connections: "
848 << max_connections_);
849 }
850
851 connections_.push_back(connection);
852 }
853
858 void closeConnection(ConnectionPtr connection) {
859 for (auto it = connections_.begin(); it != connections_.end(); ++it) {
860 if (*it == connection) {
861 (*it)->close();
862 connections_.erase(it);
863 break;
864 }
865 }
866 }
867
870 void closeAllConnections() {
871 // Flush the queue.
872 while (!queue_.empty()) {
873 queue_.pop();
874 }
875
876 for (auto const& connection : connections_) {
877 connection->close();
878 }
879
880 connections_.clear();
881 }
882
906 void garbageCollectConnections() {
907 for (auto it = connections_.begin(); it != connections_.end();) {
908 (*it)->isClosedByPeer();
909 if (!(*it)->isClosed()) {
910 ++it;
911 } else {
912 it = connections_.erase(it);
913 }
914 }
915 }
916
928 ConnectionPtr getIdleConnection() {
929 for (auto const& connection : connections_) {
930 if (!connection->isTransactionOngoing() &&
931 !connection->isClosed()) {
932 return (connection);
933 }
934 }
935
936 return (ConnectionPtr());
937 }
938
945 ConnectionPtr findBySocketFd(int socket_fd) {
946 for (auto const& connection : connections_) {
947 if (connection->isMySocket(socket_fd)) {
948 return (connection);
949 }
950 }
951
952 return (ConnectionPtr());
953 }
954
958 bool connectionsEmpty() {
959 return (connections_.empty());
960 }
961
965 bool connectionsFull() {
966 return (connections_.size() >= max_connections_);
967 }
968
972 size_t connectionCount() {
973 return (connections_.size());
974 }
975
979 size_t getMaxConnections() const {
980 return (max_connections_);
981 }
982
986 bool queueEmpty() const {
987 return (queue_.empty());
988 }
989
996 void pushRequest(RequestDescriptor const& desc) {
997 queue_.push(desc);
998 size_t size = queue_.size();
999 // If the queue size is larger than the threshold and growing, issue a
1000 // periodic warning.
1001 if ((size > QUEUE_SIZE_THRESHOLD) && (size > last_queue_size_)) {
1002 ptime now = microsec_clock::universal_time();
1003 if ((now - last_queue_warn_time_) > seconds(QUEUE_WARN_SECS)) {
1005 .arg(url_.toText())
1006 .arg(size);
1007 // Remember the last time we warned.
1008 last_queue_warn_time_ = now;
1009 }
1010 }
1011
1012 // Remember the previous size.
1013 last_queue_size_ = size;
1014 }
1015
1019 RequestDescriptor popNextRequest() {
1020 if (queue_.empty()) {
1021 isc_throw(InvalidOperation, "cannot pop, queue is empty");
1022 }
1023
1024 RequestDescriptor desc = queue_.front();
1025 queue_.pop();
1026 return (desc);
1027 }
1028
1029 private:
1031 Url url_;
1032
1034 TlsContextPtr tls_context_;
1035
1037 size_t max_connections_;
1038
1040 std::list<ConnectionPtr> connections_;
1041
1043 std::queue<RequestDescriptor> queue_;
1044
1046 ptime last_queue_warn_time_;
1047
1049 size_t last_queue_size_;
1050 };
1051
1053 typedef boost::shared_ptr<Destination> DestinationPtr;
1054
1062 DestinationPtr addDestination(const Url& url,
1063 const TlsContextPtr& tls_context) {
1064 const DestinationDescriptor& desc = std::make_pair(url, tls_context);
1065 DestinationPtr destination(new Destination(url, tls_context,
1066 max_url_connections_));
1067 destinations_[desc] = destination;
1068 return (destination);
1069 }
1070
1079 DestinationPtr findDestination(const Url& url,
1080 const TlsContextPtr& tls_context) const {
1081 const DestinationDescriptor& desc = std::make_pair(url, tls_context);
1082 auto it = destinations_.find(desc);
1083 if (it != destinations_.end()) {
1084 return (it->second);
1085 }
1086
1087 return (DestinationPtr());
1088 }
1089
1101 void removeDestination(const Url& url,
1102 const TlsContextPtr& tls_context) {
1103 const DestinationDescriptor& desc = std::make_pair(url, tls_context);
1104 auto it = destinations_.find(desc);
1105 if (it != destinations_.end()) {
1106 it->second->closeAllConnections();
1107 destinations_.erase(it);
1108 }
1109 }
1110
1112 IOServicePtr io_service_;
1113
1115 std::map<DestinationDescriptor, DestinationPtr> destinations_;
1116
1118 std::mutex pool_mutex_;
1119
1121 size_t max_url_connections_;
1122};
1123
1124Connection::Connection(const IOServicePtr& io_service,
1125 const TlsContextPtr& tls_context,
1126 const ConnectionPoolPtr& conn_pool,
1127 const Url& url)
1128 : io_service_(io_service), conn_pool_(conn_pool), url_(url),
1129 tls_context_(tls_context), tcp_socket_(), tls_socket_(),
1130 timer_(new IntervalTimer(io_service)), current_request_(),
1131 current_response_(), parser_(), current_callback_(), buf_(), input_buf_(),
1132 current_transid_(0), close_callback_(), started_(false),
1133 need_handshake_(false), closed_(false) {
1134 if (!tls_context) {
1135 tcp_socket_.reset(new asiolink::TCPSocket<SocketCallback>(io_service));
1136 } else {
1137 tls_socket_.reset(new asiolink::TLSSocket<SocketCallback>(io_service,
1138 tls_context));
1139 need_handshake_ = true;
1140 }
1141}
1142
1143Connection::~Connection() {
1144 close();
1145}
1146
1147void
1148Connection::resetState() {
1149 started_ = false;
1150 current_request_.reset();
1151 current_response_.reset();
1152 parser_.reset();
1153 current_callback_ = HttpClient::RequestHandler();
1154}
1155
1156void
1157Connection::closeCallback(const bool clear) {
1158 if (close_callback_) {
1159 try {
1160 if (tcp_socket_) {
1161 close_callback_(tcp_socket_->getNative());
1162 } else if (tls_socket_) {
1163 close_callback_(tls_socket_->getNative());
1164 } else {
1166 "internal error: can't find a socket to close");
1167 }
1168 } catch (...) {
1170 }
1171 }
1172
1173 if (clear) {
1174 close_callback_ = HttpClient::CloseHandler();
1175 }
1176}
1177
1178void
1179Connection::isClosedByPeer() {
1180 // This method applies only to idle connections.
1181 if (started_ || closed_) {
1182 return;
1183 }
1184 // This code was guarded by a lock so keep this.
1185 if (MultiThreadingMgr::instance().getMode()) {
1186 std::lock_guard<std::mutex> lk(mutex_);
1187 isClosedByPeerInternal();
1188 } else {
1189 isClosedByPeerInternal();
1190 }
1191}
1192
1193void
1194Connection::isClosedByPeerInternal() {
1195 // If the socket is open we check if it is possible to transmit
1196 // the data over this socket by reading from it with message
1197 // peeking. If the socket is not usable, we close it and then
1198 // re-open it. There is a narrow window of time between checking
1199 // the socket usability and actually transmitting the data over
1200 // this socket, when the peer may close the connection. In this
1201 // case we'll need to re-transmit but we don't handle it here.
1202 if (tcp_socket_) {
1203 if (tcp_socket_->getASIOSocket().is_open() &&
1204 !tcp_socket_->isUsable()) {
1205 closeCallback();
1206 closed_ = true;
1207 tcp_socket_->close();
1208 }
1209 } else if (tls_socket_) {
1210 if (tls_socket_->getASIOSocket().is_open() &&
1211 !tls_socket_->isUsable()) {
1212 closeCallback();
1213 closed_ = true;
1214 tls_socket_->close();
1215 }
1216 } else {
1217 isc_throw(Unexpected, "internal error: can't find the sending socket");
1218 }
1219}
1220
1221void
1222Connection::doTransaction(const HttpRequestPtr& request,
1223 const HttpResponsePtr& response,
1224 const long request_timeout,
1225 const HttpClient::RequestHandler& callback,
1226 const HttpClient::ConnectHandler& connect_callback,
1227 const HttpClient::HandshakeHandler& handshake_callback,
1228 const HttpClient::CloseHandler& close_callback) {
1229 if (MultiThreadingMgr::instance().getMode()) {
1230 std::lock_guard<std::mutex> lk(mutex_);
1231 doTransactionInternal(request, response, request_timeout,
1232 callback, connect_callback, handshake_callback,
1233 close_callback);
1234 } else {
1235 doTransactionInternal(request, response, request_timeout,
1236 callback, connect_callback, handshake_callback,
1237 close_callback);
1238 }
1239}
1240
1241void
1242Connection::doTransactionInternal(const HttpRequestPtr& request,
1243 const HttpResponsePtr& response,
1244 const long request_timeout,
1245 const HttpClient::RequestHandler& callback,
1246 const HttpClient::ConnectHandler& connect_callback,
1247 const HttpClient::HandshakeHandler& handshake_callback,
1248 const HttpClient::CloseHandler& close_callback) {
1249 try {
1250 started_ = true;
1251 current_request_ = request;
1252 current_response_ = response;
1253 parser_.reset(new HttpResponseParser(*current_response_));
1254 parser_->initModel();
1255 current_callback_ = callback;
1256 handshake_callback_ = handshake_callback;
1257 close_callback_ = close_callback;
1258
1259 // Starting new transaction. Generate new transaction id.
1260 ++current_transid_;
1261
1262 buf_ = request->toString();
1263
1266 .arg(request->toBriefString())
1267 .arg(url_.toText());
1268
1271 .arg(url_.toText())
1272 .arg(HttpMessageParserBase::logFormatHttpMessage(request->toString(),
1273 MAX_LOGGED_MESSAGE_SIZE));
1274
1275 // Setup request timer.
1276 scheduleTimer(request_timeout);
1277
1281 TCPEndpoint endpoint(url_.getStrippedHostname(),
1282 static_cast<unsigned short>(url_.getPort()));
1283 SocketCallback socket_cb(std::bind(&Connection::connectCallback, shared_from_this(),
1284 connect_callback, current_transid_,
1285 ph::_1));
1286
1287 // Establish new connection or use existing connection.
1288 if (tcp_socket_) {
1289 tcp_socket_->open(&endpoint, socket_cb);
1290 return;
1291 }
1292 if (tls_socket_) {
1293 tls_socket_->open(&endpoint, socket_cb);
1294 return;
1295 }
1296
1297 // Should never reach this point.
1298 isc_throw(Unexpected, "internal error: can't find a socket to open");
1299
1300 } catch (const std::exception& ex) {
1301 // Re-throw with the expected exception type.
1303 }
1304}
1305
1306void
1307Connection::close() {
1308 if (MultiThreadingMgr::instance().getMode()) {
1309 std::lock_guard<std::mutex> lk(mutex_);
1310 return (closeInternal());
1311 } else {
1312 return (closeInternal());
1313 }
1314}
1315
1316void
1317Connection::closeInternal() {
1318 // Pass in true to discard the callback.
1319 closeCallback(true);
1320
1321 closed_ = true;
1322 timer_->cancel();
1323 if (tcp_socket_) {
1324 tcp_socket_->close();
1325 }
1326 if (tls_socket_) {
1327 tls_socket_->close();
1328 }
1329
1330 auto f = [](IntervalTimerPtr, std::shared_ptr<TCPSocket<SocketCallback>>,
1331 std::shared_ptr<TLSSocket<SocketCallback>>) {};
1332 io_service_->post(std::bind(f, timer_, tcp_socket_, tls_socket_));
1333
1334 resetState();
1335}
1336
1337bool
1338Connection::isMySocket(int socket_fd) const {
1339 if (tcp_socket_) {
1340 return (tcp_socket_->getNative() == socket_fd);
1341 } else if (tls_socket_) {
1342 return (tls_socket_->getNative() == socket_fd);
1343 }
1344 // Should never reach this point.
1345 std::cerr << "internal error: can't find my socket\n";
1346 return (false);
1347}
1348
1349bool
1350Connection::checkPrematureTimeout(const uint64_t transid) {
1351 if (MultiThreadingMgr::instance().getMode()) {
1352 std::lock_guard<std::mutex> lk(mutex_);
1353 return (checkPrematureTimeoutInternal(transid));
1354 } else {
1355 return (checkPrematureTimeoutInternal(transid));
1356 }
1357}
1358
1359bool
1360Connection::checkPrematureTimeoutInternal(const uint64_t transid) {
1361 // If there is no transaction but the handlers are invoked it means
1362 // that the last transaction in the queue timed out prematurely.
1363 // Also, if there is a transaction in progress but the ID of that
1364 // transaction doesn't match the one associated with the handler it,
1365 // also means that the transaction timed out prematurely.
1366 if (!isTransactionOngoing() || (transid != current_transid_)) {
1368 .arg(isTransactionOngoing())
1369 .arg(transid)
1370 .arg(current_transid_);
1371 return (true);
1372 }
1373
1374 return (false);
1375}
1376
1377void
1378Connection::terminate(const boost::system::error_code& ec,
1379 const std::string& parsing_error) {
1380 if (MultiThreadingMgr::instance().getMode()) {
1381 std::lock_guard<std::mutex> lk(mutex_);
1382 terminateInternal(ec, parsing_error);
1383 } else {
1384 terminateInternal(ec, parsing_error);
1385 }
1386}
1387
1388void
1389Connection::terminateInternal(const boost::system::error_code& ec,
1390 const std::string& parsing_error) {
1391 HttpResponsePtr response;
1392 if (isTransactionOngoing()) {
1393
1394 timer_->cancel();
1395 if (tcp_socket_) {
1396 tcp_socket_->cancel();
1397 }
1398 if (tls_socket_) {
1399 tls_socket_->cancel();
1400 }
1401
1402 if (!ec && current_response_->isFinalized()) {
1403 response = current_response_;
1404
1407 .arg(url_.toText());
1408
1411 .arg(url_.toText())
1412 .arg(parser_ ?
1413 parser_->getBufferAsString(MAX_LOGGED_MESSAGE_SIZE) :
1414 "[HttpResponseParser is null]");
1415
1416 } else {
1417 std::string err = parsing_error.empty() ? ec.message() :
1418 parsing_error;
1419
1422 .arg(url_.toText())
1423 .arg(err);
1424
1425 // Only log the details if we have received anything and tried
1426 // to parse it.
1427 if (!parsing_error.empty()) {
1430 .arg(url_.toText())
1431 .arg(parser_ ?
1432 parser_->getBufferAsString(MAX_LOGGED_MESSAGE_SIZE) :
1433 "[HttpResponseParser is null]");
1434 }
1435 }
1436
1437 try {
1438 // The callback should take care of its own exceptions but one
1439 // never knows.
1440 if (MultiThreadingMgr::instance().getMode()) {
1441 UnlockGuard<std::mutex> lock(mutex_);
1442 current_callback_(ec, response, parsing_error);
1443 } else {
1444 current_callback_(ec, response, parsing_error);
1445 }
1446 } catch (...) {
1447 }
1448
1449 // If we're not requesting connection persistence or the
1450 // connection has timed out, we should close the socket.
1451 if (!closed_ &&
1452 (!current_request_->isPersistent() ||
1453 (ec == boost::asio::error::timed_out))) {
1454 closeInternal();
1455 }
1456
1457 resetState();
1458 }
1459
1460 // Check if there are any requests queued for this destination and start
1461 // another transaction if there is at least one.
1462 ConnectionPoolPtr conn_pool = conn_pool_.lock();
1463 if (conn_pool) {
1464 conn_pool->postProcessNextRequest(url_, tls_context_);
1465 }
1466}
1467
1468void
1469Connection::scheduleTimer(const long request_timeout) {
1470 if (request_timeout > 0) {
1471 timer_->setup(std::bind(&Connection::timerCallback, this), request_timeout,
1473 }
1474}
1475
1476void
1477Connection::doHandshake(const uint64_t transid) {
1478 // Skip the handshake if it is not needed.
1479 if (!need_handshake_) {
1480 doSend(transid);
1481 return;
1482 }
1483
1484 SocketCallback socket_cb(std::bind(&Connection::handshakeCallback,
1485 shared_from_this(),
1486 handshake_callback_,
1487 transid,
1488 ph::_1));
1489 try {
1490 tls_socket_->handshake(socket_cb);
1491
1492 } catch (...) {
1493 terminate(boost::asio::error::not_connected);
1494 }
1495}
1496
1497void
1498Connection::doSend(const uint64_t transid) {
1499 SocketCallback socket_cb(std::bind(&Connection::sendCallback,
1500 shared_from_this(),
1501 transid,
1502 ph::_1,
1503 ph::_2));
1504 try {
1505 if (tcp_socket_) {
1506 tcp_socket_->asyncSend(&buf_[0], buf_.size(), socket_cb);
1507 return;
1508 }
1509
1510 if (tls_socket_) {
1511 tls_socket_->asyncSend(&buf_[0], buf_.size(), socket_cb);
1512 return;
1513 }
1514
1515 // Should never reach this point.
1516 std::cerr << "internal error: can't find a socket to send to\n";
1518 "internal error: can't find a socket to send to");
1519 } catch (...) {
1520 terminate(boost::asio::error::not_connected);
1521 }
1522}
1523
1524void
1525Connection::doReceive(const uint64_t transid) {
1526 TCPEndpoint endpoint;
1527 SocketCallback socket_cb(std::bind(&Connection::receiveCallback,
1528 shared_from_this(),
1529 transid,
1530 ph::_1,
1531 ph::_2));
1532 try {
1533 if (tcp_socket_) {
1534 tcp_socket_->asyncReceive(static_cast<void*>(input_buf_.data()),
1535 input_buf_.size(), 0,
1536 &endpoint, socket_cb);
1537 return;
1538 }
1539 if (tls_socket_) {
1540 tls_socket_->asyncReceive(static_cast<void*>(input_buf_.data()),
1541 input_buf_.size(), 0,
1542 &endpoint, socket_cb);
1543 return;
1544 }
1545 // Should never reach this point.
1546 std::cerr << "internal error: can't find a socket to receive from\n";
1548 "internal error: can't find a socket to receive from");
1549
1550 } catch (...) {
1551 terminate(boost::asio::error::not_connected);
1552 }
1553}
1554
1555void
1556Connection::connectCallback(HttpClient::ConnectHandler connect_callback,
1557 const uint64_t transid,
1558 const boost::system::error_code& ec) {
1559 if (checkPrematureTimeout(transid)) {
1560 return;
1561 }
1562
1563 // Run user defined connect callback if specified.
1564 if (connect_callback) {
1565 // If the user defined callback indicates that the connection
1566 // should not be continued.
1567 if (tcp_socket_) {
1568 if (!connect_callback(ec, tcp_socket_->getNative())) {
1569 return;
1570 }
1571 } else if (tls_socket_) {
1572 if (!connect_callback(ec, tls_socket_->getNative())) {
1573 return;
1574 }
1575 } else {
1576 // Should never reach this point.
1577 std::cerr << "internal error: can't find a socket to connect\n";
1578 }
1579 }
1580
1581 if (ec && (ec.value() == boost::asio::error::operation_aborted)) {
1582 return;
1583
1584 // In some cases the "in progress" status code may be returned. It doesn't
1585 // indicate an error. Sending the request over the socket is expected to
1586 // be successful. Getting such status appears to be highly dependent on
1587 // the operating system.
1588 } else if (ec &&
1589 (ec.value() != boost::asio::error::in_progress) &&
1590 (ec.value() != boost::asio::error::already_connected)) {
1591 terminate(ec);
1592
1593 } else {
1594 // Start the TLS handshake asynchronously.
1595 doHandshake(transid);
1596 }
1597}
1598
1599void
1600Connection::handshakeCallback(HttpClient::ConnectHandler handshake_callback,
1601 const uint64_t transid,
1602 const boost::system::error_code& ec) {
1603 need_handshake_ = false;
1604 if (checkPrematureTimeout(transid)) {
1605 return;
1606 }
1607
1608 // Run user defined handshake callback if specified.
1609 if (handshake_callback) {
1610 // If the user defined callback indicates that the connection
1611 // should not be continued.
1612 if (tls_socket_) {
1613 if (!handshake_callback(ec, tls_socket_->getNative())) {
1614 return;
1615 }
1616 } else {
1617 // Should never reach this point.
1618 std::cerr << "internal error: can't find TLS socket\n";
1619 }
1620 }
1621
1622 if (ec && (ec.value() == boost::asio::error::operation_aborted)) {
1623 return;
1624 } else if (ec) {
1625 terminate(ec);
1626
1627 } else {
1628 // Start sending the request asynchronously.
1629 doSend(transid);
1630 }
1631}
1632
1633void
1634Connection::sendCallback(const uint64_t transid,
1635 const boost::system::error_code& ec,
1636 size_t length) {
1637 if (checkPrematureTimeout(transid)) {
1638 return;
1639 }
1640
1641 if (ec) {
1642 if (ec.value() == boost::asio::error::operation_aborted) {
1643 return;
1644
1645 // EAGAIN and EWOULDBLOCK don't really indicate an error. The length
1646 // should be 0 in this case but let's be sure.
1647 } else if ((ec.value() == boost::asio::error::would_block) ||
1648 (ec.value() == boost::asio::error::try_again)) {
1649 length = 0;
1650
1651 } else {
1652 // Any other error should cause the transaction to terminate.
1653 terminate(ec);
1654 return;
1655 }
1656 }
1657
1658 // Sending is in progress, so push back the timeout.
1659 scheduleTimer(timer_->getInterval());
1660
1661 // If any data have been sent, remove it from the buffer and only leave the
1662 // portion that still has to be sent.
1663 if (length > 0) {
1664 buf_.erase(0, length);
1665 }
1666
1667 // If there is no more data to be sent, start receiving a response. Otherwise,
1668 // continue sending.
1669 if (buf_.empty()) {
1670 doReceive(transid);
1671
1672 } else {
1673 doSend(transid);
1674 }
1675}
1676
1677void
1678Connection::receiveCallback(const uint64_t transid,
1679 const boost::system::error_code& ec,
1680 size_t length) {
1681 if (checkPrematureTimeout(transid)) {
1682 return;
1683 }
1684
1685 if (ec) {
1686 if (ec.value() == boost::asio::error::operation_aborted) {
1687 return;
1688
1689 // EAGAIN and EWOULDBLOCK don't indicate an error in this case. All
1690 // other errors should terminate the transaction.
1691 }
1692 if ((ec.value() != boost::asio::error::try_again) &&
1693 (ec.value() != boost::asio::error::would_block)) {
1694 terminate(ec);
1695 return;
1696
1697 } else {
1698 // For EAGAIN and EWOULDBLOCK the length should be 0 anyway, but let's
1699 // make sure.
1700 length = 0;
1701 }
1702 }
1703
1704 // Receiving is in progress, so push back the timeout.
1705 scheduleTimer(timer_->getInterval());
1706
1707 if (runParser(ec, length)) {
1708 doReceive(transid);
1709 }
1710}
1711
1712bool
1713Connection::runParser(const boost::system::error_code& ec, size_t length) {
1714 if (MultiThreadingMgr::instance().getMode()) {
1715 std::lock_guard<std::mutex> lk(mutex_);
1716 return (runParserInternal(ec, length));
1717 } else {
1718 return (runParserInternal(ec, length));
1719 }
1720}
1721
1722bool
1723Connection::runParserInternal(const boost::system::error_code& ec,
1724 size_t length) {
1725 // If we have received any data, let's feed the parser with it.
1726 if (length != 0) {
1727 parser_->postBuffer(static_cast<void*>(input_buf_.data()), length);
1728 parser_->poll();
1729 }
1730
1731 // If the parser still needs data, let's schedule another receive.
1732 if (parser_->needData()) {
1733 return (true);
1734
1735 } else if (parser_->httpParseOk()) {
1736 // No more data needed and parsing has been successful so far. Let's
1737 // try to finalize the response parsing.
1738 try {
1739 current_response_->finalize();
1740 terminateInternal(ec);
1741
1742 } catch (const std::exception& ex) {
1743 // If there is an error here, we need to return the error message.
1744 terminateInternal(ec, ex.what());
1745 }
1746
1747 } else {
1748 // Parsing was unsuccessful. Let's pass the error message held in the
1749 // parser.
1750 terminateInternal(ec, parser_->getErrorMessage());
1751 }
1752
1753 return (false);
1754}
1755
1756void
1757Connection::timerCallback() {
1758 // Request timeout occurred.
1759 terminate(boost::asio::error::timed_out);
1760}
1761
1762}
1763
1764namespace isc {
1765namespace http {
1766
1769public:
1793 HttpClientImpl(const IOServicePtr& io_service, size_t thread_pool_size = 0,
1794 bool defer_thread_start = false)
1795 : thread_pool_size_(thread_pool_size), thread_pool_() {
1796 if (thread_pool_size_ > 0) {
1797 // Create our own private IOService.
1798 thread_io_service_.reset(new IOService());
1799
1800 // Create the connection pool. Note that we use the thread_pool_size
1801 // as the maximum connections per URL value.
1802 conn_pool_.reset(new ConnectionPool(thread_io_service_, thread_pool_size_));
1803
1804 // Create the thread pool.
1805 thread_pool_.reset(new IoServiceThreadPool(thread_io_service_, thread_pool_size_,
1806 defer_thread_start));
1807
1809 .arg(thread_pool_size_);
1810 } else {
1811 // Single-threaded mode: use the caller's IOService,
1812 // one connection per URL.
1813 conn_pool_.reset(new ConnectionPool(io_service, 1));
1814 }
1815 }
1816
1821 stop();
1822 }
1823
1830 if (thread_pool_) {
1831 thread_pool_->checkPausePermissions();
1832 }
1833 }
1834
1836 void start() {
1837 if (thread_pool_) {
1838 thread_pool_->run();
1839 }
1840 }
1841
1844 void stop() {
1845 // Close all the connections.
1846 conn_pool_->closeAll();
1847
1848 // Stop the thread pool.
1849 if (thread_pool_) {
1850 thread_pool_->stop();
1851 }
1852 }
1853
1858 void pause() {
1859 if (!thread_pool_) {
1860 isc_throw(InvalidOperation, "HttpClient::pause - no thread pool");
1861 }
1862
1863 // Pause the thread pool.
1864 thread_pool_->pause();
1865 }
1866
1871 void resume() {
1872 if (!thread_pool_) {
1873 isc_throw(InvalidOperation, "HttpClient::resume - no thread pool");
1874 }
1875
1876 // Resume running the thread pool.
1877 thread_pool_->run();
1878 }
1879
1884 bool isRunning() {
1885 if (thread_pool_) {
1886 return (thread_pool_->isRunning());
1887 }
1888
1889 return (false);
1890 }
1891
1896 bool isStopped() {
1897 if (thread_pool_) {
1898 return (thread_pool_->isStopped());
1899 }
1900
1901 return (false);
1902 }
1903
1908 bool isPaused() {
1909 if (thread_pool_) {
1910 return (thread_pool_->isPaused());
1911 }
1912
1913 return (false);
1914 }
1915
1921 return (thread_io_service_);
1922 };
1923
1928 return (thread_pool_size_);
1929 }
1930
1934 uint16_t getThreadCount() {
1935 if (!thread_pool_) {
1936 return (0);
1937 }
1938 return (thread_pool_->getThreadCount());
1939 }
1940
1942 ConnectionPoolPtr conn_pool_;
1943
1944private:
1945
1947 size_t thread_pool_size_;
1948
1950 asiolink::IOServicePtr thread_io_service_;
1951
1954 IoServiceThreadPoolPtr thread_pool_;
1955};
1956
1957HttpClient::HttpClient(const IOServicePtr& io_service, bool multi_threading_enabled,
1958 size_t thread_pool_size, bool defer_thread_start) {
1959 if (!multi_threading_enabled && thread_pool_size) {
1961 "HttpClient thread_pool_size must be zero "
1962 "when Kea core multi-threading is disabled");
1963 }
1964
1965 impl_.reset(new HttpClientImpl(io_service, thread_pool_size,
1966 defer_thread_start));
1967}
1968
1970}
1971
1972void
1974 const TlsContextPtr& tls_context,
1975 const HttpRequestPtr& request,
1976 const HttpResponsePtr& response,
1977 const HttpClient::RequestHandler& request_callback,
1978 const HttpClient::RequestTimeout& request_timeout,
1979 const HttpClient::ConnectHandler& connect_callback,
1980 const HttpClient::HandshakeHandler& handshake_callback,
1981 const HttpClient::CloseHandler& close_callback) {
1982 if (!url.isValid()) {
1983 isc_throw(HttpClientError, "invalid URL specified for the HTTP client");
1984 }
1985
1986 if ((url.getScheme() == Url::Scheme::HTTPS) && !tls_context) {
1987 isc_throw(HttpClientError, "HTTPS URL scheme but no TLS context");
1988 }
1989
1990 if (!request) {
1991 isc_throw(HttpClientError, "HTTP request must not be null");
1992 }
1993
1994 if (!response) {
1995 isc_throw(HttpClientError, "HTTP response must not be null");
1996 }
1997
1998 if (!request_callback) {
1999 isc_throw(HttpClientError, "callback for HTTP transaction must not be null");
2000 }
2001
2002 impl_->conn_pool_->queueRequest(url, tls_context, request, response,
2003 request_timeout.value_,
2004 request_callback, connect_callback,
2005 handshake_callback, close_callback);
2006}
2007
2008void
2010 return (impl_->conn_pool_->closeIfOutOfBand(socket_fd));
2011}
2012
2013void
2015 impl_->start();
2016}
2017
2018void
2020 impl_->checkPermissions();
2021}
2022
2023void
2025 impl_->pause();
2026}
2027
2028void
2030 impl_->resume();
2031}
2032
2033void
2035 impl_->stop();
2036}
2037
2038const IOServicePtr
2040 return (impl_->getThreadIOService());
2041}
2042
2043uint16_t
2045 return (impl_->getThreadPoolSize());
2046}
2047
2048uint16_t
2050 return (impl_->getThreadCount());
2051}
2052
2053bool
2055 return (impl_->isRunning());
2056}
2057
2058bool
2060 return (impl_->isStopped());
2061}
2062
2063bool
2065 return (impl_->isPaused());
2066}
2067
2068} // end of namespace isc::http
2069} // end of namespace isc
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
A generic exception that is thrown if a function is called in a prohibited way.
A generic exception that is thrown when an unexpected error condition occurs.
A generic error raised by the HttpClient class.
Definition: client.h:26
HttpClient implementation.
Definition: client.cc:1768
ConnectionPoolPtr conn_pool_
Holds a pointer to the connection pool.
Definition: client.cc:1942
uint16_t getThreadCount()
Fetches the number of threads in the pool.
Definition: client.cc:1934
~HttpClientImpl()
Destructor.
Definition: client.cc:1820
void pause()
Pauses the client's thread pool.
Definition: client.cc:1858
uint16_t getThreadPoolSize()
Fetches the maximum size of the thread pool.
Definition: client.cc:1927
void start()
Starts running the client's thread pool, if multi-threaded.
Definition: client.cc:1836
void stop()
Close all connections, and if multi-threaded, stops the client's thread pool.
Definition: client.cc:1844
asiolink::IOServicePtr getThreadIOService()
Fetches the internal IOService used in multi-threaded mode.
Definition: client.cc:1920
void checkPermissions()
Check if the current thread can perform thread pool state transition.
Definition: client.cc:1829
bool isPaused()
Indicates if the thread pool is paused.
Definition: client.cc:1908
void resume()
Resumes running the client's thread pool.
Definition: client.cc:1871
HttpClientImpl(const IOServicePtr &io_service, size_t thread_pool_size=0, bool defer_thread_start=false)
Constructor.
Definition: client.cc:1793
bool isStopped()
Indicates if the thread pool is stopped.
Definition: client.cc:1896
bool isRunning()
Indicates if the thread pool is running.
Definition: client.cc:1884
uint16_t getThreadCount() const
Fetches the number of threads in the pool.
Definition: client.cc:2049
bool isRunning()
Indicates if the thread pool is running.
Definition: client.cc:2054
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:1957
std::function< void(const boost::system::error_code &, const HttpResponsePtr &, const std::string &)> RequestHandler
Callback type used in call to HttpClient::asyncSendRequest.
Definition: client.h:102
void stop()
Halts client-side IO activity.
Definition: client.cc:2034
bool isPaused()
Indicates if the thread pool is paused.
Definition: client.cc:2064
void pause()
Pauses the client's thread pool.
Definition: client.cc:2024
std::function< void(const int)> CloseHandler
Optional handler invoked when client closes the connection to the server.
Definition: client.h:132
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:2039
void start()
Starts running the client's thread pool, if multi-threaded.
Definition: client.cc:2014
std::function< bool(const boost::system::error_code &, const int)> ConnectHandler
Optional handler invoked when client connects to the server.
Definition: client.h:114
uint16_t getThreadPoolSize() const
Fetches the maximum size of the thread pool.
Definition: client.cc:2044
std::function< bool(const boost::system::error_code &, const int)> HandshakeHandler
Optional handler invoked when client performs the TLS handshake with the server.
Definition: client.h:127
void closeIfOutOfBand(int socket_fd)
Closes a connection if it has an out-of-band socket event.
Definition: client.cc:2009
~HttpClient()
Destructor.
Definition: client.cc:1969
void resume()
Resumes running the client's thread pool.
Definition: client.cc:2029
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:1973
bool isStopped()
Indicates if the thread pool is stopped.
Definition: client.cc:2059
void checkPermissions()
Check if the current thread can perform thread pool state transition.
Definition: client.cc:2019
static std::string logFormatHttpMessage(const std::string &message, const size_t limit=0)
Formats provided HTTP message for logging.
A generic parser for HTTP responses.
Represents an URL.
Definition: url.h:20
Scheme getScheme() const
Returns parsed scheme.
Definition: url.cc:31
bool isValid() const
Checks if the URL is valid.
Definition: url.h:47
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
Definition: http_messages.h:16
const isc::log::MessageID HTTP_CONNECTION_CLOSE_CALLBACK_FAILED
Definition: http_messages.h:23
const isc::log::MessageID HTTP_BAD_SERVER_RESPONSE_RECEIVED
Definition: http_messages.h:14
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
Definition: http_messages.h:34
boost::shared_ptr< HttpResponseParser > HttpResponseParserPtr
Pointer to the HttpResponseParser.
const isc::log::MessageID HTTP_CLIENT_REQUEST_SEND
Definition: http_messages.h:20
boost::shared_ptr< HttpResponse > HttpResponsePtr
Pointer to the HttpResponse object.
Definition: response.h:81
const isc::log::MessageID HTTP_BAD_SERVER_RESPONSE_RECEIVED_DETAILS
Definition: http_messages.h:15
const isc::log::MessageID HTTP_CLIENT_REQUEST_SEND_DETAILS
Definition: http_messages.h:21
const isc::log::MessageID HTTP_CLIENT_QUEUE_SIZE_GROWING
Definition: http_messages.h:17
const isc::log::MessageID HTTP_SERVER_RESPONSE_RECEIVED_DETAILS
Definition: http_messages.h:35
boost::shared_ptr< HttpRequest > HttpRequestPtr
Pointer to the HttpRequest object.
Definition: request.h:30
const isc::log::MessageID HTTP_PREMATURE_CONNECTION_TIMEOUT_OCCURRED
Definition: http_messages.h:32
const int DBGLVL_TRACE_BASIC
Trace basic operations.
Definition: log_dbglevels.h:69
const int DBGLVL_TRACE_DETAIL_DATA
Trace data associated with detailed operations.
Definition: log_dbglevels.h:78
const int DBGLVL_TRACE_BASIC_DATA
Trace data associated with the basic operations.
Definition: log_dbglevels.h:72
const int DBGLVL_TRACE_DETAIL
Trace detailed operations.
Definition: log_dbglevels.h:75
Defines the logger used by the top-level component of kea-lfc.
HTTP request/response timeout value.
Definition: client.h:89
long value_
Timeout value specified.
Definition: client.h:96