encoding: Block::fromStream TLV-LENGTH bugfix

This commit fixes decoding errors when TLV-LENGTH is ASCII whitespace,
and when TLV-LENGTH is zero.

refs #2728 #2729

Change-Id: Ie1b3e9ee7068dd0a4f14fd51e97ceab1dd4d898b
diff --git a/src/encoding/block.cpp b/src/encoding/block.cpp
index 4353c96..e17fe9a 100644
--- a/src/encoding/block.cpp
+++ b/src/encoding/block.cpp
@@ -199,11 +199,15 @@
 Block
 Block::fromStream(std::istream& is)
 {
-  std::istream_iterator<uint8_t> tmp_begin(is);
-  std::istream_iterator<uint8_t> tmp_end;
+  std::istream_iterator<uint8_t> begin(is >> std::noskipws);
+  std::istream_iterator<uint8_t> end;
 
-  uint32_t type = tlv::readType(tmp_begin, tmp_end);
-  uint64_t length = tlv::readVarNumber(tmp_begin, tmp_end);
+  uint32_t type = tlv::readType(begin, end);
+  uint64_t length = tlv::readVarNumber(begin, end);
+
+  if (length == 0) {
+    return dataBlock(type, static_cast<uint8_t*>(nullptr), length);
+  }
 
   if (length > MAX_SIZE_OF_BLOCK_FROM_STREAM)
     throw tlv::Error("Length of block from stream is too large");
@@ -212,8 +216,8 @@
   // we may completely lose all the bytes extracted from the stream.
 
   char buf[MAX_SIZE_OF_BLOCK_FROM_STREAM];
-  buf[0] = *tmp_begin;
-  is.read(buf+1, length-1);
+  buf[0] = *begin;
+  is.read(buf + 1, length - 1);
 
   if (length != static_cast<uint64_t>(is.gcount()) + 1) {
     throw tlv::Error("Not enough data in the buffer to fully parse TLV");
diff --git a/tests/unit-tests/encoding/block.t.cpp b/tests/unit-tests/encoding/block.t.cpp
index ac7ff51..62de9aa 100644
--- a/tests/unit-tests/encoding/block.t.cpp
+++ b/tests/unit-tests/encoding/block.t.cpp
@@ -272,7 +272,7 @@
   BOOST_CHECK(!isOk);
 }
 
-BOOST_AUTO_TEST_CASE(BlockFromStream)
+BOOST_AUTO_TEST_CASE(FromStream)
 {
   const uint8_t TEST_BUFFER[] = {0x00, 0x01, 0xfa, // ok
                                  0x01, 0x01, 0xfb, // ok
@@ -300,6 +300,45 @@
   BOOST_CHECK_THROW(Block::fromStream(stream), tlv::Error);
 }
 
+BOOST_AUTO_TEST_CASE(FromStreamWhitespace) // Bug 2728
+{
+  uint8_t PACKET[] = {
+    0x06, 0x20, // Data
+          0x07, 0x11, // Name
+                0x08, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, // NameComponent 'hello'
+                0x08, 0x01, 0x31, // NameComponent '1'
+                0x08, 0x05, 0x77, 0x6f, 0x72, 0x6c, 0x64, // NameComponent 'world'
+          0x14, 0x00, // MetaInfo empty
+          0x15, 0x00, // Content empty
+          0x16, 0x05, // SignatureInfo
+                 0x1b, 0x01, 0x01, // SignatureType RSA
+                 0x1c, 0x00, // KeyLocator empty
+          0x17, 0x00 // SignatureValue empty
+  };
+  // TLV-LENGTH of <Data> is 0x20 which happens to be ASCII whitespace
+
+  std::stringstream stream;
+  stream.write(reinterpret_cast<const char*>(PACKET), sizeof(PACKET));
+  stream.seekg(0);
+
+  Block block = Block::fromStream(stream);
+  BOOST_CHECK_NO_THROW(block.parse());
+}
+
+BOOST_AUTO_TEST_CASE(FromStreamZeroLength) // Bug 2729
+{
+  uint8_t BUFFER[] = { 0x07, 0x00 }; // TLV-LENGTH is zero
+
+  std::stringstream stream;
+  stream.write(reinterpret_cast<const char*>(BUFFER), sizeof(BUFFER));
+  stream.seekg(0);
+
+  Block block;
+  BOOST_CHECK_NO_THROW(block = Block::fromStream(stream));
+  BOOST_CHECK_EQUAL(block.type(), 0x07);
+  BOOST_CHECK_EQUAL(block.value_size(), 0);
+}
+
 BOOST_AUTO_TEST_CASE(Equality)
 {
   BOOST_CONCEPT_ASSERT((boost::EqualityComparable<Block>));