decoding+transport: Exception-safe Block parsing
Change-Id: I3e83b6ca4c8ca42b8bb1ddc8dc50c52ee366c55c
Refs: #1291
diff --git a/src/encoding/block.cpp b/src/encoding/block.cpp
index eaba9ba..07ec2f0 100644
--- a/src/encoding/block.cpp
+++ b/src/encoding/block.cpp
@@ -59,7 +59,7 @@
{
m_value_begin = m_begin;
m_value_end = m_end;
-
+
m_type = Tlv::readType(m_value_begin, m_value_end);
uint64_t length = Tlv::readVarNumber(m_value_begin, m_value_end);
@@ -93,9 +93,9 @@
Block::Block(std::istream& is)
{
- std::istream_iterator<uint8_t> tmp_begin(is);
+ std::istream_iterator<uint8_t> tmp_begin(is);
std::istream_iterator<uint8_t> tmp_end;
-
+
m_type = Tlv::readType(tmp_begin, tmp_end);
uint64_t length = Tlv::readVarNumber(tmp_begin, tmp_end);
@@ -104,7 +104,7 @@
OBufferStream os;
size_t headerLength = Tlv::writeVarNumber(os, m_type);
headerLength += Tlv::writeVarNumber(os, length);
-
+
char* buf = new char[length];
buf[0] = *tmp_begin;
is.read(buf+1, length-1);
@@ -113,7 +113,7 @@
{
delete [] buf;
throw Tlv::Error("Not enough data in the buffer to fully parse TLV");
- }
+ }
os.write(buf, length);
delete [] buf;
@@ -132,11 +132,11 @@
Block::Block(const uint8_t *buffer, size_t maxlength)
{
const uint8_t * tmp_begin = buffer;
- const uint8_t * tmp_end = buffer + maxlength;
-
+ const uint8_t * tmp_end = buffer + maxlength;
+
m_type = Tlv::readType(tmp_begin, tmp_end);
uint64_t length = Tlv::readVarNumber(tmp_begin, tmp_end);
-
+
if (length > static_cast<uint64_t>(tmp_end - tmp_begin))
{
throw Tlv::Error("Not enough data in the buffer to fully parse TLV");
@@ -155,13 +155,13 @@
Block::Block(const void *bufferX, size_t maxlength)
{
const uint8_t * buffer = reinterpret_cast<const uint8_t*>(bufferX);
-
+
const uint8_t * tmp_begin = buffer;
- const uint8_t * tmp_end = buffer + maxlength;
-
+ const uint8_t * tmp_end = buffer + maxlength;
+
m_type = Tlv::readType(tmp_begin, tmp_end);
uint64_t length = Tlv::readVarNumber(tmp_begin, tmp_end);
-
+
if (length > static_cast<uint64_t>(tmp_end - tmp_begin))
{
throw Tlv::Error("Not enough data in the buffer to fully parse TLV");
@@ -204,19 +204,75 @@
m_size = Tlv::sizeOfVarNumber(m_type) + Tlv::sizeOfVarNumber(value_size()) + value_size();
}
+bool
+Block::fromBuffer(const ConstBufferPtr& wire, size_t offset, Block& block)
+{
+ Buffer::const_iterator tempBegin = wire->begin() + offset;
+
+ uint32_t type;
+ bool ok = Tlv::readType(tempBegin, wire->end(), type);
+ if (!ok)
+ return false;
+
+ uint64_t length;
+ ok = Tlv::readVarNumber(tempBegin, wire->end(), length);
+ if (!ok)
+ return false;
+
+ if (length > static_cast<uint64_t>(wire->end() - tempBegin))
+ {
+ return false;
+ }
+
+ block = Block(wire, type,
+ wire->begin() + offset, tempBegin + length,
+ tempBegin, tempBegin + length);
+
+ return true;
+}
+
+bool
+Block::fromBuffer(const uint8_t* buffer, size_t maxSize, Block& block)
+{
+ const uint8_t* tempBegin = buffer;
+ const uint8_t* tempEnd = buffer + maxSize;
+
+ uint32_t type;
+ bool ok = Tlv::readType(tempBegin, tempEnd, type);
+ if (!ok)
+ return false;
+
+ uint64_t length;
+ ok = Tlv::readVarNumber(tempBegin, tempEnd, length);
+ if (!ok)
+ return false;
+
+ if (length > static_cast<uint64_t>(tempEnd - tempBegin))
+ {
+ return false;
+ }
+
+ BufferPtr sharedBuffer = make_shared<Buffer>(buffer, tempBegin + length);
+ block = Block(sharedBuffer, type,
+ sharedBuffer->begin(), sharedBuffer->end(),
+ sharedBuffer->begin() + (tempBegin - buffer), sharedBuffer->end());
+
+ return true;
+}
+
void
Block::parse() const
{
if (!m_subBlocks.empty() || value_size()==0)
return;
-
+
Buffer::const_iterator begin = value_begin(),
end = value_end();
while (begin != end)
{
Buffer::const_iterator element_begin = begin;
-
+
uint32_t type = Tlv::readType(begin, end);
uint64_t length = Tlv::readVarNumber(begin, end);
@@ -226,7 +282,7 @@
throw Tlv::Error("TLV length exceeds buffer length");
}
Buffer::const_iterator element_end = begin + length;
-
+
m_subBlocks.push_back(Block(m_buffer,
type,
element_begin, element_end,
@@ -261,7 +317,7 @@
for (element_const_iterator i = m_subBlocks.begin(); i != m_subBlocks.end(); ++i) {
valueSize += i->size();
}
-
+
Tlv::writeVarNumber(os, valueSize);
for (element_const_iterator i = m_subBlocks.begin(); i != m_subBlocks.end(); ++i) {
@@ -286,7 +342,7 @@
m_value_begin = m_buffer->begin();
m_value_end = m_buffer->end();
-
+
Tlv::readType(m_value_begin, m_value_end);
Tlv::readVarNumber(m_value_begin, m_value_end);
}
@@ -296,18 +352,18 @@
{
if (value_size()==0)
throw Error("Underlying value buffer is empty");
-
+
Buffer::const_iterator begin = value_begin(),
end = value_end();
Buffer::const_iterator element_begin = begin;
-
+
uint32_t type = Tlv::readType(begin, end);
uint64_t length = Tlv::readVarNumber(begin, end);
if (length != static_cast<uint64_t>(end - begin))
throw Tlv::Error("TLV length mismatches buffer length");
-
+
return Block(m_buffer,
type,
element_begin, end,
diff --git a/src/encoding/block.hpp b/src/encoding/block.hpp
index 52ed7e7..a2507b5 100644
--- a/src/encoding/block.hpp
+++ b/src/encoding/block.hpp
@@ -31,8 +31,11 @@
typedef element_container::const_iterator element_const_iterator;
/// @brief Error that can be thrown from the block
- struct Error : public std::runtime_error { Error(const std::string &what) : std::runtime_error(what) {} };
-
+ struct Error : public std::runtime_error
+ {
+ Error(const std::string& what) : std::runtime_error(what) {}
+ };
+
/**
* @brief Default constructor to create an empty Block
*/
@@ -43,11 +46,11 @@
*/
explicit
Block(const EncodingBuffer& buffer);
-
+
/**
* @brief A helper version of a constructor to create Block from the raw buffer (type and value-length parsing)
*/
- Block(const ConstBufferPtr &buffer);
+ Block(const ConstBufferPtr& buffer);
/**
* @brief Another helper to create block from a buffer, directly specifying boundaries
@@ -55,31 +58,31 @@
*
* This version will automatically detect type and position of the value within the block
*/
- Block(const ConstBufferPtr &buffer,
- const Buffer::const_iterator &begin, const Buffer::const_iterator &end,
+ Block(const ConstBufferPtr& buffer,
+ const Buffer::const_iterator& begin, const Buffer::const_iterator& end,
bool verifyLength = true);
-
+
/**
* @brief A helper version of a constructor to create Block from the raw buffer (type and value-length parsing)
*/
- Block(const uint8_t *buffer, size_t maxlength);
+ Block(const uint8_t* buffer, size_t maxlength);
- Block(const void *buffer, size_t maxlength);
+ Block(const void* buffer, size_t maxlength);
/*
- * @brief A helper version of a constructor to create Block from the stream.
+ * @brief A helper version of a constructor to create Block from the stream.
*/
Block(std::istream& is);
-
+
/**
* @brief Create Block from the wire buffer (no parsing)
*
* This version of the constructor does not do any parsing
*/
- Block(const ConstBufferPtr &wire,
+ Block(const ConstBufferPtr& wire,
uint32_t type,
- const Buffer::const_iterator &begin, const Buffer::const_iterator &end,
- const Buffer::const_iterator &valueBegin, const Buffer::const_iterator &valueEnd);
+ const Buffer::const_iterator& begin, const Buffer::const_iterator& end,
+ const Buffer::const_iterator& valueBegin, const Buffer::const_iterator& valueEnd);
/**
* @brief Create Block of a specific type with empty wire buffer
@@ -94,7 +97,7 @@
* to construct wire encoding, one need to prepend the wire buffer with type
* and value-length VAR-NUMBERs
*/
- Block(uint32_t type, const ConstBufferPtr &value);
+ Block(uint32_t type, const ConstBufferPtr& value);
/**
* @brief Create nested Block of a specific type with the specified value
@@ -104,14 +107,34 @@
* and value-length VAR-NUMBERs
*/
explicit
- Block(uint32_t type, const Block &value);
+ Block(uint32_t type, const Block& value);
+
+ /**
+ * @brief Try to construct block from Buffer, referencing data block pointed by wire
+ *
+ * @throws This method never throws an exception
+ *
+ * @returns true if Block successfully created, false if block cannot be created
+ */
+ static bool
+ fromBuffer(const ConstBufferPtr& wire, size_t offset, Block& block);
+
+ /**
+ * @brief Try to construct block from Buffer, referencing data block pointed by wire
+ *
+ * @throws This method never throws an exception
+ *
+ * @returns true if Block successfully created, false if block cannot be created
+ */
+ static bool
+ fromBuffer(const uint8_t* buffer, size_t maxSize, Block& block);
/**
* @brief Check if the Block is empty
*/
inline bool
empty() const;
-
+
/**
* @brief Check if the Block has fully encoded wire
*/
@@ -123,7 +146,7 @@
*/
inline bool
hasValue() const;
-
+
/**
* @brief Reset wire buffer of the element
*/
@@ -150,14 +173,14 @@
*/
void
encode();
-
+
inline uint32_t
type() const;
/**
* @brief Get the first subelement of the requested type
*/
- inline const Block &
+ inline const Block&
get(uint32_t type) const;
inline element_const_iterator
@@ -171,10 +194,10 @@
inline element_iterator
erase(element_iterator first, element_iterator last);
-
+
inline void
- push_back(const Block &element);
-
+ push_back(const Block& element);
+
inline Buffer::const_iterator
begin() const;
@@ -189,7 +212,7 @@
// inline const uint8_t*
// buf() const;
-
+
inline Buffer::const_iterator
value_begin() const;
@@ -216,7 +239,7 @@
inline size_t
elements_size() const;
-
+
Block
blockFromValue() const;
@@ -224,11 +247,11 @@
ConstBufferPtr m_buffer;
uint32_t m_type;
-
+
Buffer::const_iterator m_begin;
Buffer::const_iterator m_end;
uint32_t m_size;
-
+
Buffer::const_iterator m_value_begin;
Buffer::const_iterator m_value_end;
@@ -285,7 +308,7 @@
return m_type;
}
-inline const Block &
+inline const Block&
Block::get(uint32_t type) const
{
for (element_const_iterator i = m_subBlocks.begin ();
@@ -300,7 +323,7 @@
throw Error("(Block::get) Requested a non-existed type [" + boost::lexical_cast<std::string>(type) + "] from Block");
}
-
+
inline Block::element_const_iterator
Block::find(uint32_t type) const
{
@@ -409,7 +432,7 @@
{
if (!hasValue())
return 0;
-
+
return &*m_value_begin;
}
diff --git a/src/encoding/tlv.hpp b/src/encoding/tlv.hpp
index 8a2b3fc..2ef008c 100644
--- a/src/encoding/tlv.hpp
+++ b/src/encoding/tlv.hpp
@@ -69,7 +69,39 @@
/**
* @brief Read VAR-NUMBER in NDN-TLV encoding
*
- * This call will throw ndn::Tlv::Error (aka std::runtime_error) if number cannot be read
+ * @param [in] begin Begin (pointer or iterator) of the buffer
+ * @param [in] end End (pointer or iterator) of the buffer
+ * @param [out] number Read number
+ *
+ * @throws This call never throws exception
+ *
+ * @return true if number successfully read from input, false otherwise
+ */
+template<class InputIterator>
+inline bool
+readVarNumber(InputIterator &begin, const InputIterator &end, uint64_t& number);
+
+/**
+ * @brief Read TLV Type
+ *
+ * @param [in] begin Begin (pointer or iterator) of the buffer
+ * @param [in] end End (pointer or iterator) of the buffer
+ * @param [out] number Read type number
+ *
+ * @throws This call never throws exception
+ *
+ * This call is largely equivalent to tlv::readVarNumber, but exception will be thrown if type
+ * is larger than 2^32-1 (type in this library is implemented as uint32_t)
+ */
+template<class InputIterator>
+inline bool
+readType(InputIterator &begin, const InputIterator &end, uint32_t& type);
+
+
+/**
+ * @brief Read VAR-NUMBER in NDN-TLV encoding
+ *
+ * @throws This call will throw ndn::Tlv::Error (aka std::runtime_error) if number cannot be read
*
* Note that after call finished, begin will point to the first byte after the read VAR-NUMBER
*/
@@ -80,6 +112,8 @@
/**
* @brief Read TLV Type
*
+ * @throws This call will throw ndn::Tlv::Error (aka std::runtime_error) if number cannot be read
+ *
* This call is largely equivalent to tlv::readVarNumber, but exception will be thrown if type
* is larger than 2^32-1 (type in this library is implemented as uint32_t)
*/
@@ -136,12 +170,72 @@
/////////////////////////////////////////////////////////////////////////////////
template<class InputIterator>
+inline bool
+readVarNumber(InputIterator &begin, const InputIterator &end, uint64_t& number)
+{
+ if (begin == end)
+ return false;
+
+ uint8_t value = *begin;
+ ++begin;
+ if (value < 253)
+ {
+ number = value;
+ }
+ else if (value == 253)
+ {
+ if (end - begin < 2)
+ return false;
+
+ uint16_t value = *reinterpret_cast<const uint16_t*>(&*begin); // kind of dangerous... but should be efficient
+ begin += 2;
+ number = be16toh(value);
+ }
+ else if (value == 254)
+ {
+ if (end - begin < 4)
+ return false;
+
+ uint32_t value = *reinterpret_cast<const uint32_t*>(&*begin); // kind of dangerous... but should be efficient
+ begin += 4;
+ number = be32toh(value);
+ }
+ else // if (value == 255)
+ {
+ if (end - begin < 8)
+ return false;
+
+ uint64_t value = *reinterpret_cast<const uint64_t*>(&*begin);
+ begin += 8;
+
+ number = be64toh(value);
+ }
+
+ return true;
+}
+
+template<class InputIterator>
+inline bool
+readType(InputIterator &begin, const InputIterator &end, uint32_t& type)
+{
+ uint64_t number;
+ bool ok = readVarNumber(begin, end, number);
+ if (number > std::numeric_limits<uint32_t>::max())
+ {
+ return false;
+ }
+
+ type = static_cast<uint64_t> (number);
+ return true;
+}
+
+template<class InputIterator>
inline uint64_t
readVarNumber(InputIterator &begin, const InputIterator &end)
{
if (begin == end)
throw Error("Empty buffer during TLV processing");
-
+
uint8_t value = *begin;
++begin;
if (value < 253)
@@ -152,7 +246,7 @@
{
if (end - begin < 2)
throw Error("Insufficient data during TLV processing");
-
+
uint16_t value = *reinterpret_cast<const uint16_t*>(&*begin); // kind of dangerous... but should be efficient
begin += 2;
return be16toh(value);
@@ -161,7 +255,7 @@
{
if (end - begin < 4)
throw Error("Insufficient data during TLV processing");
-
+
uint32_t value = *reinterpret_cast<const uint32_t*>(&*begin); // kind of dangerous... but should be efficient
begin += 4;
return be32toh(value);
@@ -170,7 +264,7 @@
{
if (end - begin < 8)
throw Error("Insufficient data during TLV processing");
-
+
uint64_t value = *reinterpret_cast<const uint64_t*>(&*begin);
begin += 8;
@@ -180,12 +274,12 @@
template<>
inline uint64_t
-readVarNumber<std::istream_iterator<uint8_t> >(std::istream_iterator<uint8_t> &begin,
+readVarNumber<std::istream_iterator<uint8_t> >(std::istream_iterator<uint8_t> &begin,
const std::istream_iterator<uint8_t> &end)
{
if (begin == end)
throw Error("Empty buffer during TLV processing");
-
+
uint8_t value = *begin;
++begin;
if (value < 253)
@@ -196,7 +290,7 @@
{
uint8_t buffer[2];
int count = 0;
-
+
while(begin != end && count < 2){
buffer[count] = *begin;
begin++;
@@ -205,15 +299,15 @@
if (count < 2)
throw Error("Insufficient data during TLV processing");
-
- uint16_t value = *reinterpret_cast<const uint16_t*>(buffer);
+
+ uint16_t value = *reinterpret_cast<const uint16_t*>(buffer);
return be16toh(value);
}
else if (value == 254)
{
uint8_t buffer[4];
int count = 0;
-
+
while(begin != end && count < 4){
buffer[count] = *begin;
begin++;
@@ -222,7 +316,7 @@
if (count < 4)
throw Error("Insufficient data during TLV processing");
-
+
uint32_t value = *reinterpret_cast<const uint32_t*>(buffer);
return be32toh(value);
}
@@ -230,7 +324,7 @@
{
uint8_t buffer[8];
int count = 0;
-
+
while(begin != end && count < 8){
buffer[count] = *begin;
begin++;
@@ -239,7 +333,7 @@
if (count < 8)
throw Error("Insufficient data during TLV processing");
-
+
uint64_t value = *reinterpret_cast<const uint64_t*>(buffer);
return be64toh(value);
}
@@ -311,7 +405,7 @@
{
if (end - begin < 1)
throw Error("Insufficient data during TLV processing");
-
+
uint8_t value = *begin;
begin++;
return value;
@@ -320,7 +414,7 @@
{
if (end - begin < 2)
throw Error("Insufficient data during TLV processing");
-
+
uint16_t value = *reinterpret_cast<const uint16_t*>(&*begin); // kind of dangerous... but should be efficient
begin += 2;
return be16toh(value);
@@ -329,7 +423,7 @@
{
if (end - begin < 4)
throw Error("Insufficient data during TLV processing");
-
+
uint32_t value = *reinterpret_cast<const uint32_t*>(&*begin); // kind of dangerous... but should be efficient
begin += 4;
return be32toh(value);
@@ -338,7 +432,7 @@
{
if (end - begin < 8)
throw Error("Insufficient data during TLV processing");
-
+
uint64_t value = *reinterpret_cast<const uint64_t*>(&*begin);
begin += 8;
return be64toh(value);
@@ -349,8 +443,8 @@
template<>
inline uint64_t
-readNonNegativeInteger<std::istream_iterator<uint8_t> >(size_t size,
- std::istream_iterator<uint8_t> &begin,
+readNonNegativeInteger<std::istream_iterator<uint8_t> >(size_t size,
+ std::istream_iterator<uint8_t> &begin,
const std::istream_iterator<uint8_t> &end)
{
switch (size) {
@@ -358,7 +452,7 @@
{
if(begin == end)
throw Error("Insufficient data during TLV processing");
-
+
uint8_t value = *begin;
begin++;
return value;
@@ -367,16 +461,16 @@
{
uint8_t buffer[2];
int count = 0;
-
+
while(begin != end && count < 2){
buffer[count] = *begin;
begin++;
count++;
}
-
+
if (count < 2)
throw Error("Insufficient data during TLV processing");
-
+
uint16_t value = *reinterpret_cast<const uint16_t*>(buffer);
return be16toh(value);
}
@@ -384,7 +478,7 @@
{
uint8_t buffer[4];
int count = 0;
-
+
while(begin != end && count < 4){
buffer[count] = *begin;
begin++;
@@ -393,7 +487,7 @@
if (count < 4)
throw Error("Insufficient data during TLV processing");
-
+
uint32_t value = *reinterpret_cast<const uint32_t*>(buffer);
return be32toh(value);
}
@@ -401,7 +495,7 @@
{
uint8_t buffer[8];
int count = 0;
-
+
while(begin != end && count < 8){
buffer[count] = *begin;
begin++;
@@ -410,7 +504,7 @@
if (count < 8)
throw Error("Insufficient data during TLV processing");
-
+
uint64_t value = *reinterpret_cast<const uint64_t*>(buffer);
return be64toh(value);
}
diff --git a/src/transport/stream-transport.hpp b/src/transport/stream-transport.hpp
index 8562d88..713d740 100644
--- a/src/transport/stream-transport.hpp
+++ b/src/transport/stream-transport.hpp
@@ -24,12 +24,12 @@
StreamTransportImpl(base_transport& transport, boost::asio::io_service& ioService)
: m_transport(transport)
, m_socket(ioService)
- , m_partialDataSize(0)
+ , m_inputBufferSize(0)
, m_connectionInProgress(false)
, m_connectTimer(ioService)
{
}
-
+
void
connectHandler(const boost::system::error_code& error)
{
@@ -55,7 +55,7 @@
m_socket.async_send(buffer,
bind(&impl::handle_async_send, this, _1, i->first, i->second));
}
-
+
m_sendQueue.clear();
m_sendPairQueue.clear();
}
@@ -76,6 +76,7 @@
m_connectionInProgress = false;
m_transport.m_isConnected = false;
+ m_transport.m_isExpectingData = false;
m_socket.close();
throw Transport::Error(error, "error while connecting to the forwarder");
}
@@ -96,8 +97,8 @@
bind(&impl::connectHandler, this, _1));
}
}
-
- void
+
+ void
close()
{
m_connectTimer.cancel();
@@ -124,7 +125,7 @@
if (!m_transport.m_isExpectingData)
{
m_transport.m_isExpectingData = true;
- m_partialDataSize = 0;
+ m_inputBufferSize = 0;
m_socket.async_receive(boost::asio::buffer(m_inputBuffer, MAX_LENGTH), 0,
bind(&impl::handle_async_receive, this, _1, _2));
}
@@ -153,113 +154,75 @@
buffers.reserve(2);
buffers.push_back(boost::asio::buffer(header.wire(), header.size()));
buffers.push_back(boost::asio::buffer(payload.wire(), payload.size()));
-
+
m_socket.async_send(buffers,
bind(&impl::handle_async_send, this, _1, header, payload));
}
}
-
- inline void
+
+ inline bool
processAll(uint8_t* buffer, size_t& offset, size_t availableSize)
{
+ Block element;
while(offset < availableSize)
{
- Block element(buffer + offset, availableSize - offset);
- m_transport.receive(element);
+ bool ok = Block::fromBuffer(buffer + offset, availableSize - offset, element);
+ if (!ok)
+ return false;
+ m_transport.receive(element);
offset += element.size();
}
+ return true;
}
-
+
void
handle_async_receive(const boost::system::error_code& error, std::size_t bytes_recvd)
{
- /// @todo The socket is not datagram, so need to have internal buffer to handle partial data reception
-
if (error)
{
if (error == boost::system::errc::operation_canceled) {
// async receive has been explicitly cancelled (e.g., socket close)
return;
}
-
+
m_socket.close(); // closing at this point may not be that necessary
m_transport.m_isConnected = true;
throw Transport::Error(error, "error while receiving data from socket");
}
-
- if (!error && bytes_recvd > 0)
+
+ m_inputBufferSize += bytes_recvd;
+ // do magic
+
+ std::size_t offset = 0;
+ bool ok = processAll(m_inputBuffer, offset, m_inputBufferSize);
+ if (!ok && m_inputBufferSize == MAX_LENGTH && offset == 0)
{
- // m_inputBuffer has bytes_recvd received bytes of data
- if (m_partialDataSize > 0)
+ // very bad... should close connection
+ m_socket.close();
+ m_transport.m_isConnected = false;
+ m_transport.m_isExpectingData = false;
+ throw Transport::Error(boost::system::error_code(),
+ "input buffer full, but a valid TLV cannot be decoded");
+ }
+
+ if (offset > 0)
+ {
+ if (offset != m_inputBufferSize)
{
- size_t newDataSize = std::min(bytes_recvd, MAX_LENGTH-m_partialDataSize);
- std::copy(m_inputBuffer, m_inputBuffer + newDataSize, m_partialData + m_partialDataSize);
-
- m_partialDataSize += newDataSize;
-
- size_t offset = 0;
- try
- {
- processAll(m_partialData, offset, m_partialDataSize);
-
- // no exceptions => processed the whole thing
- if (bytes_recvd - newDataSize > 0)
- {
- // there is a little bit more data available
-
- offset = 0;
- m_partialDataSize = bytes_recvd - newDataSize;
- std::copy(m_inputBuffer + newDataSize, m_inputBuffer + newDataSize + m_partialDataSize, m_partialData);
-
- processAll(m_partialData, offset, m_partialDataSize);
-
- // no exceptions => processed the whole thing
- m_partialDataSize = 0;
- }
- else
- {
- // done processing
- m_partialDataSize = 0;
- }
- }
- catch(Tlv::Error &)
- {
- if (offset > 0)
- {
- m_partialDataSize -= offset;
- std::copy(m_partialData + offset, m_partialData + offset + m_partialDataSize, m_partialData);
- }
- else if (offset == 0 && m_partialDataSize == MAX_LENGTH)
- {
- // very bad... should close connection
- m_socket.close();
- m_transport.m_isConnected = true;
- throw Transport::Error(boost::system::error_code(),
- "input buffer full, but a valid TLV cannot be decoded");
- }
- }
+ std::copy(m_inputBuffer + offset, m_inputBuffer + m_inputBufferSize,
+ m_inputBuffer);
+ m_inputBufferSize -= offset;
}
else
{
- size_t offset = 0;
- try
- {
- processAll(m_inputBuffer, offset, bytes_recvd);
- }
- catch(Tlv::Error &error)
- {
- if (offset > 0)
- {
- m_partialDataSize = bytes_recvd - offset;
- std::copy(m_inputBuffer + offset, m_inputBuffer + offset + m_partialDataSize, m_partialData);
- }
- }
+ m_inputBufferSize = 0;
}
}
- m_socket.async_receive(boost::asio::buffer(m_inputBuffer, MAX_LENGTH), 0,
- bind(&impl::handle_async_receive, this, _1, _2));
+ m_socket.async_receive(boost::asio::buffer(m_inputBuffer + m_inputBufferSize,
+ MAX_LENGTH - m_inputBufferSize), 0,
+ bind(&impl::handle_async_receive, this, _1, _2));
}
void
@@ -277,12 +240,10 @@
protected:
base_transport& m_transport;
-
+
typename protocol::socket m_socket;
uint8_t m_inputBuffer[MAX_LENGTH];
-
- uint8_t m_partialData[MAX_LENGTH];
- size_t m_partialDataSize;
+ size_t m_inputBufferSize;
std::list< Block > m_sendQueue;
std::list< std::pair<Block, Block> > m_sendPairQueue;
@@ -304,7 +265,7 @@
: StreamTransportImpl<base_transport, protocol>(transport, ioService)
{
}
-
+
void
resolveHandler(const boost::system::error_code& error,
typename protocol::resolver::iterator endpoint,
@@ -314,15 +275,16 @@
{
if (error == boost::system::errc::operation_canceled)
return;
-
+
throw Transport::Error(error, "Error during resolution of host or port");
}
-
+
typename protocol::resolver::iterator end;
if (endpoint == end)
{
this->m_connectionInProgress = false;
this->m_transport.m_isConnected = false;
+ this->m_transport.m_isExpectingData = false;
this->m_socket.close();
throw Transport::Error(error, "Unable to resolve because host or port");
}
@@ -330,7 +292,7 @@
this->m_socket.async_connect(*endpoint,
bind(&impl::connectHandler, this, _1));
}
-
+
void
connect(const typename protocol::resolver::query& query)
{
diff --git a/tests/test-block.cpp b/tests/test-block.cpp
index ea0c9b4..be2d2a6 100644
--- a/tests/test-block.cpp
+++ b/tests/test-block.cpp
@@ -12,28 +12,28 @@
BOOST_AUTO_TEST_SUITE(TestBlock)
-BOOST_AUTO_TEST_CASE (Basic)
+BOOST_AUTO_TEST_CASE(Basic)
{
EncodingBuffer buffer;
EncodingEstimator estimator;
size_t s1, s2;
// VarNumber checks
-
+
s1 = buffer.prependVarNumber(252);
s2 = estimator.prependVarNumber(252);
BOOST_CHECK_EQUAL(buffer.size(), 1);
BOOST_CHECK_EQUAL(s1, 1);
BOOST_CHECK_EQUAL(s2, 1);
buffer = EncodingBuffer();
-
+
s1 = buffer.prependVarNumber(253);
s2 = estimator.prependVarNumber(253);
BOOST_CHECK_EQUAL(buffer.size(), 3);
BOOST_CHECK_EQUAL(s1, 3);
BOOST_CHECK_EQUAL(s2, 3);
buffer = EncodingBuffer();
-
+
s1 = buffer.prependVarNumber(255);
s2 = estimator.prependVarNumber(255);
BOOST_CHECK_EQUAL(buffer.size(), 3);
@@ -54,7 +54,7 @@
BOOST_CHECK_EQUAL(s1, 5);
BOOST_CHECK_EQUAL(s2, 5);
buffer = EncodingBuffer();
-
+
s1 = buffer.prependVarNumber(4294967295);
s2 = estimator.prependVarNumber(4294967295);
BOOST_CHECK_EQUAL(buffer.size(), 5);
@@ -70,21 +70,21 @@
buffer = EncodingBuffer();
// nonNegativeInteger checks
-
+
s1 = buffer.prependNonNegativeInteger(252);
s2 = estimator.prependNonNegativeInteger(252);
BOOST_CHECK_EQUAL(buffer.size(), 1);
BOOST_CHECK_EQUAL(s1, 1);
BOOST_CHECK_EQUAL(s2, 1);
buffer = EncodingBuffer();
-
+
s1 = buffer.prependNonNegativeInteger(255);
s2 = estimator.prependNonNegativeInteger(255);
BOOST_CHECK_EQUAL(buffer.size(), 1);
BOOST_CHECK_EQUAL(s1, 1);
BOOST_CHECK_EQUAL(s2, 1);
buffer = EncodingBuffer();
-
+
s1 = buffer.prependNonNegativeInteger(256);
s2 = estimator.prependNonNegativeInteger(256);
BOOST_CHECK_EQUAL(buffer.size(), 2);
@@ -121,7 +121,7 @@
buffer = EncodingBuffer();
}
-BOOST_AUTO_TEST_CASE (EncodingBufferToBlock)
+BOOST_AUTO_TEST_CASE(EncodingBufferToBlock)
{
uint8_t value[4];
@@ -140,11 +140,11 @@
BOOST_CHECK_EQUAL(block.value_size(), sizeof(value));
}
-BOOST_AUTO_TEST_CASE (BlockToBuffer)
+BOOST_AUTO_TEST_CASE(BlockToBuffer)
{
shared_ptr<Buffer> buf = make_shared<Buffer>(10);
for (int i = 0; i < 10; i++) (*buf)[i] = i;
-
+
Block block(0xab, buf);
block.encode();
@@ -163,6 +163,55 @@
BOOST_CHECK_EQUAL(buffer.capacity(), 10);
}
+BOOST_AUTO_TEST_CASE(FromBuffer)
+{
+ const uint8_t TEST_BUFFER[] = {0x00, 0x01, 0xfa, // ok
+ 0x01, 0x01, 0xfb, // ok
+ 0x03, 0x02, 0xff}; // not ok
+ BufferPtr buffer(new Buffer(TEST_BUFFER, sizeof(TEST_BUFFER)));
+
+ // using BufferPtr (avoids memory copy)
+ size_t offset = 0;
+ Block testBlock;
+ BOOST_CHECK(Block::fromBuffer(buffer, offset, testBlock));
+ BOOST_CHECK_EQUAL(testBlock.type(), 0);
+ BOOST_CHECK_EQUAL(testBlock.size(), 3);
+ BOOST_CHECK_EQUAL(testBlock.value_size(), 1);
+ BOOST_CHECK_EQUAL(*testBlock.wire(), 0x00);
+ BOOST_CHECK_EQUAL(*testBlock.value(), 0xfa);
+ offset += testBlock.size();
+
+ BOOST_CHECK(Block::fromBuffer(buffer, offset, testBlock));
+ BOOST_CHECK_EQUAL(testBlock.type(), 1);
+ BOOST_CHECK_EQUAL(testBlock.size(), 3);
+ BOOST_CHECK_EQUAL(testBlock.value_size(), 1);
+ BOOST_CHECK_EQUAL(*testBlock.wire(), 0x01);
+ BOOST_CHECK_EQUAL(*testBlock.value(), 0xfb);
+ offset += testBlock.size();
+
+ BOOST_CHECK(!Block::fromBuffer(buffer, offset, testBlock));
+
+ // just buffer, copies memory
+ offset = 0;
+ BOOST_CHECK(Block::fromBuffer(TEST_BUFFER + offset, sizeof(TEST_BUFFER) - offset, testBlock));
+ BOOST_CHECK_EQUAL(testBlock.type(), 0);
+ BOOST_CHECK_EQUAL(testBlock.size(), 3);
+ BOOST_CHECK_EQUAL(testBlock.value_size(), 1);
+ BOOST_CHECK_EQUAL(*testBlock.wire(), 0x00);
+ BOOST_CHECK_EQUAL(*testBlock.value(), 0xfa);
+ offset += testBlock.size();
+
+ BOOST_CHECK(Block::fromBuffer(TEST_BUFFER + offset, sizeof(TEST_BUFFER) - offset, testBlock));
+ BOOST_CHECK_EQUAL(testBlock.type(), 1);
+ BOOST_CHECK_EQUAL(testBlock.size(), 3);
+ BOOST_CHECK_EQUAL(testBlock.value_size(), 1);
+ BOOST_CHECK_EQUAL(*testBlock.wire(), 0x01);
+ BOOST_CHECK_EQUAL(*testBlock.value(), 0xfb);
+ offset += testBlock.size();
+
+ BOOST_CHECK(!Block::fromBuffer(TEST_BUFFER + offset, sizeof(TEST_BUFFER) - offset, testBlock));
+}
+
BOOST_AUTO_TEST_SUITE_END()
} // namespace ndn