Commit 3b092685 authored by Jelte Jansen's avatar Jelte Jansen
Browse files

[master] Merge branch 'trac1626'

parents daf2b85f 9ee14145
......@@ -30,6 +30,10 @@
using namespace std;
namespace {
const char* WHITESPACE = " \b\f\n\r\t";
} // end anonymous namespace
namespace isc {
namespace data {
......@@ -314,15 +318,49 @@ str_from_stringstream(std::istream &in, const std::string& file, const int line,
} else {
throwJSONError("String expected", file, line, pos);
}
while (c != EOF && c != '"') {
ss << c;
if (c == '\\' && in.peek() == '"') {
ss << in.get();
if (c == '\\') {
// see the spec for allowed escape characters
switch (in.peek()) {
case '"':
c = '"';
break;
case '/':
c = '/';
break;
case '\\':
c = '\\';
break;
case 'b':
c = '\b';
break;
case 'f':
c = '\f';
break;
case 'n':
c = '\n';
break;
case 'r':
c = '\r';
break;
case 't':
c = '\t';
break;
default:
throwJSONError("Bad escape", file, line, pos);
}
// drop the escaped char
in.get();
++pos;
}
ss << c;
c = in.get();
++pos;
}
if (c == EOF) {
throwJSONError("Unterminated string", file, line, pos);
}
return (ss.str());
}
......@@ -427,12 +465,12 @@ from_stringstream_list(std::istream &in, const std::string& file, int& line,
ElementPtr list = Element::createList();
ConstElementPtr cur_list_element;
skip_chars(in, " \t\n", line, pos);
skip_chars(in, WHITESPACE, line, pos);
while (c != EOF && c != ']') {
if (in.peek() != ']') {
cur_list_element = Element::fromJSON(in, file, line, pos);
list->add(cur_list_element);
skip_to(in, file, line, pos, ",]", " \t\n");
skip_to(in, file, line, pos, ",]", WHITESPACE);
}
c = in.get();
pos++;
......@@ -445,7 +483,7 @@ from_stringstream_map(std::istream &in, const std::string& file, int& line,
int& pos)
{
ElementPtr map = Element::createMap();
skip_chars(in, " \t\n", line, pos);
skip_chars(in, WHITESPACE, line, pos);
char c = in.peek();
if (c == EOF) {
throwJSONError(std::string("Unterminated map, <string> or } expected"), file, line, pos);
......@@ -456,7 +494,7 @@ from_stringstream_map(std::istream &in, const std::string& file, int& line,
while (c != EOF && c != '}') {
std::string key = str_from_stringstream(in, file, line, pos);
skip_to(in, file, line, pos, ":", " \t\n");
skip_to(in, file, line, pos, ":", WHITESPACE);
// skip the :
in.get();
pos++;
......@@ -464,7 +502,7 @@ from_stringstream_map(std::istream &in, const std::string& file, int& line,
ConstElementPtr value = Element::fromJSON(in, file, line, pos);
map->set(key, value);
skip_to(in, file, line, pos, ",}", " \t\n");
skip_to(in, file, line, pos, ",}", WHITESPACE);
c = in.get();
pos++;
}
......@@ -543,7 +581,7 @@ Element::fromJSON(std::istream &in, const std::string& file, int& line,
char c = 0;
ElementPtr element;
bool el_read = false;
skip_chars(in, " \n\t", line, pos);
skip_chars(in, WHITESPACE, line, pos);
while (c != EOF && !el_read) {
c = in.get();
pos++;
......@@ -610,7 +648,14 @@ ElementPtr
Element::fromJSON(const std::string &in) {
std::stringstream ss;
ss << in;
return (fromJSON(ss, "<string>"));
int line = 1, pos = 1;
ElementPtr result(fromJSON(ss, "<string>", line, pos));
skip_chars(ss, WHITESPACE, line, pos);
// ss must now be at end
if (ss.peek() != EOF) {
throwJSONError("Extra data", "<string>", line, pos);
}
return result;
}
// to JSON format
......@@ -642,7 +687,39 @@ NullElement::toJSON(std::ostream& ss) const {
void
StringElement::toJSON(std::ostream& ss) const {
ss << "\"";
ss << stringValue();
char c;
const std::string& str = stringValue();
for (size_t i = 0; i < str.size(); ++i) {
c = str[i];
// Escape characters as defined in JSON spec
// Note that we do not escape forward slash; this
// is allowed, but not mandatory.
switch (c) {
case '"':
ss << '\\' << c;
break;
case '\\':
ss << '\\' << c;
break;
case '\b':
ss << '\\' << 'b';
break;
case '\f':
ss << '\\' << 'f';
break;
case '\n':
ss << '\\' << 'n';
break;
case '\r':
ss << '\\' << 'r';
break;
case '\t':
ss << '\\' << 't';
break;
default:
ss << c;
}
}
ss << "\"";
}
......
......@@ -20,6 +20,7 @@
using namespace isc::data;
#include <sstream>
#include <iostream>
using std::oct;
#include <iomanip>
......@@ -90,7 +91,7 @@ TEST(Element, from_and_to_json) {
sv.push_back("-1");
sv.push_back("-1.234");
sv.push_back("-123.456");
BOOST_FOREACH(const std::string& s, sv) {
// test << operator, which uses Element::str()
std::ostringstream stream;
......@@ -122,8 +123,16 @@ TEST(Element, from_and_to_json) {
sv.push_back("{ \"a\": None}");
sv.push_back("");
sv.push_back("nul");
sv.push_back("hello\"foobar\"");
sv.push_back("\"foobar\"hello");
sv.push_back("[]hello");
sv.push_back("{}hello");
// String not delimited correctly
sv.push_back("\"hello");
sv.push_back("hello\"");
BOOST_FOREACH(std::string s, sv) {
EXPECT_THROW(el = Element::fromJSON(s), isc::data::JSONError);
}
......@@ -150,6 +159,9 @@ TEST(Element, from_and_to_json) {
EXPECT_EQ("false", Element::fromJSON("FALSE")->str());
EXPECT_EQ("true", Element::fromJSON("True")->str());
EXPECT_EQ("true", Element::fromJSON("TRUE")->str());
EXPECT_EQ("\"\"", Element::fromJSON(" \n \t \r \f \b \"\" \n \f \t \r \b")->str());
EXPECT_EQ("{ }", Element::fromJSON("{ \n \r \t \b \f }")->str());
EXPECT_EQ("[ ]", Element::fromJSON("[ \n \r \f \t \b ]")->str());
// number overflows
EXPECT_THROW(Element::fromJSON("12345678901234567890")->str(), JSONError);
......@@ -299,6 +311,43 @@ TEST(Element, create_and_value_throws) {
}
// Helper for escape check; it puts the given string in a StringElement,
// then checks for the following conditions:
// stringValue() must be same as input
// toJSON() output must be escaped
// fromJSON() on the previous output must result in original input
void
escapeHelper(const std::string& input, const std::string& expected) {
StringElement str_element = StringElement(input);
EXPECT_EQ(input, str_element.stringValue());
std::stringstream os;
str_element.toJSON(os);
EXPECT_EQ(expected, os.str());
ElementPtr str_element2 = Element::fromJSON(os.str());
EXPECT_EQ(str_element.stringValue(), str_element2->stringValue());
}
TEST(Element, escape) {
// Test whether quotes are escaped correctly when creating direct
// String elements.
escapeHelper("foo\"bar", "\"foo\\\"bar\"");
escapeHelper("foo\\bar", "\"foo\\\\bar\"");
escapeHelper("foo\bbar", "\"foo\\bbar\"");
escapeHelper("foo\fbar", "\"foo\\fbar\"");
escapeHelper("foo\nbar", "\"foo\\nbar\"");
escapeHelper("foo\rbar", "\"foo\\rbar\"");
escapeHelper("foo\tbar", "\"foo\\tbar\"");
// Bad escapes
EXPECT_THROW(Element::fromJSON("\\a"), JSONError);
EXPECT_THROW(Element::fromJSON("\\"), JSONError);
// Can't have escaped quotes outside strings
EXPECT_THROW(Element::fromJSON("\\\"\\\""), JSONError);
// Inside strings is OK
EXPECT_NO_THROW(Element::fromJSON("\"\\\"\\\"\""));
// A whitespace test
EXPECT_NO_THROW(Element::fromJSON("\" \n \r \t \f \n \n \t\""));
}
TEST(Element, ListElement) {
// this function checks the specific functions for ListElements
ElementPtr el = Element::fromJSON("[ 1, \"bar\", 3 ]");
......
......@@ -321,7 +321,7 @@ class RequestACLTest(unittest.TestCase):
' "from": "192.0.2.0/24"},' +
' {"action": "DROP",' +
' "from": "2001:db8::1"},' +
'] }')
']')
self.assertEqual(ACCEPT, acl.execute(CONTEXT4))
self.assertEqual(REJECT, acl.execute(get_context('192.0.2.2')))
self.assertEqual(DROP, acl.execute(get_context('2001:db8::1')))
......
Supports Markdown
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