From: Thomas Markwalder Date: Fri, 4 Nov 2022 16:20:44 +0000 (-0400) Subject: [#2583] Create TcpMessage base class X-Git-Tag: Kea-2.3.3~101 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c6ce6d94cf3bfcc272deeff9c2cf78fee9e8715e;p=thirdparty%2Fkea.git [#2583] Create TcpMessage base class src/lib/tcp/Makefile.am Uncommented .dox file Set version to 1.0.0 Updated list of includes to install src/lib/tcp/libkea_tcp.dox Added MT section src/lib/tcp/tcp_connection.* Created base class TcpMessage with WireData member/funcs Accessing empty wire data data() now throws src/lib/util/strutil.* Added dumpAsHex() --- diff --git a/src/lib/tcp/.gitattributes b/src/lib/tcp/.gitattributes new file mode 100644 index 0000000000..049dc4cd7c --- /dev/null +++ b/src/lib/tcp/.gitattributes @@ -0,0 +1,2 @@ +/tcp_messages.cc -diff merge=ours +/tcp_messages.h -diff merge=ours diff --git a/src/lib/tcp/Makefile.am b/src/lib/tcp/Makefile.am index 3a35cb57ae..beb20086b7 100644 --- a/src/lib/tcp/Makefile.am +++ b/src/lib/tcp/Makefile.am @@ -4,7 +4,7 @@ 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 +EXTRA_DIST = tcp.dox # Ensure that the message file is included in the distribution EXTRA_DIST += tcp_messages.mes @@ -23,7 +23,7 @@ libkea_tcp_la_SOURCES += tcp_stream.cc tcp_stream.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_LDFLAGS += -no-undefined -version-info 1:0:0 libkea_tcp_la_LIBADD = libkea_tcp_la_LIBADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la @@ -70,8 +70,10 @@ 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 + tcp_connection_acceptor.h \ + tcp_connection.h \ + tcp_connection_pool.h \ + tcp_listener.h \ + tcp_log.h \ + tcp_messages.h \ + tcp_stream.h diff --git a/src/lib/tcp/libkea_tcp.dox b/src/lib/tcp/libkea_tcp.dox index 36cf637cf8..8dce87398b 100644 --- a/src/lib/tcp/libkea_tcp.dox +++ b/src/lib/tcp/libkea_tcp.dox @@ -12,4 +12,8 @@ This is a library of classes (in the isc::kea_tcp namespace) that provide the ability to accept connections, listen for and respond to TCP messages. +@section tcpMTConsiderations Multi-Threading Consideration for TCP Library + +This library is thread safe. + */ diff --git a/src/lib/tcp/tcp_connection.cc b/src/lib/tcp/tcp_connection.cc index f483210899..ab173db4a5 100644 --- a/src/lib/tcp/tcp_connection.cc +++ b/src/lib/tcp/tcp_connection.cc @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -32,22 +33,7 @@ constexpr size_t MAX_LOGGED_MESSAGE_SIZE = 1024; namespace isc { namespace tcp { -std::string -TcpRequest::dumpAsHex(const uint8_t* data, size_t len) { - std::stringstream output; - for (unsigned int i = 0; i < len; i++) { - if (i) { - output << ":"; - } - - output << std::setfill('0') << std::setw(2) << std::hex - << static_cast(data[i]); - } - - return (output.str()); -} - -void +void TcpResponse::consumeWireData(const size_t length) { send_in_progress_ = true; wire_data_.erase(wire_data_.begin(), wire_data_.begin() + length); @@ -260,7 +246,7 @@ void TcpConnection::doWrite(TcpResponsePtr response) { try { if (response->wireDataAvail()) { - HERE("send:" << TcpRequest::dumpAsHex(response->getWireData(), response->getWireDataSize())); + HERE("send:" << isc::util::str::dumpAsHex(response->getWireData(), response->getWireDataSize())); // 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. @@ -396,7 +382,7 @@ TcpConnection::postData(TcpRequestPtr request, WireData& input_data) { // Add data to the current request. size_t bytes_used = request->postBuffer(static_cast(input_data.data()), length); // Remove bytes used. - bytes_left = length - bytes_used; + bytes_left = length - bytes_used; input_data.erase(input_data.begin(), input_data.begin() + length); } @@ -438,7 +424,7 @@ TcpConnection::postData(TcpRequestPtr request, WireData& input_data) { request = createRequest(); if (bytes_left) { // The input buffer spanned messages. Recurse to post the remainder to the - // new request. + // new request. request = postData(request, input_data); // Restart the request timer. HERE("spanning read, start request timer"); @@ -447,7 +433,7 @@ TcpConnection::postData(TcpRequestPtr request, WireData& input_data) { // No incomplete requests, start the idle timer. HERE("no waiting requests, start idle timer"); setupIdleTimer(); - } + } return (request); } @@ -551,7 +537,7 @@ TcpConnection::getRemoteEndpointAddressAsText() const { return ("(unknown address)"); } -void +void TcpConnection::setReadMax(const size_t read_max) { if (!read_max) { isc_throw(BadValue, "TcpConnection read_max must be > 0"); diff --git a/src/lib/tcp/tcp_connection.h b/src/lib/tcp/tcp_connection.h index bbdae00bdd..f3f5a8b42a 100644 --- a/src/lib/tcp/tcp_connection.h +++ b/src/lib/tcp/tcp_connection.h @@ -28,15 +28,47 @@ namespace tcp { #if 1 #define HERE(a) std::cout << __FILE__ << ":" << __FUNCTION__ << ":" << __LINE__ << " " << a << std::endl << std::flush; #else -#define HERE(a) +#define HERE(a) #endif /// @brief Defines a data structure for storing raw bytes of data on the wire. typedef std::vector WireData; typedef boost::shared_ptr WireDataPtr; +/// @brief Base class for TCP messages. +class TcpMessage { +public: + /// @brief Constructor + TcpMessage(){ + }; + + /// @brief Destructor + virtual ~TcpMessage(){ + }; + + /// @brief Returns pointer to the first byte of the wire data. + /// @throw InvalidOperation if wire data is empty (i.e. getWireDataSize() == 0). + /// @return Constant raw pointer to the data. + const uint8_t* getWireData() const { + if (wire_data_.empty()) { + isc_throw(InvalidOperation, "TcpMessage::geWireData() - cannot access empty wire data"); + } + + return (wire_data_.data()); + } + + /// @brief Returns current size of the wire data. + size_t getWireDataSize() const { + return (wire_data_.size()); + } + +protected: + /// @brief Buffer used for data in wire format data. + WireData wire_data_; +}; + /// @brief Abstract class used to receive an inbound message. -class TcpRequest { +class TcpRequest : public TcpMessage{ public: /// @brief Constructor. TcpRequest(){}; @@ -66,23 +98,6 @@ public: /// @brief Unpacks wire data once the message has been completely received. virtual void unpack() = 0; - /// @brief Returns pointer to the first byte of the wire data. - const uint8_t* getWireData() const { - return (wire_data_.data()); - } - - /// @brief Returns current size of the wire data. - size_t getWireDataSize() const { - return (wire_data_.size()); - } - - /// @brief Dumps a buffer of bytes as a string of hexadecimal digits - /// - /// @param data pointer to the data to dump - /// @param length number of bytes to dump. Caller should ensure the length - /// does not exceed the buffer. - static std::string dumpAsHex(const uint8_t* data, size_t length); - private: /// @brief Exception safe wrapper around logForamteRequest @@ -91,17 +106,13 @@ private: /// the length of the output is unlimited. /// @return Textual representation of the input buffer. std::string logFormatRequestSafe(const size_t limit = 0) const; - -protected: - /// @brief Buffer for the accumulated request data. - WireData wire_data_; }; /// @brief Defines a smart pointer to a TcpRequest. typedef boost::shared_ptr TcpRequestPtr; /// @brief Abstract class used to create and send an outbound response. -class TcpResponse { +class TcpResponse : public TcpMessage{ public: /// @brief Constructor TcpResponse() @@ -119,16 +130,6 @@ public: return (!wire_data_.empty()); } - /// @brief Returns pointer to the first byte of the wire data. - const uint8_t* getWireData() const { - return (wire_data_.data()); - } - - /// @brief Returns current size of the wire data. - size_t getWireDataSize() const { - return (wire_data_.size()); - } - /// @brief Prepares the wire data content for writing. virtual void pack() = 0; @@ -141,10 +142,6 @@ public: return(send_in_progress_); } -protected: - /// @brief Buffer used for outbound data. - WireData wire_data_; - private: /// @brief Returns true once wire data consumption has begun. bool send_in_progress_; @@ -270,7 +267,7 @@ public: /// The input data is passed into the current request's postBuffer method. /// If the request is still incomplete, we return it and wait for more /// data to post. Otherwise, the request is complete and it is passed into - /// @ref TcpConnection::requestReceived() to be processed. Upon return from + /// @ref TcpConnection::requestReceived() to be processed. Upon return from /// that, a new request is created and returned to be used for the next /// read cycle. /// @@ -281,7 +278,7 @@ public: TcpRequestPtr postData(TcpRequestPtr request, WireData& input_data); /// @brief Processes a request once it has been completely received. - /// + /// /// This function is called by @c postData() if the post results /// in a completion (i.e. request's needData() returns false). virtual void requestReceived(TcpRequestPtr request) = 0; @@ -289,7 +286,7 @@ public: /// @brief Creates a new, empty request. /// /// This function is called by @c postData(), following the completion - /// of the current request, to create a new request for accepting the + /// of the current request, to create a new request for accepting the /// next data read. /// /// @return Pointer to the new request. @@ -452,7 +449,7 @@ protected: /// @brief Maximum bytes to write in a single socket write. size_t write_max_; - /// @brief Buffer for a single socket read. + /// @brief Buffer for a single socket read. WireData input_buf_; }; diff --git a/src/lib/tcp/tcp_stream.cc b/src/lib/tcp/tcp_stream.cc index ae6eb39d53..290c482ef2 100644 --- a/src/lib/tcp/tcp_stream.cc +++ b/src/lib/tcp/tcp_stream.cc @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -45,7 +46,7 @@ TcpStreamRequest::logFormatRequest(const size_t limit) const { size_t max = (limit && (limit < wire_data_.size()) ? limit : wire_data_.size()); output << "expected_size_: " << expected_size_ << ", current size: " << wire_data_.size() << ", data: " - << dumpAsHex(wire_data_.data(), max); + << isc::util::str::dumpAsHex(wire_data_.data(), max); } catch (const std::exception& ex) { std::stringstream output; output << "logFormatRequest error: " << ex.what(); diff --git a/src/lib/util/strutil.cc b/src/lib/util/strutil.cc index f5e9b769e8..b0025f06ac 100644 --- a/src/lib/util/strutil.cc +++ b/src/lib/util/strutil.cc @@ -448,6 +448,20 @@ StringSanitizer::scrub(const std::string& original) { return (impl_->scrub(original)); } +std::string dumpAsHex(const uint8_t* data, size_t len) { + std::stringstream output; + for (unsigned int i = 0; i < len; i++) { + if (i) { + output << ":"; + } + + output << std::setfill('0') << std::setw(2) << std::hex + << static_cast(data[i]); + } + + return (output.str()); +} + } // namespace str } // namespace util } // namespace isc diff --git a/src/lib/util/strutil.h b/src/lib/util/strutil.h index 53b4e296d9..0cb967e3f7 100644 --- a/src/lib/util/strutil.h +++ b/src/lib/util/strutil.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -387,6 +388,14 @@ isPrintable(const std::vector& content) { return (true); } + +/// @brief Dumps a buffer of bytes as a string of hexadecimal digits +/// +/// @param data pointer to the data to dump +/// @param length number of bytes to dump. Caller should ensure the length +/// does not exceed the buffer. +std::string dumpAsHex(const uint8_t* data, size_t len); + } // namespace str } // namespace util } // namespace isc