From 2632f34e85c7d672f28611c0009c0b37b0b756d9 Mon Sep 17 00:00:00 2001 From: Conor Burgess Date: Tue, 13 Feb 2018 13:15:53 +0000 Subject: [PATCH] Stricter JSON standard conformance (#2153) --- Foundation/include/Poco/UTF8String.h | 8 ++++++-- Foundation/src/JSONString.cpp | 4 ++-- Foundation/src/UTF8String.cpp | 10 +++++----- Foundation/testsuite/src/StringTest.cpp | 4 ++-- Foundation/testsuite/src/UTF8StringTest.cpp | 21 ++++++++++++++++++++ Foundation/testsuite/src/UTF8StringTest.h | 3 +++ JSON/testsuite/src/JSONTest.cpp | 22 ++++++++++++++++++++- openssl | 2 +- 8 files changed, 61 insertions(+), 13 deletions(-) diff --git a/Foundation/include/Poco/UTF8String.h b/Foundation/include/Poco/UTF8String.h index feaf89552..73898c65c 100644 --- a/Foundation/include/Poco/UTF8String.h +++ b/Foundation/include/Poco/UTF8String.h @@ -57,13 +57,17 @@ struct Foundation_API UTF8 /// Remove the UTF-8 Byte Order Mark sequence (0xEF, 0xBB, 0xBF) /// from the beginning of the string, if it's there. - static std::string escape(const std::string& s); + static std::string escape(const std::string& s, bool strictJSON = false); /// Escapes a string. Special characters like tab, backslash, ... are /// escaped. Unicode characters are escaped to \uxxxx. + /// If strictJSON is true, \a and \v will be escaped to \\u0007 and \\u000B + /// instead of \\a and \\v for strict JSON conformance. - static std::string escape(const std::string::const_iterator& begin, const std::string::const_iterator& end); + static std::string escape(const std::string::const_iterator& begin, const std::string::const_iterator& end, bool strictJSON = false); /// Escapes a string. Special characters like tab, backslash, ... are /// escaped. Unicode characters are escaped to \uxxxx. + /// If strictJSON is true, \a and \v will be escaped to \\u0007 and \\u000B + /// instead of \\a and \\v for strict JSON conformance. static std::string unescape(const std::string& s); /// Creates an UTF8 string from a string that contains escaped characters. diff --git a/Foundation/src/JSONString.cpp b/Foundation/src/JSONString.cpp index 71887c5d8..8681d565c 100644 --- a/Foundation/src/JSONString.cpp +++ b/Foundation/src/JSONString.cpp @@ -42,7 +42,7 @@ void writeString(const std::string &value, T& obj, typename WriteFunc::Typ if(wrap) (obj.*write)("\"", 1); if(escapeAllUnicode) { - std::string str = Poco::UTF8::escape(value.begin(), value.end()); + std::string str = Poco::UTF8::escape(value.begin(), value.end(), true); (obj.*write)(str.c_str(), str.size()); } else @@ -52,7 +52,7 @@ void writeString(const std::string &value, T& obj, typename WriteFunc::Typ // Forward slash isn't strictly required by JSON spec, but some parsers expect it if((*it >= 0 && *it <= 31) || (*it == '"') || (*it == '\\') || (*it == '/')) { - std::string str = Poco::UTF8::escape(it, it + 1); + std::string str = Poco::UTF8::escape(it, it + 1, true); (obj.*write)(str.c_str(), str.size()); }else (obj.*write)(&(*it), 1); } diff --git a/Foundation/src/UTF8String.cpp b/Foundation/src/UTF8String.cpp index fb1162a08..e5020695a 100644 --- a/Foundation/src/UTF8String.cpp +++ b/Foundation/src/UTF8String.cpp @@ -172,13 +172,13 @@ void UTF8::removeBOM(std::string& str) } -std::string UTF8::escape(const std::string &s) +std::string UTF8::escape(const std::string &s, bool strictJSON) { - return escape(s.begin(), s.end()); + return escape(s.begin(), s.end(), strictJSON); } -std::string UTF8::escape(const std::string::const_iterator& begin, const std::string::const_iterator& end) +std::string UTF8::escape(const std::string::const_iterator& begin, const std::string::const_iterator& end, bool strictJSON) { static Poco::UInt32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, @@ -208,8 +208,8 @@ std::string UTF8::escape(const std::string::const_iterator& begin, const std::st else if (ch == '\r') result += "\\r"; else if (ch == '\b') result += "\\b"; else if (ch == '\f') result += "\\f"; - else if (ch == '\v') result += "\\v"; - else if (ch == '\a') result += "\\a"; + else if (ch == '\v') result += (strictJSON ? "\\u000B" : "\\v"); + else if (ch == '\a') result += (strictJSON ? "\\u0007" : "\\a"); else if (ch == '\\') result += "\\\\"; else if (ch == '\"') result += "\\\""; else if (ch == '/') result += "\\/"; diff --git a/Foundation/testsuite/src/StringTest.cpp b/Foundation/testsuite/src/StringTest.cpp index 265c12ffb..b5522fec3 100644 --- a/Foundation/testsuite/src/StringTest.cpp +++ b/Foundation/testsuite/src/StringTest.cpp @@ -1086,13 +1086,13 @@ void StringTest::testJSONString() assert (toJSON("\\", false) == "\\\\"); assert (toJSON("\"", false) == "\\\""); assert (toJSON("/", false) == "\\/"); - assert (toJSON("\a", false) == "\\a"); + assert (toJSON("\a", false) == "\\u0007"); assert (toJSON("\b", false) == "\\b"); assert (toJSON("\f", false) == "\\f"); assert (toJSON("\n", false) == "\\n"); assert (toJSON("\r", false) == "\\r"); assert (toJSON("\t", false) == "\\t"); - assert (toJSON("\v", false) == "\\v"); + assert (toJSON("\v", false) == "\\u000B"); assert (toJSON("a", false) == "a"); assert (toJSON("\xD0\x82", 0) == "\xD0\x82"); assert (toJSON("\xD0\x82", Poco::JSON_ESCAPE_UNICODE) == "\\u0402"); diff --git a/Foundation/testsuite/src/UTF8StringTest.cpp b/Foundation/testsuite/src/UTF8StringTest.cpp index 6f072c984..8f65f4e18 100644 --- a/Foundation/testsuite/src/UTF8StringTest.cpp +++ b/Foundation/testsuite/src/UTF8StringTest.cpp @@ -83,6 +83,25 @@ void UTF8StringTest::testTransform() } +void UTF8StringTest::testEscape() +{ + std::string s1("A \t, a \v, and an \a walk into a |, and the barman says \xD0\x82"); + + assert (UTF8::escape(s1) == "A \\t, a \\v, and an \\a walk into a |, and the barman says \\u0402"); + assert (UTF8::escape(s1, true) == "A \\t, a \\u000B, and an \\u0007 walk into a |, and the barman says \\u0402"); +} + + +void UTF8StringTest::testUnescape() +{ + std::string s1("A \\t, a \\u000B, and an \\u0007 walk into a |, and the barman says \\u0402"); + std::string s2("A \\t, a \\v, and an \\a walk into a |, and the barman says \\u0402"); + + assert (UTF8::unescape(s1) == "A \t, a \v, and an \a walk into a |, and the barman says \xD0\x82"); + assert (UTF8::unescape(s2) == "A \t, a \v, and an \a walk into a |, and the barman says \xD0\x82"); +} + + void UTF8StringTest::setUp() { } @@ -99,6 +118,8 @@ CppUnit::Test* UTF8StringTest::suite() CppUnit_addTest(pSuite, UTF8StringTest, testCompare); CppUnit_addTest(pSuite, UTF8StringTest, testTransform); + CppUnit_addTest(pSuite, UTF8StringTest, testEscape); + CppUnit_addTest(pSuite, UTF8StringTest, testUnescape); return pSuite; } diff --git a/Foundation/testsuite/src/UTF8StringTest.h b/Foundation/testsuite/src/UTF8StringTest.h index a2f0facb0..8e065dd85 100644 --- a/Foundation/testsuite/src/UTF8StringTest.h +++ b/Foundation/testsuite/src/UTF8StringTest.h @@ -27,6 +27,9 @@ public: void testCompare(); void testTransform(); + void testEscape(); + void testUnescape(); + void setUp(); void tearDown(); diff --git a/JSON/testsuite/src/JSONTest.cpp b/JSON/testsuite/src/JSONTest.cpp index 1ec9de909..00f4a810a 100644 --- a/JSON/testsuite/src/JSONTest.cpp +++ b/JSON/testsuite/src/JSONTest.cpp @@ -1938,7 +1938,6 @@ void JSONTest::testEscape0() void JSONTest::testNonEscapeUnicode() { - Poco::JSON::Object::Ptr json = new Poco::JSON::Object(); std::string chinese("{ \"name\" : \"\\u4e2d\" }"); Poco::JSON::Parser parser(new Poco::JSON::ParseHandler()); Var result = parser.parse(chinese); @@ -1959,6 +1958,27 @@ void JSONTest::testNonEscapeUnicode() object = result.extract(); ss.str(""); object->stringify(ss); assert (ss.str() == "{\"name\":\"g\xC3\xBCnter\"}"); + + Poco::JSON::Object obj1; + std::string shortEscapeStr("String with \t"); + std::string longEscapeStr("String with \a and \v plus \t for good measure"); + obj1.set("shortEscape", shortEscapeStr); + obj1.set("longEscape", longEscapeStr); + + ss.str(""); + obj1.stringify(ss); + + parser.reset(); + parser.parse(ss.str()); + result = parser.asVar(); + + assert(result.type() == typeid(Object::Ptr)); + + object = result.extract(); + Var shortEscape = object->get("shortEscape"); + Var longEscape = object->get("longEscape"); + assert(shortEscape.convert() == shortEscapeStr); + assert(longEscape.convert() == longEscapeStr); } diff --git a/openssl b/openssl index b42dcf217..26b1673ca 160000 --- a/openssl +++ b/openssl @@ -1 +1 @@ -Subproject commit b42dcf2175f30e0c21dc9a684f0b9670f4ed09c8 +Subproject commit 26b1673caad94a702b6d694f48f917a283b30777