diff --git a/trunk/doc/CHANGELOG.md b/trunk/doc/CHANGELOG.md index f6c13cfb0..ff8095c0d 100644 --- a/trunk/doc/CHANGELOG.md +++ b/trunk/doc/CHANGELOG.md @@ -7,6 +7,7 @@ The changelog for SRS. ## SRS 5.0 Changelog +* v5.0, 2022-08-28, URL: Use SrsHttpUri to parse URL and query. v5.0.48 * v5.0, 2022-08-28, Fix [#2881](https://github.com/ossrs/srs/issues/2881): HTTP: Support merging api to server. v5.0.47 * v5.0, 2022-08-27, Fix [#3108](https://github.com/ossrs/srs/issues/3108): STAT: Update stat for SRT. v5.0.46 * v5.0, 2022-08-26, Log: Stat the number of logs. v5.0.45 diff --git a/trunk/src/core/srs_core_version5.hpp b/trunk/src/core/srs_core_version5.hpp index 966d22b21..3ad316404 100644 --- a/trunk/src/core/srs_core_version5.hpp +++ b/trunk/src/core/srs_core_version5.hpp @@ -9,6 +9,6 @@ #define VERSION_MAJOR 5 #define VERSION_MINOR 0 -#define VERSION_REVISION 47 +#define VERSION_REVISION 48 #endif diff --git a/trunk/src/protocol/srs_protocol_utility.cpp b/trunk/src/protocol/srs_protocol_utility.cpp index 30e1ecf1f..43b254132 100644 --- a/trunk/src/protocol/srs_protocol_utility.cpp +++ b/trunk/src/protocol/srs_protocol_utility.cpp @@ -42,97 +42,58 @@ using namespace std; #include #include -/** - * resolve the vhost in query string - * @pram vhost, update the vhost if query contains the vhost. - * @param app, may contains the vhost in query string format: - * app?vhost=request_vhost - * app...vhost...request_vhost - * @param param, the query, for example, ?vhost=xxx - */ -void srs_vhost_resolve(string& vhost, string& app, string& param) +void srs_discovery_tc_url(string tcUrl, string& schema, string& host, string& vhost, string& app, string& stream, int& port, string& param) { - // get original param - size_t pos = 0; - if ((pos = app.find("?")) != std::string::npos) { - param = app.substr(pos); - } - - // filter tcUrl - app = srs_string_replace(app, ",", "?"); - app = srs_string_replace(app, "...", "?"); - app = srs_string_replace(app, "&&", "?"); - app = srs_string_replace(app, "&", "?"); - app = srs_string_replace(app, "=", "?"); - - if (srs_string_ends_with(app, "/_definst_")) { - app = srs_erase_last_substr(app, "/_definst_"); + // Standard URL is: + // rtmp://ip/app/app2/stream?k=v + // Where after last slash is stream. + string fullUrl = tcUrl; + fullUrl += stream.empty() ? "/" : (stream.at(0) == '/' ? stream : "/" + stream); + fullUrl += param.empty() ? "" : (param.at(0) == '?' ? param : "?" + param); + + // First, we covert the FMLE URL to standard URL: + // rtmp://ip/app/app2?k=v/stream + size_t pos_query = fullUrl.find("?"); + size_t pos_rslash = fullUrl.rfind("/"); + if (pos_rslash != string::npos && pos_query != string::npos && pos_query < pos_rslash) { + fullUrl = fullUrl.substr(0, pos_query) // rtmp://ip/app/app2 + + fullUrl.substr(pos_rslash) // /stream + + fullUrl.substr(pos_query, pos_rslash - pos_query); // ?k=v + } + + // Remove the _definst_ of FMLE URL. + if (fullUrl.find("/_definst_") != string::npos) { + fullUrl = srs_string_replace(fullUrl, "/_definst_", ""); + } + + // Parse the standard URL. + SrsHttpUri uri; + srs_error_t err = srs_success; + if ((err = uri.initialize(fullUrl)) != srs_success) { + srs_warn("Ignore parse url=%s err %s", fullUrl.c_str(), srs_error_desc(err).c_str()); + srs_freep(err); + return; } - - if ((pos = app.find("?")) != std::string::npos) { - std::string query = app.substr(pos + 1); - app = app.substr(0, pos); - if ((pos = query.find("vhost?")) != std::string::npos) { - query = query.substr(pos + 6); - if (!query.empty()) { - vhost = query; - } - } else if ((pos = query.find("domain?")) != std::string::npos) { - query = query.substr(pos + 7); - if (!query.empty()) { - vhost = query; - } - } - } + schema = uri.get_schema(); + host = uri.get_host(); + port = uri.get_port(); + stream = srs_path_basename(uri.get_path()); + param = uri.get_query().empty() ? "" : "?" + uri.get_query(); - // vhost with params. - if ((pos = vhost.find("?")) != std::string::npos) { - vhost = vhost.substr(0, pos); - } - - /* others */ -} + // Parse app without the prefix slash. + app = srs_path_dirname(uri.get_path()); + if (!app.empty() && app.at(0) == '/') app = app.substr(1); + if (app.empty()) app = SRS_CONSTS_RTMP_DEFAULT_APP; -void srs_discovery_tc_url(string tcUrl, string& schema, string& host, string& vhost, string& app, string& stream, int& port, string& param) -{ - size_t pos = std::string::npos; - std::string url = tcUrl; - - if ((pos = url.find("://")) != std::string::npos) { - schema = url.substr(0, pos); - url = url.substr(schema.length() + 3); - srs_info("discovery schema=%s", schema.c_str()); - } - - if ((pos = url.find("/")) != std::string::npos) { - host = url.substr(0, pos); - url = url.substr(host.length() + 1); - srs_info("discovery host=%s", host.c_str()); - } - - port = SRS_CONSTS_RTMP_DEFAULT_PORT; - if (schema == "https") { - port = SRS_DEFAULT_HTTPS_PORT; - } + // Try to parse vhost from query, or use host if not specified. + string vhost_in_query = uri.get_query_by_key("vhost"); + if (vhost_in_query.empty()) vhost_in_query = uri.get_query_by_key("domain"); + if (!vhost_in_query.empty() && vhost_in_query != SRS_CONSTS_RTMP_DEFAULT_VHOST) vhost = vhost_in_query; + if (vhost.empty()) vhost = host; - if ((pos = host.find(":")) != std::string::npos) { - srs_parse_hostport(host, host, port); - srs_info("discovery host=%s, port=%d", host.c_str(), port); - } - - if (url.empty()) { - app = SRS_CONSTS_RTMP_DEFAULT_APP; - } else { - app = url; - } - - vhost = host; - srs_vhost_resolve(vhost, app, param); - srs_vhost_resolve(vhost, stream, param); - - // Ignore when the param only contains the default vhost. - if (param == "?vhost=" SRS_CONSTS_RTMP_DEFAULT_VHOST) { + // Only one param, the default vhost, clear it. + if (param.find("&") == string::npos && vhost_in_query == SRS_CONSTS_RTMP_DEFAULT_VHOST) { param = ""; } } diff --git a/trunk/src/utest/srs_utest_rtmp.cpp b/trunk/src/utest/srs_utest_rtmp.cpp index 5e07f1506..edad7977f 100644 --- a/trunk/src/utest/srs_utest_rtmp.cpp +++ b/trunk/src/utest/srs_utest_rtmp.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #define SRS_DEFAULT_RECV_BUFFER_SIZE 131072 @@ -3621,3 +3622,188 @@ VOID TEST(ProtocolRTMPTest, GuessingStream) } } +VOID TEST(ProtocolRTMPTest, DiscoveryUrl) +{ + if (true) { + string tcUrl = "invalid://ip:8888/app", stream = "stream?k=v&domain=ossrs.io&k2=v2"; + string schema, host, vhost, app, param; int port; + srs_discovery_tc_url(tcUrl, schema, host, vhost, app, stream, port, param); + EXPECT_STREQ("invalid", schema.c_str()); + EXPECT_STREQ("ip", host.c_str()); + EXPECT_EQ(8888, port); + EXPECT_STREQ("app", app.c_str()); + EXPECT_STREQ("stream", stream.c_str()); + EXPECT_STREQ("ossrs.io", vhost.c_str()); + EXPECT_STREQ("?k=v&domain=ossrs.io&k2=v2", param.c_str()); + } + + if (true) { + string tcUrl = "invalid://ip/app", stream = "stream?k=v&domain=ossrs.io&k2=v2"; + string schema, host, vhost, app, param; int port; + srs_discovery_tc_url(tcUrl, schema, host, vhost, app, stream, port, param); + EXPECT_STREQ("invalid", schema.c_str()); + EXPECT_STREQ("ip", host.c_str()); + EXPECT_EQ(80, port); + EXPECT_STREQ("app", app.c_str()); + EXPECT_STREQ("stream", stream.c_str()); + EXPECT_STREQ("ossrs.io", vhost.c_str()); + EXPECT_STREQ("?k=v&domain=ossrs.io&k2=v2", param.c_str()); + } + + if (true) { + string tcUrl = "rtmp://ip/app", stream = "stream?k=v&domain=ossrs.io&k2=v2"; + string schema, host, vhost, app, param; int port; + srs_discovery_tc_url(tcUrl, schema, host, vhost, app, stream, port, param); + EXPECT_STREQ("rtmp", schema.c_str()); + EXPECT_STREQ("ip", host.c_str()); + EXPECT_EQ(1935, port); + EXPECT_STREQ("app", app.c_str()); + EXPECT_STREQ("stream", stream.c_str()); + EXPECT_STREQ("ossrs.io", vhost.c_str()); + EXPECT_STREQ("?k=v&domain=ossrs.io&k2=v2", param.c_str()); + } + + if (true) { + string tcUrl = "https://ip/app", stream = "stream?k=v&domain=ossrs.io&k2=v2"; + string schema, host, vhost, app, param; int port; + srs_discovery_tc_url(tcUrl, schema, host, vhost, app, stream, port, param); + EXPECT_STREQ("https", schema.c_str()); + EXPECT_STREQ("ip", host.c_str()); + EXPECT_EQ(443, port); + EXPECT_STREQ("app", app.c_str()); + EXPECT_STREQ("stream", stream.c_str()); + EXPECT_STREQ("ossrs.io", vhost.c_str()); + EXPECT_STREQ("?k=v&domain=ossrs.io&k2=v2", param.c_str()); + } + + if (true) { + string tcUrl = "http://ip/app", stream = "stream?k=v&domain=ossrs.io&k2=v2"; + string schema, host, vhost, app, param; int port; + srs_discovery_tc_url(tcUrl, schema, host, vhost, app, stream, port, param); + EXPECT_STREQ("http", schema.c_str()); + EXPECT_STREQ("ip", host.c_str()); + EXPECT_EQ(80, port); + EXPECT_STREQ("app", app.c_str()); + EXPECT_STREQ("stream", stream.c_str()); + EXPECT_STREQ("ossrs.io", vhost.c_str()); + EXPECT_STREQ("?k=v&domain=ossrs.io&k2=v2", param.c_str()); + } + + if (true) { + string tcUrl = "rtmp://ip/app", stream = "stream?domain=__defaultVhost__"; + string schema, host, vhost, app, param; int port; + srs_discovery_tc_url(tcUrl, schema, host, vhost, app, stream, port, param); + EXPECT_STREQ("rtmp", schema.c_str()); + EXPECT_STREQ("ip", host.c_str()); + EXPECT_EQ(1935, port); + EXPECT_STREQ("app", app.c_str()); + EXPECT_STREQ("stream", stream.c_str()); + EXPECT_STREQ("ip", vhost.c_str()); + EXPECT_TRUE(param.empty()); + } + + if (true) { + string tcUrl = "rtmp://ip/app/_definst_", stream = "stream?k=v&domain=ossrs.io&k2=v2"; + string schema, host, vhost, app, param; int port; + srs_discovery_tc_url(tcUrl, schema, host, vhost, app, stream, port, param); + EXPECT_STREQ("rtmp", schema.c_str()); + EXPECT_STREQ("ip", host.c_str()); + EXPECT_EQ(1935, port); + EXPECT_STREQ("app", app.c_str()); + EXPECT_STREQ("stream", stream.c_str()); + EXPECT_STREQ("ossrs.io", vhost.c_str()); + EXPECT_STREQ("?k=v&domain=ossrs.io&k2=v2", param.c_str()); + } + + if (true) { + string tcUrl = "rtmp://ip", stream = "stream?k=v&domain=ossrs.io&k2=v2"; + string schema, host, vhost, app, param; int port; + srs_discovery_tc_url(tcUrl, schema, host, vhost, app, stream, port, param); + EXPECT_STREQ("rtmp", schema.c_str()); + EXPECT_STREQ("ip", host.c_str()); + EXPECT_EQ(1935, port); + EXPECT_STREQ("__defaultApp__", app.c_str()); + EXPECT_STREQ("stream", stream.c_str()); + EXPECT_STREQ("ossrs.io", vhost.c_str()); + EXPECT_STREQ("?k=v&domain=ossrs.io&k2=v2", param.c_str()); + } + + if (true) { + string tcUrl = "rtmp://ossrs.io/app/app2", stream = "stream?k=v&k2=v2"; + string schema, host, vhost, app, param; int port; + srs_discovery_tc_url(tcUrl, schema, host, vhost, app, stream, port, param); + EXPECT_STREQ("rtmp", schema.c_str()); + EXPECT_STREQ("ossrs.io", host.c_str()); + EXPECT_EQ(1935, port); + EXPECT_STREQ("app/app2", app.c_str()); + EXPECT_STREQ("stream", stream.c_str()); + EXPECT_STREQ("ossrs.io", vhost.c_str()); + EXPECT_STREQ("?k=v&k2=v2", param.c_str()); + } + + if (true) { + string tcUrl = "rtmp://ip/app/app2", stream = "stream?k=v&domain=ossrs.io&k2=v2"; + string schema, host, vhost, app, param; int port; + srs_discovery_tc_url(tcUrl, schema, host, vhost, app, stream, port, param); + EXPECT_STREQ("rtmp", schema.c_str()); + EXPECT_STREQ("ip", host.c_str()); + EXPECT_EQ(1935, port); + EXPECT_STREQ("app/app2", app.c_str()); + EXPECT_STREQ("stream", stream.c_str()); + EXPECT_STREQ("ossrs.io", vhost.c_str()); + EXPECT_STREQ("?k=v&domain=ossrs.io&k2=v2", param.c_str()); + } + + if (true) { + string tcUrl = "rtmp://ip/app/app2", stream = "stream?k=v&vhost=ossrs.io&k2=v2"; + string schema, host, vhost, app, param; int port; + srs_discovery_tc_url(tcUrl, schema, host, vhost, app, stream, port, param); + EXPECT_STREQ("rtmp", schema.c_str()); + EXPECT_STREQ("ip", host.c_str()); + EXPECT_EQ(1935, port); + EXPECT_STREQ("app/app2", app.c_str()); + EXPECT_STREQ("stream", stream.c_str()); + EXPECT_STREQ("ossrs.io", vhost.c_str()); + EXPECT_STREQ("?k=v&vhost=ossrs.io&k2=v2", param.c_str()); + } + + if (true) { + string tcUrl = "rtmp://ip/app/app2", stream = "stream?k=v&k2=v2"; + string schema, host, vhost, app, param; int port; + srs_discovery_tc_url(tcUrl, schema, host, vhost, app, stream, port, param); + EXPECT_STREQ("rtmp", schema.c_str()); + EXPECT_STREQ("ip", host.c_str()); + EXPECT_EQ(1935, port); + EXPECT_STREQ("app/app2", app.c_str()); + EXPECT_STREQ("stream", stream.c_str()); + EXPECT_STREQ("ip", vhost.c_str()); + EXPECT_STREQ("?k=v&k2=v2", param.c_str()); + } + + if (true) { + string tcUrl = "rtmp://ip/app/app2?k=v", stream = "stream"; + string schema, host, vhost, app, param; int port; + srs_discovery_tc_url(tcUrl, schema, host, vhost, app, stream, port, param); + EXPECT_STREQ("rtmp", schema.c_str()); + EXPECT_STREQ("ip", host.c_str()); + EXPECT_EQ(1935, port); + EXPECT_STREQ("app/app2", app.c_str()); + EXPECT_STREQ("stream", stream.c_str()); + EXPECT_STREQ("ip", vhost.c_str()); + EXPECT_STREQ("?k=v", param.c_str()); + } + + if (true) { + string tcUrl = "rtmp://ip/app?k=v", stream = "stream"; + string schema, host, vhost, app, param; int port; + srs_discovery_tc_url(tcUrl, schema, host, vhost, app, stream, port, param); + EXPECT_STREQ("rtmp", schema.c_str()); + EXPECT_STREQ("ip", host.c_str()); + EXPECT_EQ(1935, port); + EXPECT_STREQ("app", app.c_str()); + EXPECT_STREQ("stream", stream.c_str()); + EXPECT_STREQ("ip", vhost.c_str()); + EXPECT_STREQ("?k=v", param.c_str()); + } +} +