Kea 3.3.3
tcp_client.cc
Go to the documentation of this file.
1// Copyright (C) 2018-2026 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
12#include <asiolink/tls_socket.h>
13#include <tcp/tcp_client.h>
14#include <tcp/tcp_log.h>
15#include <tcp/tcp_messages.h>
17#include <util/str.h>
19#include <util/unlock_guard.h>
20
21#include <boost/enable_shared_from_this.hpp>
22#include <boost/weak_ptr.hpp>
23
24#include <atomic>
25#include <array>
26#include <functional>
27#include <iostream>
28#include <map>
29#include <mutex>
30#include <queue>
31#include <thread>
32
33using namespace isc;
34using namespace isc::asiolink;
35using namespace isc::tcp;
36using namespace isc::util;
37using namespace boost::posix_time;
38
39namespace ph = std::placeholders;
40
41namespace {
42
44typedef std::function<void(boost::system::error_code ec, size_t length)>
46
52class SocketCallback {
53public:
54
60 SocketCallback(SocketCallbackFunction socket_callback)
61 : callback_(socket_callback) {
62 }
63
70 void operator()(boost::system::error_code ec, size_t length = 0) {
71 if (ec.value() == boost::asio::error::operation_aborted) {
72 return;
73 }
74 callback_(ec, length);
75 }
76
77private:
78
80 SocketCallbackFunction callback_;
81
82};
83
84class ConnectionPool;
85
87typedef boost::shared_ptr<ConnectionPool> ConnectionPoolPtr;
88
104class Connection : public boost::enable_shared_from_this<Connection> {
105public:
106
115 explicit Connection(const IOServicePtr& io_service,
116 const TlsContextPtr& tls_context,
117 const ConnectionPoolPtr& conn_pool,
118 const IOAddress& address,
119 const uint16_t port);
120
122 ~Connection();
123
141 void doTransaction(const WireDataPtr& request,
142 const WireDataPtr& response,
143 const bool persistent,
144 const long request_timeout,
145 const TcpClient::CompleteCheck& complete_check,
146 const TcpClient::RequestHandler& callback,
147 const TcpClient::ConnectHandler& connect_callback,
148 const TcpClient::HandshakeHandler& handshake_callback,
149 const TcpClient::CloseHandler& close_callback);
150
152 void close();
153
157 bool isTransactionOngoing() const {
158 return (started_);
159 }
160
164 bool isClosed() const {
165 return (closed_);
166 }
167
172 void isClosedByPeer();
173
179 bool isMySocket(int socket_fd) const;
180
196 bool checkPrematureTimeout(const uint64_t transid);
197
198private:
199
219 void doTransactionInternal(const WireDataPtr& request,
220 const WireDataPtr& response,
221 const bool persistent,
222 const long request_timeout,
223 const TcpClient::CompleteCheck& complete_check,
224 const TcpClient::RequestHandler& callback,
225 const TcpClient::ConnectHandler& connect_callback,
226 const TcpClient::HandshakeHandler& handshake_callback,
227 const TcpClient::CloseHandler& close_callback);
228
232 void closeInternal();
233
240 void isClosedByPeerInternal();
241
259 bool checkPrematureTimeoutInternal(const uint64_t transid);
260
267 void resetState();
268
278 void terminate(const boost::system::error_code& ec,
279 const std::string& error_msg = "");
280
292 void terminateInternal(const boost::system::error_code& ec,
293 std::string error_msg = "");
294
301 bool runCompleteCheck(const boost::system::error_code& ec, size_t length);
302
311 bool runCompleteCheckInternal(const boost::system::error_code& ec, size_t length);
312
316 void scheduleTimer(const long request_timeout);
317
323 void doHandshake(const uint64_t transid);
324
330 void doSend(const uint64_t transid);
331
337 void doReceive(const uint64_t transid);
338
349 void connectCallback(TcpClient::ConnectHandler connect_callback,
350 const uint64_t transid,
351 const boost::system::error_code& ec);
352
362 void handshakeCallback(TcpClient::HandshakeHandler handshake_callback,
363 const uint64_t transid,
364 const boost::system::error_code& ec);
365
376 void sendCallback(const uint64_t transid, const boost::system::error_code& ec,
377 size_t length);
378
385 void receiveCallback(const uint64_t transid,
386 const boost::system::error_code& ec,
387 size_t length);
388
390 void timerCallback();
391
401 void closeCallback(const bool clear = false);
402
404 IOServicePtr io_service_;
405
410 boost::weak_ptr<ConnectionPool> conn_pool_;
411
413 IOAddress address_;
414
416 uint16_t port_;
417
419 TlsContextPtr tls_context_;
420
422 std::shared_ptr<TCPSocket<SocketCallback>> tcp_socket_;
423
425 std::shared_ptr<TLSSocket<SocketCallback>> tls_socket_;
426
428 IntervalTimerPtr timer_;
429
431 WireDataPtr current_request_;
432
434 WireDataPtr current_response_;
435
437 bool current_persistent_;
438
440 bool current_response_complete_;
441
443 TcpClient::CompleteCheck current_complete_check_;
444
446 TcpClient::RequestHandler current_callback_;
447
449 std::vector<uint8_t> buf_;
450
452 size_t position_;
453
455 std::array<uint8_t, 32768> input_buf_;
456
458 uint64_t current_transid_;
459
461 TcpClient::HandshakeHandler handshake_callback_;
462
464 TcpClient::CloseHandler close_callback_;
465
467 std::atomic<bool> started_;
468
470 std::atomic<bool> need_handshake_;
471
473 std::atomic<bool> closed_;
474
476 std::mutex mutex_;
477};
478
480typedef boost::shared_ptr<Connection> ConnectionPtr;
481
489class ConnectionPool : public boost::enable_shared_from_this<ConnectionPool> {
490public:
491
498 explicit ConnectionPool(const IOServicePtr& io_service, size_t max_addr_connections)
499 : io_service_(io_service), destinations_(), pool_mutex_(),
500 max_addr_connections_(max_addr_connections) {
501 }
502
506 ~ConnectionPool() {
507 closeAll();
508 }
509
516 void processNextRequest(const IOAddress& address,
517 const uint16_t port,
518 const TlsContextPtr& tls_context) {
519 if (MultiThreadingMgr::instance().getMode()) {
520 std::lock_guard<std::mutex> lk(pool_mutex_);
521 return (processNextRequestInternal(address, port, tls_context));
522 } else {
523 return (processNextRequestInternal(address, port, tls_context));
524 }
525 }
526
533 void postProcessNextRequest(const IOAddress& address,
534 const uint16_t port,
535 const TlsContextPtr& tls_context) {
536 io_service_->post(std::bind(&ConnectionPool::processNextRequest,
537 shared_from_this(),
538 address, port,
539 tls_context));
540 }
541
565 void queueRequest(const IOAddress& address,
566 const uint16_t port,
567 const TlsContextPtr& tls_context,
568 const WireDataPtr& request,
569 const WireDataPtr& response,
570 const bool persistent,
571 const long request_timeout,
572 const TcpClient::CompleteCheck& complete_check,
573 const TcpClient::RequestHandler& request_callback,
574 const TcpClient::ConnectHandler& connect_callback,
575 const TcpClient::HandshakeHandler& handshake_callback,
576 const TcpClient::CloseHandler& close_callback) {
577 if (MultiThreadingMgr::instance().getMode()) {
578 std::lock_guard<std::mutex> lk(pool_mutex_);
579 return (queueRequestInternal(address, port, tls_context,
580 request, response, persistent,
581 request_timeout, complete_check,
582 request_callback, connect_callback,
583 handshake_callback, close_callback));
584 } else {
585 return (queueRequestInternal(address, port, tls_context,
586 request, response, persistent,
587 request_timeout, complete_check,
588 request_callback, connect_callback,
589 handshake_callback, close_callback));
590 }
591 }
592
595 void closeAll() {
596 if (MultiThreadingMgr::instance().getMode()) {
597 std::lock_guard<std::mutex> lk(pool_mutex_);
598 closeAllInternal();
599 } else {
600 closeAllInternal();
601 }
602 }
603
616 void closeIfOutOfBand(int socket_fd) {
617 if (MultiThreadingMgr::instance().getMode()) {
618 std::lock_guard<std::mutex> lk(pool_mutex_);
619 closeIfOutOfBandInternal(socket_fd);
620 } else {
621 closeIfOutOfBandInternal(socket_fd);
622 }
623 }
624
625private:
626
635 void processNextRequestInternal(const IOAddress& address,
636 const uint16_t port,
637 const TlsContextPtr& tls_context) {
638 // Check if there is a queue for this address. If there is no queue,
639 // there is no request queued either.
640 DestinationPtr destination = findDestination(address, port, tls_context);
641 if (destination) {
642 // Remove closed connections.
643 destination->garbageCollectConnections();
644 if (!destination->queueEmpty()) {
645 // We have at least one queued request. Do we have an
646 // idle connection?
647 ConnectionPtr connection = destination->getIdleConnection();
648 if (!connection) {
649 // No idle connections.
650 if (destination->connectionsFull()) {
651 return;
652 }
653 // Room to make another connection with this destination,
654 // so make one.
655 connection.reset(new Connection(io_service_,
656 tls_context,
657 shared_from_this(),
658 address, port));
659 destination->addConnection(connection);
660 }
661
662 // Dequeue the oldest request and start a transaction for it using
663 // the idle connection.
664 RequestDescriptor desc = destination->popNextRequest();
665 connection->doTransaction(desc.request_,
666 desc.response_,
667 desc.persistent_,
668 desc.request_timeout_,
669 desc.complete_check_,
670 desc.callback_,
671 desc.connect_callback_,
672 desc.handshake_callback_,
673 desc.close_callback_);
674 }
675 }
676 }
677
703 void queueRequestInternal(const IOAddress& address,
704 const uint16_t port,
705 const TlsContextPtr& tls_context,
706 const WireDataPtr& request,
707 const WireDataPtr& response,
708 const bool persistent,
709 const long request_timeout,
710 const TcpClient::CompleteCheck& complete_check,
711 const TcpClient::RequestHandler& request_callback,
712 const TcpClient::ConnectHandler& connect_callback,
713 const TcpClient::HandshakeHandler& handshake_callback,
714 const TcpClient::CloseHandler& close_callback) {
715 ConnectionPtr connection;
716 // Find the destination for the requested address.
717 DestinationPtr destination = findDestination(address, port, tls_context);
718 if (destination) {
719 // Remove closed connections.
720 destination->garbageCollectConnections();
721 // Found it, look for an idle connection.
722 connection = destination->getIdleConnection();
723 } else {
724 // Doesn't exist yet so it's a new destination.
725 destination = addDestination(address, port, tls_context);
726 }
727
728 if (!connection) {
729 if (destination->connectionsFull()) {
730 // All connections busy, queue it.
731 destination->pushRequest(RequestDescriptor(request,
732 response,
733 persistent,
734 request_timeout,
735 complete_check,
736 request_callback,
737 connect_callback,
738 handshake_callback,
739 close_callback));
740 return;
741 }
742
743 // Room to make another connection with this destination, so make one.
744 connection.reset(new Connection(io_service_, tls_context,
745 shared_from_this(),
746 address, port));
747 destination->addConnection(connection);
748 }
749
750 // Use the connection to start the transaction.
751 connection->doTransaction(request, response, persistent,
752 request_timeout, complete_check,
753 request_callback, connect_callback,
754 handshake_callback, close_callback);
755 }
756
761 void closeAllInternal() {
762 for (auto const& destination : destinations_) {
763 destination.second->closeAllConnections();
764 }
765
766 destinations_.clear();
767 }
768
783 void closeIfOutOfBandInternal(int socket_fd) {
784 for (auto const& destination : destinations_) {
785 // First we look for a connection with the socket.
786 ConnectionPtr connection = destination.second->findBySocketFd(socket_fd);
787 if (connection) {
788 if (!connection->isTransactionOngoing()) {
789 // Socket has no transaction, so any ready event is
790 // out-of-band (other end probably closed), so
791 // let's close it. Note we do not remove any queued
792 // requests, as this might somehow be occurring in
793 // between them.
794 destination.second->closeConnection(connection);
795 }
796
797 return;
798 }
799 }
800 }
801
804 struct RequestDescriptor {
820 RequestDescriptor(const WireDataPtr& request,
821 const WireDataPtr& response,
822 const bool persistent,
823 const long& request_timeout,
824 const TcpClient::CompleteCheck& complete_check,
825 const TcpClient::RequestHandler& callback,
826 const TcpClient::ConnectHandler& connect_callback,
827 const TcpClient::HandshakeHandler& handshake_callback,
828 const TcpClient::CloseHandler& close_callback)
829 : request_(request),
830 response_(response),
831 persistent_(persistent),
832 request_timeout_(request_timeout),
833 complete_check_(complete_check),
834 callback_(callback),
835 connect_callback_(connect_callback),
836 handshake_callback_(handshake_callback),
837 close_callback_(close_callback) {
838 }
839
841 WireDataPtr request_;
842
844 WireDataPtr response_;
845
847 bool persistent_;
848
850 long request_timeout_;
851
853 TcpClient::CompleteCheck complete_check_;
854
857
859 TcpClient::ConnectHandler connect_callback_;
860
862 TcpClient::HandshakeHandler handshake_callback_;
863
865 TcpClient::CloseHandler close_callback_;
866 };
867
869 struct DestinationDescriptor {
870 // Constructor.
871 DestinationDescriptor(const IOAddress& address,
872 const uint16_t port,
873 const TlsContextPtr& tls_context)
874 : address_(address), port_(port), tls_context_(tls_context) {
875 }
876
877 // Members.
878 IOAddress address_;
879 uint16_t port_;
880 TlsContextPtr tls_context_;
881
882 // Compare method.
883 bool operator<(const DestinationDescriptor& other) const {
884 return ((address_ < other.address_) ||
885 ((address_ == other.address_) && (port_ < other.port_)) ||
886 ((address_ == other.address_) && (port_ == other.port_) &&
887 (tls_context_ < other.tls_context_)));
888 }
889 };
890
892 class Destination {
893 public:
895 const size_t QUEUE_SIZE_THRESHOLD = 2048;
897 const int QUEUE_WARN_SECS = 5;
898
906 Destination(IOAddress const& address,
907 uint16_t const port,
908 TlsContextPtr tls_context,
909 size_t max_connections)
910 : address_(address), port_(port), tls_context_(tls_context),
911 max_connections_(max_connections), connections_(), queue_(),
912 last_queue_warn_time_(min_date_time), last_queue_size_(0) {
913 }
914
916 ~Destination() {
917 closeAllConnections();
918 }
919
927 void addConnection(ConnectionPtr connection) {
928 if (connectionsFull()) {
929 isc_throw(BadValue, "address: " << address_.toText()
930 << ", already at maximum connections: "
931 << max_connections_);
932 }
933
934 connections_.push_back(connection);
935 }
936
941 void closeConnection(ConnectionPtr connection) {
942 for (auto it = connections_.begin(); it != connections_.end(); ++it) {
943 if (*it == connection) {
944 (*it)->close();
945 connections_.erase(it);
946 break;
947 }
948 }
949 }
950
953 void closeAllConnections() {
954 // Flush the queue.
955 while (!queue_.empty()) {
956 queue_.pop();
957 }
958
959 for (auto const& connection : connections_) {
960 connection->close();
961 }
962
963 connections_.clear();
964 }
965
989 void garbageCollectConnections() {
990 for (auto it = connections_.begin(); it != connections_.end();) {
991 (*it)->isClosedByPeer();
992 if (!(*it)->isClosed()) {
993 ++it;
994 } else {
995 it = connections_.erase(it);
996 }
997 }
998 }
999
1011 ConnectionPtr getIdleConnection() {
1012 for (auto const& connection : connections_) {
1013 if (!connection->isTransactionOngoing() &&
1014 !connection->isClosed()) {
1015 return (connection);
1016 }
1017 }
1018
1019 return (ConnectionPtr());
1020 }
1021
1028 ConnectionPtr findBySocketFd(int socket_fd) {
1029 for (auto const& connection : connections_) {
1030 if (connection->isMySocket(socket_fd)) {
1031 return (connection);
1032 }
1033 }
1034
1035 return (ConnectionPtr());
1036 }
1037
1041 bool connectionsEmpty() {
1042 return (connections_.empty());
1043 }
1044
1048 bool connectionsFull() {
1049 return (connections_.size() >= max_connections_);
1050 }
1051
1055 size_t connectionCount() {
1056 return (connections_.size());
1057 }
1058
1062 size_t getMaxConnections() const {
1063 return (max_connections_);
1064 }
1065
1069 bool queueEmpty() const {
1070 return (queue_.empty());
1071 }
1072
1079 void pushRequest(RequestDescriptor const& desc) {
1080 queue_.push(desc);
1081 size_t size = queue_.size();
1082 // If the queue size is larger than the threshold and growing,
1083 // issue a periodic warning.
1084 if ((size > QUEUE_SIZE_THRESHOLD) && (size > last_queue_size_)) {
1085 ptime now = microsec_clock::universal_time();
1086 if ((now - last_queue_warn_time_) > seconds(QUEUE_WARN_SECS)) {
1088 .arg(address_.toText())
1089 .arg(port_)
1090 .arg(size);
1091 // Remember the last time we warned.
1092 last_queue_warn_time_ = now;
1093 }
1094 }
1095
1096 // Remember the previous size.
1097 last_queue_size_ = size;
1098 }
1099
1103 RequestDescriptor popNextRequest() {
1104 if (queue_.empty()) {
1105 isc_throw(InvalidOperation, "cannot pop, queue is empty");
1106 }
1107
1108 RequestDescriptor desc = queue_.front();
1109 queue_.pop();
1110 return (desc);
1111 }
1112
1113 private:
1115 IOAddress address_;
1116
1118 uint16_t port_;
1119
1121 TlsContextPtr tls_context_;
1122
1124 size_t max_connections_;
1125
1127 std::list<ConnectionPtr> connections_;
1128
1130 std::queue<RequestDescriptor> queue_;
1131
1133 ptime last_queue_warn_time_;
1134
1136 size_t last_queue_size_;
1137 };
1138
1140 typedef boost::shared_ptr<Destination> DestinationPtr;
1141
1150 DestinationPtr addDestination(const IOAddress& address,
1151 const uint16_t port,
1152 const TlsContextPtr& tls_context) {
1153 DestinationDescriptor desc(address, port, tls_context);
1154 DestinationPtr destination(new Destination(address, port, tls_context,
1155 max_addr_connections_));
1156 destinations_[desc] = destination;
1157 return (destination);
1158 }
1159
1169 DestinationPtr findDestination(const IOAddress& address,
1170 const uint16_t port,
1171 const TlsContextPtr& tls_context) const {
1172 DestinationDescriptor desc(address, port, tls_context);
1173 auto it = destinations_.find(desc);
1174 if (it != destinations_.end()) {
1175 return (it->second);
1176 }
1177
1178 return (DestinationPtr());
1179 }
1180
1193 void removeDestination(const IOAddress& address,
1194 const uint16_t port,
1195 const TlsContextPtr& tls_context) {
1196 DestinationDescriptor desc(address, port, tls_context);
1197 auto it = destinations_.find(desc);
1198 if (it != destinations_.end()) {
1199 it->second->closeAllConnections();
1200 destinations_.erase(it);
1201 }
1202 }
1203
1205 IOServicePtr io_service_;
1206
1208 std::map<DestinationDescriptor, DestinationPtr> destinations_;
1209
1211 std::mutex pool_mutex_;
1212
1214 size_t max_addr_connections_;
1215};
1216
1217Connection::Connection(const IOServicePtr& io_service,
1218 const TlsContextPtr& tls_context,
1219 const ConnectionPoolPtr& conn_pool,
1220 const IOAddress& address,
1221 const uint16_t port)
1222 : io_service_(io_service), conn_pool_(conn_pool), address_(address),
1223 port_(port), tls_context_(tls_context), tcp_socket_(), tls_socket_(),
1224 timer_(new IntervalTimer(io_service)), current_request_(),
1225 current_response_(), current_persistent_(false),
1226 current_response_complete_(false), current_complete_check_(),
1227 current_callback_(), buf_(), position_(0), input_buf_(),
1228 current_transid_(0), close_callback_(), started_(false),
1229 need_handshake_(false), closed_(false) {
1230 if (!tls_context) {
1231 tcp_socket_.reset(new asiolink::TCPSocket<SocketCallback>(io_service));
1232 } else {
1233 tls_socket_.reset(new asiolink::TLSSocket<SocketCallback>(io_service,
1234 tls_context));
1235 need_handshake_ = true;
1236 }
1237}
1238
1239Connection::~Connection() {
1240 close();
1241}
1242
1243void
1244Connection::resetState() {
1245 started_ = false;
1246 current_request_.reset();
1247 current_response_.reset();
1248 current_persistent_ = false;
1249 current_response_complete_ = false;
1250 current_callback_ = TcpClient::RequestHandler();
1251}
1252
1253void
1254Connection::closeCallback(const bool clear) {
1255 if (close_callback_) {
1256 try {
1257 if (tcp_socket_) {
1258 close_callback_(tcp_socket_->getNative());
1259 } else if (tls_socket_) {
1260 close_callback_(tls_socket_->getNative());
1261 } else {
1262 isc_throw(Unexpected,
1263 "internal error: can't find a socket to close");
1264 }
1265 } catch (...) {
1267 }
1268 }
1269
1270 if (clear) {
1271 close_callback_ = TcpClient::CloseHandler();
1272 }
1273}
1274
1275void
1276Connection::isClosedByPeer() {
1277 // This method applies only to idle connections.
1278 if (started_ || closed_) {
1279 return;
1280 }
1281 // This code was guarded by a lock so keep this.
1282 if (MultiThreadingMgr::instance().getMode()) {
1283 std::lock_guard<std::mutex> lk(mutex_);
1284 isClosedByPeerInternal();
1285 } else {
1286 isClosedByPeerInternal();
1287 }
1288}
1289
1290void
1291Connection::isClosedByPeerInternal() {
1292 // If the socket is open we check if it is possible to transmit
1293 // the data over this socket by reading from it with message
1294 // peeking. If the socket is not usable, we close it and then
1295 // re-open it. There is a narrow window of time between checking
1296 // the socket usability and actually transmitting the data over
1297 // this socket, when the peer may close the connection. In this
1298 // case we'll need to re-transmit but we don't handle it here.
1299 if (tcp_socket_) {
1300 if (tcp_socket_->getASIOSocket().is_open() &&
1301 !tcp_socket_->isUsable()) {
1302 closeCallback();
1303 closed_ = true;
1304 tcp_socket_->close();
1305 }
1306 } else if (tls_socket_) {
1307 if (tls_socket_->getASIOSocket().is_open() &&
1308 !tls_socket_->isUsable()) {
1309 closeCallback();
1310 closed_ = true;
1311 tls_socket_->close();
1312 }
1313 } else {
1314 isc_throw(Unexpected, "internal error: can't find the sending socket");
1315 }
1316}
1317
1318void
1319Connection::doTransaction(const WireDataPtr& request,
1320 const WireDataPtr& response,
1321 const bool persistent,
1322 const long request_timeout,
1323 const TcpClient::CompleteCheck& complete_check,
1324 const TcpClient::RequestHandler& callback,
1325 const TcpClient::ConnectHandler& connect_callback,
1326 const TcpClient::HandshakeHandler& handshake_callback,
1327 const TcpClient::CloseHandler& close_callback) {
1328 if (MultiThreadingMgr::instance().getMode()) {
1329 std::lock_guard<std::mutex> lk(mutex_);
1330 doTransactionInternal(request, response, persistent, request_timeout,
1331 complete_check, callback, connect_callback,
1332 handshake_callback, close_callback);
1333 } else {
1334 doTransactionInternal(request, response, persistent, request_timeout,
1335 complete_check, callback, connect_callback,
1336 handshake_callback, close_callback);
1337 }
1338}
1339
1340void
1341Connection::doTransactionInternal(const WireDataPtr& request,
1342 const WireDataPtr& response,
1343 const bool persistent,
1344 const long request_timeout,
1345 const TcpClient::CompleteCheck& complete_check,
1346 const TcpClient::RequestHandler& callback,
1347 const TcpClient::ConnectHandler& connect_callback,
1348 const TcpClient::HandshakeHandler& handshake_callback,
1349 const TcpClient::CloseHandler& close_callback) {
1350 try {
1351 started_ = true;
1352 current_request_ = request;
1353 current_response_ = response;
1354 current_persistent_ = persistent;
1355 current_complete_check_ = complete_check;
1356 current_callback_ = callback;
1357 handshake_callback_ = handshake_callback;
1358 close_callback_ = close_callback;
1359
1360 // Starting new transaction. Generate new transaction id.
1361 ++current_transid_;
1362
1363 buf_ = *request;
1364 if (buf_.empty()) {
1365 isc_throw(Unexpected, "empty request");
1366 }
1367 position_ = 0;
1368
1369 size_t to_dump = request->size();
1370 bool truncated = false;
1371 if (to_dump > 100) {
1372 to_dump = 100;
1373 truncated = true;
1374 }
1377 .arg(str::dumpAsHex(request->data(), to_dump) +
1378 (truncated ? "..." : ""))
1379 .arg(address_.toText())
1380 .arg(port_);
1381
1382 // Setup request timer.
1383 scheduleTimer(request_timeout);
1384
1388 TCPEndpoint endpoint(address_, port_);
1389 SocketCallback socket_cb(std::bind(&Connection::connectCallback,
1390 shared_from_this(),
1391 connect_callback,
1392 current_transid_,
1393 ph::_1));
1394
1395 // Establish new connection or use existing connection.
1396 if (tcp_socket_) {
1397 tcp_socket_->open(&endpoint, socket_cb);
1398 return;
1399 }
1400 if (tls_socket_) {
1401 tls_socket_->open(&endpoint, socket_cb);
1402 return;
1403 }
1404
1405 // Should never reach this point.
1406 isc_throw(Unexpected, "internal error: can't find a socket to open");
1407
1408 } catch (const std::exception& ex) {
1409 // Re-throw with the expected exception type.
1410 isc_throw(TcpClientError, ex.what());
1411 }
1412}
1413
1414void
1415Connection::close() {
1416 if (MultiThreadingMgr::instance().getMode()) {
1417 std::lock_guard<std::mutex> lk(mutex_);
1418 return (closeInternal());
1419 } else {
1420 return (closeInternal());
1421 }
1422}
1423
1424void
1425Connection::closeInternal() {
1426 // Pass in true to discard the callback.
1427 closeCallback(true);
1428
1429 closed_ = true;
1430 timer_->cancel();
1431 if (tcp_socket_) {
1432 tcp_socket_->close();
1433 }
1434 if (tls_socket_) {
1435 tls_socket_->close();
1436 }
1437
1438 resetState();
1439}
1440
1441bool
1442Connection::isMySocket(int socket_fd) const {
1443 if (tcp_socket_) {
1444 return (tcp_socket_->getNative() == socket_fd);
1445 } else if (tls_socket_) {
1446 return (tls_socket_->getNative() == socket_fd);
1447 }
1448 // Should never reach this point.
1449 std::cerr << "internal error: can't find my socket\n";
1450 return (false);
1451}
1452
1453bool
1454Connection::checkPrematureTimeout(const uint64_t transid) {
1455 if (MultiThreadingMgr::instance().getMode()) {
1456 std::lock_guard<std::mutex> lk(mutex_);
1457 return (checkPrematureTimeoutInternal(transid));
1458 } else {
1459 return (checkPrematureTimeoutInternal(transid));
1460 }
1461}
1462
1463bool
1464Connection::checkPrematureTimeoutInternal(const uint64_t transid) {
1465 // If there is no transaction but the handlers are invoked it means
1466 // that the last transaction in the queue timed out prematurely.
1467 // Also, if there is a transaction in progress but the ID of that
1468 // transaction doesn't match the one associated with the handler it,
1469 // also means that the transaction timed out prematurely.
1470 if (!isTransactionOngoing() || (transid != current_transid_)) {
1472 .arg(isTransactionOngoing())
1473 .arg(transid)
1474 .arg(current_transid_);
1475 return (true);
1476 }
1477
1478 return (false);
1479}
1480
1481void
1482Connection::terminate(const boost::system::error_code& ec,
1483 const std::string& error_msg) {
1484 if (MultiThreadingMgr::instance().getMode()) {
1485 std::lock_guard<std::mutex> lk(mutex_);
1486 terminateInternal(ec, error_msg);
1487 } else {
1488 terminateInternal(ec, error_msg);
1489 }
1490}
1491
1492void
1493Connection::terminateInternal(const boost::system::error_code& ec,
1494 std::string error_msg) {
1495 WireDataPtr response;
1496 if (isTransactionOngoing()) {
1497
1498 timer_->cancel();
1499 if (tcp_socket_) {
1500 tcp_socket_->cancel();
1501 }
1502 if (tls_socket_) {
1503 tls_socket_->cancel();
1504 }
1505
1506 if (!ec && current_response_complete_) {
1507 response = current_response_;
1508
1511 .arg(address_.toText())
1512 .arg(port_);
1513 } else {
1514 if (error_msg.empty()) {
1515 error_msg = ec.message();
1516 }
1519 .arg(address_.toText())
1520 .arg(port_)
1521 .arg(error_msg);
1522
1523 // Only log the details if we have received anything.
1524 if (!current_response_->empty()) {
1525 size_t to_dump = current_response_->size();
1526 bool truncated = false;
1527 if (to_dump > 100) {
1528 to_dump = 100;
1529 truncated = true;
1530 }
1533 .arg(address_.toText())
1534 .arg(port_)
1535 .arg(str::dumpAsHex(current_response_->data(), to_dump) +
1536 (truncated ? "..." : ""));
1537 }
1538 }
1539
1540 try {
1541 // The callback should take care of its own exceptions but one
1542 // never knows.
1543 if (MultiThreadingMgr::instance().getMode()) {
1544 UnlockGuard<std::mutex> lock(mutex_);
1545 current_callback_(ec, response, error_msg);
1546 } else {
1547 current_callback_(ec, response, error_msg);
1548 }
1549 } catch (...) {
1550 }
1551
1552 // If we're not requesting connection persistence or the
1553 // connection has timed out, we should close the socket.
1554 if (!closed_ &&
1555 (!current_persistent_ || (ec == boost::asio::error::timed_out))) {
1556 closeInternal();
1557 }
1558
1559 resetState();
1560 }
1561
1562 // Check if there are any requests queued for this destination and start
1563 // another transaction if there is at least one.
1564 ConnectionPoolPtr conn_pool = conn_pool_.lock();
1565 if (conn_pool) {
1566 conn_pool->postProcessNextRequest(address_, port_, tls_context_);
1567 }
1568}
1569
1570void
1571Connection::scheduleTimer(const long request_timeout) {
1572 if (request_timeout > 0) {
1573 timer_->setup(std::bind(&Connection::timerCallback, this), request_timeout,
1575 }
1576}
1577
1578void
1579Connection::doHandshake(const uint64_t transid) {
1580 // Skip the handshake if it is not needed.
1581 if (!need_handshake_) {
1582 doSend(transid);
1583 return;
1584 }
1585
1586 SocketCallback socket_cb(std::bind(&Connection::handshakeCallback,
1587 shared_from_this(),
1588 handshake_callback_,
1589 transid,
1590 ph::_1));
1591 try {
1592 tls_socket_->handshake(socket_cb);
1593
1594 } catch (...) {
1595 terminate(boost::asio::error::not_connected);
1596 }
1597}
1598
1599void
1600Connection::doSend(const uint64_t transid) {
1601 SocketCallback socket_cb(std::bind(&Connection::sendCallback,
1602 shared_from_this(),
1603 transid,
1604 ph::_1,
1605 ph::_2));
1606 try {
1607 // doSend is called only when there is something to send so
1608 // remaining is guaranteed to be greater than 0 and position_
1609 // to point inside the buffer.
1610 size_t remaining = buf_.size() - position_;
1611 if (tcp_socket_) {
1612 tcp_socket_->asyncSend(&buf_[position_], remaining, socket_cb);
1613 return;
1614 }
1615
1616 if (tls_socket_) {
1617 tls_socket_->asyncSend(&buf_[position_], remaining, socket_cb);
1618 return;
1619 }
1620
1621 // Should never reach this point.
1622 std::cerr << "internal error: can't find a socket to send to\n";
1623 isc_throw(Unexpected,
1624 "internal error: can't find a socket to send to");
1625 } catch (...) {
1626 terminate(boost::asio::error::not_connected);
1627 }
1628}
1629
1630void
1631Connection::doReceive(const uint64_t transid) {
1632 TCPEndpoint endpoint;
1633 SocketCallback socket_cb(std::bind(&Connection::receiveCallback,
1634 shared_from_this(),
1635 transid,
1636 ph::_1,
1637 ph::_2));
1638 try {
1639 if (tcp_socket_) {
1640 tcp_socket_->asyncReceive(static_cast<void*>(input_buf_.data()),
1641 input_buf_.size(), 0,
1642 &endpoint, socket_cb);
1643 return;
1644 }
1645 if (tls_socket_) {
1646 tls_socket_->asyncReceive(static_cast<void*>(input_buf_.data()),
1647 input_buf_.size(), 0,
1648 &endpoint, socket_cb);
1649 return;
1650 }
1651 // Should never reach this point.
1652 std::cerr << "internal error: can't find a socket to receive from\n";
1653 isc_throw(Unexpected,
1654 "internal error: can't find a socket to receive from");
1655
1656 } catch (...) {
1657 terminate(boost::asio::error::not_connected);
1658 }
1659}
1660
1661void
1662Connection::connectCallback(TcpClient::ConnectHandler connect_callback,
1663 const uint64_t transid,
1664 const boost::system::error_code& ec) {
1665 if (checkPrematureTimeout(transid)) {
1666 return;
1667 }
1668
1669 // Run user defined connect callback if specified.
1670 if (connect_callback) {
1671 // If the user defined callback indicates that the connection
1672 // should not be continued.
1673 if (tcp_socket_) {
1674 if (!connect_callback(ec, tcp_socket_->getNative())) {
1675 return;
1676 }
1677 } else if (tls_socket_) {
1678 if (!connect_callback(ec, tls_socket_->getNative())) {
1679 return;
1680 }
1681 } else {
1682 // Should never reach this point.
1683 std::cerr << "internal error: can't find a socket to connect\n";
1684 }
1685 }
1686
1687 if (ec && (ec.value() == boost::asio::error::operation_aborted)) {
1688 return;
1689
1690 // In some cases the "in progress" status code may be returned. It doesn't
1691 // indicate an error. Sending the request over the socket is expected to
1692 // be successful. Getting such status appears to be highly dependent on
1693 // the operating system.
1694 } else if (ec &&
1695 (ec.value() != boost::asio::error::in_progress) &&
1696 (ec.value() != boost::asio::error::already_connected)) {
1697 terminate(ec);
1698
1699 } else {
1700 // Start the TLS handshake asynchronously.
1701 doHandshake(transid);
1702 }
1703}
1704
1705void
1706Connection::handshakeCallback(TcpClient::ConnectHandler handshake_callback,
1707 const uint64_t transid,
1708 const boost::system::error_code& ec) {
1709 need_handshake_ = false;
1710 if (checkPrematureTimeout(transid)) {
1711 return;
1712 }
1713
1714 // Run user defined handshake callback if specified.
1715 if (handshake_callback) {
1716 // If the user defined callback indicates that the connection
1717 // should not be continued.
1718 if (tls_socket_) {
1719 if (!handshake_callback(ec, tls_socket_->getNative())) {
1720 return;
1721 }
1722 } else {
1723 // Should never reach this point.
1724 std::cerr << "internal error: can't find TLS socket\n";
1725 }
1726 }
1727
1728 if (ec && (ec.value() == boost::asio::error::operation_aborted)) {
1729 return;
1730 } else if (ec) {
1731 terminate(ec);
1732
1733 } else {
1734 // Start sending the request asynchronously.
1735 doSend(transid);
1736 }
1737}
1738
1739void
1740Connection::sendCallback(const uint64_t transid,
1741 const boost::system::error_code& ec,
1742 size_t length) {
1743 if (checkPrematureTimeout(transid)) {
1744 return;
1745 }
1746
1747 if (ec) {
1748 if (ec.value() == boost::asio::error::operation_aborted) {
1749 return;
1750
1751 // EAGAIN and EWOULDBLOCK don't really indicate an error. The length
1752 // should be 0 in this case but let's be sure.
1753 } else if ((ec.value() == boost::asio::error::would_block) ||
1754 (ec.value() == boost::asio::error::try_again)) {
1755 length = 0;
1756
1757 } else {
1758 // Any other error should cause the transaction to terminate.
1759 terminate(ec);
1760 return;
1761 }
1762 }
1763
1764 // Sending is in progress, so push back the timeout.
1765 scheduleTimer(timer_->getInterval());
1766
1767 // If any data have been sent, remove it from the buffer and only leave the
1768 // portion that still has to be sent.
1769 if (length >= buf_.size() - position_) {
1770 position_ = buf_.size();
1771 } else {
1772 position_ += length;
1773 }
1774
1775 // If there is no more data to be sent, start receiving a response. Otherwise,
1776 // continue sending.
1777 if (position_ == buf_.size()) {
1778 doReceive(transid);
1779
1780 } else {
1781 doSend(transid);
1782 }
1783}
1784
1785void
1786Connection::receiveCallback(const uint64_t transid,
1787 const boost::system::error_code& ec,
1788 size_t length) {
1789 if (checkPrematureTimeout(transid)) {
1790 return;
1791 }
1792
1793 if (ec) {
1794 if (ec.value() == boost::asio::error::operation_aborted) {
1795 return;
1796
1797 // EAGAIN and EWOULDBLOCK don't indicate an error in this case. All
1798 // other errors should terminate the transaction.
1799 }
1800 if ((ec.value() != boost::asio::error::try_again) &&
1801 (ec.value() != boost::asio::error::would_block)) {
1802 terminate(ec);
1803 return;
1804
1805 } else {
1806 // For EAGAIN and EWOULDBLOCK the length should be 0 anyway, but let's
1807 // make sure.
1808 length = 0;
1809 }
1810 }
1811
1812 // Receiving is in progress, so push back the timeout.
1813 scheduleTimer(timer_->getInterval());
1814
1815 if (runCompleteCheck(ec, length)) {
1816 doReceive(transid);
1817 }
1818}
1819
1820bool
1821Connection::runCompleteCheck(const boost::system::error_code& ec, size_t length) {
1822 if (MultiThreadingMgr::instance().getMode()) {
1823 std::lock_guard<std::mutex> lk(mutex_);
1824 return (runCompleteCheckInternal(ec, length));
1825 } else {
1826 return (runCompleteCheckInternal(ec, length));
1827 }
1828}
1829
1830bool
1831Connection::runCompleteCheckInternal(const boost::system::error_code& ec,
1832 size_t length) {
1833 // If we have received any data, let's store it.
1834 if (length != 0) {
1835 current_response_->insert(current_response_->end(),
1836 input_buf_.begin(),
1837 input_buf_.begin() + length);
1838 }
1839
1840 // If data is still needed, let's schedule another receive.
1841 int status = -1;
1842 std::string err = "";
1843 if (current_complete_check_) {
1844 status = current_complete_check_(current_response_, err);
1845 } else {
1846 err = "Internal error: no completion checker?";
1847 }
1848 if (status == 0) {
1849 return (true);
1850 } else if (status > 0) {
1851 // No more data needed.
1852 current_response_complete_ = true;
1853 terminateInternal(ec);
1854 } else {
1855 // Error case.
1856 terminateInternal(ec, err);
1857 }
1858
1859 return (false);
1860}
1861
1862void
1863Connection::timerCallback() {
1864 // Request timeout occurred.
1865 terminate(boost::asio::error::timed_out);
1866}
1867
1868}
1869
1870namespace isc {
1871namespace tcp {
1872
1875public:
1899 TcpClientImpl(const IOServicePtr& io_service, size_t thread_pool_size = 0,
1900 bool defer_thread_start = false)
1901 : thread_pool_size_(thread_pool_size), thread_pool_() {
1902 if (thread_pool_size_ > 0) {
1903 // Create our own private IOService.
1904 thread_io_service_.reset(new IOService());
1905
1906 // Create the connection pool. Note that we use the thread_pool_size
1907 // as the maximum connections per address.
1908 conn_pool_.reset(new ConnectionPool(thread_io_service_, thread_pool_size_));
1909
1910 // Create the thread pool.
1911 thread_pool_.reset(new IoServiceThreadPool(thread_io_service_, thread_pool_size_,
1912 defer_thread_start));
1913
1916 .arg(thread_pool_size_);
1917 } else {
1918 // Single-threaded mode: use the caller's IOService,
1919 // one connection per address.
1920 conn_pool_.reset(new ConnectionPool(io_service, 1));
1921 }
1922 }
1923
1928 stop();
1929 }
1930
1937 if (thread_pool_) {
1938 thread_pool_->checkPausePermissions();
1939 }
1940 }
1941
1943 void start() {
1944 if (thread_pool_) {
1945 thread_pool_->run();
1946 }
1947 }
1948
1951 void stop() {
1952 // Close all the connections.
1953 conn_pool_->closeAll();
1954
1955 // Stop the thread pool.
1956 if (thread_pool_) {
1957 thread_pool_->stop();
1958 }
1959
1960 if (thread_io_service_) {
1961 thread_io_service_->stopAndPoll();
1962 thread_io_service_->stop();
1963 }
1964 }
1965
1970 void pause() {
1971 if (!thread_pool_) {
1972 isc_throw(InvalidOperation, "TcpClient::pause - no thread pool");
1973 }
1974
1975 // Pause the thread pool.
1976 thread_pool_->pause();
1977 }
1978
1983 void resume() {
1984 if (!thread_pool_) {
1985 isc_throw(InvalidOperation, "TcpClient::resume - no thread pool");
1986 }
1987
1988 // Resume running the thread pool.
1989 thread_pool_->run();
1990 }
1991
1996 bool isRunning() {
1997 if (thread_pool_) {
1998 return (thread_pool_->isRunning());
1999 }
2000
2001 return (false);
2002 }
2003
2008 bool isStopped() {
2009 if (thread_pool_) {
2010 return (thread_pool_->isStopped());
2011 }
2012
2013 return (false);
2014 }
2015
2020 bool isPaused() {
2021 if (thread_pool_) {
2022 return (thread_pool_->isPaused());
2023 }
2024
2025 return (false);
2026 }
2027
2033 return (thread_io_service_);
2034 };
2035
2040 return (thread_pool_size_);
2041 }
2042
2046 uint16_t getThreadCount() {
2047 if (!thread_pool_) {
2048 return (0);
2049 }
2050 return (thread_pool_->getThreadCount());
2051 }
2052
2054 ConnectionPoolPtr conn_pool_;
2055
2056private:
2057
2059 size_t thread_pool_size_;
2060
2062 asiolink::IOServicePtr thread_io_service_;
2063
2066 IoServiceThreadPoolPtr thread_pool_;
2067};
2068
2069TcpClient::TcpClient(const IOServicePtr& io_service, bool multi_threading_enabled,
2070 size_t thread_pool_size, bool defer_thread_start) {
2071 if (!multi_threading_enabled && thread_pool_size) {
2073 "TcpClient thread_pool_size must be zero "
2074 "when Kea core multi-threading is disabled");
2075 }
2076
2077 impl_.reset(new TcpClientImpl(io_service, thread_pool_size,
2078 defer_thread_start));
2079}
2080
2082 impl_->stop();
2083}
2084
2085void
2087 const uint16_t port,
2088 const TlsContextPtr& tls_context,
2089 const WireDataPtr& request,
2090 const WireDataPtr& response,
2091 const bool persistent,
2092 const TcpClient::CompleteCheck& complete_check,
2093 const TcpClient::RequestHandler& request_callback,
2094 const TcpClient::RequestTimeout& request_timeout,
2095 const TcpClient::ConnectHandler& connect_callback,
2096 const TcpClient::HandshakeHandler& handshake_callback,
2097 const TcpClient::CloseHandler& close_callback) {
2098 if (!request) {
2099 isc_throw(TcpClientError, "TCP request must not be null");
2100 }
2101
2102 if (request->empty()) {
2103 isc_throw(TcpClientError, "TCP request must not be empty");
2104 }
2105
2106 if (!response) {
2107 isc_throw(TcpClientError, "TCP response must not be null");
2108 }
2109
2110 if (!complete_check) {
2111 isc_throw(TcpClientError, "TCP response completion checker must not be null");
2112 }
2113
2114 if (!request_callback) {
2115 isc_throw(TcpClientError, "callback for TCP transaction must not be null");
2116 }
2117
2118 impl_->conn_pool_->queueRequest(address, port, tls_context,
2119 request, response, persistent,
2120 request_timeout.value_,
2121 complete_check,
2122 request_callback, connect_callback,
2123 handshake_callback, close_callback);
2124}
2125
2126void
2128 return (impl_->conn_pool_->closeIfOutOfBand(socket_fd));
2129}
2130
2131void
2133 impl_->start();
2134}
2135
2136void
2138 impl_->checkPermissions();
2139}
2140
2141void
2143 impl_->pause();
2144}
2145
2146void
2148 impl_->resume();
2149}
2150
2151void
2153 impl_->stop();
2154}
2155
2156const IOServicePtr
2158 return (impl_->getThreadIOService());
2159}
2160
2161uint16_t
2163 return (impl_->getThreadPoolSize());
2164}
2165
2166uint16_t
2168 return (impl_->getThreadCount());
2169}
2170
2171bool
2173 return (impl_->isRunning());
2174}
2175
2176bool
2178 return (impl_->isStopped());
2179}
2180
2181bool
2183 return (impl_->isPaused());
2184}
2185
2186} // end of namespace isc::tcp
2187} // end of namespace isc
A generic exception that is thrown if a function is called in a prohibited way.
A generic error raised by the TcpClient class.
Definition tcp_client.h:25
TcpClient implementation.
void resume()
Resumes running the client's thread pool.
uint16_t getThreadPoolSize()
Fetches the maximum size of the thread pool.
void start()
Starts running the client's thread pool, if multi-threaded.
ConnectionPoolPtr conn_pool_
Holds a pointer to the connection pool.
bool isPaused()
Indicates if the thread pool is paused.
void checkPermissions()
Check if the current thread can perform thread pool state transition.
uint16_t getThreadCount()
Fetches the number of threads in the pool.
bool isRunning()
Indicates if the thread pool is running.
void pause()
Pauses the client's thread pool.
TcpClientImpl(const IOServicePtr &io_service, size_t thread_pool_size=0, bool defer_thread_start=false)
Constructor.
asiolink::IOServicePtr getThreadIOService()
Fetches the internal IOService used in multi-threaded mode.
void stop()
Close all connections, and if multi-threaded, stops the client's thread pool.
bool isStopped()
Indicates if the thread pool is stopped.
~TcpClientImpl()
Destructor.
std::function< int(const WireDataPtr &, std::string &)> CompleteCheck
Completion check type.
Definition tcp_client.h:106
void closeIfOutOfBand(int socket_fd)
Closes a connection if it has an out-of-band socket event.
void start()
Starts running the client's thread pool, if multi-threaded.
uint16_t getThreadCount() const
Fetches the number of threads in the pool.
std::function< void(const boost::system::error_code &, const WireDataPtr &, const std::string &)> RequestHandler
Callback type used in call to TcpClient::asyncSendRequest.
Definition tcp_client.h:96
void stop()
Halts client-side IO activity.
bool isPaused()
Indicates if the thread pool is paused.
uint16_t getThreadPoolSize() const
Fetches the maximum size of the thread pool.
void checkPermissions()
Check if the current thread can perform thread pool state transition.
void asyncSendRequest(const asiolink::IOAddress &address, const uint16_t port, const asiolink::TlsContextPtr &tls_context, const WireDataPtr &request, const WireDataPtr &response, const bool persistent, const CompleteCheck &complete_check, 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 TCP request for a given address.
bool isRunning()
Indicates if the thread pool is running.
std::function< bool(const boost::system::error_code &, const int)> ConnectHandler
Optional handler invoked when client connects to the server.
Definition tcp_client.h:119
void pause()
Pauses the client's thread pool.
bool isStopped()
Indicates if the thread pool is stopped.
TcpClient(const asiolink::IOServicePtr &io_service, bool multi_threading_enabled, size_t thread_pool_size=0, bool defer_thread_start=false)
Constructor.
std::function< bool(const boost::system::error_code &, const int)> HandshakeHandler
Optional handler invoked when client performs the TLS handshake with the server.
Definition tcp_client.h:133
void resume()
Resumes running the client's thread pool.
const asiolink::IOServicePtr getThreadIOService() const
Fetches a pointer to the internal IOService used to drive the thread-pool in multi-threaded mode.
~TcpClient()
Destructor.
std::function< void(const int)> CloseHandler
Optional handler invoked when client closes the connection to the server.
Definition tcp_client.h:138
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
bool operator<(Element const &a, Element const &b)
Test less than.
Definition data.cc:274
const int DBGLVL_TRACE_BASIC
Trace basic operations.
const int DBGLVL_TRACE_BASIC_DATA
Trace data associated with the basic operations.
const int DBGLVL_TRACE_DETAIL
Trace detailed operations.
std::function< void(boost::system::error_code ec, size_t length)> SocketCallbackFunction
Type of the function implementing a callback invoked by the SocketCallback functor.
const isc::log::MessageID TCP_CLIENT_PREMATURE_CONNECTION_TIMEOUT_OCCURRED
const isc::log::MessageID TCP_CLIENT_BAD_SERVER_RESPONSE_RECEIVED
const isc::log::MessageID TCP_CLIENT_BAD_SERVER_RESPONSE_RECEIVED_DETAILS
const isc::log::MessageID TCP_CLIENT_SERVER_RESPONSE_RECEIVED
isc::log::Logger tcp_logger("tcp")
Defines the logger used within libkea-tcp library.
Definition tcp_log.h:18
boost::shared_ptr< WireData > WireDataPtr
Definition wire_data.h:18
const isc::log::MessageID TCP_CLIENT_QUEUE_SIZE_GROWING
const isc::log::MessageID TCP_CLIENT_CONNECTION_CLOSE_CALLBACK_FAILED
const isc::log::MessageID TCP_CLIENT_REQUEST_SEND
const isc::log::MessageID TCP_CLIENT_MT_STARTED
string dumpAsHex(const uint8_t *data, size_t length)
Dumps a buffer of bytes as a string of hexadecimal digits.
Definition str.cc:330
Defines the logger used by the top-level component of kea-lfc.
TCP request/response timeout value.
Definition tcp_client.h:83
long value_
Timeout value specified.
Definition tcp_client.h:90