Kea 2.5.5
client.cc
Go to the documentation of this file.
1// Copyright (C) 2018-2023 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(IOService& 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
409 boost::weak_ptr<ConnectionPool> conn_pool_;
410
412 Url url_;
413
415 TlsContextPtr tls_context_;
416
418 std::unique_ptr<TCPSocket<SocketCallback> > tcp_socket_;
419
421 std::unique_ptr<TLSSocket<SocketCallback> > tls_socket_;
422
424 IntervalTimer timer_;
425
427 HttpRequestPtr current_request_;
428
430 HttpResponsePtr current_response_;
431
433 HttpResponseParserPtr parser_;
434
436 HttpClient::RequestHandler current_callback_;
437
439 std::string buf_;
440
442 std::array<char, 32768> input_buf_;
443
445 uint64_t current_transid_;
446
448 HttpClient::HandshakeHandler handshake_callback_;
449
451 HttpClient::CloseHandler close_callback_;
452
454 std::atomic<bool> started_;
455
457 std::atomic<bool> need_handshake_;
458
460 std::atomic<bool> closed_;
461
463 std::mutex mutex_;
464};
465
467typedef boost::shared_ptr<Connection> ConnectionPtr;
468
476class ConnectionPool : public boost::enable_shared_from_this<ConnectionPool> {
477public:
478
485 explicit ConnectionPool(IOService& io_service, size_t max_url_connections)
486 : io_service_(io_service), destinations_(), pool_mutex_(),
487 max_url_connections_(max_url_connections) {
488 }
489
493 ~ConnectionPool() {
494 closeAll();
495 }
496
502 void processNextRequest(const Url& url, const TlsContextPtr& tls_context) {
503 if (MultiThreadingMgr::instance().getMode()) {
504 std::lock_guard<std::mutex> lk(pool_mutex_);
505 return (processNextRequestInternal(url, tls_context));
506 } else {
507 return (processNextRequestInternal(url, tls_context));
508 }
509 }
510
516 void postProcessNextRequest(const Url& url,
517 const TlsContextPtr& tls_context) {
518 io_service_.post(std::bind(&ConnectionPool::processNextRequest,
519 shared_from_this(), url, tls_context));
520 }
521
542 void queueRequest(const Url& url,
543 const TlsContextPtr& tls_context,
544 const HttpRequestPtr& request,
545 const HttpResponsePtr& response,
546 const long request_timeout,
547 const HttpClient::RequestHandler& request_callback,
548 const HttpClient::ConnectHandler& connect_callback,
549 const HttpClient::HandshakeHandler& handshake_callback,
550 const HttpClient::CloseHandler& close_callback) {
551 if (MultiThreadingMgr::instance().getMode()) {
552 std::lock_guard<std::mutex> lk(pool_mutex_);
553 return (queueRequestInternal(url, tls_context, request, response,
554 request_timeout, request_callback,
555 connect_callback, handshake_callback,
556 close_callback));
557 } else {
558 return (queueRequestInternal(url, tls_context, request, response,
559 request_timeout, request_callback,
560 connect_callback, handshake_callback,
561 close_callback));
562 }
563 }
564
567 void closeAll() {
568 if (MultiThreadingMgr::instance().getMode()) {
569 std::lock_guard<std::mutex> lk(pool_mutex_);
570 closeAllInternal();
571 } else {
572 closeAllInternal();
573 }
574 }
575
588 void closeIfOutOfBand(int socket_fd) {
589 if (MultiThreadingMgr::instance().getMode()) {
590 std::lock_guard<std::mutex> lk(pool_mutex_);
591 closeIfOutOfBandInternal(socket_fd);
592 } else {
593 closeIfOutOfBandInternal(socket_fd);
594 }
595 }
596
597private:
598
606 void processNextRequestInternal(const Url& url,
607 const TlsContextPtr& tls_context) {
608 // Check if there is a queue for this URL. If there is no queue, there
609 // is no request queued either.
610 DestinationPtr destination = findDestination(url, tls_context);
611 if (destination) {
612 // Remove closed connections.
613 destination->garbageCollectConnections();
614 if (!destination->queueEmpty()) {
615 // We have at least one queued request. Do we have an
616 // idle connection?
617 ConnectionPtr connection = destination->getIdleConnection();
618 if (!connection) {
619 // No idle connections.
620 if (destination->connectionsFull()) {
621 return;
622 }
623 // Room to make another connection with this destination,
624 // so make one.
625 connection.reset(new Connection(io_service_, tls_context,
626 shared_from_this(), url));
627 destination->addConnection(connection);
628 }
629
630 // Dequeue the oldest request and start a transaction for it using
631 // the idle connection.
632 RequestDescriptor desc = destination->popNextRequest();
633 connection->doTransaction(desc.request_, desc.response_,
634 desc.request_timeout_, desc.callback_,
635 desc.connect_callback_,
636 desc.handshake_callback_,
637 desc.close_callback_);
638 }
639 }
640 }
641
664 void queueRequestInternal(const Url& url,
665 const TlsContextPtr& tls_context,
666 const HttpRequestPtr& request,
667 const HttpResponsePtr& response,
668 const long request_timeout,
669 const HttpClient::RequestHandler& request_callback,
670 const HttpClient::ConnectHandler& connect_callback,
671 const HttpClient::HandshakeHandler& handshake_callback,
672 const HttpClient::CloseHandler& close_callback) {
673 ConnectionPtr connection;
674 // Find the destination for the requested URL.
675 DestinationPtr destination = findDestination(url, tls_context);
676 if (destination) {
677 // Remove closed connections.
678 destination->garbageCollectConnections();
679 // Found it, look for an idle connection.
680 connection = destination->getIdleConnection();
681 } else {
682 // Doesn't exist yet so it's a new destination.
683 destination = addDestination(url, tls_context);
684 }
685
686 if (!connection) {
687 if (destination->connectionsFull()) {
688 // All connections busy, queue it.
689 destination->pushRequest(RequestDescriptor(request, response,
690 request_timeout,
691 request_callback,
692 connect_callback,
693 handshake_callback,
694 close_callback));
695 return;
696 }
697
698 // Room to make another connection with this destination, so make one.
699 connection.reset(new Connection(io_service_, tls_context,
700 shared_from_this(), url));
701 destination->addConnection(connection);
702 }
703
704 // Use the connection to start the transaction.
705 connection->doTransaction(request, response, request_timeout, request_callback,
706 connect_callback, handshake_callback, close_callback);
707 }
708
713 void closeAllInternal() {
714 for (auto const& destination : destinations_) {
715 destination.second->closeAllConnections();
716 }
717
718 destinations_.clear();
719 }
720
735 void closeIfOutOfBandInternal(int socket_fd) {
736 for (auto const& destination : destinations_) {
737 // First we look for a connection with the socket.
738 ConnectionPtr connection = destination.second->findBySocketFd(socket_fd);
739 if (connection) {
740 if (!connection->isTransactionOngoing()) {
741 // Socket has no transaction, so any ready event is
742 // out-of-band (other end probably closed), so
743 // let's close it. Note we do not remove any queued
744 // requests, as this might somehow be occurring in
745 // between them.
746 destination.second->closeConnection(connection);
747 }
748
749 return;
750 }
751 }
752 }
753
756 struct RequestDescriptor {
770 RequestDescriptor(const HttpRequestPtr& request,
771 const HttpResponsePtr& response,
772 const long& request_timeout,
773 const HttpClient::RequestHandler& callback,
774 const HttpClient::ConnectHandler& connect_callback,
775 const HttpClient::HandshakeHandler& handshake_callback,
776 const HttpClient::CloseHandler& close_callback)
777 : request_(request), response_(response),
778 request_timeout_(request_timeout), callback_(callback),
779 connect_callback_(connect_callback),
780 handshake_callback_(handshake_callback),
781 close_callback_(close_callback) {
782 }
783
785 HttpRequestPtr request_;
786
788 HttpResponsePtr response_;
789
791 long request_timeout_;
792
795
797 HttpClient::ConnectHandler connect_callback_;
798
800 HttpClient::HandshakeHandler handshake_callback_;
801
803 HttpClient::CloseHandler close_callback_;
804 };
805
807 typedef std::pair<Url, TlsContextPtr> DestinationDescriptor;
808
810 class Destination {
811 public:
813 const size_t QUEUE_SIZE_THRESHOLD = 2048;
815 const int QUEUE_WARN_SECS = 5;
816
823 Destination(Url const& url, TlsContextPtr tls_context, size_t max_connections)
824 : url_(url), tls_context_(tls_context),
825 max_connections_(max_connections), connections_(), queue_(),
826 last_queue_warn_time_(min_date_time), last_queue_size_(0) {
827 }
828
830 ~Destination() {
831 closeAllConnections();
832 }
833
841 void addConnection(ConnectionPtr connection) {
842 if (connectionsFull()) {
843 isc_throw(BadValue, "URL: " << url_.toText()
844 << ", already at maximum connections: "
845 << max_connections_);
846 }
847
848 connections_.push_back(connection);
849 }
850
855 void closeConnection(ConnectionPtr connection) {
856 for (auto it = connections_.begin(); it != connections_.end(); ++it) {
857 if (*it == connection) {
858 (*it)->close();
859 connections_.erase(it);
860 break;
861 }
862 }
863 }
864
867 void closeAllConnections() {
868 // Flush the queue.
869 while (!queue_.empty()) {
870 queue_.pop();
871 }
872
873 for (auto const& connection : connections_) {
874 connection->close();
875 }
876
877 connections_.clear();
878 }
879
903 void garbageCollectConnections() {
904 for (auto it = connections_.begin(); it != connections_.end();) {
905 (*it)->isClosedByPeer();
906 if (!(*it)->isClosed()) {
907 ++it;
908 } else {
909 it = connections_.erase(it);
910 }
911 }
912 }
913
925 ConnectionPtr getIdleConnection() {
926 for (auto const& connection : connections_) {
927 if (!connection->isTransactionOngoing() &&
928 !connection->isClosed()) {
929 return (connection);
930 }
931 }
932
933 return (ConnectionPtr());
934 }
935
942 ConnectionPtr findBySocketFd(int socket_fd) {
943 for (auto const& connection : connections_) {
944 if (connection->isMySocket(socket_fd)) {
945 return (connection);
946 }
947 }
948
949 return (ConnectionPtr());
950 }
951
955 bool connectionsEmpty() {
956 return (connections_.empty());
957 }
958
962 bool connectionsFull() {
963 return (connections_.size() >= max_connections_);
964 }
965
969 size_t connectionCount() {
970 return (connections_.size());
971 }
972
976 size_t getMaxConnections() const {
977 return (max_connections_);
978 }
979
983 bool queueEmpty() const {
984 return (queue_.empty());
985 }
986
993 void pushRequest(RequestDescriptor const& desc) {
994 queue_.push(desc);
995 size_t size = queue_.size();
996 // If the queue size is larger than the threshold and growing, issue a
997 // periodic warning.
998 if ((size > QUEUE_SIZE_THRESHOLD) && (size > last_queue_size_)) {
999 ptime now = microsec_clock::universal_time();
1000 if ((now - last_queue_warn_time_) > seconds(QUEUE_WARN_SECS)) {
1002 .arg(url_.toText())
1003 .arg(size);
1004 // Remember the last time we warned.
1005 last_queue_warn_time_ = now;
1006 }
1007 }
1008
1009 // Remember the previous size.
1010 last_queue_size_ = size;
1011 }
1012
1016 RequestDescriptor popNextRequest() {
1017 if (queue_.empty()) {
1018 isc_throw(InvalidOperation, "cannot pop, queue is empty");
1019 }
1020
1021 RequestDescriptor desc = queue_.front();
1022 queue_.pop();
1023 return (desc);
1024 }
1025
1026 private:
1028 Url url_;
1029
1031 TlsContextPtr tls_context_;
1032
1034 size_t max_connections_;
1035
1037 std::list<ConnectionPtr> connections_;
1038
1040 std::queue<RequestDescriptor> queue_;
1041
1043 ptime last_queue_warn_time_;
1044
1046 size_t last_queue_size_;
1047 };
1048
1050 typedef boost::shared_ptr<Destination> DestinationPtr;
1051
1059 DestinationPtr addDestination(const Url& url,
1060 const TlsContextPtr& tls_context) {
1061 const DestinationDescriptor& desc = std::make_pair(url, tls_context);
1062 DestinationPtr destination(new Destination(url, tls_context,
1063 max_url_connections_));
1064 destinations_[desc] = destination;
1065 return (destination);
1066 }
1067
1076 DestinationPtr findDestination(const Url& url,
1077 const TlsContextPtr& tls_context) const {
1078 const DestinationDescriptor& desc = std::make_pair(url, tls_context);
1079 auto it = destinations_.find(desc);
1080 if (it != destinations_.end()) {
1081 return (it->second);
1082 }
1083
1084 return (DestinationPtr());
1085 }
1086
1098 void removeDestination(const Url& url,
1099 const TlsContextPtr& tls_context) {
1100 const DestinationDescriptor& desc = std::make_pair(url, tls_context);
1101 auto it = destinations_.find(desc);
1102 if (it != destinations_.end()) {
1103 it->second->closeAllConnections();
1104 destinations_.erase(it);
1105 }
1106 }
1107
1109 IOService& io_service_;
1110
1112 std::map<DestinationDescriptor, DestinationPtr> destinations_;
1113
1115 std::mutex pool_mutex_;
1116
1118 size_t max_url_connections_;
1119};
1120
1121Connection::Connection(IOService& io_service,
1122 const TlsContextPtr& tls_context,
1123 const ConnectionPoolPtr& conn_pool,
1124 const Url& url)
1125 : conn_pool_(conn_pool), url_(url), tls_context_(tls_context),
1126 tcp_socket_(), tls_socket_(), timer_(io_service),
1127 current_request_(), current_response_(), parser_(),
1128 current_callback_(), buf_(), input_buf_(), current_transid_(0),
1129 close_callback_(), started_(false), need_handshake_(false),
1130 closed_(false) {
1131 if (!tls_context) {
1132 tcp_socket_.reset(new asiolink::TCPSocket<SocketCallback>(io_service));
1133 } else {
1134 tls_socket_.reset(new asiolink::TLSSocket<SocketCallback>(io_service,
1135 tls_context));
1136 need_handshake_ = true;
1137 }
1138}
1139
1140Connection::~Connection() {
1141 close();
1142}
1143
1144void
1145Connection::resetState() {
1146 started_ = false;
1147 current_request_.reset();
1148 current_response_.reset();
1149 parser_.reset();
1150 current_callback_ = HttpClient::RequestHandler();
1151}
1152
1153void
1154Connection::closeCallback(const bool clear) {
1155 if (close_callback_) {
1156 try {
1157 if (tcp_socket_) {
1158 close_callback_(tcp_socket_->getNative());
1159 } else if (tls_socket_) {
1160 close_callback_(tls_socket_->getNative());
1161 } else {
1163 "internal error: can't find a socket to close");
1164 }
1165 } catch (...) {
1167 }
1168 }
1169
1170 if (clear) {
1171 close_callback_ = HttpClient::CloseHandler();
1172 }
1173}
1174
1175void
1176Connection::isClosedByPeer() {
1177 // This method applies only to idle connections.
1178 if (started_ || closed_) {
1179 return;
1180 }
1181 // This code was guarded by a lock so keep this.
1182 if (MultiThreadingMgr::instance().getMode()) {
1183 std::lock_guard<std::mutex> lk(mutex_);
1184 isClosedByPeerInternal();
1185 } else {
1186 isClosedByPeerInternal();
1187 }
1188}
1189
1190void
1191Connection::isClosedByPeerInternal() {
1192 // If the socket is open we check if it is possible to transmit
1193 // the data over this socket by reading from it with message
1194 // peeking. If the socket is not usable, we close it and then
1195 // re-open it. There is a narrow window of time between checking
1196 // the socket usability and actually transmitting the data over
1197 // this socket, when the peer may close the connection. In this
1198 // case we'll need to re-transmit but we don't handle it here.
1199 if (tcp_socket_) {
1200 if (tcp_socket_->getASIOSocket().is_open() &&
1201 !tcp_socket_->isUsable()) {
1202 closeCallback();
1203 closed_ = true;
1204 tcp_socket_->close();
1205 }
1206 } else if (tls_socket_) {
1207 if (tls_socket_->getASIOSocket().is_open() &&
1208 !tls_socket_->isUsable()) {
1209 closeCallback();
1210 closed_ = true;
1211 tls_socket_->close();
1212 }
1213 } else {
1214 isc_throw(Unexpected, "internal error: can't find the sending socket");
1215 }
1216}
1217
1218void
1219Connection::doTransaction(const HttpRequestPtr& request,
1220 const HttpResponsePtr& response,
1221 const long request_timeout,
1222 const HttpClient::RequestHandler& callback,
1223 const HttpClient::ConnectHandler& connect_callback,
1224 const HttpClient::HandshakeHandler& handshake_callback,
1225 const HttpClient::CloseHandler& close_callback) {
1226 if (MultiThreadingMgr::instance().getMode()) {
1227 std::lock_guard<std::mutex> lk(mutex_);
1228 doTransactionInternal(request, response, request_timeout,
1229 callback, connect_callback, handshake_callback,
1230 close_callback);
1231 } else {
1232 doTransactionInternal(request, response, request_timeout,
1233 callback, connect_callback, handshake_callback,
1234 close_callback);
1235 }
1236}
1237
1238void
1239Connection::doTransactionInternal(const HttpRequestPtr& request,
1240 const HttpResponsePtr& response,
1241 const long request_timeout,
1242 const HttpClient::RequestHandler& callback,
1243 const HttpClient::ConnectHandler& connect_callback,
1244 const HttpClient::HandshakeHandler& handshake_callback,
1245 const HttpClient::CloseHandler& close_callback) {
1246 try {
1247 started_ = true;
1248 current_request_ = request;
1249 current_response_ = response;
1250 parser_.reset(new HttpResponseParser(*current_response_));
1251 parser_->initModel();
1252 current_callback_ = callback;
1253 handshake_callback_ = handshake_callback;
1254 close_callback_ = close_callback;
1255
1256 // Starting new transaction. Generate new transaction id.
1257 ++current_transid_;
1258
1259 buf_ = request->toString();
1260
1263 .arg(request->toBriefString())
1264 .arg(url_.toText());
1265
1268 .arg(url_.toText())
1269 .arg(HttpMessageParserBase::logFormatHttpMessage(request->toString(),
1270 MAX_LOGGED_MESSAGE_SIZE));
1271
1272 // Setup request timer.
1273 scheduleTimer(request_timeout);
1274
1278 TCPEndpoint endpoint(url_.getStrippedHostname(),
1279 static_cast<unsigned short>(url_.getPort()));
1280 SocketCallback socket_cb(std::bind(&Connection::connectCallback, shared_from_this(),
1281 connect_callback, current_transid_,
1282 ph::_1));
1283
1284 // Establish new connection or use existing connection.
1285 if (tcp_socket_) {
1286 tcp_socket_->open(&endpoint, socket_cb);
1287 return;
1288 }
1289 if (tls_socket_) {
1290 tls_socket_->open(&endpoint, socket_cb);
1291 return;
1292 }
1293
1294 // Should never reach this point.
1295 isc_throw(Unexpected, "internal error: can't find a socket to open");
1296
1297 } catch (const std::exception& ex) {
1298 // Re-throw with the expected exception type.
1300 }
1301}
1302
1303void
1304Connection::close() {
1305 if (MultiThreadingMgr::instance().getMode()) {
1306 std::lock_guard<std::mutex> lk(mutex_);
1307 return (closeInternal());
1308 } else {
1309 return (closeInternal());
1310 }
1311}
1312
1313void
1314Connection::closeInternal() {
1315 // Pass in true to discard the callback.
1316 closeCallback(true);
1317
1318 closed_ = true;
1319 timer_.cancel();
1320 if (tcp_socket_) {
1321 tcp_socket_->close();
1322 }
1323 if (tls_socket_) {
1324 tls_socket_->close();
1325 }
1326
1327 resetState();
1328}
1329
1330bool
1331Connection::isMySocket(int socket_fd) const {
1332 if (tcp_socket_) {
1333 return (tcp_socket_->getNative() == socket_fd);
1334 } else if (tls_socket_) {
1335 return (tls_socket_->getNative() == socket_fd);
1336 }
1337 // Should never reach this point.
1338 std::cerr << "internal error: can't find my socket\n";
1339 return (false);
1340}
1341
1342bool
1343Connection::checkPrematureTimeout(const uint64_t transid) {
1344 if (MultiThreadingMgr::instance().getMode()) {
1345 std::lock_guard<std::mutex> lk(mutex_);
1346 return (checkPrematureTimeoutInternal(transid));
1347 } else {
1348 return (checkPrematureTimeoutInternal(transid));
1349 }
1350}
1351
1352bool
1353Connection::checkPrematureTimeoutInternal(const uint64_t transid) {
1354 // If there is no transaction but the handlers are invoked it means
1355 // that the last transaction in the queue timed out prematurely.
1356 // Also, if there is a transaction in progress but the ID of that
1357 // transaction doesn't match the one associated with the handler it,
1358 // also means that the transaction timed out prematurely.
1359 if (!isTransactionOngoing() || (transid != current_transid_)) {
1361 .arg(isTransactionOngoing())
1362 .arg(transid)
1363 .arg(current_transid_);
1364 return (true);
1365 }
1366
1367 return (false);
1368}
1369
1370void
1371Connection::terminate(const boost::system::error_code& ec,
1372 const std::string& parsing_error) {
1373 if (MultiThreadingMgr::instance().getMode()) {
1374 std::lock_guard<std::mutex> lk(mutex_);
1375 terminateInternal(ec, parsing_error);
1376 } else {
1377 terminateInternal(ec, parsing_error);
1378 }
1379}
1380
1381void
1382Connection::terminateInternal(const boost::system::error_code& ec,
1383 const std::string& parsing_error) {
1384 HttpResponsePtr response;
1385 if (isTransactionOngoing()) {
1386
1387 timer_.cancel();
1388 if (tcp_socket_) {
1389 tcp_socket_->cancel();
1390 }
1391 if (tls_socket_) {
1392 tls_socket_->cancel();
1393 }
1394
1395 if (!ec && current_response_->isFinalized()) {
1396 response = current_response_;
1397
1400 .arg(url_.toText());
1401
1404 .arg(url_.toText())
1405 .arg(parser_ ?
1406 parser_->getBufferAsString(MAX_LOGGED_MESSAGE_SIZE) :
1407 "[HttpResponseParser is null]");
1408
1409 } else {
1410 std::string err = parsing_error.empty() ? ec.message() :
1411 parsing_error;
1412
1415 .arg(url_.toText())
1416 .arg(err);
1417
1418 // Only log the details if we have received anything and tried
1419 // to parse it.
1420 if (!parsing_error.empty()) {
1423 .arg(url_.toText())
1424 .arg(parser_ ?
1425 parser_->getBufferAsString(MAX_LOGGED_MESSAGE_SIZE) :
1426 "[HttpResponseParser is null]");
1427 }
1428 }
1429
1430 try {
1431 // The callback should take care of its own exceptions but one
1432 // never knows.
1433 if (MultiThreadingMgr::instance().getMode()) {
1434 UnlockGuard<std::mutex> lock(mutex_);
1435 current_callback_(ec, response, parsing_error);
1436 } else {
1437 current_callback_(ec, response, parsing_error);
1438 }
1439 } catch (...) {
1440 }
1441
1442 // If we're not requesting connection persistence or the
1443 // connection has timed out, we should close the socket.
1444 if (!closed_ &&
1445 (!current_request_->isPersistent() ||
1446 (ec == boost::asio::error::timed_out))) {
1447 closeInternal();
1448 }
1449
1450 resetState();
1451 }
1452
1453 // Check if there are any requests queued for this destination and start
1454 // another transaction if there is at least one.
1455 ConnectionPoolPtr conn_pool = conn_pool_.lock();
1456 if (conn_pool) {
1457 conn_pool->postProcessNextRequest(url_, tls_context_);
1458 }
1459}
1460
1461void
1462Connection::scheduleTimer(const long request_timeout) {
1463 if (request_timeout > 0) {
1464 timer_.setup(std::bind(&Connection::timerCallback, this), request_timeout,
1465 IntervalTimer::ONE_SHOT);
1466 }
1467}
1468
1469void
1470Connection::doHandshake(const uint64_t transid) {
1471 // Skip the handshake if it is not needed.
1472 if (!need_handshake_) {
1473 doSend(transid);
1474 return;
1475 }
1476
1477 SocketCallback socket_cb(std::bind(&Connection::handshakeCallback,
1478 shared_from_this(),
1479 handshake_callback_,
1480 transid,
1481 ph::_1));
1482 try {
1483 tls_socket_->handshake(socket_cb);
1484
1485 } catch (...) {
1486 terminate(boost::asio::error::not_connected);
1487 }
1488}
1489
1490void
1491Connection::doSend(const uint64_t transid) {
1492 SocketCallback socket_cb(std::bind(&Connection::sendCallback,
1493 shared_from_this(),
1494 transid,
1495 ph::_1,
1496 ph::_2));
1497 try {
1498 if (tcp_socket_) {
1499 tcp_socket_->asyncSend(&buf_[0], buf_.size(), socket_cb);
1500 return;
1501 }
1502
1503 if (tls_socket_) {
1504 tls_socket_->asyncSend(&buf_[0], buf_.size(), socket_cb);
1505 return;
1506 }
1507
1508 // Should never reach this point.
1509 std::cerr << "internal error: can't find a socket to send to\n";
1511 "internal error: can't find a socket to send to");
1512 } catch (...) {
1513 terminate(boost::asio::error::not_connected);
1514 }
1515}
1516
1517void
1518Connection::doReceive(const uint64_t transid) {
1519 TCPEndpoint endpoint;
1520 SocketCallback socket_cb(std::bind(&Connection::receiveCallback,
1521 shared_from_this(),
1522 transid,
1523 ph::_1,
1524 ph::_2));
1525 try {
1526 if (tcp_socket_) {
1527 tcp_socket_->asyncReceive(static_cast<void*>(input_buf_.data()),
1528 input_buf_.size(), 0,
1529 &endpoint, socket_cb);
1530 return;
1531 }
1532 if (tls_socket_) {
1533 tls_socket_->asyncReceive(static_cast<void*>(input_buf_.data()),
1534 input_buf_.size(), 0,
1535 &endpoint, socket_cb);
1536 return;
1537 }
1538 // Should never reach this point.
1539 std::cerr << "internal error: can't find a socket to receive from\n";
1541 "internal error: can't find a socket to receive from");
1542
1543 } catch (...) {
1544 terminate(boost::asio::error::not_connected);
1545 }
1546}
1547
1548void
1549Connection::connectCallback(HttpClient::ConnectHandler connect_callback,
1550 const uint64_t transid,
1551 const boost::system::error_code& ec) {
1552 if (checkPrematureTimeout(transid)) {
1553 return;
1554 }
1555
1556 // Run user defined connect callback if specified.
1557 if (connect_callback) {
1558 // If the user defined callback indicates that the connection
1559 // should not be continued.
1560 if (tcp_socket_) {
1561 if (!connect_callback(ec, tcp_socket_->getNative())) {
1562 return;
1563 }
1564 } else if (tls_socket_) {
1565 if (!connect_callback(ec, tls_socket_->getNative())) {
1566 return;
1567 }
1568 } else {
1569 // Should never reach this point.
1570 std::cerr << "internal error: can't find a socket to connect\n";
1571 }
1572 }
1573
1574 if (ec && (ec.value() == boost::asio::error::operation_aborted)) {
1575 return;
1576
1577 // In some cases the "in progress" status code may be returned. It doesn't
1578 // indicate an error. Sending the request over the socket is expected to
1579 // be successful. Getting such status appears to be highly dependent on
1580 // the operating system.
1581 } else if (ec &&
1582 (ec.value() != boost::asio::error::in_progress) &&
1583 (ec.value() != boost::asio::error::already_connected)) {
1584 terminate(ec);
1585
1586 } else {
1587 // Start the TLS handshake asynchronously.
1588 doHandshake(transid);
1589 }
1590}
1591
1592void
1593Connection::handshakeCallback(HttpClient::ConnectHandler handshake_callback,
1594 const uint64_t transid,
1595 const boost::system::error_code& ec) {
1596 need_handshake_ = false;
1597 if (checkPrematureTimeout(transid)) {
1598 return;
1599 }
1600
1601 // Run user defined handshake callback if specified.
1602 if (handshake_callback) {
1603 // If the user defined callback indicates that the connection
1604 // should not be continued.
1605 if (tls_socket_) {
1606 if (!handshake_callback(ec, tls_socket_->getNative())) {
1607 return;
1608 }
1609 } else {
1610 // Should never reach this point.
1611 std::cerr << "internal error: can't find TLS socket\n";
1612 }
1613 }
1614
1615 if (ec && (ec.value() == boost::asio::error::operation_aborted)) {
1616 return;
1617 } else if (ec) {
1618 terminate(ec);
1619
1620 } else {
1621 // Start sending the request asynchronously.
1622 doSend(transid);
1623 }
1624}
1625
1626void
1627Connection::sendCallback(const uint64_t transid,
1628 const boost::system::error_code& ec,
1629 size_t length) {
1630 if (checkPrematureTimeout(transid)) {
1631 return;
1632 }
1633
1634 if (ec) {
1635 if (ec.value() == boost::asio::error::operation_aborted) {
1636 return;
1637
1638 // EAGAIN and EWOULDBLOCK don't really indicate an error. The length
1639 // should be 0 in this case but let's be sure.
1640 } else if ((ec.value() == boost::asio::error::would_block) ||
1641 (ec.value() == boost::asio::error::try_again)) {
1642 length = 0;
1643
1644 } else {
1645 // Any other error should cause the transaction to terminate.
1646 terminate(ec);
1647 return;
1648 }
1649 }
1650
1651 // Sending is in progress, so push back the timeout.
1652 scheduleTimer(timer_.getInterval());
1653
1654 // If any data have been sent, remove it from the buffer and only leave the
1655 // portion that still has to be sent.
1656 if (length > 0) {
1657 buf_.erase(0, length);
1658 }
1659
1660 // If there is no more data to be sent, start receiving a response. Otherwise,
1661 // continue sending.
1662 if (buf_.empty()) {
1663 doReceive(transid);
1664
1665 } else {
1666 doSend(transid);
1667 }
1668}
1669
1670void
1671Connection::receiveCallback(const uint64_t transid,
1672 const boost::system::error_code& ec,
1673 size_t length) {
1674 if (checkPrematureTimeout(transid)) {
1675 return;
1676 }
1677
1678 if (ec) {
1679 if (ec.value() == boost::asio::error::operation_aborted) {
1680 return;
1681
1682 // EAGAIN and EWOULDBLOCK don't indicate an error in this case. All
1683 // other errors should terminate the transaction.
1684 }
1685 if ((ec.value() != boost::asio::error::try_again) &&
1686 (ec.value() != boost::asio::error::would_block)) {
1687 terminate(ec);
1688 return;
1689
1690 } else {
1691 // For EAGAIN and EWOULDBLOCK the length should be 0 anyway, but let's
1692 // make sure.
1693 length = 0;
1694 }
1695 }
1696
1697 // Receiving is in progress, so push back the timeout.
1698 scheduleTimer(timer_.getInterval());
1699
1700 if (runParser(ec, length)) {
1701 doReceive(transid);
1702 }
1703}
1704
1705bool
1706Connection::runParser(const boost::system::error_code& ec, size_t length) {
1707 if (MultiThreadingMgr::instance().getMode()) {
1708 std::lock_guard<std::mutex> lk(mutex_);
1709 return (runParserInternal(ec, length));
1710 } else {
1711 return (runParserInternal(ec, length));
1712 }
1713}
1714
1715bool
1716Connection::runParserInternal(const boost::system::error_code& ec,
1717 size_t length) {
1718 // If we have received any data, let's feed the parser with it.
1719 if (length != 0) {
1720 parser_->postBuffer(static_cast<void*>(input_buf_.data()), length);
1721 parser_->poll();
1722 }
1723
1724 // If the parser still needs data, let's schedule another receive.
1725 if (parser_->needData()) {
1726 return (true);
1727
1728 } else if (parser_->httpParseOk()) {
1729 // No more data needed and parsing has been successful so far. Let's
1730 // try to finalize the response parsing.
1731 try {
1732 current_response_->finalize();
1733 terminateInternal(ec);
1734
1735 } catch (const std::exception& ex) {
1736 // If there is an error here, we need to return the error message.
1737 terminateInternal(ec, ex.what());
1738 }
1739
1740 } else {
1741 // Parsing was unsuccessful. Let's pass the error message held in the
1742 // parser.
1743 terminateInternal(ec, parser_->getErrorMessage());
1744 }
1745
1746 return (false);
1747}
1748
1749void
1750Connection::timerCallback() {
1751 // Request timeout occurred.
1752 terminate(boost::asio::error::timed_out);
1753}
1754
1755}
1756
1757namespace isc {
1758namespace http {
1759
1762public:
1786 HttpClientImpl(IOService& io_service, size_t thread_pool_size = 0,
1787 bool defer_thread_start = false)
1788 : thread_pool_size_(thread_pool_size), thread_pool_() {
1789 if (thread_pool_size_ > 0) {
1790 // Create our own private IOService.
1791 thread_io_service_.reset(new IOService());
1792
1793 // Create the connection pool. Note that we use the thread_pool_size
1794 // as the maximum connections per URL value.
1795 conn_pool_.reset(new ConnectionPool(*thread_io_service_, thread_pool_size_));
1796
1797 // Create the thread pool.
1798 thread_pool_.reset(new IoServiceThreadPool(thread_io_service_, thread_pool_size_,
1799 defer_thread_start));
1800
1802 .arg(thread_pool_size_);
1803 } else {
1804 // Single-threaded mode: use the caller's IOService,
1805 // one connection per URL.
1806 conn_pool_.reset(new ConnectionPool(io_service, 1));
1807 }
1808 }
1809
1814 stop();
1815 }
1816
1823 if (thread_pool_) {
1824 thread_pool_->checkPausePermissions();
1825 }
1826 }
1827
1829 void start() {
1830 if (thread_pool_) {
1831 thread_pool_->run();
1832 }
1833 }
1834
1837 void stop() {
1838 // Close all the connections.
1839 conn_pool_->closeAll();
1840
1841 // Stop the thread pool.
1842 if (thread_pool_) {
1843 thread_pool_->stop();
1844 }
1845 }
1846
1851 void pause() {
1852 if (!thread_pool_) {
1853 isc_throw(InvalidOperation, "HttpClient::pause - no thread pool");
1854 }
1855
1856 // Pause the thread pool.
1857 thread_pool_->pause();
1858 }
1859
1864 void resume() {
1865 if (!thread_pool_) {
1866 isc_throw(InvalidOperation, "HttpClient::resume - no thread pool");
1867 }
1868
1869 // Resume running the thread pool.
1870 thread_pool_->run();
1871 }
1872
1877 bool isRunning() {
1878 if (thread_pool_) {
1879 return (thread_pool_->isRunning());
1880 }
1881
1882 return (false);
1883 }
1884
1889 bool isStopped() {
1890 if (thread_pool_) {
1891 return (thread_pool_->isStopped());
1892 }
1893
1894 return (false);
1895 }
1896
1901 bool isPaused() {
1902 if (thread_pool_) {
1903 return (thread_pool_->isPaused());
1904 }
1905
1906 return (false);
1907 }
1908
1914 return (thread_io_service_);
1915 };
1916
1921 return (thread_pool_size_);
1922 }
1923
1927 uint16_t getThreadCount() {
1928 if (!thread_pool_) {
1929 return (0);
1930 }
1931 return (thread_pool_->getThreadCount());
1932 }
1933
1935 ConnectionPoolPtr conn_pool_;
1936
1937private:
1938
1940 size_t thread_pool_size_;
1941
1943 asiolink::IOServicePtr thread_io_service_;
1944
1947 IoServiceThreadPoolPtr thread_pool_;
1948};
1949
1950HttpClient::HttpClient(IOService& io_service, bool multi_threading_enabled,
1951 size_t thread_pool_size, bool defer_thread_start) {
1952 if (!multi_threading_enabled && thread_pool_size) {
1954 "HttpClient thread_pool_size must be zero "
1955 "when Kea core multi-threading is disabled");
1956 }
1957
1958 impl_.reset(new HttpClientImpl(io_service, thread_pool_size,
1959 defer_thread_start));
1960}
1961
1963}
1964
1965void
1967 const TlsContextPtr& tls_context,
1968 const HttpRequestPtr& request,
1969 const HttpResponsePtr& response,
1970 const HttpClient::RequestHandler& request_callback,
1971 const HttpClient::RequestTimeout& request_timeout,
1972 const HttpClient::ConnectHandler& connect_callback,
1973 const HttpClient::HandshakeHandler& handshake_callback,
1974 const HttpClient::CloseHandler& close_callback) {
1975 if (!url.isValid()) {
1976 isc_throw(HttpClientError, "invalid URL specified for the HTTP client");
1977 }
1978
1979 if ((url.getScheme() == Url::Scheme::HTTPS) && !tls_context) {
1980 isc_throw(HttpClientError, "HTTPS URL scheme but no TLS context");
1981 }
1982
1983 if (!request) {
1984 isc_throw(HttpClientError, "HTTP request must not be null");
1985 }
1986
1987 if (!response) {
1988 isc_throw(HttpClientError, "HTTP response must not be null");
1989 }
1990
1991 if (!request_callback) {
1992 isc_throw(HttpClientError, "callback for HTTP transaction must not be null");
1993 }
1994
1995 impl_->conn_pool_->queueRequest(url, tls_context, request, response,
1996 request_timeout.value_,
1997 request_callback, connect_callback,
1998 handshake_callback, close_callback);
1999}
2000
2001void
2003 return (impl_->conn_pool_->closeIfOutOfBand(socket_fd));
2004}
2005
2006void
2008 impl_->start();
2009}
2010
2011void
2013 impl_->checkPermissions();
2014}
2015
2016void
2018 impl_->pause();
2019}
2020
2021void
2023 impl_->resume();
2024}
2025
2026void
2028 impl_->stop();
2029}
2030
2031const IOServicePtr
2033 return (impl_->getThreadIOService());
2034}
2035
2036uint16_t
2038 return (impl_->getThreadPoolSize());
2039}
2040
2041uint16_t
2043 return (impl_->getThreadCount());
2044}
2045
2046bool
2048 return (impl_->isRunning());
2049}
2050
2051bool
2053 return (impl_->isStopped());
2054}
2055
2056bool
2058 return (impl_->isPaused());
2059}
2060
2061} // end of namespace isc::http
2062} // 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:1761
HttpClientImpl(IOService &io_service, size_t thread_pool_size=0, bool defer_thread_start=false)
Constructor.
Definition: client.cc:1786
ConnectionPoolPtr conn_pool_
Holds a pointer to the connection pool.
Definition: client.cc:1935
uint16_t getThreadCount()
Fetches the number of threads in the pool.
Definition: client.cc:1927
~HttpClientImpl()
Destructor.
Definition: client.cc:1813
void pause()
Pauses the client's thread pool.
Definition: client.cc:1851
uint16_t getThreadPoolSize()
Fetches the maximum size of the thread pool.
Definition: client.cc:1920
void start()
Starts running the client's thread pool, if multi-threaded.
Definition: client.cc:1829
void stop()
Close all connections, and if multi-threaded, stops the client's thread pool.
Definition: client.cc:1837
asiolink::IOServicePtr getThreadIOService()
Fetches the internal IOService used in multi-threaded mode.
Definition: client.cc:1913
void checkPermissions()
Check if the current thread can perform thread pool state transition.
Definition: client.cc:1822
bool isPaused()
Indicates if the thread pool is paused.
Definition: client.cc:1901
void resume()
Resumes running the client's thread pool.
Definition: client.cc:1864
bool isStopped()
Indicates if the thread pool is stopped.
Definition: client.cc:1889
bool isRunning()
Indicates if the thread pool is running.
Definition: client.cc:1877
uint16_t getThreadCount() const
Fetches the number of threads in the pool.
Definition: client.cc:2042
bool isRunning()
Indicates if the thread pool is running.
Definition: client.cc:2047
HttpClient(asiolink::IOService &io_service, bool multi_threading_enabled, size_t thread_pool_size=0, bool defer_thread_start=false)
Constructor.
Definition: client.cc:1950
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:2027
bool isPaused()
Indicates if the thread pool is paused.
Definition: client.cc:2057
void pause()
Pauses the client's thread pool.
Definition: client.cc:2017
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:2032
void start()
Starts running the client's thread pool, if multi-threaded.
Definition: client.cc:2007
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:2037
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:2002
~HttpClient()
Destructor.
Definition: client.cc:1962
void resume()
Resumes running the client's thread pool.
Definition: client.cc:2022
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:1966
bool isStopped()
Indicates if the thread pool is stopped.
Definition: client.cc:2052
void checkPermissions()
Check if the current thread can perform thread pool state transition.
Definition: client.cc:2012
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
#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
Definition: edns.h:19
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