]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#2583] Create TcpMessage base class
authorThomas Markwalder <tmark@isc.org>
Fri, 4 Nov 2022 16:20:44 +0000 (12:20 -0400)
committerThomas Markwalder <tmark@isc.org>
Thu, 10 Nov 2022 19:43:23 +0000 (14:43 -0500)
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()

src/lib/tcp/.gitattributes [new file with mode: 0644]
src/lib/tcp/Makefile.am
src/lib/tcp/libkea_tcp.dox
src/lib/tcp/tcp_connection.cc
src/lib/tcp/tcp_connection.h
src/lib/tcp/tcp_stream.cc
src/lib/util/strutil.cc
src/lib/util/strutil.h

diff --git a/src/lib/tcp/.gitattributes b/src/lib/tcp/.gitattributes
new file mode 100644 (file)
index 0000000..049dc4c
--- /dev/null
@@ -0,0 +1,2 @@
+/tcp_messages.cc                -diff merge=ours
+/tcp_messages.h                 -diff merge=ours
index 3a35cb57aec38537d50a8f08ee225337cbfdf576..beb20086b73256180bd6753e29c9705ab4888269 100644 (file)
@@ -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
index 36cf637cf878d7f5dd5603b26486c34756b09694..8dce87398bf605ecbcac0b453caf25d05f164df0 100644 (file)
@@ -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.
+
 */
index f483210899c17eb7076e62456daf0ce90e853c77..ab173db4a526e1f9553c9c81c751d910d8525eff 100644 (file)
@@ -11,6 +11,7 @@
 #include <tcp/tcp_connection_pool.h>
 #include <tcp/tcp_log.h>
 #include <tcp/tcp_messages.h>
+#include <util/strutil.h>
 #include <boost/make_shared.hpp>
 
 #include <iomanip>
@@ -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<unsigned short>(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<void*>(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");
index bbdae00bddb5651bc26ae74fa5871e30f276bf32..f3f5a8b42a7cef5130cf38905719e5773e7bf5df 100644 (file)
@@ -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<uint8_t> WireData;
 typedef boost::shared_ptr<WireData> 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<TcpRequest> 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_;
 };
 
index ae6eb39d536513ac70591381b60e50ac43617ac3..290c482ef215c9bfad8fb213a4278f31bb757f84 100644 (file)
@@ -7,6 +7,7 @@
 #include <config.h>
 
 #include <tcp/tcp_stream.h>
+#include <util/strutil.h>
 
 #include <iomanip>
 #include <sstream>
@@ -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();
index f5e9b769e89c8de9584206642e0d35a144f188d7..b0025f06ac403cd9bf4208a7b891a7ca238d792c 100644 (file)
@@ -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<unsigned short>(data[i]);
+    }
+
+    return (output.str());
+}
+
 } // namespace str
 } // namespace util
 } // namespace isc
index 53b4e296d9aa0547cc8efcc310ac645f9024e79f..0cb967e3f79d8241ad9c80b257e7b0d53b5efccb 100644 (file)
@@ -11,6 +11,7 @@
 #include <cctype>
 #include <stdint.h>
 #include <string>
+#include <iomanip>
 #include <sstream>
 #include <vector>
 #include <exceptions/exceptions.h>
@@ -387,6 +388,14 @@ isPrintable(const std::vector<uint8_t>& 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