Commit 607dbbf5 authored by Kenton Varda's avatar Kenton Varda

Merge pull request #258 from kamalmarhubi/json-decodeRaw

Implement most of JsonCodec::decodeRaw
parents e93c9aca f60fe3c2
...@@ -11,6 +11,7 @@ Bryan Borham <bjboreham@gmail.com>: Initial MSVC support ...@@ -11,6 +11,7 @@ Bryan Borham <bjboreham@gmail.com>: Initial MSVC support
Philip Quinn <p@partylemon.com>: cmake build and other assorted bits Philip Quinn <p@partylemon.com>: cmake build and other assorted bits
Brian Taylor <el.wubo@gmail.com>: emacs syntax highlighting Brian Taylor <el.wubo@gmail.com>: emacs syntax highlighting
Ben Laurie <ben@links.org>: discovered and responsibly disclosed security bugs Ben Laurie <ben@links.org>: discovered and responsibly disclosed security bugs
Kamal Marhubi <kamal@marhubi.com>: JSON parser
This file does not list people who maintain their own Cap'n Proto This file does not list people who maintain their own Cap'n Proto
implementations as separate projects. Those people are awesome too! :) implementations as separate projects. Those people are awesome too! :)
...@@ -21,7 +21,9 @@ ...@@ -21,7 +21,9 @@
#include "json.h" #include "json.h"
#include <capnp/test-util.h> #include <capnp/test-util.h>
#include <capnp/compat/json.capnp.h>
#include <kj/debug.h> #include <kj/debug.h>
#include <kj/string.h>
#include <kj/test.h> #include <kj/test.h>
namespace capnp { namespace capnp {
...@@ -181,6 +183,293 @@ KJ_TEST("encode union") { ...@@ -181,6 +183,293 @@ KJ_TEST("encode union") {
KJ_EXPECT(json.encode(root) == "{\"before\":\"a\",\"middle\":44,\"bar\":321,\"after\":\"c\"}"); KJ_EXPECT(json.encode(root) == "{\"before\":\"a\",\"middle\":44,\"bar\":321,\"after\":\"c\"}");
} }
KJ_TEST("basic json decoding") {
// TODO(cleanup): this test is a mess!
JsonCodec json;
{
MallocMessageBuilder message;
auto root = message.initRoot<JsonValue>();
json.decodeRaw("null", root);
KJ_EXPECT(root.which() == JsonValue::NULL_);
KJ_EXPECT(root.getNull() == VOID);
}
{
MallocMessageBuilder message;
auto root = message.initRoot<JsonValue>();
json.decodeRaw("false", root);
KJ_EXPECT(root.which() == JsonValue::BOOLEAN);
KJ_EXPECT(root.getBoolean() == false);
}
{
MallocMessageBuilder message;
auto root = message.initRoot<JsonValue>();
json.decodeRaw("true", root);
KJ_EXPECT(root.which() == JsonValue::BOOLEAN);
KJ_EXPECT(root.getBoolean() == true);
}
{
MallocMessageBuilder message;
auto root = message.initRoot<JsonValue>();
json.decodeRaw("\"foo\"", root);
KJ_EXPECT(root.which() == JsonValue::STRING);
KJ_EXPECT(kj::str("foo") == root.getString());
}
{
MallocMessageBuilder message;
auto root = message.initRoot<JsonValue>();
json.decodeRaw(R"("\"")", root);
KJ_EXPECT(root.which() == JsonValue::STRING);
KJ_EXPECT(kj::str("\"") == root.getString());
}
{
MallocMessageBuilder message;
auto root = message.initRoot<JsonValue>();
json.decodeRaw(R"("\\abc\"d\\e")", root);
KJ_EXPECT(root.which() == JsonValue::STRING);
KJ_EXPECT(kj::str("\\abc\"d\\e") == root.getString());
}
{
MallocMessageBuilder message;
auto root = message.initRoot<JsonValue>();
json.decodeRaw(R"("\"\\\/\b\f\n\r\t\u0003abc\u0064\u0065f")", root);
KJ_EXPECT(root.which() == JsonValue::STRING);
KJ_EXPECT(kj::str("\"\\/\b\f\n\r\t\x03""abcdef") == root.getString(), root.getString());
}
{
MallocMessageBuilder message;
auto root = message.initRoot<JsonValue>();
json.decodeRaw("[]", root);
KJ_EXPECT(root.which() == JsonValue::ARRAY, root.which());
KJ_EXPECT(root.getArray().size() == 0);
}
{
MallocMessageBuilder message;
auto root = message.initRoot<JsonValue>();
json.decodeRaw("[true]", root);
KJ_EXPECT(root.which() == JsonValue::ARRAY);
auto array = root.getArray();
KJ_EXPECT(array.size() == 1, array.size());
KJ_EXPECT(root.getArray()[0].which() == JsonValue::BOOLEAN);
KJ_EXPECT(root.getArray()[0].getBoolean() == true);
}
{
MallocMessageBuilder message;
auto root = message.initRoot<JsonValue>();
json.decodeRaw(" [ true , false\t\n , null]", root);
KJ_EXPECT(root.which() == JsonValue::ARRAY);
auto array = root.getArray();
KJ_EXPECT(array.size() == 3);
KJ_EXPECT(array[0].which() == JsonValue::BOOLEAN);
KJ_EXPECT(array[0].getBoolean() == true);
KJ_EXPECT(array[1].which() == JsonValue::BOOLEAN);
KJ_EXPECT(array[1].getBoolean() == false);
KJ_EXPECT(array[2].which() == JsonValue::NULL_);
KJ_EXPECT(array[2].getNull() == VOID);
}
{
MallocMessageBuilder message;
auto root = message.initRoot<JsonValue>();
json.decodeRaw("{}", root);
KJ_EXPECT(root.which() == JsonValue::OBJECT, root.which());
KJ_EXPECT(root.getObject().size() == 0);
}
{
MallocMessageBuilder message;
auto root = message.initRoot<JsonValue>();
json.decodeRaw(R"({"some": null})", root);
KJ_EXPECT(root.which() == JsonValue::OBJECT, root.which());
auto object = root.getObject();
KJ_EXPECT(object.size() == 1);
KJ_EXPECT(kj::str("some") == object[0].getName());
KJ_EXPECT(object[0].getValue().which() == JsonValue::NULL_);
}
{
MallocMessageBuilder message;
auto root = message.initRoot<JsonValue>();
json.decodeRaw(R"({"foo\n\tbaz": "a val", "bar": ["a", -5.5e11, { "z": {}}]})", root);
KJ_EXPECT(root.which() == JsonValue::OBJECT, root.which());
auto object = root.getObject();
KJ_EXPECT(object.size() == 2);
KJ_EXPECT(kj::str("foo\n\tbaz") == object[0].getName());
KJ_EXPECT(object[0].getValue().which() == JsonValue::STRING);
KJ_EXPECT(kj::str("a val") == object[0].getValue().getString());
KJ_EXPECT(kj::str("bar") == object[1].getName());
KJ_EXPECT(object[1].getValue().which() == JsonValue::ARRAY);
auto array = object[1].getValue().getArray();
KJ_EXPECT(array.size() == 3, array.size());
KJ_EXPECT(array[0].which() == JsonValue::STRING);
KJ_EXPECT(kj::str("a") == array[0].getString());
KJ_EXPECT(array[1].which() == JsonValue::NUMBER);
KJ_EXPECT(array[1].getNumber() == -5.5e11);
KJ_EXPECT(array[2].which() == JsonValue::OBJECT);
KJ_EXPECT(array[2].getObject().size() == 1);
KJ_EXPECT(array[2].getObject()[0].getValue().which() == JsonValue::OBJECT);
KJ_EXPECT(array[2].getObject()[0].getValue().getObject().size() == 0);
}
{
MallocMessageBuilder message;
auto root = message.initRoot<JsonValue>();
json.decodeRaw("123", root);
KJ_EXPECT(root.which() == JsonValue::NUMBER);
KJ_EXPECT(root.getNumber() == 123);
}
{
MallocMessageBuilder message;
auto root = message.initRoot<JsonValue>();
KJ_EXPECT_THROW_MESSAGE("input", json.decodeRaw("z", root));
}
{
MallocMessageBuilder message;
auto root = message.initRoot<JsonValue>();
// Leading + not allowed in numbers.
KJ_EXPECT_THROW_MESSAGE("Unexpected", json.decodeRaw("+123", root));
}
{
MallocMessageBuilder message;
auto root = message.initRoot<JsonValue>();
KJ_EXPECT_THROW_MESSAGE("Overflow", json.decodeRaw("1e1024", root));
}
{
MallocMessageBuilder message;
auto root = message.initRoot<JsonValue>();
KJ_EXPECT_THROW_MESSAGE("Underflow", json.decodeRaw("1e-1023", root));
}
{
MallocMessageBuilder message;
auto root = message.initRoot<JsonValue>();
KJ_EXPECT_THROW_MESSAGE("Unexpected", json.decodeRaw("[00]", root));
KJ_EXPECT_THROW_MESSAGE("Unexpected", json.decodeRaw("[01]", root));
}
{
MallocMessageBuilder message;
auto root = message.initRoot<JsonValue>();
KJ_EXPECT_THROW_MESSAGE("ends prematurely", json.decodeRaw("-", root));
}
{
MallocMessageBuilder message;
auto root = message.initRoot<JsonValue>();
json.decodeRaw("-5", root);
KJ_EXPECT(root.which() == JsonValue::NUMBER);
KJ_EXPECT(root.getNumber() == -5);
}
{
MallocMessageBuilder message;
auto root = message.initRoot<JsonValue>();
json.decodeRaw("-5.5", root);
KJ_EXPECT(root.which() == JsonValue::NUMBER);
KJ_EXPECT(root.getNumber() == -5.5);
}
{
MallocMessageBuilder message;
auto root = message.initRoot<JsonValue>();
KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("a", root));
KJ_EXPECT_THROW_MESSAGE("ends prematurely", json.decodeRaw("[", root));
KJ_EXPECT_THROW_MESSAGE("ends prematurely", json.decodeRaw("{", root));
KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("[}", root));
KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("{]", root));
KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("[}]", root));
KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("[1, , ]", root));
KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("[,]", root));
KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("[true,]", root));
KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("[, 1]", root));
KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("[1\"\"]", root));
KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("[1,, \"\"]", root));
KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("{\"a\"1: 0}", root));
KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw(R"({"some": null,})", root));
KJ_EXPECT_THROW_MESSAGE("Input remains", json.decodeRaw("11a", root));
KJ_EXPECT_THROW_MESSAGE("Invalid escape", json.decodeRaw(R"("\z")", root));
KJ_EXPECT_THROW_MESSAGE("Invalid escape", json.decodeRaw(R"("\z")", root));
KJ_EXPECT_THROW_MESSAGE("ends prematurely", json.decodeRaw(R"(["\n\", 3])", root));
KJ_EXPECT_THROW_MESSAGE("Invalid hex", json.decodeRaw(R"("\u12zz")", root));
KJ_EXPECT_THROW_MESSAGE("ends prematurely", json.decodeRaw("-", root));
KJ_EXPECT_THROW_MESSAGE("Unexpected input", json.decodeRaw("--", root));
}
}
KJ_TEST("maximum nesting depth") {
JsonCodec json;
auto input = kj::str(R"({"foo": "a", "bar": ["b", { "baz": [-5.5e11] }, [ [ 1 ], { "z": 2 }]]})");
// `input` has a maximum nesting depth of 4, reached 3 times.
{
MallocMessageBuilder message;
auto root = message.initRoot<JsonValue>();
json.decodeRaw(input, root);
}
{
json.setMaxNestingDepth(0);
MallocMessageBuilder message;
auto root = message.initRoot<JsonValue>();
KJ_EXPECT_THROW_MESSAGE("nest",
json.decodeRaw(input, root));
}
{
json.setMaxNestingDepth(3);
MallocMessageBuilder message;
auto root = message.initRoot<JsonValue>();
KJ_EXPECT_THROW_MESSAGE("nest",
json.decodeRaw(input, root));
}
{
json.setMaxNestingDepth(4);
MallocMessageBuilder message;
auto root = message.initRoot<JsonValue>();
json.decodeRaw(input, root);
}
}
class TestHandler: public JsonCodec::Handler<Text> { class TestHandler: public JsonCodec::Handler<Text> {
public: public:
void encode(const JsonCodec& codec, Text::Reader input, void encode(const JsonCodec& codec, Text::Reader input,
......
This diff is collapsed.
...@@ -71,6 +71,10 @@ public: ...@@ -71,6 +71,10 @@ public:
// Enable to insert newlines, indentation, and other extra spacing into the output. The default // Enable to insert newlines, indentation, and other extra spacing into the output. The default
// is to use minimal whitespace. // is to use minimal whitespace.
void setMaxNestingDepth(size_t maxNestingDepth);
// Set maximum nesting depth when decoding JSON to prevent highly nested input from overflowing
// the call stack. The default is 64.
template <typename T> template <typename T>
kj::String encode(T&& value); kj::String encode(T&& value);
// Encode any Cap'n Proto value to JSON, including primitives and // Encode any Cap'n Proto value to JSON, including primitives and
......
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