Commit 77027490 authored by Stephen Morris's avatar Stephen Morris

[trac499] Add "normalization" of received data

... i.e. copying TCP data into the receive buffer omitting the leading
two-byte message length.
parent be701769
......@@ -26,6 +26,8 @@
#include <exceptions/exceptions.h>
#include <coroutine.h>
#include <dns/buffer.h>
#include <asiolink/io_error.h>
#include <asiolink/io_socket.h>
......@@ -224,6 +226,21 @@ public:
/// needed.
virtual bool receiveComplete(const void* data, size_t length) = 0;
/// \brief Append Normalized Data
///
/// When a UDP buffer is received, the entire buffer contains the data.
/// When a TCP buffer is received, the first two bytes of the buffer hold
/// a length count. This method removes those bytes from the buffer.
///
/// \param inbuf Input buffer. This contains the data received over the
/// network connection.
/// \param length Amount of data in the input buffer. If TCP, this includes
/// the two-byte count field.
/// \param outbuf Pointer to output buffer to which the data will be
/// appended.
virtual void appendNormalizedData(const void* inbuf, size_t length,
isc::dns::OutputBufferPtr outbuf) = 0;
/// \brief Cancel I/O On AsioSocket
virtual void cancel() = 0;
......@@ -321,6 +338,15 @@ public:
virtual bool receiveComplete(void*, size_t, size_t&) {
return (true);
}
/// \brief Append Normalized Data
///
/// \param inbuf Unused.
/// \param length Unused.
/// \param outbuf unused.
virtual void appendNormalizedData(const void*, size_t,
isc::dns::OutputBufferPtr)
{
}
/// \brief Cancel I/O On AsioSocket
///
......
......@@ -72,8 +72,8 @@ struct IOFetchData {
boost::shared_ptr<IOEndpoint> remote; ///< Where the fetch was sent
isc::dns::Question question; ///< Question to be asked
isc::dns::OutputBufferPtr msgbuf; ///< Wire buffer for question
isc::dns::OutputBufferPtr buffer; ///< Received data held here
boost::shared_array<char> data; ///< Temporary array for data
isc::dns::OutputBufferPtr received; ///< Received data put here
boost::shared_array<char> staging; ///< Temporary array for received data
IOFetch::Callback* callback; ///< Called on I/O Completion
size_t cumulative; ///< Cumulative received amount
bool stopped; ///< Have we stopped running?
......@@ -122,8 +122,8 @@ struct IOFetchData {
),
question(query),
msgbuf(new isc::dns::OutputBuffer(512)),
buffer(buff),
data(new char[IOFetch::MIN_LENGTH]),
received(buff),
staging(new char[IOFetch::MIN_LENGTH]),
callback(cb),
cumulative(0),
stopped(false),
......@@ -211,11 +211,11 @@ IOFetch::operator()(asio::error_code ec, size_t length) {
// we check if the operation is complete and if not, loop to read again.
data_->origin = ASIO_RECVSOCK;
do {
CORO_YIELD data_->socket->asyncReceive(data_->data.get(),
CORO_YIELD data_->socket->asyncReceive(data_->staging.get(),
static_cast<size_t>(MIN_LENGTH), data_->cumulative,
data_->remote.get(), *this);
data_->cumulative += length;
} while (!data_->socket->receiveComplete(data_->data.get(),
} while (!data_->socket->receiveComplete(data_->staging.get(),
data_->cumulative));
/// Copy the answer into the response buffer. (TODO: If the
......@@ -223,7 +223,8 @@ IOFetch::operator()(asio::error_code ec, size_t length) {
/// MutableBufferSequence, then it could be written to directly by
/// async_receive_from() and this additional copy step would be
/// unnecessary.)
data_->buffer->writeData(data_->data.get(), length);
data_->socket->appendNormalizedData(data_->staging.get(),
data_->cumulative, data_->received);
// Finished with this socket, so close it. This will not generate an
// I/O error, but reset the origin to unknown in case we change this.
......
......@@ -147,6 +147,21 @@ public:
/// \return true if the receive is complete, false if not.
virtual bool receiveComplete(const void* data, size_t length);
/// \brief Append Normalized Data
///
/// When a UDP buffer is received, the entire buffer contains the data.
/// When a TCP buffer is received, the first two bytes of the buffer hold
/// a length count. This method removes those bytes from the buffer.
///
/// \param inbuf Input buffer. This contains the data received over the
/// network connection.
/// \param length Amount of data in the input buffer. If TCP, this includes
/// the two-byte count field.
/// \param outbuf Pointer to output buffer to which the data will be
/// appended
virtual void appendNormalizedData(const void* inbuf, size_t length,
isc::dns::OutputBufferPtr outbuf);
/// \brief Cancel I/O On Socket
virtual void cancel();
......@@ -338,6 +353,16 @@ TCPSocket<C>::receiveComplete(const void* data, size_t length) {
return (complete);
}
// Copy buffer less leading two bytes to the target buffer.
template <typename C> void
TCPSocket<C>::appendNormalizedData(const void* inbuf, size_t length,
isc::dns::OutputBufferPtr outbuf)
{
const uint8_t* bytebuff = static_cast<const uint8_t*>(inbuf);
outbuf->writeData(bytebuff + 2, length - 2);
}
// Cancel I/O on the socket. No-op if the socket is not open.
template <typename C> void
......
......@@ -47,7 +47,7 @@ namespace asiolink {
const asio::ip::address TEST_HOST(asio::ip::address::from_string("127.0.0.1"));
const uint16_t TEST_PORT(5301);
// FIXME Shouldn't we send something that is real message?
const char TEST_DATA[] = "TEST DATA";
const char TEST_DATA[] = "Test response from server to client";
/// \brief Test fixture for the asiolink::IOFetch.
class IOFetchTest : public virtual ::testing::Test, public virtual IOFetch::Callback
......@@ -262,30 +262,11 @@ public:
// when one of the "servers" in this class has sent back the TEST_DATA.
// Check the data is as expected/
if (expected_ == IOFetch::SUCCESS) {
size_t offset = 0; // Offset into start of buffer of data
if (protocol_ == IOFetch::UDP) {
// Check the length of data received against the amount expected.
EXPECT_EQ(sizeof(TEST_DATA), result_buff_->getLength());
} else {
// Check the length of data received against the amount expected
EXPECT_EQ(sizeof(TEST_DATA) + 2, result_buff_->getLength());
// Check the count field. This should be equal to the total
// length of the packet less 2 (the count field is equal to
// the total length of the message less the count field itself -
// RFC 1035, section 4.2.2).
uint16_t count = readUint16(result_buff_->getData());
EXPECT_EQ(result_buff_->getLength(), count + 2);
// Update offset and count for the content check.
offset += 2;
}
const void* start = static_cast<const void*>(
static_cast<const uint8_t*>(result_buff_->getData()) + offset);
EXPECT_TRUE(memcmp(TEST_DATA, start, sizeof(TEST_DATA)) == 0);
const uint8_t* start = static_cast<const uint8_t*>(result_buff_->getData());
EXPECT_TRUE(equal(TEST_DATA, (TEST_DATA + sizeof(TEST_DATA) - 1),
start));
}
// ... and cause the run loop to exit.
......
......@@ -274,6 +274,58 @@ clientReadComplete(TCPSocket<TCPCallback>& client, TCPCallback& client_cb,
return (complete);
}
// Receive complete method should return true only if the count in the first
// two bytes is equal to the size of the rest if the buffer.
TEST(TCPSocket, receiveComplete) {
IOService service; // Used to instantiate socket
TCPSocket<TCPCallback> test(service); // Socket under test
uint8_t buffer[32]; // Buffer to check
// Expect that the value is true whatever number is written in the first
// two bytes of the buffer.
uint16_t count = 0;
for (uint32_t i = 0; i < (2 << 16); ++i, ++count) {
writeUint16(count, buffer);
if (count == (sizeof(buffer) - 2)) {
EXPECT_TRUE(test.receiveComplete(buffer, sizeof(buffer)));
} else {
EXPECT_FALSE(test.receiveComplete(buffer, sizeof(buffer)));
}
}
}
// Check that the normalized data copy only copies all but the first two bytes
// of the buffer (whatever the count).
TEST(TCPSocket, appendNormalizedData) {
IOService service; // Used to instantiate socket
TCPSocket<TCPCallback> test(service); // Socket under test
uint8_t inbuff[32]; // Buffer to check
isc::dns::OutputBufferPtr outbuff(new isc::dns::OutputBuffer(sizeof(inbuff)));
// Where data is written
// Initialize the input buffer with data.
for (uint8_t i = 0; i < sizeof(inbuff); ++i) {
inbuff[i] = i + 1; // An arbitrary number
}
// Loop to ensure that entire buffer is copied on all count values, no
// matter what.
uint16_t count = 0;
for (uint32_t i = 0; i < (2 << 16); ++i, ++count) {
writeUint16(count, inbuff);
outbuff->clear();
test.appendNormalizedData(inbuff, sizeof(inbuff), outbuff);
EXPECT_EQ((sizeof(inbuff) - 2), outbuff->getLength());
const uint8_t* outptr = static_cast<const uint8_t*>(outbuff->getData());
EXPECT_TRUE(equal(&inbuff[2], &inbuff[sizeof(inbuff) - 1], outptr));
}
}
// TODO: Need to add a test to check the cancel() method
// Tests the operation of a TCPSocket by opening it, sending an asynchronous
......
......@@ -35,8 +35,11 @@
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <dns/buffer.h>
#include <asio.hpp>
#include <asiolink/asiolink_utilities.h>
#include <asiolink/io_service.h>
#include <asiolink/udp_endpoint.h>
#include <asiolink/udp_socket.h>
......@@ -162,6 +165,53 @@ private:
boost::shared_ptr<PrivateData> ptr_; ///< Pointer to private data
};
// Receive complete method should return true regardless of what is in the first
// two bytes of a buffer.
TEST(UDPSocket, receiveComplete) {
IOService service; // Used to instantiate socket
UDPSocket<UDPCallback> test(service); // Socket under test
uint8_t buffer[32]; // Buffer to check
// Expect that the value is true whatever number is written in the first
// two bytes of the buffer.
uint16_t count = 0;
for (uint32_t i = 0; i < (2 << 16); ++i, ++count) {
writeUint16(count, buffer);
EXPECT_TRUE(test.receiveComplete(buffer, sizeof(buffer)));
}
}
// Check that the normalized data copy copies the entire buffer regardless of
// the first two bytes.
TEST(UDPSocket, appendNormalizedData) {
IOService service; // Used to instantiate socket
UDPSocket<UDPCallback> test(service); // Socket under test
uint8_t inbuff[32]; // Buffer to check
isc::dns::OutputBufferPtr outbuff(new isc::dns::OutputBuffer(sizeof(inbuff)));
// Where data is written
// Initialize the input buffer with data.
for (uint8_t i = 0; i < sizeof(inbuff); ++i) {
inbuff[i] = i + 1; // An arbitrary number
}
// Loop to ensure that entire buffer is copied on all count values, no
// matter what.
uint16_t count = 0;
for (uint32_t i = 0; i < (2 << 16); ++i, ++count) {
writeUint16(count, inbuff);
outbuff->clear();
test.appendNormalizedData(inbuff, sizeof(inbuff), outbuff);
EXPECT_EQ(sizeof(inbuff), outbuff->getLength());
const uint8_t* outptr = static_cast<const uint8_t*>(outbuff->getData());
EXPECT_TRUE(equal(&inbuff[0], &inbuff[sizeof(inbuff) - 1], outptr));
}
}
// TODO: Need to add a test to check the cancel() method
// Tests the operation of a UDPSocket by opening it, sending an asynchronous
......
......@@ -137,6 +137,24 @@ public:
return (true);
}
/// \brief Append Normalized Data
///
/// When a UDP buffer is received, the entire buffer contains the data.
/// When a TCP buffer is received, the first two bytes of the buffer hold
/// a length count. This method removes those bytes from the buffer.
///
/// \param inbuf Input buffer. This contains the data received over the
/// network connection.
/// \param length Amount of data in the input buffer. If TCP, this includes
/// the two-byte count field.
/// \param outbuf Pointer to output buffer to which the data will be
/// appended
virtual void appendNormalizedData(const void* inbuf, size_t length,
isc::dns::OutputBufferPtr outbuf)
{
outbuf->writeData(inbuf, length);
}
/// \brief Cancel I/O On Socket
virtual void cancel();
......@@ -271,6 +289,7 @@ UDPSocket<C>::asyncReceive(void* data, size_t length, size_t offset,
}
// Cancel I/O on the socket. No-op if the socket is not open.
template <typename C> void
UDPSocket<C>::cancel() {
if (isopen_) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment