Kea 3.1.8
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 std::array<uint8_t, 32768> input_buf_;
453
455 uint64_t current_transid_;
456
458 TcpClient::HandshakeHandler handshake_callback_;
459
461 TcpClient::CloseHandler close_callback_;
462
464 std::atomic<bool> started_;
465
467 std::atomic<bool> need_handshake_;
468
470 std::atomic<bool> closed_;
471
473 std::mutex mutex_;
474};
475
477typedef boost::shared_ptr<Connection> ConnectionPtr;
478
486class ConnectionPool : public boost::enable_shared_from_this<ConnectionPool> {
487public:
488
495 explicit ConnectionPool(const IOServicePtr& io_service, size_t max_addr_connections)
496 : io_service_(io_service), destinations_(), pool_mutex_(),
497 max_addr_connections_(max_addr_connections) {
498 }
499
503 ~ConnectionPool() {
504 closeAll();
505 }
506
513 void processNextRequest(const IOAddress& address,
514 const uint16_t port,
515 const TlsContextPtr& tls_context) {
516 if (MultiThreadingMgr::instance().getMode()) {
517 std::lock_guard<std::mutex> lk(pool_mutex_);
518 return (processNextRequestInternal(address, port, tls_context));
519 } else {
520 return (processNextRequestInternal(address, port, tls_context));
521 }
522 }
523
530 void postProcessNextRequest(const IOAddress& address,
531 const uint16_t port,
532 const TlsContextPtr& tls_context) {
533 io_service_->post(std::bind(&ConnectionPool::processNextRequest,
534 shared_from_this(),
535 address, port,
536 tls_context));
537 }
538
562 void queueRequest(const IOAddress& address,
563 const uint16_t port,
564 const TlsContextPtr& tls_context,
565 const WireDataPtr& request,
566 const WireDataPtr& response,
567 const bool persistent,
568 const long request_timeout,
569 const TcpClient::CompleteCheck& complete_check,
570 const TcpClient::RequestHandler& request_callback,
571 const TcpClient::ConnectHandler& connect_callback,
572 const TcpClient::HandshakeHandler& handshake_callback,
573 const TcpClient::CloseHandler& close_callback) {
574 if (MultiThreadingMgr::instance().getMode()) {
575 std::lock_guard<std::mutex> lk(pool_mutex_);
576 return (queueRequestInternal(address, port, tls_context,
577 request, response, persistent,
578 request_timeout, complete_check,
579 request_callback, connect_callback,
580 handshake_callback, close_callback));
581 } else {
582 return (queueRequestInternal(address, port, tls_context,
583 request, response, persistent,
584 request_timeout, complete_check,
585 request_callback, connect_callback,
586 handshake_callback, close_callback));
587 }
588 }
589
592 void closeAll() {
593 if (MultiThreadingMgr::instance().getMode()) {
594 std::lock_guard<std::mutex> lk(pool_mutex_);
595 closeAllInternal();
596 } else {
597 closeAllInternal();
598 }
599 }
600
613 void closeIfOutOfBand(int socket_fd) {
614 if (MultiThreadingMgr::instance().getMode()) {
615 std::lock_guard<std::mutex> lk(pool_mutex_);
616 closeIfOutOfBandInternal(socket_fd);
617 } else {
618 closeIfOutOfBandInternal(socket_fd);
619 }
620 }
621
622private:
623
632 void processNextRequestInternal(const IOAddress& address,
633 const uint16_t port,
634 const TlsContextPtr& tls_context) {
635 // Check if there is a queue for this address. If there is no queue,
636 // there is no request queued either.
637 DestinationPtr destination = findDestination(address, port, tls_context);
638 if (destination) {
639 // Remove closed connections.
640 destination->garbageCollectConnections();
641 if (!destination->queueEmpty()) {
642 // We have at least one queued request. Do we have an
643 // idle connection?
644 ConnectionPtr connection = destination->getIdleConnection();
645 if (!connection) {
646 // No idle connections.
647 if (destination->connectionsFull()) {
648 return;
649 }
650 // Room to make another connection with this destination,
651 // so make one.
652 connection.reset(new Connection(io_service_,
653 tls_context,
654 shared_from_this(),
655 address, port));
656 destination->addConnection(connection);
657 }
658
659 // Dequeue the oldest request and start a transaction for it using
660 // the idle connection.
661 RequestDescriptor desc = destination->popNextRequest();
662 connection->doTransaction(desc.request_,
663 desc.response_,
664 desc.persistent_,
665 desc.request_timeout_,
666 desc.complete_check_,
667 desc.callback_,
668 desc.connect_callback_,
669 desc.handshake_callback_,
670 desc.close_callback_);
671 }
672 }
673 }
674
700 void queueRequestInternal(const IOAddress& address,
701 const uint16_t port,
702 const TlsContextPtr& tls_context,
703 const WireDataPtr& request,
704 const WireDataPtr& response,
705 const bool persistent,
706 const long request_timeout,
707 const TcpClient::CompleteCheck& complete_check,
708 const TcpClient::RequestHandler& request_callback,
709 const TcpClient::ConnectHandler& connect_callback,
710 const TcpClient::HandshakeHandler& handshake_callback,
711 const TcpClient::CloseHandler& close_callback) {
712 ConnectionPtr connection;
713 // Find the destination for the requested address.
714 DestinationPtr destination = findDestination(address, port, tls_context);
715 if (destination) {
716 // Remove closed connections.
717 destination->garbageCollectConnections();
718 // Found it, look for an idle connection.
719 connection = destination->getIdleConnection();
720 } else {
721 // Doesn't exist yet so it's a new destination.
722 destination = addDestination(address, port, tls_context);
723 }
724
725 if (!connection) {
726 if (destination->connectionsFull()) {
727 // All connections busy, queue it.
728 destination->pushRequest(RequestDescriptor(request,
729 response,
730 persistent,
731 request_timeout,
732 complete_check,
733 request_callback,
734 connect_callback,
735 handshake_callback,
736 close_callback));
737 return;
738 }
739
740 // Room to make another connection with this destination, so make one.
741 connection.reset(new Connection(io_service_, tls_context,
742 shared_from_this(),
743 address, port));
744 destination->addConnection(connection);
745 }
746
747 // Use the connection to start the transaction.
748 connection->doTransaction(request, response, persistent,
749 request_timeout, complete_check,
750 request_callback, connect_callback,
751 handshake_callback, close_callback);
752 }
753
758 void closeAllInternal() {
759 for (auto const& destination : destinations_) {
760 destination.second->closeAllConnections();
761 }
762
763 destinations_.clear();
764 }
765
780 void closeIfOutOfBandInternal(int socket_fd) {
781 for (auto const& destination : destinations_) {
782 // First we look for a connection with the socket.
783 ConnectionPtr connection = destination.second->findBySocketFd(socket_fd);
784 if (connection) {
785 if (!connection->isTransactionOngoing()) {
786 // Socket has no transaction, so any ready event is
787 // out-of-band (other end probably closed), so
788 // let's close it. Note we do not remove any queued
789 // requests, as this might somehow be occurring in
790 // between them.
791 destination.second->closeConnection(connection);
792 }
793
794 return;
795 }
796 }
797 }
798
801 struct RequestDescriptor {
817 RequestDescriptor(const WireDataPtr& request,
818 const WireDataPtr& response,
819 const bool persistent,
820 const long& request_timeout,
821 const TcpClient::CompleteCheck& complete_check,
822 const TcpClient::RequestHandler& callback,
823 const TcpClient::ConnectHandler& connect_callback,
824 const TcpClient::HandshakeHandler& handshake_callback,
825 const TcpClient::CloseHandler& close_callback)
826 : request_(request),
827 response_(response),
828 persistent_(persistent),
829 request_timeout_(request_timeout),
830 complete_check_(complete_check),
831 callback_(callback),
832 connect_callback_(connect_callback),
833 handshake_callback_(handshake_callback),
834 close_callback_(close_callback) {
835 }
836
838 WireDataPtr request_;
839
841 WireDataPtr response_;
842
844 bool persistent_;
845
847 long request_timeout_;
848
850 TcpClient::CompleteCheck complete_check_;
851
854
856 TcpClient::ConnectHandler connect_callback_;
857
859 TcpClient::HandshakeHandler handshake_callback_;
860
862 TcpClient::CloseHandler close_callback_;
863 };
864
866 struct DestinationDescriptor {
867 // Constructor.
868 DestinationDescriptor(const IOAddress& address,
869 const uint16_t port,
870 const TlsContextPtr& tls_context)
871 : address_(address), port_(port), tls_context_(tls_context) {
872 }
873
874 // Members.
875 IOAddress address_;
876 uint16_t port_;
877 TlsContextPtr tls_context_;
878
879 // Compare method.
880 bool operator<(const DestinationDescriptor& other) const {
881 return ((address_ < other.address_) ||
882 ((address_ == other.address_) && (port_ < other.port_)) ||
883 ((address_ == other.address_) && (port_ == other.port_) &&
884 (tls_context_ < other.tls_context_)));
885 }
886 };
887
889 class Destination {
890 public:
892 const size_t QUEUE_SIZE_THRESHOLD = 2048;
894 const int QUEUE_WARN_SECS = 5;
895
903 Destination(IOAddress const& address,
904 uint16_t const port,
905 TlsContextPtr tls_context,
906 size_t max_connections)
907 : address_(address), port_(port), tls_context_(tls_context),
908 max_connections_(max_connections), connections_(), queue_(),
909 last_queue_warn_time_(min_date_time), last_queue_size_(0) {
910 }
911
913 ~Destination() {
914 closeAllConnections();
915 }
916
924 void addConnection(ConnectionPtr connection) {
925 if (connectionsFull()) {
926 isc_throw(BadValue, "address: " << address_.toText()
927 << ", already at maximum connections: "
928 << max_connections_);
929 }
930
931 connections_.push_back(connection);
932 }
933
938 void closeConnection(ConnectionPtr connection) {
939 for (auto it = connections_.begin(); it != connections_.end(); ++it) {
940 if (*it == connection) {
941 (*it)->close();
942 connections_.erase(it);
943 break;
944 }
945 }
946 }
947
950 void closeAllConnections() {
951 // Flush the queue.
952 while (!queue_.empty()) {
953 queue_.pop();
954 }
955
956 for (auto const& connection : connections_) {
957 connection->close();
958 }
959
960 connections_.clear();
961 }
962
986 void garbageCollectConnections() {
987 for (auto it = connections_.begin(); it != connections_.end();) {
988 (*it)->isClosedByPeer();
989 if (!(*it)->isClosed()) {
990 ++it;
991 } else {
992 it = connections_.erase(it);
993 }
994 }
995 }
996
1008 ConnectionPtr getIdleConnection() {
1009 for (auto const& connection : connections_) {
1010 if (!connection->isTransactionOngoing() &&
1011 !connection->isClosed()) {
1012 return (connection);
1013 }
1014 }
1015
1016 return (ConnectionPtr());
1017 }
1018
1025 ConnectionPtr findBySocketFd(int socket_fd) {
1026 for (auto const& connection : connections_) {
1027 if (connection->isMySocket(socket_fd)) {
1028 return (connection);
1029 }
1030 }
1031
1032 return (ConnectionPtr());
1033 }
1034
1038 bool connectionsEmpty() {
1039 return (connections_.empty());
1040 }
1041
1045 bool connectionsFull() {
1046 return (connections_.size() >= max_connections_);
1047 }
1048
1052 size_t connectionCount() {
1053 return (connections_.size());
1054 }
1055
1059 size_t getMaxConnections() const {
1060 return (max_connections_);
1061 }
1062
1066 bool queueEmpty() const {
1067 return (queue_.empty());
1068 }
1069
1076 void pushRequest(RequestDescriptor const& desc) {
1077 queue_.push(desc);
1078 size_t size = queue_.size();
1079 // If the queue size is larger than the threshold and growing,
1080 // issue a periodic warning.
1081 if ((size > QUEUE_SIZE_THRESHOLD) && (size > last_queue_size_)) {
1082 ptime now = microsec_clock::universal_time();
1083 if ((now - last_queue_warn_time_) > seconds(QUEUE_WARN_SECS)) {
1085 .arg(address_.toText())
1086 .arg(port_)
1087 .arg(size);
1088 // Remember the last time we warned.
1089 last_queue_warn_time_ = now;
1090 }
1091 }
1092
1093 // Remember the previous size.
1094 last_queue_size_ = size;
1095 }
1096
1100 RequestDescriptor popNextRequest() {
1101 if (queue_.empty()) {
1102 isc_throw(InvalidOperation, "cannot pop, queue is empty");
1103 }
1104
1105 RequestDescriptor desc = queue_.front();
1106 queue_.pop();
1107 return (desc);
1108 }
1109
1110 private:
1112 IOAddress address_;
1113
1115 uint16_t port_;
1116
1118 TlsContextPtr tls_context_;
1119
1121 size_t max_connections_;
1122
1124 std::list<ConnectionPtr> connections_;
1125
1127 std::queue<RequestDescriptor> queue_;
1128
1130 ptime last_queue_warn_time_;
1131
1133 size_t last_queue_size_;
1134 };
1135
1137 typedef boost::shared_ptr<Destination> DestinationPtr;
1138
1147 DestinationPtr addDestination(const IOAddress& address,
1148 const uint16_t port,
1149 const TlsContextPtr& tls_context) {
1150 DestinationDescriptor desc(address, port, tls_context);
1151 DestinationPtr destination(new Destination(address, port, tls_context,
1152 max_addr_connections_));
1153 destinations_[desc] = destination;
1154 return (destination);
1155 }
1156
1166 DestinationPtr findDestination(const IOAddress& address,
1167 const uint16_t port,
1168 const TlsContextPtr& tls_context) const {
1169 DestinationDescriptor desc(address, port, tls_context);
1170 auto it = destinations_.find(desc);
1171 if (it != destinations_.end()) {
1172 return (it->second);
1173 }
1174
1175 return (DestinationPtr());
1176 }
1177
1190 void removeDestination(const IOAddress& address,
1191 const uint16_t port,
1192 const TlsContextPtr& tls_context) {
1193 DestinationDescriptor desc(address, port, tls_context);
1194 auto it = destinations_.find(desc);
1195 if (it != destinations_.end()) {
1196 it->second->closeAllConnections();
1197 destinations_.erase(it);
1198 }
1199 }
1200
1202 IOServicePtr io_service_;
1203
1205 std::map<DestinationDescriptor, DestinationPtr> destinations_;
1206
1208 std::mutex pool_mutex_;
1209
1211 size_t max_addr_connections_;
1212};
1213
1214Connection::Connection(const IOServicePtr& io_service,
1215 const TlsContextPtr& tls_context,
1216 const ConnectionPoolPtr& conn_pool,
1217 const IOAddress& address,
1218 const uint16_t port)
1219 : io_service_(io_service), conn_pool_(conn_pool), address_(address),
1220 port_(port), tls_context_(tls_context), tcp_socket_(), tls_socket_(),
1221 timer_(new IntervalTimer(io_service)), current_request_(),
1222 current_response_(), current_persistent_(false),
1223 current_response_complete_(false), current_complete_check_(),
1224 current_callback_(), buf_(), input_buf_(), current_transid_(0),
1225 close_callback_(), started_(false), need_handshake_(false),
1226 closed_(false) {
1227 if (!tls_context) {
1228 tcp_socket_.reset(new asiolink::TCPSocket<SocketCallback>(io_service));
1229 } else {
1230 tls_socket_.reset(new asiolink::TLSSocket<SocketCallback>(io_service,
1231 tls_context));
1232 need_handshake_ = true;
1233 }
1234}
1235
1236Connection::~Connection() {
1237 close();
1238}
1239
1240void
1241Connection::resetState() {
1242 started_ = false;
1243 current_request_.reset();
1244 current_response_.reset();
1245 current_persistent_ = false;
1246 current_response_complete_ = false;
1247 current_callback_ = TcpClient::RequestHandler();
1248}
1249
1250void
1251Connection::closeCallback(const bool clear) {
1252 if (close_callback_) {
1253 try {
1254 if (tcp_socket_) {
1255 close_callback_(tcp_socket_->getNative());
1256 } else if (tls_socket_) {
1257 close_callback_(tls_socket_->getNative());
1258 } else {
1259 isc_throw(Unexpected,
1260 "internal error: can't find a socket to close");
1261 }
1262 } catch (...) {
1264 }
1265 }
1266
1267 if (clear) {
1268 close_callback_ = TcpClient::CloseHandler();
1269 }
1270}
1271
1272void
1273Connection::isClosedByPeer() {
1274 // This method applies only to idle connections.
1275 if (started_ || closed_) {
1276 return;
1277 }
1278 // This code was guarded by a lock so keep this.
1279 if (MultiThreadingMgr::instance().getMode()) {
1280 std::lock_guard<std::mutex> lk(mutex_);
1281 isClosedByPeerInternal();
1282 } else {
1283 isClosedByPeerInternal();
1284 }
1285}
1286
1287void
1288Connection::isClosedByPeerInternal() {
1289 // If the socket is open we check if it is possible to transmit
1290 // the data over this socket by reading from it with message
1291 // peeking. If the socket is not usable, we close it and then
1292 // re-open it. There is a narrow window of time between checking
1293 // the socket usability and actually transmitting the data over
1294 // this socket, when the peer may close the connection. In this
1295 // case we'll need to re-transmit but we don't handle it here.
1296 if (tcp_socket_) {
1297 if (tcp_socket_->getASIOSocket().is_open() &&
1298 !tcp_socket_->isUsable()) {
1299 closeCallback();
1300 closed_ = true;
1301 tcp_socket_->close();
1302 }
1303 } else if (tls_socket_) {
1304 if (tls_socket_->getASIOSocket().is_open() &&
1305 !tls_socket_->isUsable()) {
1306 closeCallback();
1307 closed_ = true;
1308 tls_socket_->close();
1309 }
1310 } else {
1311 isc_throw(Unexpected, "internal error: can't find the sending socket");
1312 }
1313}
1314
1315void
1316Connection::doTransaction(const WireDataPtr& request,
1317 const WireDataPtr& response,
1318 const bool persistent,
1319 const long request_timeout,
1320 const TcpClient::CompleteCheck& complete_check,
1321 const TcpClient::RequestHandler& callback,
1322 const TcpClient::ConnectHandler& connect_callback,
1323 const TcpClient::HandshakeHandler& handshake_callback,
1324 const TcpClient::CloseHandler& close_callback) {
1325 if (MultiThreadingMgr::instance().getMode()) {
1326 std::lock_guard<std::mutex> lk(mutex_);
1327 doTransactionInternal(request, response, persistent, request_timeout,
1328 complete_check, callback, connect_callback,
1329 handshake_callback, close_callback);
1330 } else {
1331 doTransactionInternal(request, response, persistent, request_timeout,
1332 complete_check, callback, connect_callback,
1333 handshake_callback, close_callback);
1334 }
1335}
1336
1337void
1338Connection::doTransactionInternal(const WireDataPtr& request,
1339 const WireDataPtr& response,
1340 const bool persistent,
1341 const long request_timeout,
1342 const TcpClient::CompleteCheck& complete_check,
1343 const TcpClient::RequestHandler& callback,
1344 const TcpClient::ConnectHandler& connect_callback,
1345 const TcpClient::HandshakeHandler& handshake_callback,
1346 const TcpClient::CloseHandler& close_callback) {
1347 try {
1348 started_ = true;
1349 current_request_ = request;
1350 current_response_ = response;
1351 current_persistent_ = persistent;
1352 current_complete_check_ = complete_check;
1353 current_callback_ = callback;
1354 handshake_callback_ = handshake_callback;
1355 close_callback_ = close_callback;
1356
1357 // Starting new transaction. Generate new transaction id.
1358 ++current_transid_;
1359
1360 buf_ = *request;
1361
1362 size_t to_dump = request->size();
1363 bool truncated = false;
1364 if (to_dump > 100) {
1365 to_dump = 100;
1366 truncated = true;
1367 }
1370 .arg(str::dumpAsHex(request->data(), to_dump) +
1371 (truncated ? "..." : ""))
1372 .arg(address_.toText())
1373 .arg(port_);
1374
1375 // Setup request timer.
1376 scheduleTimer(request_timeout);
1377
1381 TCPEndpoint endpoint(address_, port_);
1382 SocketCallback socket_cb(std::bind(&Connection::connectCallback,
1383 shared_from_this(),
1384 connect_callback,
1385 current_transid_,
1386 ph::_1));
1387
1388 // Establish new connection or use existing connection.
1389 if (tcp_socket_) {
1390 tcp_socket_->open(&endpoint, socket_cb);
1391 return;
1392 }
1393 if (tls_socket_) {
1394 tls_socket_->open(&endpoint, socket_cb);
1395 return;
1396 }
1397
1398 // Should never reach this point.
1399 isc_throw(Unexpected, "internal error: can't find a socket to open");
1400
1401 } catch (const std::exception& ex) {
1402 // Re-throw with the expected exception type.
1403 isc_throw(TcpClientError, ex.what());
1404 }
1405}
1406
1407void
1408Connection::close() {
1409 if (MultiThreadingMgr::instance().getMode()) {
1410 std::lock_guard<std::mutex> lk(mutex_);
1411 return (closeInternal());
1412 } else {
1413 return (closeInternal());
1414 }
1415}
1416
1417void
1418Connection::closeInternal() {
1419 // Pass in true to discard the callback.
1420 closeCallback(true);
1421
1422 closed_ = true;
1423 timer_->cancel();
1424 if (tcp_socket_) {
1425 tcp_socket_->close();
1426 }
1427 if (tls_socket_) {
1428 tls_socket_->close();
1429 }
1430
1431 resetState();
1432}
1433
1434bool
1435Connection::isMySocket(int socket_fd) const {
1436 if (tcp_socket_) {
1437 return (tcp_socket_->getNative() == socket_fd);
1438 } else if (tls_socket_) {
1439 return (tls_socket_->getNative() == socket_fd);
1440 }
1441 // Should never reach this point.
1442 std::cerr << "internal error: can't find my socket\n";
1443 return (false);
1444}
1445
1446bool
1447Connection::checkPrematureTimeout(const uint64_t transid) {
1448 if (MultiThreadingMgr::instance().getMode()) {
1449 std::lock_guard<std::mutex> lk(mutex_);
1450 return (checkPrematureTimeoutInternal(transid));
1451 } else {
1452 return (checkPrematureTimeoutInternal(transid));
1453 }
1454}
1455
1456bool
1457Connection::checkPrematureTimeoutInternal(const uint64_t transid) {
1458 // If there is no transaction but the handlers are invoked it means
1459 // that the last transaction in the queue timed out prematurely.
1460 // Also, if there is a transaction in progress but the ID of that
1461 // transaction doesn't match the one associated with the handler it,
1462 // also means that the transaction timed out prematurely.
1463 if (!isTransactionOngoing() || (transid != current_transid_)) {
1465 .arg(isTransactionOngoing())
1466 .arg(transid)
1467 .arg(current_transid_);
1468 return (true);
1469 }
1470
1471 return (false);
1472}
1473
1474void
1475Connection::terminate(const boost::system::error_code& ec,
1476 const std::string& error_msg) {
1477 if (MultiThreadingMgr::instance().getMode()) {
1478 std::lock_guard<std::mutex> lk(mutex_);
1479 terminateInternal(ec, error_msg);
1480 } else {
1481 terminateInternal(ec, error_msg);
1482 }
1483}
1484
1485void
1486Connection::terminateInternal(const boost::system::error_code& ec,
1487 std::string error_msg) {
1488 WireDataPtr response;
1489 if (isTransactionOngoing()) {
1490
1491 timer_->cancel();
1492 if (tcp_socket_) {
1493 tcp_socket_->cancel();
1494 }
1495 if (tls_socket_) {
1496 tls_socket_->cancel();
1497 }
1498
1499 if (!ec && current_response_complete_) {
1500 response = current_response_;
1501
1504 .arg(address_.toText())
1505 .arg(port_);
1506 } else {
1507 if (error_msg.empty()) {
1508 error_msg = ec.message();
1509 }
1512 .arg(address_.toText())
1513 .arg(port_)
1514 .arg(error_msg);
1515
1516 // Only log the details if we have received anything.
1517 if (!current_response_->empty()) {
1518 size_t to_dump = current_response_->size();
1519 bool truncated = false;
1520 if (to_dump > 100) {
1521 to_dump = 100;
1522 truncated = true;
1523 }
1526 .arg(address_.toText())
1527 .arg(port_)
1528 .arg(str::dumpAsHex(current_response_->data(), to_dump) +
1529 (truncated ? "..." : ""));
1530 }
1531 }
1532
1533 try {
1534 // The callback should take care of its own exceptions but one
1535 // never knows.
1536 if (MultiThreadingMgr::instance().getMode()) {
1537 UnlockGuard<std::mutex> lock(mutex_);
1538 current_callback_(ec, response, error_msg);
1539 } else {
1540 current_callback_(ec, response, error_msg);
1541 }
1542 } catch (...) {
1543 }
1544
1545 // If we're not requesting connection persistence or the
1546 // connection has timed out, we should close the socket.
1547 if (!closed_ &&
1548 (!current_persistent_ || (ec == boost::asio::error::timed_out))) {
1549 closeInternal();
1550 }
1551
1552 resetState();
1553 }
1554
1555 // Check if there are any requests queued for this destination and start
1556 // another transaction if there is at least one.
1557 ConnectionPoolPtr conn_pool = conn_pool_.lock();
1558 if (conn_pool) {
1559 conn_pool->postProcessNextRequest(address_, port_, tls_context_);
1560 }
1561}
1562
1563void
1564Connection::scheduleTimer(const long request_timeout) {
1565 if (request_timeout > 0) {
1566 timer_->setup(std::bind(&Connection::timerCallback, this), request_timeout,
1568 }
1569}
1570
1571void
1572Connection::doHandshake(const uint64_t transid) {
1573 // Skip the handshake if it is not needed.
1574 if (!need_handshake_) {
1575 doSend(transid);
1576 return;
1577 }
1578
1579 SocketCallback socket_cb(std::bind(&Connection::handshakeCallback,
1580 shared_from_this(),
1581 handshake_callback_,
1582 transid,
1583 ph::_1));
1584 try {
1585 tls_socket_->handshake(socket_cb);
1586
1587 } catch (...) {
1588 terminate(boost::asio::error::not_connected);
1589 }
1590}
1591
1592void
1593Connection::doSend(const uint64_t transid) {
1594 SocketCallback socket_cb(std::bind(&Connection::sendCallback,
1595 shared_from_this(),
1596 transid,
1597 ph::_1,
1598 ph::_2));
1599 try {
1600 if (tcp_socket_) {
1601 tcp_socket_->asyncSend(&buf_[0], buf_.size(), socket_cb);
1602 return;
1603 }
1604
1605 if (tls_socket_) {
1606 tls_socket_->asyncSend(&buf_[0], buf_.size(), socket_cb);
1607 return;
1608 }
1609
1610 // Should never reach this point.
1611 std::cerr << "internal error: can't find a socket to send to\n";
1612 isc_throw(Unexpected,
1613 "internal error: can't find a socket to send to");
1614 } catch (...) {
1615 terminate(boost::asio::error::not_connected);
1616 }
1617}
1618
1619void
1620Connection::doReceive(const uint64_t transid) {
1621 TCPEndpoint endpoint;
1622 SocketCallback socket_cb(std::bind(&Connection::receiveCallback,
1623 shared_from_this(),
1624 transid,
1625 ph::_1,
1626 ph::_2));
1627 try {
1628 if (tcp_socket_) {
1629 tcp_socket_->asyncReceive(static_cast<void*>(input_buf_.data()),
1630 input_buf_.size(), 0,
1631 &endpoint, socket_cb);
1632 return;
1633 }
1634 if (tls_socket_) {
1635 tls_socket_->asyncReceive(static_cast<void*>(input_buf_.data()),
1636 input_buf_.size(), 0,
1637 &endpoint, socket_cb);
1638 return;
1639 }
1640 // Should never reach this point.
1641 std::cerr << "internal error: can't find a socket to receive from\n";
1642 isc_throw(Unexpected,
1643 "internal error: can't find a socket to receive from");
1644
1645 } catch (...) {
1646 terminate(boost::asio::error::not_connected);
1647 }
1648}
1649
1650void
1651Connection::connectCallback(TcpClient::ConnectHandler connect_callback,
1652 const uint64_t transid,
1653 const boost::system::error_code& ec) {
1654 if (checkPrematureTimeout(transid)) {
1655 return;
1656 }
1657
1658 // Run user defined connect callback if specified.
1659 if (connect_callback) {
1660 // If the user defined callback indicates that the connection
1661 // should not be continued.
1662 if (tcp_socket_) {
1663 if (!connect_callback(ec, tcp_socket_->getNative())) {
1664 return;
1665 }
1666 } else if (tls_socket_) {
1667 if (!connect_callback(ec, tls_socket_->getNative())) {
1668 return;
1669 }
1670 } else {
1671 // Should never reach this point.
1672 std::cerr << "internal error: can't find a socket to connect\n";
1673 }
1674 }
1675
1676 if (ec && (ec.value() == boost::asio::error::operation_aborted)) {
1677 return;
1678
1679 // In some cases the "in progress" status code may be returned. It doesn't
1680 // indicate an error. Sending the request over the socket is expected to
1681 // be successful. Getting such status appears to be highly dependent on
1682 // the operating system.
1683 } else if (ec &&
1684 (ec.value() != boost::asio::error::in_progress) &&
1685 (ec.value() != boost::asio::error::already_connected)) {
1686 terminate(ec);
1687
1688 } else {
1689 // Start the TLS handshake asynchronously.
1690 doHandshake(transid);
1691 }
1692}
1693
1694void
1695Connection::handshakeCallback(TcpClient::ConnectHandler handshake_callback,
1696 const uint64_t transid,
1697 const boost::system::error_code& ec) {
1698 need_handshake_ = false;
1699 if (checkPrematureTimeout(transid)) {
1700 return;
1701 }
1702
1703 // Run user defined handshake callback if specified.
1704 if (handshake_callback) {
1705 // If the user defined callback indicates that the connection
1706 // should not be continued.
1707 if (tls_socket_) {
1708 if (!handshake_callback(ec, tls_socket_->getNative())) {
1709 return;
1710 }
1711 } else {
1712 // Should never reach this point.
1713 std::cerr << "internal error: can't find TLS socket\n";
1714 }
1715 }
1716
1717 if (ec && (ec.value() == boost::asio::error::operation_aborted)) {
1718 return;
1719 } else if (ec) {
1720 terminate(ec);
1721
1722 } else {
1723 // Start sending the request asynchronously.
1724 doSend(transid);
1725 }
1726}
1727
1728void
1729Connection::sendCallback(const uint64_t transid,
1730 const boost::system::error_code& ec,
1731 size_t length) {
1732 if (checkPrematureTimeout(transid)) {
1733 return;
1734 }
1735
1736 if (ec) {
1737 if (ec.value() == boost::asio::error::operation_aborted) {
1738 return;
1739
1740 // EAGAIN and EWOULDBLOCK don't really indicate an error. The length
1741 // should be 0 in this case but let's be sure.
1742 } else if ((ec.value() == boost::asio::error::would_block) ||
1743 (ec.value() == boost::asio::error::try_again)) {
1744 length = 0;
1745
1746 } else {
1747 // Any other error should cause the transaction to terminate.
1748 terminate(ec);
1749 return;
1750 }
1751 }
1752
1753 // Sending is in progress, so push back the timeout.
1754 scheduleTimer(timer_->getInterval());
1755
1756 // If any data have been sent, remove it from the buffer and only leave the
1757 // portion that still has to be sent.
1758 if (length > 0) {
1759 if (length >= buf_.size()) {
1760 buf_.clear();
1761 } else {
1762 buf_.erase(buf_.begin(), buf_.begin() + length);
1763 }
1764 }
1765
1766 // If there is no more data to be sent, start receiving a response. Otherwise,
1767 // continue sending.
1768 if (buf_.empty()) {
1769 doReceive(transid);
1770
1771 } else {
1772 doSend(transid);
1773 }
1774}
1775
1776void
1777Connection::receiveCallback(const uint64_t transid,
1778 const boost::system::error_code& ec,
1779 size_t length) {
1780 if (checkPrematureTimeout(transid)) {
1781 return;
1782 }
1783
1784 if (ec) {
1785 if (ec.value() == boost::asio::error::operation_aborted) {
1786 return;
1787
1788 // EAGAIN and EWOULDBLOCK don't indicate an error in this case. All
1789 // other errors should terminate the transaction.
1790 }
1791 if ((ec.value() != boost::asio::error::try_again) &&
1792 (ec.value() != boost::asio::error::would_block)) {
1793 terminate(ec);
1794 return;
1795
1796 } else {
1797 // For EAGAIN and EWOULDBLOCK the length should be 0 anyway, but let's
1798 // make sure.
1799 length = 0;
1800 }
1801 }
1802
1803 // Receiving is in progress, so push back the timeout.
1804 scheduleTimer(timer_->getInterval());
1805
1806 if (runCompleteCheck(ec, length)) {
1807 doReceive(transid);
1808 }
1809}
1810
1811bool
1812Connection::runCompleteCheck(const boost::system::error_code& ec, size_t length) {
1813 if (MultiThreadingMgr::instance().getMode()) {
1814 std::lock_guard<std::mutex> lk(mutex_);
1815 return (runCompleteCheckInternal(ec, length));
1816 } else {
1817 return (runCompleteCheckInternal(ec, length));
1818 }
1819}
1820
1821bool
1822Connection::runCompleteCheckInternal(const boost::system::error_code& ec,
1823 size_t length) {
1824 // If we have received any data, let's store it.
1825 if (length != 0) {
1826 current_response_->insert(current_response_->end(),
1827 input_buf_.begin(),
1828 input_buf_.begin() + length);
1829 }
1830
1831 // If data is still needed, let's schedule another receive.
1832 int status = -1;
1833 std::string err = "";
1834 if (current_complete_check_) {
1835 status = current_complete_check_(current_response_, err);
1836 } else {
1837 err = "Internal error: no completion checker?";
1838 }
1839 if (status == 0) {
1840 return (true);
1841 } else if (status > 0) {
1842 // No more data needed.
1843 current_response_complete_ = true;
1844 terminateInternal(ec);
1845 } else {
1846 // Error case.
1847 terminateInternal(ec, err);
1848 }
1849
1850 return (false);
1851}
1852
1853void
1854Connection::timerCallback() {
1855 // Request timeout occurred.
1856 terminate(boost::asio::error::timed_out);
1857}
1858
1859}
1860
1861namespace isc {
1862namespace tcp {
1863
1866public:
1890 TcpClientImpl(const IOServicePtr& io_service, size_t thread_pool_size = 0,
1891 bool defer_thread_start = false)
1892 : thread_pool_size_(thread_pool_size), thread_pool_() {
1893 if (thread_pool_size_ > 0) {
1894 // Create our own private IOService.
1895 thread_io_service_.reset(new IOService());
1896
1897 // Create the connection pool. Note that we use the thread_pool_size
1898 // as the maximum connections per address.
1899 conn_pool_.reset(new ConnectionPool(thread_io_service_, thread_pool_size_));
1900
1901 // Create the thread pool.
1902 thread_pool_.reset(new IoServiceThreadPool(thread_io_service_, thread_pool_size_,
1903 defer_thread_start));
1904
1907 .arg(thread_pool_size_);
1908 } else {
1909 // Single-threaded mode: use the caller's IOService,
1910 // one connection per address.
1911 conn_pool_.reset(new ConnectionPool(io_service, 1));
1912 }
1913 }
1914
1919 stop();
1920 }
1921
1928 if (thread_pool_) {
1929 thread_pool_->checkPausePermissions();
1930 }
1931 }
1932
1934 void start() {
1935 if (thread_pool_) {
1936 thread_pool_->run();
1937 }
1938 }
1939
1942 void stop() {
1943 // Close all the connections.
1944 conn_pool_->closeAll();
1945
1946 // Stop the thread pool.
1947 if (thread_pool_) {
1948 thread_pool_->stop();
1949 }
1950
1951 if (thread_io_service_) {
1952 thread_io_service_->stopAndPoll();
1953 thread_io_service_->stop();
1954 }
1955 }
1956
1961 void pause() {
1962 if (!thread_pool_) {
1963 isc_throw(InvalidOperation, "TcpClient::pause - no thread pool");
1964 }
1965
1966 // Pause the thread pool.
1967 thread_pool_->pause();
1968 }
1969
1974 void resume() {
1975 if (!thread_pool_) {
1976 isc_throw(InvalidOperation, "TcpClient::resume - no thread pool");
1977 }
1978
1979 // Resume running the thread pool.
1980 thread_pool_->run();
1981 }
1982
1987 bool isRunning() {
1988 if (thread_pool_) {
1989 return (thread_pool_->isRunning());
1990 }
1991
1992 return (false);
1993 }
1994
1999 bool isStopped() {
2000 if (thread_pool_) {
2001 return (thread_pool_->isStopped());
2002 }
2003
2004 return (false);
2005 }
2006
2011 bool isPaused() {
2012 if (thread_pool_) {
2013 return (thread_pool_->isPaused());
2014 }
2015
2016 return (false);
2017 }
2018
2024 return (thread_io_service_);
2025 };
2026
2031 return (thread_pool_size_);
2032 }
2033
2037 uint16_t getThreadCount() {
2038 if (!thread_pool_) {
2039 return (0);
2040 }
2041 return (thread_pool_->getThreadCount());
2042 }
2043
2045 ConnectionPoolPtr conn_pool_;
2046
2047private:
2048
2050 size_t thread_pool_size_;
2051
2053 asiolink::IOServicePtr thread_io_service_;
2054
2057 IoServiceThreadPoolPtr thread_pool_;
2058};
2059
2060TcpClient::TcpClient(const IOServicePtr& io_service, bool multi_threading_enabled,
2061 size_t thread_pool_size, bool defer_thread_start) {
2062 if (!multi_threading_enabled && thread_pool_size) {
2064 "TcpClient thread_pool_size must be zero "
2065 "when Kea core multi-threading is disabled");
2066 }
2067
2068 impl_.reset(new TcpClientImpl(io_service, thread_pool_size,
2069 defer_thread_start));
2070}
2071
2073 impl_->stop();
2074}
2075
2076void
2078 const uint16_t port,
2079 const TlsContextPtr& tls_context,
2080 const WireDataPtr& request,
2081 const WireDataPtr& response,
2082 const bool persistent,
2083 const TcpClient::CompleteCheck& complete_check,
2084 const TcpClient::RequestHandler& request_callback,
2085 const TcpClient::RequestTimeout& request_timeout,
2086 const TcpClient::ConnectHandler& connect_callback,
2087 const TcpClient::HandshakeHandler& handshake_callback,
2088 const TcpClient::CloseHandler& close_callback) {
2089 if (!request) {
2090 isc_throw(TcpClientError, "TCP request must not be null");
2091 }
2092
2093 if (!response) {
2094 isc_throw(TcpClientError, "TCP response must not be null");
2095 }
2096
2097 if (!complete_check) {
2098 isc_throw(TcpClientError, "TCP response completion checker must not be null");
2099 }
2100
2101 if (!request_callback) {
2102 isc_throw(TcpClientError, "callback for TCP transaction must not be null");
2103 }
2104
2105 impl_->conn_pool_->queueRequest(address, port, tls_context,
2106 request, response, persistent,
2107 request_timeout.value_,
2108 complete_check,
2109 request_callback, connect_callback,
2110 handshake_callback, close_callback);
2111}
2112
2113void
2115 return (impl_->conn_pool_->closeIfOutOfBand(socket_fd));
2116}
2117
2118void
2120 impl_->start();
2121}
2122
2123void
2125 impl_->checkPermissions();
2126}
2127
2128void
2130 impl_->pause();
2131}
2132
2133void
2135 impl_->resume();
2136}
2137
2138void
2140 impl_->stop();
2141}
2142
2143const IOServicePtr
2145 return (impl_->getThreadIOService());
2146}
2147
2148uint16_t
2150 return (impl_->getThreadPoolSize());
2151}
2152
2153uint16_t
2155 return (impl_->getThreadCount());
2156}
2157
2158bool
2160 return (impl_->isRunning());
2161}
2162
2163bool
2165 return (impl_->isStopped());
2166}
2167
2168bool
2170 return (impl_->isPaused());
2171}
2172
2173} // end of namespace isc::tcp
2174} // 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:277
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