mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-29 21:18:02 +00:00
[#2583] Initial addition of src/lib/tcp
Added first draft of TcpListener classes. They compile but don't do anything useful. configure.ac src/lib/Makefile.am added tcp src/lib/tcp/README src/lib/tcp/tcp_connection.cc src/lib/tcp/tcp_connection.h src/lib/tcp/tcp_connection_acceptor.h src/lib/tcp/tcp_connection_pool.cc src/lib/tcp/tcp_connection_pool.h src/lib/tcp/tcp_listener.cc src/lib/tcp/tcp_listener.h src/lib/tcp/tcp_log.cc src/lib/tcp/tcp_log.h src/lib/tcp/tcp_messages.cc src/lib/tcp/tcp_messages.h src/lib/tcp/tcp_messages.mes src/lib/tcp/tests/.gitignore src/lib/tcp/tests/Makefile.am src/lib/tcp/tests/run_unittests.cc - new files
This commit is contained in:
parent
6fb9bb04a3
commit
32d7bf4e93
@ -1583,6 +1583,8 @@ AC_CONFIG_FILES([src/lib/process/testutils/Makefile])
|
||||
AC_CONFIG_FILES([src/lib/stats/Makefile])
|
||||
AC_CONFIG_FILES([src/lib/stats/tests/Makefile])
|
||||
AC_CONFIG_FILES([src/lib/stats/testutils/Makefile])
|
||||
AC_CONFIG_FILES([src/lib/tcp/Makefile])
|
||||
AC_CONFIG_FILES([src/lib/tcp/tests/Makefile])
|
||||
AC_CONFIG_FILES([src/lib/testutils/Makefile])
|
||||
AC_CONFIG_FILES([src/lib/testutils/dhcp_test_lib.sh],
|
||||
[chmod +x src/lib/testutils/dhcp_test_lib.sh])
|
||||
|
@ -9,7 +9,7 @@ if HAVE_PGSQL
|
||||
SUBDIRS += pgsql
|
||||
endif
|
||||
|
||||
SUBDIRS += config_backend hooks dhcp http config stats
|
||||
SUBDIRS += config_backend hooks dhcp tcp http config stats
|
||||
|
||||
if HAVE_NETCONF
|
||||
SUBDIRS += yang
|
||||
|
76
src/lib/tcp/Makefile.am
Normal file
76
src/lib/tcp/Makefile.am
Normal file
@ -0,0 +1,76 @@
|
||||
SUBDIRS = . tests
|
||||
|
||||
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
|
||||
AM_CPPFLAGS += $(BOOST_INCLUDES) $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES)
|
||||
AM_CXXFLAGS = $(KEA_CXXFLAGS)
|
||||
|
||||
EXTRA_DIST = # tcp.dox
|
||||
|
||||
# Ensure that the message file is included in the distribution
|
||||
EXTRA_DIST += tcp_messages.mes
|
||||
|
||||
CLEANFILES = *.gcno *.gcda
|
||||
|
||||
lib_LTLIBRARIES = libkea-tcp.la
|
||||
|
||||
libkea_tcp_la_SOURCES = tcp_connection.cc tcp_connection.h
|
||||
libkea_tcp_la_SOURCES += tcp_connection_pool.cc tcp_connection_pool.h
|
||||
libkea_tcp_la_SOURCES += tcp_listener.cc tcp_listener.h
|
||||
libkea_tcp_la_SOURCES += tcp_log.cc tcp_log.h
|
||||
libkea_tcp_la_SOURCES += tcp_messages.cc tcp_messages.h
|
||||
|
||||
libkea_tcp_la_CXXFLAGS = $(AM_CXXFLAGS)
|
||||
libkea_tcp_la_CPPFLAGS = $(AM_CPPFLAGS)
|
||||
libkea_tcp_la_LDFLAGS = $(AM_LDFLAGS)
|
||||
libkea_tcp_la_LDFLAGS += -no-undefined -version-info 52:0:0
|
||||
|
||||
libkea_tcp_la_LIBADD =
|
||||
libkea_tcp_la_LIBADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
|
||||
libkea_tcp_la_LIBADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
|
||||
libkea_tcp_la_LIBADD += $(top_builddir)/src/lib/cc/libkea-cc.la
|
||||
libkea_tcp_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la
|
||||
libkea_tcp_la_LIBADD += $(top_builddir)/src/lib/util/libkea-util.la
|
||||
libkea_tcp_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
|
||||
libkea_tcp_la_LIBADD += $(LOG4CPLUS_LIBS) $(BOOST_LIBS) $(CRYPTO_LIBS)
|
||||
|
||||
# If we want to get rid of all generated messages files, we need to use
|
||||
# make maintainer-clean. The proper way to introduce custom commands for
|
||||
# that operation is to define maintainer-clean-local target. However,
|
||||
# make maintainer-clean also removes Makefile, so running configure script
|
||||
# is required. To make it easy to rebuild messages without going through
|
||||
# reconfigure, a new target messages-clean has been added.
|
||||
maintainer-clean-local:
|
||||
rm -f tcp_messages.h tcp_messages.cc
|
||||
|
||||
# To regenerate messages files, one can do:
|
||||
#
|
||||
# make messages-clean
|
||||
# make messages
|
||||
#
|
||||
# This is needed only when a .mes file is modified.
|
||||
messages-clean: maintainer-clean-local
|
||||
|
||||
if GENERATE_MESSAGES
|
||||
|
||||
# Define rule to build logging source files from message file
|
||||
messages: tcp_messages.h tcp_messages.cc
|
||||
@echo Message files regenerated
|
||||
|
||||
tcp_messages.h tcp_messages.cc: tcp_messages.mes
|
||||
$(top_builddir)/src/lib/log/compiler/kea-msg-compiler $(top_srcdir)/src/lib/tcp/tcp_messages.mes
|
||||
|
||||
else
|
||||
|
||||
messages tcp_messages.h tcp_messages.cc:
|
||||
@echo Messages generation disabled. Configure with --enable-generate-messages to enable it.
|
||||
|
||||
endif
|
||||
|
||||
# Specify the headers for copying into the installation directory tree.
|
||||
libkea_tcp_includedir = $(pkgincludedir)/tcp
|
||||
libkea_tcp_include_HEADERS = \
|
||||
tcp_messages.h \
|
||||
tcp_connection.h \
|
||||
tcp_connection_pool.h \
|
||||
tcp_listener.h \
|
||||
tcp_log.h
|
1
src/lib/tcp/README
Normal file
1
src/lib/tcp/README
Normal file
@ -0,0 +1 @@
|
||||
The tcp library is intended to provide support for TCP server/listeners.
|
578
src/lib/tcp/tcp_connection.cc
Normal file
578
src/lib/tcp/tcp_connection.cc
Normal file
@ -0,0 +1,578 @@
|
||||
// Copyright (C) 2022 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#if 0
|
||||
#include <config.h>
|
||||
|
||||
#include <asiolink/asio_wrapper.h>
|
||||
#include <tcp/tcp_connection.h>
|
||||
#include <tcp/tcp_connection_pool.h>
|
||||
#include <tcp/tcp_log.h>
|
||||
#include <tcp/tcp_messages.h>
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <functional>
|
||||
|
||||
using namespace isc::asiolink;
|
||||
namespace ph = std::placeholders;
|
||||
|
||||
namespace {
|
||||
|
||||
/// @brief Maximum size of the HTTP message that can be logged.
|
||||
///
|
||||
/// The part of the HTTP message beyond this value is truncated.
|
||||
constexpr size_t MAX_LOGGED_MESSAGE_SIZE = 1024;
|
||||
|
||||
}
|
||||
|
||||
namespace isc {
|
||||
namespace http {
|
||||
|
||||
void
|
||||
TcpConnection::
|
||||
SocketCallback::operator()(boost::system::error_code ec, size_t length) {
|
||||
if (ec.value() == boost::asio::error::operation_aborted) {
|
||||
return;
|
||||
}
|
||||
callback_(ec, length);
|
||||
}
|
||||
|
||||
TcpConnection::TcpConnection(asiolink::IOService& io_service,
|
||||
const TcpAcceptorPtr& acceptor,
|
||||
const TlsContextPtr& tls_context,
|
||||
TcpConnectionPool& connection_pool,
|
||||
const TcpResponseCreatorPtr& response_creator,
|
||||
const TcpAcceptorCallback& callback,
|
||||
const long request_timeout,
|
||||
const long idle_timeout)
|
||||
: request_timer_(io_service),
|
||||
request_timeout_(request_timeout),
|
||||
tls_context_(tls_context),
|
||||
idle_timeout_(idle_timeout),
|
||||
tcp_socket_(),
|
||||
tls_socket_(),
|
||||
acceptor_(acceptor),
|
||||
connection_pool_(connection_pool),
|
||||
response_creator_(response_creator),
|
||||
acceptor_callback_(callback) {
|
||||
if (!tls_context) {
|
||||
tcp_socket_.reset(new asiolink::TCPSocket<SocketCallback>(io_service));
|
||||
} else {
|
||||
tls_socket_.reset(new asiolink::TLSSocket<SocketCallback>(io_service,
|
||||
tls_context));
|
||||
}
|
||||
}
|
||||
|
||||
TcpConnection::~TcpConnection() {
|
||||
close();
|
||||
}
|
||||
|
||||
void
|
||||
TcpConnection::recordParameters(const TcpRequestPtr& request) const {
|
||||
if (!request) {
|
||||
// Should never happen.
|
||||
return;
|
||||
}
|
||||
|
||||
// Record the remote address.
|
||||
request->setRemote(getRemoteEndpointAddressAsText());
|
||||
|
||||
// Record TLS parameters.
|
||||
if (!tls_socket_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The connection uses HTTPS aka HTTP over TLS.
|
||||
request->setTls(true);
|
||||
|
||||
// Record the first commonName of the subjectName of the client
|
||||
// certificate when wanted.
|
||||
if (TcpRequest::recordSubject_) {
|
||||
request->setSubject(tls_socket_->getTlsStream().getSubject());
|
||||
}
|
||||
|
||||
// Record the first commonName of the issuerName of the client
|
||||
// certificate when wanted.
|
||||
if (TcpRequest::recordIssuer_) {
|
||||
request->setIssuer(tls_socket_->getTlsStream().getIssuer());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TcpConnection::shutdownCallback(const boost::system::error_code&) {
|
||||
tls_socket_->close();
|
||||
}
|
||||
|
||||
void
|
||||
TcpConnection::shutdown() {
|
||||
request_timer_.cancel();
|
||||
if (tcp_socket_) {
|
||||
tcp_socket_->close();
|
||||
return;
|
||||
}
|
||||
if (tls_socket_) {
|
||||
// Create instance of the callback to close the socket.
|
||||
SocketCallback cb(std::bind(&TcpConnection::shutdownCallback,
|
||||
shared_from_this(),
|
||||
ph::_1)); // error_code
|
||||
tls_socket_->shutdown(cb);
|
||||
return;
|
||||
}
|
||||
// Not reachable?
|
||||
isc_throw(Unexpected, "internal error: unable to shutdown the socket");
|
||||
}
|
||||
|
||||
void
|
||||
TcpConnection::close() {
|
||||
request_timer_.cancel();
|
||||
if (tcp_socket_) {
|
||||
tcp_socket_->close();
|
||||
return;
|
||||
}
|
||||
if (tls_socket_) {
|
||||
tls_socket_->close();
|
||||
return;
|
||||
}
|
||||
// Not reachable?
|
||||
isc_throw(Unexpected, "internal error: unable to close the socket");
|
||||
}
|
||||
|
||||
void
|
||||
TcpConnection::shutdownConnection() {
|
||||
try {
|
||||
LOG_DEBUG(asiolink_logger, isc::log::DBGLVL_TRACE_BASIC,
|
||||
TCP_CONNECTION_SHUTDOWN)
|
||||
.arg(getRemoteEndpointAddressAsText());
|
||||
connection_pool_.shutdown(shared_from_this());
|
||||
} catch (...) {
|
||||
LOG_ERROR(asiolink_logger, TCP_CONNECTION_SHUTDOWN_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TcpConnection::stopThisConnection() {
|
||||
try {
|
||||
LOG_DEBUG(asiolink_logger, isc::log::DBGLVL_TRACE_BASIC,
|
||||
TCP_CONNECTION_STOP)
|
||||
.arg(getRemoteEndpointAddressAsText());
|
||||
connection_pool_.stop(shared_from_this());
|
||||
} catch (...) {
|
||||
LOG_ERROR(asiolink_logger, TCP_CONNECTION_STOP_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TcpConnection::asyncAccept() {
|
||||
// Create instance of the callback. It is safe to pass the local instance
|
||||
// of the callback, because the underlying boost functions make copies
|
||||
// as needed.
|
||||
TcpAcceptorCallback cb = std::bind(&TcpConnection::acceptorCallback,
|
||||
shared_from_this(),
|
||||
ph::_1); // error
|
||||
try {
|
||||
TcpsAcceptorPtr tls_acceptor =
|
||||
boost::dynamic_pointer_cast<TcpsAcceptor>(acceptor_);
|
||||
if (!tls_acceptor) {
|
||||
if (!tcp_socket_) {
|
||||
isc_throw(Unexpected, "internal error: TCP socket is null");
|
||||
}
|
||||
acceptor_->asyncAccept(*tcp_socket_, cb);
|
||||
} else {
|
||||
if (!tls_socket_) {
|
||||
isc_throw(Unexpected, "internal error: TLS socket is null");
|
||||
}
|
||||
tls_acceptor->asyncAccept(*tls_socket_, cb);
|
||||
}
|
||||
} catch (const std::exception& ex) {
|
||||
isc_throw(TcpConnectionError, "unable to start accepting TCP "
|
||||
"connections: " << ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TcpConnection::doHandshake() {
|
||||
// Skip the handshake if the socket is not a TLS one.
|
||||
if (!tls_socket_) {
|
||||
doRead();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create instance of the callback. It is safe to pass the local instance
|
||||
// of the callback, because the underlying boost functions make copies
|
||||
// as needed.
|
||||
SocketCallback cb(std::bind(&TcpConnection::handshakeCallback,
|
||||
shared_from_this(),
|
||||
ph::_1)); // error
|
||||
try {
|
||||
tls_socket_->handshake(cb);
|
||||
|
||||
} catch (const std::exception& ex) {
|
||||
isc_throw(TcpConnectionError, "unable to perform TLS handshake: "
|
||||
<< ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TcpConnection::doRead(TransactionPtr transaction) {
|
||||
try {
|
||||
TCPEndpoint endpoint;
|
||||
|
||||
// Transaction hasn't been created if we are starting to read the
|
||||
// new request.
|
||||
if (!transaction) {
|
||||
transaction = Transaction::create(response_creator_);
|
||||
recordParameters(transaction->getRequest());
|
||||
}
|
||||
|
||||
// Create instance of the callback. It is safe to pass the local instance
|
||||
// of the callback, because the underlying std functions make copies
|
||||
// as needed.
|
||||
SocketCallback cb(std::bind(&TcpConnection::socketReadCallback,
|
||||
shared_from_this(),
|
||||
transaction,
|
||||
ph::_1, // error
|
||||
ph::_2)); //bytes_transferred
|
||||
if (tcp_socket_) {
|
||||
tcp_socket_->asyncReceive(static_cast<void*>(transaction->getInputBufData()),
|
||||
transaction->getInputBufSize(),
|
||||
0, &endpoint, cb);
|
||||
return;
|
||||
}
|
||||
if (tls_socket_) {
|
||||
tls_socket_->asyncReceive(static_cast<void*>(transaction->getInputBufData()),
|
||||
transaction->getInputBufSize(),
|
||||
0, &endpoint, cb);
|
||||
return;
|
||||
}
|
||||
} catch (...) {
|
||||
stopThisConnection();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TcpConnection::doWrite(TcpConnection::TransactionPtr transaction) {
|
||||
try {
|
||||
if (transaction->outputDataAvail()) {
|
||||
// Create instance of the callback. It is safe to pass the local instance
|
||||
// of the callback, because the underlying std functions make copies
|
||||
// as needed.
|
||||
SocketCallback cb(std::bind(&TcpConnection::socketWriteCallback,
|
||||
shared_from_this(),
|
||||
transaction,
|
||||
ph::_1, // error
|
||||
ph::_2)); // bytes_transferred
|
||||
if (tcp_socket_) {
|
||||
tcp_socket_->asyncSend(transaction->getOutputBufData(),
|
||||
transaction->getOutputBufSize(),
|
||||
cb);
|
||||
return;
|
||||
}
|
||||
if (tls_socket_) {
|
||||
tls_socket_->asyncSend(transaction->getOutputBufData(),
|
||||
transaction->getOutputBufSize(),
|
||||
cb);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// The isPersistent() function may throw if the request hasn't
|
||||
// been created, i.e. the HTTP headers weren't parsed. We catch
|
||||
// this exception below and close the connection since we're
|
||||
// unable to tell if the connection should remain persistent
|
||||
// or not. The default is to close it.
|
||||
if (!transaction->getRequest()->isPersistent()) {
|
||||
stopThisConnection();
|
||||
|
||||
} else {
|
||||
// The connection is persistent and we are done sending
|
||||
// the previous response. Start listening for the next
|
||||
// requests.
|
||||
setupIdleTimer();
|
||||
doRead();
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
stopThisConnection();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TcpConnection::asyncSendResponse(const ConstTcpResponsePtr& response,
|
||||
TransactionPtr transaction) {
|
||||
transaction->setOutputBuf(response->toString());
|
||||
doWrite(transaction);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TcpConnection::acceptorCallback(const boost::system::error_code& ec) {
|
||||
if (!acceptor_->isOpen()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ec) {
|
||||
stopThisConnection();
|
||||
}
|
||||
|
||||
acceptor_callback_(ec);
|
||||
|
||||
if (!ec) {
|
||||
if (!tls_context_) {
|
||||
LOG_DEBUG(asiolink_logger, isc::log::DBGLVL_TRACE_DETAIL,
|
||||
TCP_REQUEST_RECEIVE_START)
|
||||
.arg(getRemoteEndpointAddressAsText())
|
||||
.arg(static_cast<unsigned>(request_timeout_/1000));
|
||||
} else {
|
||||
LOG_DEBUG(asiolink_logger, isc::log::DBGLVL_TRACE_DETAIL,
|
||||
TCP_CONNECTION_HANDSHAKE_START)
|
||||
.arg(getRemoteEndpointAddressAsText())
|
||||
.arg(static_cast<unsigned>(request_timeout_/1000));
|
||||
}
|
||||
|
||||
setupRequestTimer();
|
||||
doHandshake();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TcpConnection::handshakeCallback(const boost::system::error_code& ec) {
|
||||
if (ec) {
|
||||
LOG_INFO(asiolink_logger, TCP_CONNECTION_HANDSHAKE_FAILED)
|
||||
.arg(getRemoteEndpointAddressAsText())
|
||||
.arg(ec.message());
|
||||
stopThisConnection();
|
||||
} else {
|
||||
LOG_DEBUG(asiolink_logger, isc::log::DBGLVL_TRACE_DETAIL,
|
||||
HTTPS_REQUEST_RECEIVE_START)
|
||||
.arg(getRemoteEndpointAddressAsText());
|
||||
|
||||
doRead();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TcpConnection::socketReadCallback(TcpConnection::TransactionPtr transaction,
|
||||
boost::system::error_code ec, size_t length) {
|
||||
if (ec) {
|
||||
// IO service has been stopped and the connection is probably
|
||||
// going to be shutting down.
|
||||
if (ec.value() == boost::asio::error::operation_aborted) {
|
||||
return;
|
||||
|
||||
// EWOULDBLOCK and EAGAIN are special cases. Everything else is
|
||||
// treated as fatal error.
|
||||
} else if ((ec.value() != boost::asio::error::try_again) &&
|
||||
(ec.value() != boost::asio::error::would_block)) {
|
||||
stopThisConnection();
|
||||
|
||||
// We got EWOULDBLOCK or EAGAIN which indicate that we may be able to
|
||||
// read something from the socket on the next attempt. Just make sure
|
||||
// we don't try to read anything now in case there is any garbage
|
||||
// passed in length.
|
||||
} else {
|
||||
length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Receiving is in progress, so push back the timeout.
|
||||
setupRequestTimer(transaction);
|
||||
|
||||
if (length != 0) {
|
||||
LOG_DEBUG(asiolink_logger, isc::log::DBGLVL_TRACE_DETAIL_DATA,
|
||||
TCP_DATA_RECEIVED)
|
||||
.arg(length)
|
||||
.arg(getRemoteEndpointAddressAsText());
|
||||
|
||||
transaction->getParser()->postBuffer(static_cast<void*>(transaction->getInputBufData()),
|
||||
length);
|
||||
transaction->getParser()->poll();
|
||||
}
|
||||
|
||||
if (transaction->getParser()->needData()) {
|
||||
// The parser indicates that the some part of the message being
|
||||
// received is still missing, so continue to read.
|
||||
doRead(transaction);
|
||||
|
||||
} else {
|
||||
try {
|
||||
// The whole message has been received, so let's finalize it.
|
||||
transaction->getRequest()->finalize();
|
||||
|
||||
LOG_DEBUG(asiolink_logger, isc::log::DBGLVL_TRACE_BASIC,
|
||||
TCP_CLIENT_REQUEST_RECEIVED)
|
||||
.arg(getRemoteEndpointAddressAsText());
|
||||
|
||||
LOG_DEBUG(asiolink_logger, isc::log::DBGLVL_TRACE_BASIC_DATA,
|
||||
TCP_CLIENT_REQUEST_RECEIVED_DETAILS)
|
||||
.arg(getRemoteEndpointAddressAsText())
|
||||
.arg(transaction->getParser()->getBufferAsString(MAX_LOGGED_MESSAGE_SIZE));
|
||||
|
||||
} catch (const std::exception& ex) {
|
||||
LOG_DEBUG(asiolink_logger, isc::log::DBGLVL_TRACE_BASIC,
|
||||
TCP_BAD_CLIENT_REQUEST_RECEIVED)
|
||||
.arg(getRemoteEndpointAddressAsText())
|
||||
.arg(ex.what());
|
||||
|
||||
LOG_DEBUG(asiolink_logger, isc::log::DBGLVL_TRACE_BASIC_DATA,
|
||||
TCP_BAD_CLIENT_REQUEST_RECEIVED_DETAILS)
|
||||
.arg(getRemoteEndpointAddressAsText())
|
||||
.arg(transaction->getParser()->getBufferAsString(MAX_LOGGED_MESSAGE_SIZE));
|
||||
}
|
||||
|
||||
// Don't want to timeout if creation of the response takes long.
|
||||
request_timer_.cancel();
|
||||
|
||||
// Create the response from the received request using the custom
|
||||
// response creator.
|
||||
TcpResponsePtr response = response_creator_->createTcpResponse(transaction->getRequest());
|
||||
LOG_DEBUG(asiolink_logger, isc::log::DBGLVL_TRACE_BASIC,
|
||||
TCP_SERVER_RESPONSE_SEND)
|
||||
.arg(response->toBriefString())
|
||||
.arg(getRemoteEndpointAddressAsText());
|
||||
|
||||
LOG_DEBUG(asiolink_logger, isc::log::DBGLVL_TRACE_BASIC_DATA,
|
||||
TCP_SERVER_RESPONSE_SEND_DETAILS)
|
||||
.arg(getRemoteEndpointAddressAsText())
|
||||
.arg(TcpMessageParserBase::logFormatTcpMessage(response->toString(),
|
||||
MAX_LOGGED_MESSAGE_SIZE));
|
||||
|
||||
// Response created. Activate the timer again.
|
||||
setupRequestTimer(transaction);
|
||||
|
||||
// Start sending the response.
|
||||
asyncSendResponse(response, transaction);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TcpConnection::socketWriteCallback(TcpConnection::TransactionPtr transaction,
|
||||
boost::system::error_code ec, size_t length) {
|
||||
if (ec) {
|
||||
// IO service has been stopped and the connection is probably
|
||||
// going to be shutting down.
|
||||
if (ec.value() == boost::asio::error::operation_aborted) {
|
||||
return;
|
||||
|
||||
// EWOULDBLOCK and EAGAIN are special cases. Everything else is
|
||||
// treated as fatal error.
|
||||
} else if ((ec.value() != boost::asio::error::try_again) &&
|
||||
(ec.value() != boost::asio::error::would_block)) {
|
||||
stopThisConnection();
|
||||
|
||||
// We got EWOULDBLOCK or EAGAIN which indicate that we may be able to
|
||||
// read something from the socket on the next attempt.
|
||||
} else {
|
||||
// Sending is in progress, so push back the timeout.
|
||||
setupRequestTimer(transaction);
|
||||
|
||||
doWrite(transaction);
|
||||
}
|
||||
}
|
||||
|
||||
// Since each transaction has its own output buffer, it is not really
|
||||
// possible that the number of bytes written is larger than the size
|
||||
// of the buffer. But, let's be safe and set the length to the size
|
||||
// of the buffer if that unexpected condition occurs.
|
||||
if (length > transaction->getOutputBufSize()) {
|
||||
length = transaction->getOutputBufSize();
|
||||
}
|
||||
|
||||
if (length <= transaction->getOutputBufSize()) {
|
||||
// Sending is in progress, so push back the timeout.
|
||||
setupRequestTimer(transaction);
|
||||
}
|
||||
|
||||
// Eat the 'length' number of bytes from the output buffer and only
|
||||
// leave the part of the response that hasn't been sent.
|
||||
transaction->consumeOutputBuf(length);
|
||||
|
||||
// Schedule the write of the unsent data.
|
||||
doWrite(transaction);
|
||||
}
|
||||
|
||||
void
|
||||
TcpConnection::setupRequestTimer(TransactionPtr transaction) {
|
||||
// Pass raw pointer rather than shared_ptr to this object,
|
||||
// because IntervalTimer already passes shared pointer to the
|
||||
// IntervalTimerImpl to make sure that the callback remains
|
||||
// valid.
|
||||
request_timer_.setup(std::bind(&TcpConnection::requestTimeoutCallback,
|
||||
this, transaction),
|
||||
request_timeout_, IntervalTimer::ONE_SHOT);
|
||||
}
|
||||
|
||||
void
|
||||
TcpConnection::setupIdleTimer() {
|
||||
request_timer_.setup(std::bind(&TcpConnection::idleTimeoutCallback,
|
||||
this),
|
||||
idle_timeout_, IntervalTimer::ONE_SHOT);
|
||||
}
|
||||
|
||||
void
|
||||
TcpConnection::requestTimeoutCallback(TransactionPtr transaction) {
|
||||
LOG_DEBUG(asiolink_logger, isc::log::DBGLVL_TRACE_DETAIL,
|
||||
TCP_CLIENT_REQUEST_TIMEOUT_OCCURRED)
|
||||
.arg(getRemoteEndpointAddressAsText());
|
||||
|
||||
// We need to differentiate the transactions between a normal response and the
|
||||
// timeout. We create new transaction from the current transaction. It is
|
||||
// to preserve the request we're responding to.
|
||||
auto spawned_transaction = Transaction::spawn(response_creator_, transaction);
|
||||
|
||||
// The new transaction inherits the request from the original transaction
|
||||
// if such transaction exists.
|
||||
auto request = spawned_transaction->getRequest();
|
||||
|
||||
// Depending on when the timeout occurred, the TCP version of the request
|
||||
// may or may not be available. Therefore we check if the HTTP version is
|
||||
// set in the request. If it is not available, we need to create a dummy
|
||||
// request with the default HTTP/1.0 version. This version will be used
|
||||
// in the response.
|
||||
if (request->context()->http_version_major_ == 0) {
|
||||
request.reset(new TcpRequest(TcpRequest::Method::HTTP_POST, "/",
|
||||
TcpVersion::HTTP_10(),
|
||||
HostTcpHeader("dummy")));
|
||||
request->finalize();
|
||||
}
|
||||
|
||||
// Create the timeout response.
|
||||
TcpResponsePtr response =
|
||||
response_creator_->createStockTcpResponse(request,
|
||||
TcpStatusCode::REQUEST_TIMEOUT);
|
||||
|
||||
// Send the HTTP 408 status.
|
||||
asyncSendResponse(response, spawned_transaction);
|
||||
}
|
||||
|
||||
void
|
||||
TcpConnection::idleTimeoutCallback() {
|
||||
LOG_DEBUG(asiolink_logger, isc::log::DBGLVL_TRACE_DETAIL,
|
||||
TCP_IDLE_CONNECTION_TIMEOUT_OCCURRED)
|
||||
.arg(getRemoteEndpointAddressAsText());
|
||||
// In theory we should shutdown first and stop/close after but
|
||||
// it is better to put the connection management responsibility
|
||||
// on the client... so simply drop idle connections.
|
||||
stopThisConnection();
|
||||
}
|
||||
|
||||
std::string
|
||||
TcpConnection::getRemoteEndpointAddressAsText() const {
|
||||
try {
|
||||
if (tcp_socket_) {
|
||||
if (tcp_socket_->getASIOSocket().is_open()) {
|
||||
return (tcp_socket_->getASIOSocket().remote_endpoint().address().to_string());
|
||||
}
|
||||
} else if (tls_socket_) {
|
||||
if (tls_socket_->getASIOSocket().is_open()) {
|
||||
return (tls_socket_->getASIOSocket().remote_endpoint().address().to_string());
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
}
|
||||
return ("(unknown address)");
|
||||
}
|
||||
|
||||
} // end of namespace isc::http
|
||||
} // end of namespace isc
|
||||
#endif
|
281
src/lib/tcp/tcp_connection.h
Normal file
281
src/lib/tcp/tcp_connection.h
Normal file
@ -0,0 +1,281 @@
|
||||
// Copyright (C) 2022 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef TCP_CONNECTION_H
|
||||
#define TCP_CONNECTION_H
|
||||
|
||||
#include <asiolink/interval_timer.h>
|
||||
#include <asiolink/io_service.h>
|
||||
#include <tcp/tcp_connection_acceptor.h>
|
||||
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace isc {
|
||||
namespace tcp {
|
||||
|
||||
/// @todo TKM these are place holders while I think output how it should work
|
||||
typedef util::InputBuffer TcpRequest;
|
||||
typedef boost::shared_ptr<TcpRequest> TcpRequestPtr;
|
||||
|
||||
typedef util::OutputBuffer TcpResponse;
|
||||
typedef boost::shared_ptr<TcpResponse> TcpResponsePtr;
|
||||
|
||||
|
||||
/// @brief Generic error reported within @ref TcpConnection class.
|
||||
class TcpConnectionError : public Exception {
|
||||
public:
|
||||
TcpConnectionError(const char* file, size_t line, const char* what) :
|
||||
isc::Exception(file, line, what) { };
|
||||
};
|
||||
|
||||
/// @brief Forward declaration to the @ref TcpConnectionPool.
|
||||
///
|
||||
/// This declaration is needed because we don't include the header file
|
||||
/// declaring @ref TcpConnectionPool to avoid circular inclusion.
|
||||
class TcpConnectionPool;
|
||||
|
||||
class TcpConnection;
|
||||
/// @brief Pointer to the @ref TcpConnection.
|
||||
typedef boost::shared_ptr<TcpConnection> TcpConnectionPtr;
|
||||
|
||||
/// @brief Accepts and handles a single TCP connection.
|
||||
class TcpConnection : public boost::enable_shared_from_this<TcpConnection> {
|
||||
private:
|
||||
|
||||
/// @brief Type of the function implementing a callback invoked by the
|
||||
/// @c SocketCallback functor.
|
||||
typedef std::function<void(boost::system::error_code ec, size_t length)>
|
||||
SocketCallbackFunction;
|
||||
|
||||
/// @brief Functor associated with the socket object.
|
||||
///
|
||||
/// This functor calls a callback function specified in the constructor.
|
||||
class SocketCallback {
|
||||
public:
|
||||
|
||||
/// @brief Constructor.
|
||||
///
|
||||
/// @param socket_callback Callback to be invoked by the functor upon
|
||||
/// an event associated with the socket.
|
||||
SocketCallback(SocketCallbackFunction socket_callback)
|
||||
: callback_(socket_callback) {
|
||||
}
|
||||
|
||||
/// @brief Operator called when event associated with a socket occurs.
|
||||
///
|
||||
/// This operator returns immediately when received error code is
|
||||
/// @c boost::system::error_code is equal to
|
||||
/// @c boost::asio::error::operation_aborted, i.e. the callback is not
|
||||
/// invoked.
|
||||
///
|
||||
/// @param ec Error code.
|
||||
/// @param length Data length.
|
||||
void operator()(boost::system::error_code ec, size_t length = 0);
|
||||
|
||||
private:
|
||||
/// @brief Supplied callback.
|
||||
SocketCallbackFunction callback_;
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
/// @brief Constructor.
|
||||
///
|
||||
/// @param io_service IO service to be used by the connection.
|
||||
/// @param acceptor Pointer to the TCP acceptor object used to listen for
|
||||
/// new TCP connections.
|
||||
/// @param tls_context TLS context.
|
||||
/// @param connection_pool Connection pool in which this connection is
|
||||
/// stored.
|
||||
/// @param response_creator Pointer to the response creator object used to
|
||||
/// create TCP response from the TCP request received.
|
||||
/// @param callback Callback invoked when new connection is accepted.
|
||||
/// @param request_timeout Configured timeout for a TCP request.
|
||||
/// @param idle_timeout Timeout after which persistent TCP connection is
|
||||
/// closed by the server.
|
||||
TcpConnection(asiolink::IOService& io_service,
|
||||
const TcpConnectionAcceptorPtr& acceptor,
|
||||
const asiolink::TlsContextPtr& tls_context,
|
||||
TcpConnectionPool& connection_pool,
|
||||
const TcpConnectionAcceptorCallback& callback,
|
||||
const long idle_timeout);
|
||||
|
||||
/// @brief Destructor.
|
||||
///
|
||||
/// Closes current connection.
|
||||
virtual ~TcpConnection();
|
||||
|
||||
/// @brief Asynchronously accepts new connection.
|
||||
///
|
||||
/// When the connection is established successfully, the timeout timer is
|
||||
/// setup and the asynchronous handshake with client is performed.
|
||||
void asyncAccept();
|
||||
|
||||
/// @brief Shutdown the socket.
|
||||
void shutdown();
|
||||
|
||||
/// @brief Closes the socket.
|
||||
void close();
|
||||
|
||||
/// @brief Asynchronously performs TLS handshake.
|
||||
///
|
||||
/// When the handshake is performed successfully or skipped because TLS
|
||||
/// was not enabled, the asynchronous read from the socket is started.
|
||||
void doHandshake();
|
||||
|
||||
/// @brief Starts asynchronous read from the socket.
|
||||
///
|
||||
/// The data received over the socket are supplied to the TCP parser until
|
||||
/// the parser signals that the entire request has been received or until
|
||||
/// the parser signals an error. In the former case the server creates an
|
||||
/// TCP response using supplied response creator object.
|
||||
///
|
||||
/// In case of error the connection is stopped.
|
||||
///
|
||||
/// @param request Pointer to the request for which the read
|
||||
/// operation should be performed. It defaults to null pointer which
|
||||
/// indicates that this function should create new request.
|
||||
void doRead();
|
||||
|
||||
protected:
|
||||
|
||||
/// @brief Starts asynchronous write to the socket.
|
||||
///
|
||||
/// The @c output_buf_ must contain the data to be sent.
|
||||
///
|
||||
/// In case of error the connection is stopped.
|
||||
///
|
||||
/// @param request Pointer to the request for which the write
|
||||
/// operation should be performed.
|
||||
void doWrite();
|
||||
|
||||
/// @brief Sends TCP response asynchronously.
|
||||
///
|
||||
/// Internally it calls @ref TcpConnection::doWrite to send the data.
|
||||
///
|
||||
/// @param response Pointer to the TCP response to be sent.
|
||||
/// @param request Pointer to the request.
|
||||
void asyncSendResponse(const TcpResponsePtr& response);
|
||||
|
||||
/// @brief Local callback invoked when new connection is accepted.
|
||||
///
|
||||
/// It invokes external (supplied via constructor) acceptor callback. If
|
||||
/// the acceptor is not opened it returns immediately. If the connection
|
||||
/// is accepted successfully the @ref TcpConnection::doRead or
|
||||
/// @ref TcpConnection::doHandshake is called.
|
||||
///
|
||||
/// @param ec Error code.
|
||||
void acceptorCallback(const boost::system::error_code& ec);
|
||||
|
||||
/// @brief Local callback invoked when TLS handshake is performed.
|
||||
///
|
||||
/// If the handshake is performed successfully the @ref
|
||||
/// TcpConnection::doRead is called.
|
||||
///
|
||||
/// @param ec Error code.
|
||||
void handshakeCallback(const boost::system::error_code& ec);
|
||||
|
||||
/// @brief Callback invoked when new data is received over the socket.
|
||||
///
|
||||
/// This callback supplies the data to the TCP parser and continues
|
||||
/// parsing. When the parser signals end of the TCP request the callback
|
||||
/// prepares a response and starts asynchronous send over the socket.
|
||||
///
|
||||
/// @param request Pointer to the request for which the callback
|
||||
/// is invoked.
|
||||
/// @param ec Error code.
|
||||
/// @param length Length of the received data.
|
||||
void socketReadCallback(TcpRequestPtr request,
|
||||
boost::system::error_code ec,
|
||||
size_t length);
|
||||
|
||||
/// @brief Callback invoked when data is sent over the socket.
|
||||
///
|
||||
/// @param request Pointer to the request for which the callback
|
||||
/// is invoked.
|
||||
/// @param ec Error code.
|
||||
/// @param length Length of the data sent.
|
||||
virtual void socketWriteCallback(TcpRequestPtr request,
|
||||
boost::system::error_code ec,
|
||||
size_t length);
|
||||
|
||||
/// @brief Callback invoked when TLS shutdown is performed.
|
||||
///
|
||||
/// The TLS socket is unconditionally closed but the callback is called
|
||||
/// only when the peer has answered so the connection should be
|
||||
/// explicitly closed in all cases, i.e. do not rely on this handler.
|
||||
///
|
||||
/// @param ec Error code (ignored).
|
||||
void shutdownCallback(const boost::system::error_code& ec);
|
||||
|
||||
/// @brief Reset timer for detecting request timeouts.
|
||||
///
|
||||
/// @param request Pointer to the request to be guarded by the timeout.
|
||||
void setupRequestTimer(TcpRequestPtr request = TcpRequestPtr());
|
||||
|
||||
/// @brief Reset timer for detecting idle timeout in persistent connections.
|
||||
void setupIdleTimer();
|
||||
|
||||
/// @brief Callback invoked when the TCP Request Timeout occurs.
|
||||
///
|
||||
/// This callback creates TCP response with Request Timeout error code
|
||||
/// and sends it to the client.
|
||||
///
|
||||
/// @param request Pointer to the request for which timeout occurs.
|
||||
void requestTimeoutCallback(TcpRequestPtr request);
|
||||
|
||||
void idleTimeoutCallback();
|
||||
|
||||
/// @brief Shuts down current connection.
|
||||
///
|
||||
/// Copied from the next method @ref stopThisConnection
|
||||
void shutdownConnection();
|
||||
|
||||
/// @brief Stops current connection.
|
||||
void stopThisConnection();
|
||||
|
||||
/// @brief returns remote address in textual form
|
||||
std::string getRemoteEndpointAddressAsText() const;
|
||||
|
||||
/// @brief Timer used to detect Request Timeout.
|
||||
asiolink::IntervalTimer request_timer_;
|
||||
|
||||
/// @brief Configured Request Timeout in milliseconds.
|
||||
long request_timeout_;
|
||||
|
||||
/// @brief TLS context.
|
||||
asiolink::TlsContextPtr tls_context_;
|
||||
|
||||
/// @brief Timeout after which the persistent TCP connection is shut
|
||||
/// down by the server.
|
||||
long idle_timeout_;
|
||||
|
||||
/// @brief TCP socket used by this connection.
|
||||
std::unique_ptr<asiolink::TCPSocket<SocketCallback> > tcp_socket_;
|
||||
|
||||
/// @brief TLS socket used by this connection.
|
||||
std::unique_ptr<asiolink::TLSSocket<SocketCallback> > tls_socket_;
|
||||
|
||||
/// @brief Pointer to the TCP acceptor used to accept new connections.
|
||||
TcpConnectionAcceptorPtr acceptor_;
|
||||
|
||||
/// @brief Connection pool holding this connection.
|
||||
TcpConnectionPool& connection_pool_;
|
||||
|
||||
/// @brief External TCP acceptor callback.
|
||||
TcpConnectionAcceptorCallback acceptor_callback_;
|
||||
};
|
||||
|
||||
} // end of namespace isc::tcp
|
||||
} // end of namespace isc
|
||||
|
||||
#endif
|
38
src/lib/tcp/tcp_connection_acceptor.h
Normal file
38
src/lib/tcp/tcp_connection_acceptor.h
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright (C) 2022 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef HTTP_ACCEPTOR_H
|
||||
#define HTTP_ACCEPTOR_H
|
||||
|
||||
#include <asiolink/tcp_acceptor.h>
|
||||
#include <asiolink/tls_acceptor.h>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/system/system_error.hpp>
|
||||
#include <functional>
|
||||
|
||||
namespace isc {
|
||||
namespace tcp {
|
||||
|
||||
/// @brief Type of the callback for the TCP acceptor used in this library.
|
||||
typedef std::function<void(const boost::system::error_code&)> TcpConnectionAcceptorCallback;
|
||||
|
||||
/// @brief Type of the TCP acceptor used in this library.
|
||||
typedef asiolink::TCPAcceptor<TcpConnectionAcceptorCallback> TcpConnectionAcceptor;
|
||||
|
||||
/// @brief Type of shared pointer to TCP acceptors.
|
||||
typedef boost::shared_ptr<TcpConnectionAcceptor> TcpConnectionAcceptorPtr;
|
||||
|
||||
/// @brief Type of the TLS acceptor used in this library.
|
||||
typedef asiolink::TLSAcceptor<TcpConnectionAcceptorCallback> TlsConnectionAcceptor;
|
||||
|
||||
/// @brief Type of shared pointer to TLS acceptors.
|
||||
typedef boost::shared_ptr<TlsConnectionAcceptor> TlsConnectionAcceptorPtr;
|
||||
|
||||
} // end of namespace isc::tcp
|
||||
} // end of namespace isc
|
||||
|
||||
#endif
|
78
src/lib/tcp/tcp_connection_pool.cc
Normal file
78
src/lib/tcp/tcp_connection_pool.cc
Normal file
@ -0,0 +1,78 @@
|
||||
// Copyright (C) 2022 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#if 0
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <asiolink/asio_wrapper.h>
|
||||
#include <tcp/tcp_connection_pool.h>
|
||||
#include <util/multi_threading_mgr.h>
|
||||
|
||||
namespace isc {
|
||||
namespace tcp {
|
||||
|
||||
void
|
||||
TcpConnectionPool::start(const TcpConnectionPtr& connection) {
|
||||
if (util::MultiThreadingMgr::instance().getMode()) {
|
||||
std::lock_guard<std::mutex> lk(mutex_);
|
||||
connections_.insert(connections_.end(), connection);
|
||||
} else {
|
||||
connections_.insert(connections_.end(), connection);
|
||||
}
|
||||
|
||||
connection->asyncAccept();
|
||||
}
|
||||
|
||||
void
|
||||
TcpConnectionPool::stop(const TcpConnectionPtr& connection) {
|
||||
if (util::MultiThreadingMgr::instance().getMode()) {
|
||||
std::lock_guard<std::mutex> lk(mutex_);
|
||||
connections_.remove(connection);
|
||||
} else {
|
||||
connections_.remove(connection);
|
||||
}
|
||||
|
||||
connection->close();
|
||||
}
|
||||
|
||||
void
|
||||
TcpConnectionPool::shutdown(const TcpConnectionPtr& connection) {
|
||||
if (util::MultiThreadingMgr::instance().getMode()) {
|
||||
std::lock_guard<std::mutex> lk(mutex_);
|
||||
connections_.remove(connection);
|
||||
} else {
|
||||
connections_.remove(connection);
|
||||
}
|
||||
|
||||
connection->shutdown();
|
||||
}
|
||||
|
||||
void
|
||||
TcpConnectionPool::stopAll() {
|
||||
if (util::MultiThreadingMgr::instance().getMode()) {
|
||||
std::lock_guard<std::mutex> lk(mutex_);
|
||||
stopAllInternal();
|
||||
} else {
|
||||
stopAllInternal();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TcpConnectionPool::stopAllInternal() {
|
||||
for (auto connection = connections_.begin();
|
||||
connection != connections_.end();
|
||||
++connection) {
|
||||
(*connection)->close();
|
||||
}
|
||||
|
||||
connections_.clear();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
78
src/lib/tcp/tcp_connection_pool.h
Normal file
78
src/lib/tcp/tcp_connection_pool.h
Normal file
@ -0,0 +1,78 @@
|
||||
// Copyright (C) 2022 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef TCP_CONNECTION_POOL_H
|
||||
#define TCP_CONNECTION_POOL_H
|
||||
|
||||
#include <tcp/tcp_connection.h>
|
||||
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
|
||||
namespace isc {
|
||||
namespace tcp {
|
||||
|
||||
/// @brief Pool of active TCP connections.
|
||||
///
|
||||
/// The TCP server is designed to handle many connections simultaneously.
|
||||
/// The communication between the client and the server may take long time
|
||||
/// and the server must be able to react on other events while the communication
|
||||
/// with the clients is in progress. Thus, the server must track active
|
||||
/// connections and gracefully close them when needed. An obvious case when the
|
||||
/// connections must be terminated by the server is when the shutdown signal
|
||||
/// is received.
|
||||
///
|
||||
/// This object is a simple container for the server connections which provides
|
||||
/// means to terminate them on request.
|
||||
class TcpConnectionPool {
|
||||
public:
|
||||
|
||||
/// @brief Start new connection.
|
||||
///
|
||||
/// The connection is inserted to the pool and the
|
||||
/// @ref TcpConnection::asyncAccept is invoked.
|
||||
///
|
||||
/// @param connection Pointer to the new connection.
|
||||
void start(const TcpConnectionPtr& connection);
|
||||
|
||||
/// @brief Removes a connection from the pool and shutdown it.
|
||||
///
|
||||
/// Shutdown is specific to TLS and is a first part of graceful close (note it is
|
||||
/// NOT the same as TCP shutdown system call).
|
||||
///
|
||||
/// @note if the TLS connection stalls e.g. the peer does not try I/O
|
||||
/// on it the connection has to be explicitly stopped.
|
||||
///
|
||||
/// @param connection Pointer to the connection.
|
||||
void shutdown(const TcpConnectionPtr& connection);
|
||||
|
||||
/// @brief Removes a connection from the pool and stops it.
|
||||
///
|
||||
/// @param connection Pointer to the connection.
|
||||
void stop(const TcpConnectionPtr& connection);
|
||||
|
||||
/// @brief Stops all connections and removes them from the pool.
|
||||
void stopAll();
|
||||
|
||||
protected:
|
||||
|
||||
/// @brief Stops all connections and removes them from the pool.
|
||||
///
|
||||
/// Must be called from with a thread-safe context.
|
||||
void stopAllInternal();
|
||||
|
||||
/// @brief Set of connections.
|
||||
std::list<TcpConnectionPtr> connections_;
|
||||
|
||||
/// @brief Mutex to protect the internal state.
|
||||
std::mutex mutex_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
110
src/lib/tcp/tcp_listener.cc
Normal file
110
src/lib/tcp/tcp_listener.cc
Normal file
@ -0,0 +1,110 @@
|
||||
// Copyright (C) 2022 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#include <config.h>
|
||||
#include <asiolink/asio_wrapper.h>
|
||||
#include <tcp/tcp_listener.h>
|
||||
|
||||
using namespace isc::asiolink;
|
||||
namespace ph = std::placeholders;
|
||||
|
||||
namespace isc {
|
||||
namespace tcp {
|
||||
|
||||
TcpListener::TcpListener(IOService& io_service,
|
||||
const IOAddress& server_address,
|
||||
const unsigned short server_port,
|
||||
const TlsContextPtr& tls_context,
|
||||
const long idle_timeout)
|
||||
: io_service_(io_service), tls_context_(tls_context), acceptor_(),
|
||||
endpoint_(), connections_(), idle_timeout_(idle_timeout) {
|
||||
|
||||
// Create the TCP or TLS acceptor.
|
||||
// @todo TKM - hmmm.... need to understand this better..
|
||||
if (!tls_context) {
|
||||
acceptor_.reset(new TcpConnectionAcceptor(io_service));
|
||||
} else {
|
||||
acceptor_.reset(new TlsConnectionAcceptor(io_service));
|
||||
}
|
||||
|
||||
// Try creating an endpoint. This may cause exceptions.
|
||||
try {
|
||||
endpoint_.reset(new TCPEndpoint(server_address, server_port));
|
||||
|
||||
} catch (...) {
|
||||
isc_throw(TcpListenerError, "unable to create TCP endpoint for "
|
||||
<< server_address << ":" << server_port);
|
||||
}
|
||||
|
||||
// Idle persistent connection timeout is signed and must be greater than 0.
|
||||
if (idle_timeout_ <= 0) {
|
||||
isc_throw(TcpListenerError, "Invalid desired TCP idle persistent connection"
|
||||
" timeout " << idle_timeout_);
|
||||
}
|
||||
}
|
||||
|
||||
const TCPEndpoint&
|
||||
TcpListener::getEndpoint() const {
|
||||
return (*endpoint_);
|
||||
}
|
||||
|
||||
void
|
||||
TcpListener::start() {
|
||||
try {
|
||||
acceptor_->open(*endpoint_);
|
||||
acceptor_->setOption(TcpConnectionAcceptor::ReuseAddress(true));
|
||||
acceptor_->bind(*endpoint_);
|
||||
acceptor_->listen();
|
||||
|
||||
} catch (const boost::system::system_error& ex) {
|
||||
stop();
|
||||
isc_throw(TcpListenerError, "unable to setup TCP acceptor for "
|
||||
"listening for incoming TCP clients: " << ex.what());
|
||||
}
|
||||
|
||||
accept();
|
||||
}
|
||||
|
||||
void
|
||||
TcpListener::stop() {
|
||||
connections_.stopAll();
|
||||
acceptor_->close();
|
||||
}
|
||||
|
||||
void
|
||||
TcpListener::accept() {
|
||||
TcpConnectionAcceptorCallback acceptor_callback =
|
||||
std::bind(&TcpListener::acceptHandler, this, ph::_1);
|
||||
|
||||
TcpConnectionPtr conn = createConnection(acceptor_callback);
|
||||
|
||||
// Add this new connection to the pool.
|
||||
connections_.start(conn);
|
||||
}
|
||||
|
||||
void
|
||||
TcpListener::acceptHandler(const boost::system::error_code&) {
|
||||
// The new connection has arrived. Set the acceptor to continue
|
||||
// accepting new connections.
|
||||
accept();
|
||||
}
|
||||
|
||||
TcpConnectionPtr
|
||||
TcpListener::createConnection(const TcpConnectionAcceptorCallback& /* callback */) {
|
||||
#if 1
|
||||
isc_throw(NotImplemented, "TcpListener::createConnection:");
|
||||
#else
|
||||
TcpConnectionPtr
|
||||
/// @todo TKM - I think what we want is to define TcpConnectionFactory
|
||||
/// instead of a response creator. Let TcpListener accept a factory
|
||||
/// for that, which is used here to create for BLQ an LeaseQueryConnection
|
||||
return (connection_factory_(io_service_, acceptor_, tls_context_,
|
||||
connections_, callback, idle_timeout_));
|
||||
#endif
|
||||
}
|
||||
|
||||
} // end of namespace isc::tcp
|
||||
} // end of namespace isc
|
122
src/lib/tcp/tcp_listener.h
Normal file
122
src/lib/tcp/tcp_listener.h
Normal file
@ -0,0 +1,122 @@
|
||||
// Copyright (C) 2022 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef TCP_LISTENER_H
|
||||
#define TCP_LISTENER_H
|
||||
|
||||
#include <asiolink/io_service.h>
|
||||
#include <asiolink/io_address.h>
|
||||
#include <asiolink/tcp_endpoint.h>
|
||||
#include <tcp/tcp_connection_pool.h>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
|
||||
namespace isc {
|
||||
namespace tcp {
|
||||
|
||||
/// @brief A generic error raised by the @ref TcpListener class.
|
||||
class TcpListenerError : public Exception {
|
||||
public:
|
||||
TcpListenerError(const char* file, size_t line, const char* what) :
|
||||
isc::Exception(file, line, what) { };
|
||||
};
|
||||
|
||||
/// @brief ementation of the @ref TcpListener.
|
||||
class TcpListener {
|
||||
public:
|
||||
|
||||
/// @brief Constructor.
|
||||
///
|
||||
/// This constructor creates new server endpoint using the specified IP
|
||||
/// address and port. It also validates other specified parameters.
|
||||
///
|
||||
/// This constructor does not start accepting new connections! To start
|
||||
/// accepting connections run @ref TcpListener::start.
|
||||
///
|
||||
/// @param io_service IO service to be used by the listener.
|
||||
/// @param server_address Address on which the TCP service should run.
|
||||
/// @param server_port Port number on which the TCP service should run.
|
||||
/// @param tls_context TLS context.
|
||||
/// @param idle_timeout Timeout after which an idle persistent TCP
|
||||
/// connection is closed by the server.
|
||||
///
|
||||
/// @throw TcpListenerError when any of the specified parameters is
|
||||
/// invalid.
|
||||
TcpListener(asiolink::IOService& io_service,
|
||||
const asiolink::IOAddress& server_address,
|
||||
const unsigned short server_port,
|
||||
const asiolink::TlsContextPtr& tls_context,
|
||||
const long idle_timeout);
|
||||
|
||||
/// @brief Virtual destructor.
|
||||
virtual ~TcpListener() {
|
||||
}
|
||||
|
||||
/// @brief Returns reference to the current listener endpoint.
|
||||
const asiolink::TCPEndpoint& getEndpoint() const;
|
||||
|
||||
/// @brief Starts accepting new connections.
|
||||
///
|
||||
/// This method starts accepting and handling new TCP connections on
|
||||
/// the IP address and port number specified in the constructor.
|
||||
///
|
||||
/// If the method is invoked successfully, it must not be invoked again
|
||||
/// until @ref TcpListener::stop is called.
|
||||
///
|
||||
/// @throw TcpListenerError if an error occurred.
|
||||
void start();
|
||||
|
||||
/// @brief Stops all active connections and shuts down the service.
|
||||
void stop();
|
||||
|
||||
protected:
|
||||
|
||||
/// @brief Creates @ref TcpConnection instance and adds it to the
|
||||
/// pool of active connections.
|
||||
///
|
||||
/// The next accepted connection will be handled by this instance.
|
||||
void accept();
|
||||
|
||||
/// @brief Callback invoked when the new connection is accepted.
|
||||
///
|
||||
/// It calls @c TcpListener::accept to create new @c TcpConnection
|
||||
/// instance.
|
||||
///
|
||||
/// @param ec Error code passed to the handler. This is currently ignored.
|
||||
void acceptHandler(const boost::system::error_code& ec);
|
||||
|
||||
/// @brief Creates an instance of the @c TcpConnection.
|
||||
///
|
||||
/// This method is virtual so as it can be overridden when customized
|
||||
/// connections are to be used, e.g. in case of unit testing.
|
||||
///
|
||||
/// @return Pointer to the created connection.
|
||||
virtual TcpConnectionPtr createConnection(const TcpConnectionAcceptorCallback& callback);
|
||||
|
||||
/// @brief Reference to the IO service.
|
||||
asiolink::IOService& io_service_;
|
||||
|
||||
/// @brief TLS context.
|
||||
asiolink::TlsContextPtr tls_context_;
|
||||
|
||||
/// @brief Acceptor instance.
|
||||
TcpConnectionAcceptorPtr acceptor_;
|
||||
|
||||
/// @brief Pointer to the endpoint representing IP address and port on
|
||||
/// which the service is running.
|
||||
boost::scoped_ptr<asiolink::TCPEndpoint> endpoint_;
|
||||
|
||||
/// @brief Pool of active connections.
|
||||
TcpConnectionPool connections_;
|
||||
|
||||
/// @brief Timeout after which idle persistent connection is closed by
|
||||
/// the server.
|
||||
long idle_timeout_;
|
||||
};
|
||||
|
||||
} // end of namespace isc::asiolink
|
||||
} // end of namespace isc
|
||||
|
||||
#endif // TCP_LISTENER_H
|
21
src/lib/tcp/tcp_log.cc
Normal file
21
src/lib/tcp/tcp_log.cc
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright (C) 2022 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at tcp://mozilla.org/MPL/2.0/.
|
||||
|
||||
/// Defines the logger used by the libkea-tcp library.
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <tcp/tcp_log.h>
|
||||
|
||||
namespace isc {
|
||||
namespace tcp {
|
||||
|
||||
/// @brief Defines the logger used within libkea-tcp library.
|
||||
isc::log::Logger tcp_logger("tcp");
|
||||
|
||||
} // namespace tcp
|
||||
} // namespace isc
|
||||
|
23
src/lib/tcp/tcp_log.h
Normal file
23
src/lib/tcp/tcp_log.h
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright (C) 2022 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at tcp://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef TCP_LOG_H
|
||||
#define TCP_LOG_H
|
||||
|
||||
#include <log/logger_support.h>
|
||||
#include <log/macros.h>
|
||||
#include <tcp/tcp_messages.h>
|
||||
|
||||
namespace isc {
|
||||
namespace tcp {
|
||||
|
||||
/// Define the logger used within libkea-tcp library.
|
||||
extern isc::log::Logger tcp_logger;
|
||||
|
||||
} // namespace tcp
|
||||
} // namespace isc
|
||||
|
||||
#endif // TCP_LOG_H
|
55
src/lib/tcp/tcp_messages.cc
Normal file
55
src/lib/tcp/tcp_messages.cc
Normal file
@ -0,0 +1,55 @@
|
||||
// File created from ../../../src/lib/tcp/tcp_messages.mes
|
||||
|
||||
#include <cstddef>
|
||||
#include <log/message_types.h>
|
||||
#include <log/message_initializer.h>
|
||||
|
||||
namespace isc {
|
||||
namespace asiolink {
|
||||
|
||||
extern const isc::log::MessageID TCP_BAD_CLIENT_REQUEST_RECEIVED = "TCP_BAD_CLIENT_REQUEST_RECEIVED";
|
||||
extern const isc::log::MessageID TCP_CLIENT_REQUEST_RECEIVED = "TCP_CLIENT_REQUEST_RECEIVED";
|
||||
extern const isc::log::MessageID TCP_CLIENT_REQUEST_RECEIVED_DETAILS = "TCP_CLIENT_REQUEST_RECEIVED_DETAILS";
|
||||
extern const isc::log::MessageID TCP_CONNECTION_CLOSE_CALLBACK_FAILED = "TCP_CONNECTION_CLOSE_CALLBACK_FAILED";
|
||||
extern const isc::log::MessageID TCP_CONNECTION_HANDSHAKE_FAILED = "TCP_CONNECTION_HANDSHAKE_FAILED";
|
||||
extern const isc::log::MessageID TCP_CONNECTION_HANDSHAKE_START = "TCP_CONNECTION_HANDSHAKE_START";
|
||||
extern const isc::log::MessageID TCP_CONNECTION_SHUTDOWN = "TCP_CONNECTION_SHUTDOWN";
|
||||
extern const isc::log::MessageID TCP_CONNECTION_SHUTDOWN_FAILED = "TCP_CONNECTION_SHUTDOWN_FAILED";
|
||||
extern const isc::log::MessageID TCP_CONNECTION_STOP = "TCP_CONNECTION_STOP";
|
||||
extern const isc::log::MessageID TCP_CONNECTION_STOP_FAILED = "TCP_CONNECTION_STOP_FAILED";
|
||||
extern const isc::log::MessageID TCP_DATA_RECEIVED = "TCP_DATA_RECEIVED";
|
||||
extern const isc::log::MessageID TCP_IDLE_CONNECTION_TIMEOUT_OCCURRED = "TCP_IDLE_CONNECTION_TIMEOUT_OCCURRED";
|
||||
extern const isc::log::MessageID TCP_PREMATURE_CONNECTION_TIMEOUT_OCCURRED = "TCP_PREMATURE_CONNECTION_TIMEOUT_OCCURRED";
|
||||
extern const isc::log::MessageID TCP_REQUEST_RECEIVE_START = "TCP_REQUEST_RECEIVE_START";
|
||||
extern const isc::log::MessageID TCP_SERVER_RESPONSE_SEND = "TCP_SERVER_RESPONSE_SEND";
|
||||
extern const isc::log::MessageID TCP_SERVER_RESPONSE_SEND_DETAILS = "TCP_SERVER_RESPONSE_SEND_DETAILS";
|
||||
|
||||
} // namespace asiolink
|
||||
} // namespace isc
|
||||
|
||||
namespace {
|
||||
|
||||
const char* values[] = {
|
||||
"TCP_BAD_CLIENT_REQUEST_RECEIVED", "bad request received from %1: %2",
|
||||
"TCP_CLIENT_REQUEST_RECEIVED", "received TCP request from %1",
|
||||
"TCP_CLIENT_REQUEST_RECEIVED_DETAILS", "detailed information about well-formed request received from %1:\n%2",
|
||||
"TCP_CONNECTION_CLOSE_CALLBACK_FAILED", "Connection close callback threw an exception",
|
||||
"TCP_CONNECTION_HANDSHAKE_FAILED", "TLS handshake with %1 failed with %2",
|
||||
"TCP_CONNECTION_HANDSHAKE_START", "start TLS handshake with %1 with timeout %2",
|
||||
"TCP_CONNECTION_SHUTDOWN", "shutting down TCP connection from %1",
|
||||
"TCP_CONNECTION_SHUTDOWN_FAILED", "shutting down TCP connection failed",
|
||||
"TCP_CONNECTION_STOP", "stopping TCP connection from %1",
|
||||
"TCP_CONNECTION_STOP_FAILED", "stopping TCP connection failed",
|
||||
"TCP_DATA_RECEIVED", "received %1 bytes from %2",
|
||||
"TCP_IDLE_CONNECTION_TIMEOUT_OCCURRED", "closing persistent connection with %1 as a result of a timeout",
|
||||
"TCP_PREMATURE_CONNECTION_TIMEOUT_OCCURRED", "premature connection timeout occurred: in transaction ? %1, transid: %2, current_transid: %3",
|
||||
"TCP_REQUEST_RECEIVE_START", "start receiving request from %1 with timeout %2",
|
||||
"TCP_SERVER_RESPONSE_SEND", "sending TCP response %1 to %2",
|
||||
"TCP_SERVER_RESPONSE_SEND_DETAILS", "detailed information about response sent to %1:\n%2",
|
||||
NULL
|
||||
};
|
||||
|
||||
const isc::log::MessageInitializer initializer(values);
|
||||
|
||||
} // Anonymous namespace
|
||||
|
31
src/lib/tcp/tcp_messages.h
Normal file
31
src/lib/tcp/tcp_messages.h
Normal file
@ -0,0 +1,31 @@
|
||||
// File created from ../../../src/lib/tcp/tcp_messages.mes
|
||||
|
||||
#ifndef TCP_MESSAGES_H
|
||||
#define TCP_MESSAGES_H
|
||||
|
||||
#include <log/message_types.h>
|
||||
|
||||
namespace isc {
|
||||
namespace asiolink {
|
||||
|
||||
extern const isc::log::MessageID TCP_BAD_CLIENT_REQUEST_RECEIVED;
|
||||
extern const isc::log::MessageID TCP_CLIENT_REQUEST_RECEIVED;
|
||||
extern const isc::log::MessageID TCP_CLIENT_REQUEST_RECEIVED_DETAILS;
|
||||
extern const isc::log::MessageID TCP_CONNECTION_CLOSE_CALLBACK_FAILED;
|
||||
extern const isc::log::MessageID TCP_CONNECTION_HANDSHAKE_FAILED;
|
||||
extern const isc::log::MessageID TCP_CONNECTION_HANDSHAKE_START;
|
||||
extern const isc::log::MessageID TCP_CONNECTION_SHUTDOWN;
|
||||
extern const isc::log::MessageID TCP_CONNECTION_SHUTDOWN_FAILED;
|
||||
extern const isc::log::MessageID TCP_CONNECTION_STOP;
|
||||
extern const isc::log::MessageID TCP_CONNECTION_STOP_FAILED;
|
||||
extern const isc::log::MessageID TCP_DATA_RECEIVED;
|
||||
extern const isc::log::MessageID TCP_IDLE_CONNECTION_TIMEOUT_OCCURRED;
|
||||
extern const isc::log::MessageID TCP_PREMATURE_CONNECTION_TIMEOUT_OCCURRED;
|
||||
extern const isc::log::MessageID TCP_REQUEST_RECEIVE_START;
|
||||
extern const isc::log::MessageID TCP_SERVER_RESPONSE_SEND;
|
||||
extern const isc::log::MessageID TCP_SERVER_RESPONSE_SEND_DETAILS;
|
||||
|
||||
} // namespace asiolink
|
||||
} // namespace isc
|
||||
|
||||
#endif // TCP_MESSAGES_H
|
96
src/lib/tcp/tcp_messages.mes
Normal file
96
src/lib/tcp/tcp_messages.mes
Normal file
@ -0,0 +1,96 @@
|
||||
# Copyright (C) 2022 Internet Systems Consortium, Inc. ("ISC")
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
$NAMESPACE isc::asiolink
|
||||
|
||||
% TCP_BAD_CLIENT_REQUEST_RECEIVED bad request received from %1: %2
|
||||
This debug message is issued when an TCP client sends malformed request to
|
||||
the server. This includes TCP requests using unexpected content types,
|
||||
including malformed JSON etc. The first argument specifies an address of
|
||||
the remote endpoint which sent the request. The second argument provides
|
||||
a detailed error message.
|
||||
|
||||
% TCP_CLIENT_REQUEST_RECEIVED received TCP request from %1
|
||||
This debug message is issued when the server finished receiving a TCP
|
||||
request from the remote endpoint. The address of the remote endpoint is
|
||||
specified as an argument.
|
||||
|
||||
% TCP_CLIENT_REQUEST_RECEIVED_DETAILS detailed information about well-formed request received from %1:\n%2
|
||||
This debug message is issued when the TCP server receives a well-formed
|
||||
request. It includes detailed information about the received request. The
|
||||
first argument specifies an address of the remote endpoint which sent the
|
||||
request. The second argument provides the request in the textual format.
|
||||
The request is truncated by the logger if it is too large to be printed.
|
||||
|
||||
% TCP_CONNECTION_CLOSE_CALLBACK_FAILED Connection close callback threw an exception
|
||||
This is an error message emitted when the close connection callback
|
||||
registered on the connection failed unexpectedly. This is a programmatic
|
||||
error that should be submitted as a bug.
|
||||
|
||||
% TCP_CONNECTION_HANDSHAKE_FAILED TLS handshake with %1 failed with %2
|
||||
This information message is issued when the TLS handshake failed at the
|
||||
server side. The client address and the error message are displayed.
|
||||
|
||||
% TCP_CONNECTION_HANDSHAKE_START start TLS handshake with %1 with timeout %2
|
||||
This debug message is issued when the server starts the TLS handshake
|
||||
with the remote endpoint. The first argument specifies the address
|
||||
of the remote endpoint. The second argument specifies request timeout in
|
||||
seconds.
|
||||
|
||||
% TCP_CONNECTION_SHUTDOWN shutting down TCP connection from %1
|
||||
This debug message is issued when one of the TCP connections is shut down.
|
||||
The connection can be stopped as a result of an error or after the
|
||||
successful message exchange with a client.
|
||||
|
||||
% TCP_CONNECTION_SHUTDOWN_FAILED shutting down TCP connection failed
|
||||
This error message is issued when an error occurred during shutting down
|
||||
a TCP connection with a client.
|
||||
|
||||
% TCP_CONNECTION_STOP stopping TCP connection from %1
|
||||
This debug message is issued when one of the TCP connections is stopped.
|
||||
The connection can be stopped as a result of an error or after the
|
||||
successful message exchange with a client.
|
||||
|
||||
% TCP_CONNECTION_STOP_FAILED stopping TCP connection failed
|
||||
This error message is issued when an error occurred during closing a
|
||||
TCP connection with a client.
|
||||
|
||||
% TCP_DATA_RECEIVED received %1 bytes from %2
|
||||
This debug message is issued when the server receives a chunk of data from
|
||||
the remote endpoint. This may include the whole request or only a part
|
||||
of the request. The first argument specifies the amount of received data.
|
||||
The second argument specifies an address of the remote endpoint which
|
||||
produced the data.
|
||||
|
||||
% TCP_IDLE_CONNECTION_TIMEOUT_OCCURRED closing persistent connection with %1 as a result of a timeout
|
||||
This debug message is issued when the persistent TCP connection is being
|
||||
closed as a result of being idle.
|
||||
|
||||
% TCP_PREMATURE_CONNECTION_TIMEOUT_OCCURRED premature connection timeout occurred: in transaction ? %1, transid: %2, current_transid: %3
|
||||
This warning message is issued when unexpected timeout occurred during the
|
||||
transaction. This is proven to occur when the system clock is moved manually
|
||||
or as a result of synchronization with a time server. Any ongoing transactions
|
||||
will be interrupted. New transactions should be conducted normally.
|
||||
|
||||
% TCP_REQUEST_RECEIVE_START start receiving request from %1 with timeout %2
|
||||
This debug message is issued when the server starts receiving new request
|
||||
over the established connection. The first argument specifies the address
|
||||
of the remote endpoint. The second argument specifies request timeout in
|
||||
seconds.
|
||||
|
||||
% TCP_SERVER_RESPONSE_SEND sending TCP response %1 to %2
|
||||
This debug message is issued when the server is starting to send a TCP
|
||||
response to a remote endpoint. The first argument holds basic information
|
||||
about the response (TCP version number and status code). The second
|
||||
argument specifies an address of the remote endpoint.
|
||||
|
||||
% TCP_SERVER_RESPONSE_SEND_DETAILS detailed information about response sent to %1:\n%2
|
||||
This debug message is issued right before the server sends a TCP response
|
||||
to the client. It includes detailed information about the response. The
|
||||
first argument specifies an address of the remote endpoint to which the
|
||||
response is being sent. The second argument provides a response in the
|
||||
textual form. The response is truncated by the logger if it is too large
|
||||
to be printed.
|
1
src/lib/tcp/tests/.gitignore
vendored
Normal file
1
src/lib/tcp/tests/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
run_unittests
|
58
src/lib/tcp/tests/Makefile.am
Normal file
58
src/lib/tcp/tests/Makefile.am
Normal file
@ -0,0 +1,58 @@
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
|
||||
AM_CPPFLAGS += $(BOOST_INCLUDES) $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES)
|
||||
TEST_CA_DIR = $(abs_srcdir)/../testutils/ca
|
||||
AM_CPPFLAGS += -DTEST_CA_DIR=\"$(TEST_CA_DIR)\"
|
||||
|
||||
AM_CXXFLAGS = $(KEA_CXXFLAGS)
|
||||
|
||||
if USE_STATIC_LINK
|
||||
AM_LDFLAGS = -static
|
||||
endif
|
||||
|
||||
CLEANFILES = *.gcno *.gcda test-socket
|
||||
|
||||
DISTCLEANFILES =
|
||||
|
||||
noinst_SCRIPTS =
|
||||
|
||||
TESTS_ENVIRONMENT = $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
|
||||
|
||||
TESTS =
|
||||
if HAVE_GTEST
|
||||
TESTS += run_unittests
|
||||
run_unittests_SOURCES = run_unittests.cc
|
||||
#run_unittests_SOURCES += tcp_listner_unittest.cc
|
||||
|
||||
#if HAVE_OPENSSL
|
||||
#run_unittests_SOURCES += tls_unittest.cc
|
||||
#run_unittests_SOURCES += tls_acceptor_unittest.cc
|
||||
#run_unittests_SOURCES += tls_socket_unittest.cc
|
||||
#endif
|
||||
#if HAVE_BOTAN_BOOST
|
||||
#run_unittests_SOURCES += tls_unittest.cc
|
||||
#run_unittests_SOURCES += tls_acceptor_unittest.cc
|
||||
#run_unittests_SOURCES += tls_socket_unittest.cc
|
||||
#endif
|
||||
|
||||
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
|
||||
|
||||
run_unittests_LDADD = $(top_builddir)/src/lib/tcp/libkea-tcp.la
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
|
||||
run_unittests_LDADD += $(LOG4CPLUS_LIBS) $(BOOST_LIBS) $(CRYPTO_LIBS)
|
||||
run_unittests_LDADD += $(GTEST_LDADD)
|
||||
|
||||
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS)
|
||||
|
||||
# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
|
||||
# KEA_CXXFLAGS)
|
||||
run_unittests_CXXFLAGS = $(AM_CXXFLAGS)
|
||||
if USE_GXX
|
||||
run_unittests_CXXFLAGS += -Wno-unused-parameter -Wno-unused-private-field
|
||||
endif
|
||||
endif
|
||||
|
||||
noinst_PROGRAMS = $(TESTS)
|
19
src/lib/tcp/tests/run_unittests.cc
Normal file
19
src/lib/tcp/tests/run_unittests.cc
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright (C) 2022 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <util/unittests/run_all.h>
|
||||
#include <log/logger_manager.h>
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv); // Initialize Google test
|
||||
isc::log::LoggerManager::init("unittest"); // Set a root logger name
|
||||
return (isc::util::unittests::run_all());
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user