[c++] Use RapidJSON's iterative parser

Use RapidJSON's iterative parser to handle deeply nested JSON data
without causing a stack overflow.

Added a unit test with a deeply nested JSON array to test this.
This commit is contained in:
Christopher Warrington 2017-09-08 15:14:23 -07:00 коммит произвёл Chad Walters
Родитель 04a5cb5662
Коммит e2f74ee413
3 изменённых файлов: 43 добавлений и 12 удалений

Просмотреть файл

@ -31,6 +31,8 @@ different versioning scheme, following the Haskell community's
used as a template" have been fixed.
[Issue #538](https://github.com/Microsoft/bond/issues/538)
* Guard against overflows in OutputMemoryStream, blob, and SimpleArray.
* Use RapidJSON's iterative parser to handle deeply nested JSON data without
causing a stack overflow.
### C# ###

Просмотреть файл

@ -5,6 +5,8 @@
#include "encoding.h"
#include "detail/rapidjson_helper.h"
#include "rapidjson/document.h"
#include "rapidjson/reader.h"
#include <boost/call_traits.hpp>
#include <boost/make_shared.hpp>
@ -21,7 +23,7 @@ class SimpleJsonReader
{
public:
typedef BufferT Buffer;
typedef DOMParser<SimpleJsonReader&> Parser;
typedef DOMParser<SimpleJsonReader&> Parser;
typedef SimpleJsonWriter<Buffer> Writer;
typedef rapidjson::Value Field;
@ -49,7 +51,7 @@ public:
_document(that._document),
_value(that._value)
{}
bool ReadVersion()
{
return false;
@ -60,24 +62,27 @@ public:
// Don't need to reparse for nested fields
if (!_value || _value == _document.get())
{
_document->ParseStream<rapidjson::kParseStopWhenDoneFlag>(_stream);
const unsigned parseFlags = rapidjson::kParseIterativeFlag | rapidjson::kParseStopWhenDoneFlag;
_document->ParseStream<parseFlags>(_stream);
BOOST_ASSERT(!_document->HasParseError());
_value = _document.get();
}
}
const Field* FindField(uint16_t id, const Metadata& metadata, BondDataType type)
{
// BT_INT32 may be an enum. This allows us to decode symbolic enum values
// when parsing using runtime schema. The assumption is that runtime schema
// matches JSON payload. If it doesn't, nothing horrible will happen, but
// we might not indicate a required field missing for an int32 field if we
// we might not indicate a required field missing for an int32 field if we
// mistake a string member with matching name for it.
return FindField(id, metadata, type, type == BT_INT32);
}
const Field* FindField(uint16_t id, const Metadata& metadata, BondDataType type, bool is_enum);
template <typename T>
void Read(T& var)
{
@ -103,7 +108,7 @@ public:
void Skip(const T&)
{}
bool operator==(const SimpleJsonReader& rhs) const
{
return _value == rhs._value;
@ -122,7 +127,7 @@ public:
{
return _input;
}
private:
rapidjson::Value::ConstMemberIterator MemberBegin() const
{

Просмотреть файл

@ -1,5 +1,7 @@
#include "precompiled.h"
#include "json_tests.h"
#include <boost/format.hpp>
#include <locale>
#include <stdarg.h>
@ -187,10 +189,8 @@ TEST_CASE_END
template <uint16_t N, typename Reader, typename Writer>
void StringTests(const char* name)
void StringTests(UnitTestSuite& suite)
{
UnitTestSuite suite(name);
AddTestCase<TEST_ID(N),
StringRoundtripTest, Reader, Writer>(suite, "Roundtrip string/wstring");
@ -199,14 +199,38 @@ void StringTests(const char* name)
}
TEST_CASE_BEGIN(DeepNesting)
{
const size_t nestingDepth = 10000;
std::string listOpens(nestingDepth, '[');
std::string listCloses(nestingDepth, ']');
std::string deeplyNestedList = boost::str(
boost::format("{\"deeplyNestedList\": %strue%s}") % listOpens % listCloses);
bond::SimpleJsonReader<const char*> json_reader(deeplyNestedList.c_str());
// The type here doesn't really matter. We need something with no
// required fields, as we're really just testing that we can parse a
// deeply nested JSON array without crashing.
SimpleStruct to;
bond::Deserialize(json_reader, to);
}
TEST_CASE_END
void JSONTest::Initialize()
{
UnitTestSuite suite("Simple JSON test");
TEST_SIMPLE_JSON_PROTOCOL(
StringTests<
0x1c04,
bond::SimpleJsonReader<bond::InputBuffer>,
bond::SimpleJsonWriter<bond::OutputBuffer> >("Simple JSON test");
bond::SimpleJsonWriter<bond::OutputBuffer> >(suite);
);
AddTestCase<TEST_ID(0x1c05), DeepNesting>(suite, "Deeply nested JSON struct");
}
bool init_unit_test()