From 77209caf044d58c7d87770b377027a65fe696466 Mon Sep 17 00:00:00 2001 From: Puneetha Ramachandra Date: Tue, 1 Dec 2020 03:16:11 -0800 Subject: [PATCH] Fix content reader for partial input When there is partial message in reader, popping out the characters lead to parse errors on the subsequent attempt to parse. To avoid this, the last matched characters' index is stored during parsing and reset on error. --- src/content_stream.cpp | 31 ++++++++++++++++++++----------- src/content_stream.h | 1 + src/content_stream_test.cpp | 16 ++++++++++++++++ 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/src/content_stream.cpp b/src/content_stream.cpp index e7c6628..05d7f47 100644 --- a/src/content_stream.cpp +++ b/src/content_stream.cpp @@ -44,13 +44,17 @@ void ContentReader::close() { } std::string ContentReader::read() { + matched_idx = 0; + // Find Content-Length header prefix if (!scan("Content-Length:")) { return ""; } + // Skip whitespace and tabs while (matchAny(" \t")) { } + // Parse length size_t len = 0; while (true) { @@ -68,10 +72,16 @@ std::string ContentReader::read() { if (!match("\r\n\r\n")) { return ""; } + // Read message - if (!buffer(len)) { + if (!buffer(len + matched_idx)) { return ""; } + + for (size_t i = 0; i < matched_idx; i++) { + buf.pop_front(); + } + std::string out; out.reserve(len); for (size_t i = 0; i < len; i++) { @@ -97,18 +107,17 @@ bool ContentReader::scan(const char* str) { } bool ContentReader::match(const uint8_t* seq, size_t len) { - if (!buffer(len)) { + if (!buffer(len + matched_idx)) { return false; } - auto it = buf.begin(); + auto it = matched_idx; for (size_t i = 0; i < len; i++, it++) { - if (*it != seq[i]) { + if (buf[it] != seq[i]) { return false; } } - for (size_t i = 0; i < len; i++) { - buf.pop_front(); - } + + matched_idx += len; return true; } @@ -118,12 +127,12 @@ bool ContentReader::match(const char* str) { } char ContentReader::matchAny(const char* chars) { - if (!buffer(1)) { + if (!buffer(1 + matched_idx)) { return false; } - int c = buf.front(); + int c = buf[matched_idx]; if (auto p = strchr(chars, c)) { - buf.pop_front(); + matched_idx++; return *p; } return 0; @@ -177,4 +186,4 @@ bool ContentWriter::write(const std::string& msg) const { writer->write(msg.data(), msg.size()); } -} // namespace dap \ No newline at end of file +} // namespace dap diff --git a/src/content_stream.h b/src/content_stream.h index f01fef7..1fd0849 100644 --- a/src/content_stream.h +++ b/src/content_stream.h @@ -47,6 +47,7 @@ class ContentReader { std::shared_ptr reader; std::deque buf; + uint32_t matched_idx = 0; }; class ContentWriter { diff --git a/src/content_stream_test.cpp b/src/content_stream_test.cpp index 8742333..80939a8 100644 --- a/src/content_stream_test.cpp +++ b/src/content_stream_test.cpp @@ -81,3 +81,19 @@ TEST(ContentStreamTest, ShortRead) { ASSERT_EQ(cs.read(), "Content payload number three"); ASSERT_EQ(cs.read(), ""); } + +TEST(ContentStreamTest, PartialReadAndParse) { + auto sb = std::make_shared(); + dap::ContentReader cs(sb); + sb->write("Content"); + ASSERT_EQ(cs.read(), ""); + sb->write("-Length: "); + ASSERT_EQ(cs.read(), ""); + sb->write("26"); + ASSERT_EQ(cs.read(), ""); + sb->write("\r\n\r\n"); + ASSERT_EQ(cs.read(), ""); + sb->write("Content payload number one"); + ASSERT_EQ(cs.read(), "Content payload number one"); + ASSERT_EQ(cs.read(), ""); +}