diff --git a/trunk/doc/CHANGELOG.md b/trunk/doc/CHANGELOG.md index c2665d786..a89078de2 100644 --- a/trunk/doc/CHANGELOG.md +++ b/trunk/doc/CHANGELOG.md @@ -8,6 +8,7 @@ The changelog for SRS. ## SRS 4.0 Changelog +* v4.0, 2021-10-20, Merge [#1758](https://github.com/ossrs/srs/pull/1758): JSON: Support escape special chars. v4.0.182 * v4.0, 2021-10-19, Merge [#1754](https://github.com/ossrs/srs/pull/1754): RTMP: If port is explicity set to 0, use default 1935. v4.0.181 * v4.0, 2021-10-18, Merge [#2670](https://github.com/ossrs/srs/pull/2670): SRT: Solve mpegts demux assert bug. v4.0.180 * v4.0, 2021-10-16, Merge [#2665](https://github.com/ossrs/srs/pull/2665): API: Fix the same 'client_id' error when asynchronous call. v4.0.179 diff --git a/trunk/src/core/srs_core_version4.hpp b/trunk/src/core/srs_core_version4.hpp index 541d651f9..43cd0562b 100644 --- a/trunk/src/core/srs_core_version4.hpp +++ b/trunk/src/core/srs_core_version4.hpp @@ -9,6 +9,6 @@ #define VERSION_MAJOR 4 #define VERSION_MINOR 0 -#define VERSION_REVISION 181 +#define VERSION_REVISION 182 #endif diff --git a/trunk/src/protocol/srs_protocol_json.cpp b/trunk/src/protocol/srs_protocol_json.cpp index a6e91b8eb..c715be31d 100644 --- a/trunk/src/protocol/srs_protocol_json.cpp +++ b/trunk/src/protocol/srs_protocol_json.cpp @@ -1538,17 +1538,31 @@ SrsJsonArray* SrsJsonAny::to_array() return p; } -string escape(string v) +// @see https://github.com/ossrs/srs/pull/1758/files#diff-9568479ef5cb0aa1ade2381e11e9c066c01bf9c4bbed70ffa27094d08bb27380R370 +// @see https://github.com/json-parser/json-builder/blob/2d8c6671926d104c5dcd43ccd2b1431a3f0299e0/json-builder.c#L495 +string json_serialize_string(const string& v) { stringstream ss; - for (int i = 0; i < (int)v.length(); i++) { - if (v.at(i) == '"') { - ss << '\\'; + ss << "\""; + + const char* start = v.data(); + const char* end = start + v.length(); + for (const char* p = start; p < end; ++p) { + switch (*p) { + case '"': ss << '\\' << '"'; break; + case '\\': ss << '\\' << '\\'; 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 << *p; } - ss << v.at(i); } + ss << "\""; + return ss.str(); } @@ -1556,7 +1570,9 @@ string SrsJsonAny::dumps() { switch (marker) { case SRS_JSON_String: { - return "\"" + escape(to_str()) + "\""; + SrsJsonString* p = dynamic_cast(this); + srs_assert(p != NULL); + return json_serialize_string(p->value); } case SRS_JSON_Boolean: { return to_boolean()? "true" : "false"; diff --git a/trunk/src/utest/srs_utest_amf0.cpp b/trunk/src/utest/srs_utest_amf0.cpp index aa36d574e..e01b8e856 100644 --- a/trunk/src/utest/srs_utest_amf0.cpp +++ b/trunk/src/utest/srs_utest_amf0.cpp @@ -2626,6 +2626,72 @@ VOID TEST(ProtocolJSONTest, Dumps) } } +VOID TEST(ProtocolJSONTest, DumpsSpecialChars) +{ + if (true) { + SrsJsonAny* p = SrsJsonAny::str("hello"); + EXPECT_TRUE(p->is_string()); + EXPECT_STREQ("\"hello\"", p->dumps().c_str()); + srs_freep(p); + } + + if (true) { + SrsJsonAny* p = SrsJsonAny::str("he\"llo"); + EXPECT_TRUE(p->is_string()); + EXPECT_STREQ("\"he\\\"llo\"", p->dumps().c_str()); + srs_freep(p); + } + + if (true) { + SrsJsonAny* p = SrsJsonAny::str("he\\llo"); + EXPECT_TRUE(p->is_string()); + EXPECT_STREQ("\"he\\\\llo\"", p->dumps().c_str()); + srs_freep(p); + } + + if (true) { + SrsJsonAny* p = SrsJsonAny::str("he\nllo"); + EXPECT_TRUE(p->is_string()); + EXPECT_STREQ("\"he\\nllo\"", p->dumps().c_str()); + srs_freep(p); + } + + if (true) { + SrsJsonAny* p = SrsJsonAny::str("he\tllo"); + EXPECT_TRUE(p->is_string()); + EXPECT_STREQ("\"he\\tllo\"", p->dumps().c_str()); + srs_freep(p); + } + + if (true) { + SrsJsonAny* p = SrsJsonAny::str("he\bllo"); + EXPECT_TRUE(p->is_string()); + EXPECT_STREQ("\"he\\bllo\"", p->dumps().c_str()); + srs_freep(p); + } + + if (true) { + SrsJsonAny* p = SrsJsonAny::str("he\fllo"); + EXPECT_TRUE(p->is_string()); + EXPECT_STREQ("\"he\\fllo\"", p->dumps().c_str()); + srs_freep(p); + } + + if (true) { + SrsJsonAny* p = SrsJsonAny::str("he\rllo"); + EXPECT_TRUE(p->is_string()); + EXPECT_STREQ("\"he\\rllo\"", p->dumps().c_str()); + srs_freep(p); + } + + if (true) { + SrsJsonAny* p = SrsJsonAny::str("hello视频"); + EXPECT_TRUE(p->is_string()); + EXPECT_STREQ("\"hello\xE8\xA7\x86\xE9\xA2\x91\"", p->dumps().c_str()); + srs_freep(p); + } +} + VOID TEST(ProtocolJSONTest, Parse) { if (true) {