mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
support query parsing and escape
This commit is contained in:
parent
dc7124cd05
commit
b38f30c3ee
6 changed files with 468 additions and 7 deletions
|
@ -2993,7 +2993,7 @@ srs_error_t SrsRtcConnection::negotiate_play_capability(SrsRequest* req, const S
|
|||
track->media_->pt_ = remote_payload.payload_type_;
|
||||
|
||||
vector<SrsMediaPayloadType> red_pts = remote_media_desc.find_media_with_encoding_name("red");
|
||||
if (!red_pts.empty() && !track->red_) {
|
||||
if (!red_pts.empty() && track->red_) {
|
||||
SrsMediaPayloadType red_pt = red_pts.at(0);
|
||||
|
||||
track->red_->pt_of_publisher_ = track->red_->pt_;
|
||||
|
|
|
@ -287,6 +287,7 @@
|
|||
#define ERROR_INOTIFY_CREATE 3092
|
||||
#define ERROR_INOTIFY_OPENFD 3093
|
||||
#define ERROR_INOTIFY_WATCH 3094
|
||||
#define ERROR_HTTP_URL_UNESCAPE 3095
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// HTTP/StreamCaster protocol error.
|
||||
|
|
|
@ -1056,9 +1056,6 @@ srs_error_t srs_av_base64_encode(std::string plaintext, std::string& cipher)
|
|||
uint8_t* p = (uint8_t*)plaintext.c_str();
|
||||
while(si < n) {
|
||||
// Convert 3x 8bit source bytes into 4 bytes
|
||||
uint32_t v1 = uint32_t(p[si+0]) << 16;
|
||||
uint32_t v2 = uint32_t(p[si+1]) << 8;
|
||||
uint32_t v3 = uint32_t(p[si+2]);
|
||||
val = (uint32_t(p[si + 0]) << 16) | (uint32_t(p[si + 1])<< 8) | uint32_t(p[si + 2]);
|
||||
|
||||
cipher += encoder[val>>18&0x3f];
|
||||
|
|
|
@ -36,6 +36,7 @@ using namespace std;
|
|||
#include <srs_kernel_file.hpp>
|
||||
#include <srs_protocol_json.hpp>
|
||||
#include <srs_core_autofree.hpp>
|
||||
#include <srs_protocol_utility.hpp>
|
||||
|
||||
#define SRS_HTTP_DEFAULT_PAGE "index.html"
|
||||
|
||||
|
@ -898,8 +899,6 @@ SrsHttpUri::~SrsHttpUri()
|
|||
|
||||
srs_error_t SrsHttpUri::initialize(string _url)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
schema = host = path = query = "";
|
||||
|
||||
url = _url;
|
||||
|
@ -944,7 +943,7 @@ srs_error_t SrsHttpUri::initialize(string _url)
|
|||
username_ = username_.substr(0, pos);
|
||||
}
|
||||
|
||||
return err;
|
||||
return parse_query();
|
||||
}
|
||||
|
||||
void SrsHttpUri::set_schema(std::string v)
|
||||
|
@ -988,6 +987,15 @@ string SrsHttpUri::get_query()
|
|||
return query;
|
||||
}
|
||||
|
||||
string SrsHttpUri::get_query_by_key(std::string key)
|
||||
{
|
||||
map<string, string>::iterator it = query_values_.find(key);
|
||||
if(it == query_values_.end()) {
|
||||
return "";
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
std::string SrsHttpUri::username()
|
||||
{
|
||||
return username_;
|
||||
|
@ -1013,6 +1021,331 @@ string SrsHttpUri::get_uri_field(string uri, void* php_u, int ifield)
|
|||
return uri.substr(offset, len);
|
||||
}
|
||||
|
||||
srs_error_t SrsHttpUri::parse_query()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
if(query.empty()) {
|
||||
return err;
|
||||
}
|
||||
|
||||
size_t begin = query.find("?");
|
||||
if(string::npos != begin) {
|
||||
begin++;
|
||||
} else {
|
||||
begin = 0;
|
||||
}
|
||||
string query_str = query.substr(begin);
|
||||
query_values_.clear();
|
||||
srs_parse_query_string(query_str, query_values_);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
// @see golang net/url/url.go
|
||||
namespace {
|
||||
enum EncodeMode {
|
||||
encodePath,
|
||||
encodePathSegment,
|
||||
encodeHost,
|
||||
encodeZone,
|
||||
encodeUserPassword,
|
||||
encodeQueryComponent,
|
||||
encodeFragment,
|
||||
};
|
||||
|
||||
bool should_escape(uint8_t c, EncodeMode mode) {
|
||||
// §2.3 Unreserved characters (alphanum)
|
||||
if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(encodeHost == mode || encodeZone == mode) {
|
||||
// §3.2.2 Host allows
|
||||
// sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
|
||||
// as part of reg-name.
|
||||
// We add : because we include :port as part of host.
|
||||
// We add [ ] because we include [ipv6]:port as part of host.
|
||||
// We add < > because they're the only characters left that
|
||||
// we could possibly allow, and Parse will reject them if we
|
||||
// escape them (because hosts can't use %-encoding for
|
||||
// ASCII bytes).
|
||||
switch(c) {
|
||||
case '!':
|
||||
case '$':
|
||||
case '&':
|
||||
case '\'':
|
||||
case '(':
|
||||
case ')':
|
||||
case '*':
|
||||
case '+':
|
||||
case ',':
|
||||
case ';':
|
||||
case '=':
|
||||
case ':':
|
||||
case '[':
|
||||
case ']':
|
||||
case '<':
|
||||
case '>':
|
||||
case '"':
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
switch(c) {
|
||||
case '-':
|
||||
case '_':
|
||||
case '.':
|
||||
case '~': // §2.3 Unreserved characters (mark)
|
||||
return false;
|
||||
case '$':
|
||||
case '&':
|
||||
case '+':
|
||||
case ',':
|
||||
case '/':
|
||||
case ':':
|
||||
case ';':
|
||||
case '=':
|
||||
case '?':
|
||||
case '@': // §2.2 Reserved characters (reserved)
|
||||
// Different sections of the URL allow a few of
|
||||
// the reserved characters to appear unescaped.
|
||||
switch (mode) {
|
||||
case encodePath: // §3.3
|
||||
// The RFC allows : @ & = + $ but saves / ; , for assigning
|
||||
// meaning to individual path segments. This package
|
||||
// only manipulates the path as a whole, so we allow those
|
||||
// last three as well. That leaves only ? to escape.
|
||||
return c == '?';
|
||||
|
||||
case encodePathSegment: // §3.3
|
||||
// The RFC allows : @ & = + $ but saves / ; , for assigning
|
||||
// meaning to individual path segments.
|
||||
return c == '/' || c == ';' || c == ',' || c == '?';
|
||||
|
||||
case encodeUserPassword: // §3.2.1
|
||||
// The RFC allows ';', ':', '&', '=', '+', '$', and ',' in
|
||||
// userinfo, so we must escape only '@', '/', and '?'.
|
||||
// The parsing of userinfo treats ':' as special so we must escape
|
||||
// that too.
|
||||
return c == '@' || c == '/' || c == '?' || c == ':';
|
||||
|
||||
case encodeQueryComponent: // §3.4
|
||||
// The RFC reserves (so we must escape) everything.
|
||||
return true;
|
||||
|
||||
case encodeFragment: // §4.1
|
||||
// The RFC text is silent but the grammar allows
|
||||
// everything, so escape nothing.
|
||||
return false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(mode == encodeFragment) {
|
||||
// RFC 3986 §2.2 allows not escaping sub-delims. A subset of sub-delims are
|
||||
// included in reserved from RFC 2396 §2.2. The remaining sub-delims do not
|
||||
// need to be escaped. To minimize potential breakage, we apply two restrictions:
|
||||
// (1) we always escape sub-delims outside of the fragment, and (2) we always
|
||||
// escape single quote to avoid breaking callers that had previously assumed that
|
||||
// single quotes would be escaped. See issue #19917.
|
||||
switch (c) {
|
||||
case '!':
|
||||
case '(':
|
||||
case ')':
|
||||
case '*':
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Everything else must be escaped.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ishex(uint8_t c) {
|
||||
if( '0' <= c && c <= '9') {
|
||||
return true;
|
||||
} else if('a' <= c && c <= 'f') {
|
||||
return true;
|
||||
} else if( 'A' <= c && c <= 'F') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t hex_to_num(uint8_t c) {
|
||||
if('0' <= c && c <= '9') {
|
||||
return c - '0';
|
||||
} else if('a' <= c && c <= 'f') {
|
||||
return c - 'a' + 10;
|
||||
} else if('A' <= c && c <= 'F') {
|
||||
return c - 'A' + 10;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
srs_error_t unescapse(string s, string& value, EncodeMode mode) {
|
||||
srs_error_t err = srs_success;
|
||||
int n = 0;
|
||||
bool has_plus = false;
|
||||
int i = 0;
|
||||
// Count %, check that they're well-formed.
|
||||
while(i < s.length()) {
|
||||
switch (s.at(i)) {
|
||||
case '%':
|
||||
{
|
||||
n++;
|
||||
if((i+2) >= s.length() || !ishex(s.at(i+1)) || !ishex(s.at(i+2))) {
|
||||
string msg = s.substr(i);
|
||||
if(msg.length() > 3) {
|
||||
msg = msg.substr(0, 3);
|
||||
}
|
||||
return srs_error_new(ERROR_HTTP_URL_UNESCAPE, "invalid URL escape: %s", msg.c_str());
|
||||
}
|
||||
|
||||
// Per https://tools.ietf.org/html/rfc3986#page-21
|
||||
// in the host component %-encoding can only be used
|
||||
// for non-ASCII bytes.
|
||||
// But https://tools.ietf.org/html/rfc6874#section-2
|
||||
// introduces %25 being allowed to escape a percent sign
|
||||
// in IPv6 scoped-address literals. Yay.
|
||||
if(encodeHost == mode && hex_to_num(s.at(i+1)) < 8 && s.substr(i, 3) != "%25") {
|
||||
return srs_error_new(ERROR_HTTP_URL_UNESCAPE, "invalid URL escap: %s", s.substr(i, 3).c_str());
|
||||
}
|
||||
|
||||
if(encodeZone == mode) {
|
||||
// RFC 6874 says basically "anything goes" for zone identifiers
|
||||
// and that even non-ASCII can be redundantly escaped,
|
||||
// but it seems prudent to restrict %-escaped bytes here to those
|
||||
// that are valid host name bytes in their unescaped form.
|
||||
// That is, you can use escaping in the zone identifier but not
|
||||
// to introduce bytes you couldn't just write directly.
|
||||
// But Windows puts spaces here! Yay.
|
||||
uint8_t v = (hex_to_num(s.at(i+1)) << 4) | (hex_to_num(s.at(i+2)));
|
||||
if("%25" != s.substr(i, 3) && ' ' != v && should_escape(v, encodeHost)) {
|
||||
return srs_error_new(ERROR_HTTP_URL_UNESCAPE, "invalid URL escap: %s", s.substr(i, 3).c_str());
|
||||
}
|
||||
}
|
||||
i += 3;
|
||||
}
|
||||
break;
|
||||
case '+':
|
||||
has_plus = encodeQueryComponent == mode;
|
||||
i++;
|
||||
break;
|
||||
default:
|
||||
if((encodeHost == mode || encodeZone == mode) && ((uint8_t)s.at(i) < 0x80) && should_escape(s.at(i), mode)) {
|
||||
return srs_error_new(ERROR_HTTP_URL_UNESCAPE, "invalid character %u in host name", s.at(i));
|
||||
}
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(0 == n && !has_plus) {
|
||||
value = s;
|
||||
return err;
|
||||
}
|
||||
|
||||
value.clear();
|
||||
//value.resize(s.length() - 2*n);
|
||||
for(int i = 0; i < s.length(); ++i) {
|
||||
switch(s.at(i)) {
|
||||
case '%':
|
||||
value += (hex_to_num(s.at(i+1))<<4 | hex_to_num(s.at(i+2)));
|
||||
i += 2;
|
||||
break;
|
||||
case '+':
|
||||
if(encodeQueryComponent == mode) {
|
||||
value += " ";
|
||||
} else {
|
||||
value += "+";
|
||||
}
|
||||
break;
|
||||
default:
|
||||
value += s.at(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
string escape(string s, EncodeMode mode) {
|
||||
int space_count = 0;
|
||||
int hex_count = 0;
|
||||
for(int i = 0; i < s.length(); ++i) {
|
||||
uint8_t c = s.at(i);
|
||||
if(should_escape(c, mode)) {
|
||||
if(' ' == c && encodeQueryComponent == mode) {
|
||||
space_count++;
|
||||
} else {
|
||||
hex_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(0 == space_count && 0 == hex_count) {
|
||||
return s;
|
||||
}
|
||||
|
||||
string value;
|
||||
if(0 == hex_count) {
|
||||
value = s;
|
||||
for(int i = 0; i < s.length(); ++i) {
|
||||
if(' ' == s.at(i)) {
|
||||
value[i] = '+';
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
//value.resize(s.length() + 2*hex_count);
|
||||
const char escape_code[] = "0123456789ABCDEF";
|
||||
//int j = 0;
|
||||
for(int i = 0; i < s.length(); ++i) {
|
||||
uint8_t c = s.at(i);
|
||||
if(' ' == c && encodeQueryComponent == mode) {
|
||||
value += '+';
|
||||
} else if (should_escape(c, mode)) {
|
||||
value += '%';
|
||||
value += escape_code[c>>4];
|
||||
value += escape_code[c&15];
|
||||
//j += 3;
|
||||
} else {
|
||||
value += s[i];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
string SrsHttpUri::query_escape(std::string s)
|
||||
{
|
||||
return escape(s, encodeQueryComponent);
|
||||
}
|
||||
|
||||
string SrsHttpUri::path_escape(std::string s)
|
||||
{
|
||||
return escape(s, encodePathSegment);
|
||||
}
|
||||
|
||||
srs_error_t SrsHttpUri::query_unescape(std::string s, std::string& value)
|
||||
{
|
||||
return unescapse(s, value, encodeQueryComponent);
|
||||
}
|
||||
|
||||
srs_error_t SrsHttpUri::path_unescape(std::string s, std::string& value)
|
||||
{
|
||||
return unescapse(s, value, encodePathSegment);
|
||||
}
|
||||
|
||||
// For #if !defined(SRS_EXPORT_LIBRTMP)
|
||||
#endif
|
||||
|
||||
|
|
|
@ -518,6 +518,7 @@ private:
|
|||
std::string query;
|
||||
std::string username_;
|
||||
std::string password_;
|
||||
std::map<std::string, std::string> query_values_;
|
||||
public:
|
||||
SrsHttpUri();
|
||||
virtual ~SrsHttpUri();
|
||||
|
@ -533,12 +534,19 @@ public:
|
|||
virtual int get_port();
|
||||
virtual std::string get_path();
|
||||
virtual std::string get_query();
|
||||
virtual std::string get_query_by_key(std::string key);
|
||||
virtual std::string username();
|
||||
virtual std::string password();
|
||||
private:
|
||||
// Get the parsed url field.
|
||||
// @return return empty string if not set.
|
||||
virtual std::string get_uri_field(std::string uri, void* hp_u, int field);
|
||||
srs_error_t parse_query();
|
||||
public:
|
||||
static std::string query_escape(std::string s);
|
||||
static std::string path_escape(std::string s);
|
||||
static srs_error_t query_unescape(std::string s, std::string& value);
|
||||
static srs_error_t path_unescape(std::string s, std::string& value);
|
||||
};
|
||||
|
||||
// For #if !defined(SRS_EXPORT_LIBRTMP)
|
||||
|
|
|
@ -1903,3 +1903,125 @@ VOID TEST(ProtocolHTTPTest, ParseUri)
|
|||
EXPECT_STREQ("/0", uri.get_path().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
VOID TEST(ProtocolHTTPTest, ParseHttpUri)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
std::string url_str = "http://me.com";
|
||||
url_str += "?xxxIdxxx=xxxxxCPrzDUzxxxxx&Action=DescribeXXX";
|
||||
url_str += "&XXXUid=000140000&AppId=xxxx2rxxx&Caller=rtc&Format=JSON&QueryAppId=xxx9xrxxx";
|
||||
url_str += "&Region=cn-hangzhou&RequestId=xxx6i4bmxxx74kxxx" ;
|
||||
url_str += "&SignatureMethod=HMAC-SHA1&SignatureNonce=xxxk1q0t42v37ske24j329xxxx";
|
||||
url_str += "&SignatureVersion=1.0&Timestamp=2020-11-02T09:10:28Z&Version=2018-01-11";
|
||||
url_str += "&Signature=xxxGXBBGnoR4vHsTcUxxx+tRM";
|
||||
SrsHttpUri uri;
|
||||
HELPER_ASSERT_SUCCESS(uri.initialize(url_str));
|
||||
EXPECT_STREQ("http", uri.get_schema().c_str());
|
||||
EXPECT_EQ(80, uri.get_port());
|
||||
EXPECT_STREQ("me.com", uri.get_host().c_str());
|
||||
EXPECT_STREQ("", uri.get_path().c_str());
|
||||
EXPECT_STREQ("xxxxxCPrzDUzxxxxx", uri.get_query_by_key("xxxIdxxx").c_str());
|
||||
EXPECT_STREQ("DescribeXXX", uri.get_query_by_key("Action").c_str());
|
||||
EXPECT_STREQ("000140000", uri.get_query_by_key("XXXUid").c_str());
|
||||
EXPECT_STREQ("rtc", uri.get_query_by_key("Caller").c_str());
|
||||
EXPECT_STREQ("xxxx2rxxx", uri.get_query_by_key("AppId").c_str());
|
||||
EXPECT_STREQ("JSON", uri.get_query_by_key("Format").c_str());
|
||||
EXPECT_STREQ("xxx9xrxxx", uri.get_query_by_key("QueryAppId").c_str());
|
||||
EXPECT_STREQ("cn-hangzhou", uri.get_query_by_key("Region").c_str());
|
||||
EXPECT_STREQ("xxx6i4bmxxx74kxxx", uri.get_query_by_key("RequestId").c_str());
|
||||
EXPECT_STREQ("HMAC-SHA1", uri.get_query_by_key("SignatureMethod").c_str());
|
||||
EXPECT_STREQ("xxxk1q0t42v37ske24j329xxxx", uri.get_query_by_key("SignatureNonce").c_str());
|
||||
EXPECT_STREQ("1.0", uri.get_query_by_key("SignatureVersion").c_str());
|
||||
EXPECT_STREQ("2020-11-02T09:10:28Z", uri.get_query_by_key("Timestamp").c_str());
|
||||
EXPECT_STREQ("2018-01-11", uri.get_query_by_key("Version").c_str());
|
||||
EXPECT_STREQ("xxxGXBBGnoR4vHsTcUxxx+tRM", uri.get_query_by_key("Signature").c_str());
|
||||
|
||||
}
|
||||
|
||||
struct EscapeTest {
|
||||
string in;
|
||||
string out;
|
||||
srs_error_t err;
|
||||
};
|
||||
|
||||
VOID TEST(ProtocolHTTPTest, QueryEscape)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
//Test query unescapse
|
||||
if(true) {
|
||||
struct EscapeTest unescape[] = {
|
||||
{"", "", srs_success},
|
||||
{"abc", "abc", srs_success},
|
||||
{"1%41", "1A", srs_success},
|
||||
{"1%41%42%43", "1ABC", srs_success},
|
||||
{"%4a", "J", srs_success},
|
||||
{"%6F", "o", srs_success},
|
||||
{"%"/* not enough characters after %*/, "", srs_error_new(ERROR_HTTP_URL_UNESCAPE, "%")},
|
||||
{"%a"/* not enough characters after % */, "", srs_error_new(ERROR_HTTP_URL_UNESCAPE, "%a")},
|
||||
{"%1" /* not enough characters after % */, "", srs_error_new(ERROR_HTTP_URL_UNESCAPE, "%1")},
|
||||
{"123%45%6"/* not enough characters after % */, "", srs_error_new(ERROR_HTTP_URL_UNESCAPE, "%6")},
|
||||
{"%zzzzz"/* invalid hex digits */, "", srs_error_new(ERROR_HTTP_URL_UNESCAPE, "%zz")},
|
||||
{"a+b", "a b", srs_success},
|
||||
{"a%20b", "a b", srs_success}
|
||||
};
|
||||
|
||||
for(int i = 0; i < (sizeof(unescape) / sizeof(struct EscapeTest)); ++i) {
|
||||
struct EscapeTest& d = unescape[i];
|
||||
string value;
|
||||
if(srs_success == d.err) {
|
||||
HELPER_ASSERT_SUCCESS(SrsHttpUri::query_unescape(d.in, value));
|
||||
EXPECT_STREQ(d.out.c_str(), value.c_str());
|
||||
} else {
|
||||
HELPER_ASSERT_FAILED(SrsHttpUri::query_unescape(d.in, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Test Escape
|
||||
if(true) {
|
||||
struct EscapeTest escape[] = {
|
||||
{"", "", srs_success},
|
||||
{"abc", "abc", srs_success},
|
||||
{"one two", "one+two", srs_success},
|
||||
{"10%", "10%25", srs_success},
|
||||
{" ?&=#+%!<>#\"{}|\\^[]`☺\t:/@$'()*,;",
|
||||
"+%3F%26%3D%23%2B%25%21%3C%3E%23%22%7B%7D%7C%5C%5E%5B%5D%60%E2%98%BA%09%3A%2F%40%24%27%28%29%2A%2C%3B", srs_success},
|
||||
};
|
||||
for(int i = 0; i < (sizeof(escape) / sizeof(struct EscapeTest)); ++i) {
|
||||
struct EscapeTest& d = escape[i];
|
||||
EXPECT_STREQ(d.out.c_str(), SrsHttpUri::query_escape(d.in).c_str());
|
||||
|
||||
string value;
|
||||
HELPER_ASSERT_SUCCESS(SrsHttpUri::query_unescape(d.out, value));
|
||||
EXPECT_STREQ(d.in.c_str(), value.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VOID TEST(ProtocolHTTPTest, PathEscape)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
struct EscapeTest path[] = {
|
||||
{"", "", srs_success},
|
||||
{"abc", "abc", srs_success},
|
||||
{"abc+def", "abc+def", srs_success},
|
||||
{"a/b", "a%2Fb", srs_success},
|
||||
{"one two", "one%20two", srs_success},
|
||||
{"10%", "10%25", srs_success},
|
||||
{" ?&=#+%!<>#\"{}|\\^[]`☺\t:/@$'()*,;",
|
||||
"%20%3F&=%23+%25%21%3C%3E%23%22%7B%7D%7C%5C%5E%5B%5D%60%E2%98%BA%09:%2F@$%27%28%29%2A%2C%3B",
|
||||
srs_success},
|
||||
};
|
||||
|
||||
for(int i = 0; i < (sizeof(path) / sizeof(struct EscapeTest)); ++i) {
|
||||
struct EscapeTest& d = path[i];
|
||||
EXPECT_STREQ(d.out.c_str(), SrsHttpUri::path_escape(d.in).c_str());
|
||||
|
||||
string value;
|
||||
HELPER_ASSERT_SUCCESS(SrsHttpUri::path_unescape(d.out, value));
|
||||
EXPECT_STREQ(d.in.c_str(), value.c_str());
|
||||
EXPECT_EQ(0, memcmp(d.in.c_str(), value.c_str(), d.in.length()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue