AI: Use SrsHttpUri for URL parsing and add legacy RTMP URL conversion. v7.0.72 (#4463)
Refactors the `srs_net_url_parse_tcurl` function to use the robust `SrsHttpUri` class for URL parsing and implements a dedicated legacy RTMP URL conversion function to handle various URL formats consistently. --------- Co-authored-by: OSSRS-AI <winlinam@gmail.com>
This commit is contained in:
parent
0d6d36d1fb
commit
b834be67a9
|
|
@ -7,6 +7,7 @@ The changelog for SRS.
|
|||
<a name="v7-changes"></a>
|
||||
|
||||
## SRS 7.0 Changelog
|
||||
* v7.0, 2025-09-01, Merge [#4463](https://github.com/ossrs/srs/pull/4463): AI: Use SrsHttpUri for URL parsing and add legacy RTMP URL conversion. v7.0.72 (#4463)
|
||||
* v7.0, 2025-09-01, Merge [#4462](https://github.com/ossrs/srs/pull/4462): HTTP: Rename HTTP hijack to dynamic match for better clarity. v7.0.71 (#4462)
|
||||
* v7.0, 2025-08-31, Merge [#4461](https://github.com/ossrs/srs/pull/4461): AI: Extract shared components and improve SRS server architecture. v7.0.70 (#4461)
|
||||
* v7.0, 2025-08-31, Merge [#4460](https://github.com/ossrs/srs/pull/4460): AI: Always enable SRT protocol. v7.0.69 (#4460)
|
||||
|
|
|
|||
|
|
@ -60,7 +60,9 @@ class SrsPidFileLocker;
|
|||
// Initialize global shared variables cross all threads.
|
||||
extern srs_error_t srs_global_initialize();
|
||||
|
||||
// SRS RTMP server, initialize and listen, start connection service thread, destroy client.
|
||||
// SrsServer is the main server class of SRS (Simple Realtime Server) that provides comprehensive
|
||||
// streaming media server functionality. It serves as the central orchestrator for all streaming
|
||||
// protocols and services in a single-threaded, coroutine-based architecture.
|
||||
class SrsServer : public ISrsReloadHandler, // Reload framework for permormance optimization.
|
||||
public ISrsLiveSourceHandler,
|
||||
public ISrsTcpHandler,
|
||||
|
|
@ -69,7 +71,6 @@ class SrsServer : public ISrsReloadHandler, // Reload framework for permormance
|
|||
public ISrsUdpMuxHandler
|
||||
{
|
||||
private:
|
||||
// TODO: FIXME: Extract an HttpApiServer.
|
||||
ISrsHttpServeMux *http_api_mux_;
|
||||
SrsHttpServer *http_server_;
|
||||
|
||||
|
|
@ -118,8 +119,12 @@ private:
|
|||
// Stream Caster for GB28181.
|
||||
SrsGbListener *stream_caster_gb28181_;
|
||||
#endif
|
||||
|
||||
private:
|
||||
// SRT acceptors for MPEG-TS over SRT.
|
||||
std::vector<SrsSrtAcceptor *> srt_acceptors_;
|
||||
|
||||
private:
|
||||
// WebRTC UDP listeners for RTC server functionality.
|
||||
std::vector<SrsUdpMuxListener *> rtc_listeners_;
|
||||
// WebRTC session manager.
|
||||
|
|
|
|||
|
|
@ -9,6 +9,6 @@
|
|||
|
||||
#define VERSION_MAJOR 7
|
||||
#define VERSION_MINOR 0
|
||||
#define VERSION_REVISION 71
|
||||
#define VERSION_REVISION 72
|
||||
|
||||
#endif
|
||||
|
|
@ -232,6 +232,11 @@ extern bool srs_net_url_is_rtmp(std::string url);
|
|||
extern void srs_net_url_parse_tcurl(std::string tcUrl, std::string &schema, std::string &host, std::string &vhost, std::string &app,
|
||||
std::string &stream, int &port, std::string ¶m);
|
||||
|
||||
// Convert legacy RTMP URL format to standard format.
|
||||
// Legacy format: rtmp://ip/app/app2?vhost=xxx/stream
|
||||
// Standard format: rtmp://ip/app/app2/stream?vhost=xxx
|
||||
extern std::string srs_net_url_convert_legacy_rtmp_url(const std::string &url);
|
||||
|
||||
// Guessing stream by app and param, to make OBS happy. For example:
|
||||
// rtmp://ip/live/livestream
|
||||
// rtmp://ip/live/livestream?secret=xxx
|
||||
|
|
|
|||
|
|
@ -43,35 +43,28 @@ using namespace std;
|
|||
|
||||
void srs_net_url_parse_tcurl(string tcUrl, string &schema, string &host, string &vhost, string &app, string &stream, int &port, string ¶m)
|
||||
{
|
||||
// For compatibility, transform
|
||||
// rtmp://ip/app...vhost...VHOST/stream
|
||||
// to typical format:
|
||||
// rtmp://ip/app?vhost=VHOST/stream
|
||||
string fullUrl = srs_strings_replace(tcUrl, "...vhost...", "?vhost=");
|
||||
|
||||
// Standard URL is:
|
||||
// rtmp://ip/app/app2/stream?k=v
|
||||
// Where after last slash is stream.
|
||||
// Build the full URL with stream and param if provided
|
||||
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 , or:
|
||||
// rtmp://ip/app/app2#k=v/stream
|
||||
size_t pos_query = fullUrl.find_first_of("?#");
|
||||
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
|
||||
}
|
||||
// For compatibility, transform legacy ...vhost... format
|
||||
// rtmp://ip/app...vhost...VHOST/stream
|
||||
// to query parameter format:
|
||||
// rtmp://ip/app?vhost=VHOST/stream
|
||||
fullUrl = srs_strings_replace(fullUrl, "...vhost...", "?vhost=");
|
||||
|
||||
// Convert legacy RTMP URL format to standard format
|
||||
// Legacy: rtmp://ip/app/app2?vhost=xxx/stream
|
||||
// Standard: rtmp://ip/app/app2/stream?vhost=xxx
|
||||
fullUrl = srs_net_url_convert_legacy_rtmp_url(fullUrl);
|
||||
|
||||
// Remove the _definst_ of FMLE URL.
|
||||
if (fullUrl.find("/_definst_") != string::npos) {
|
||||
fullUrl = srs_strings_replace(fullUrl, "/_definst_", "");
|
||||
}
|
||||
|
||||
// Parse the standard URL.
|
||||
// Parse the standard URL using SrsHttpUri.
|
||||
SrsHttpUri uri;
|
||||
srs_error_t err = srs_success;
|
||||
if ((err = uri.initialize(fullUrl)) != srs_success) {
|
||||
|
|
@ -80,6 +73,7 @@ void srs_net_url_parse_tcurl(string tcUrl, string &schema, string &host, string
|
|||
return;
|
||||
}
|
||||
|
||||
// Extract basic URL components
|
||||
schema = uri.get_schema();
|
||||
host = uri.get_host();
|
||||
port = uri.get_port();
|
||||
|
|
@ -94,7 +88,7 @@ void srs_net_url_parse_tcurl(string tcUrl, string &schema, string &host, string
|
|||
if (app.empty())
|
||||
app = SRS_CONSTS_RTMP_DEFAULT_APP;
|
||||
|
||||
// Try to parse vhost from query, or use host if not specified.
|
||||
// Discover vhost from query parameters, 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");
|
||||
|
|
@ -109,6 +103,36 @@ void srs_net_url_parse_tcurl(string tcUrl, string &schema, string &host, string
|
|||
}
|
||||
}
|
||||
|
||||
string srs_net_url_convert_legacy_rtmp_url(const string &url)
|
||||
{
|
||||
// Check if this is a legacy RTMP URL format: rtmp://ip/app/app2?vhost=xxx/stream
|
||||
// We need to convert it to standard format: rtmp://ip/app/app2/stream?vhost=xxx
|
||||
|
||||
// Find the query part starting with ?
|
||||
size_t query_pos = url.find('?');
|
||||
|
||||
// Find the last slash in the URL
|
||||
size_t last_slash_pos = url.rfind('/');
|
||||
if (last_slash_pos == string::npos) {
|
||||
// No slash in URL, return as is
|
||||
return url;
|
||||
}
|
||||
|
||||
// Check for normal legacy case: query exists and slash is after query
|
||||
if (query_pos != string::npos && last_slash_pos > query_pos) {
|
||||
// Normal legacy case: rtmp://ip/app/app2?vhost=xxx/stream
|
||||
string base_url = url.substr(0, query_pos); // rtmp://ip/app/app2
|
||||
string query_part = url.substr(query_pos, last_slash_pos - query_pos); // ?vhost=xxx
|
||||
string stream_part = url.substr(last_slash_pos); // /stream
|
||||
|
||||
// Reconstruct as standard format: base_url + stream_part + query_part
|
||||
return base_url + stream_part + query_part;
|
||||
}
|
||||
|
||||
// No conversion needed, return as is
|
||||
return url;
|
||||
}
|
||||
|
||||
void srs_net_url_guess_stream(string &app, string ¶m, string &stream)
|
||||
{
|
||||
size_t pos = std::string::npos;
|
||||
|
|
|
|||
|
|
@ -56,6 +56,11 @@ class ISrsReader;
|
|||
extern void srs_net_url_parse_tcurl(std::string tcUrl, std::string &schema, std::string &host, std::string &vhost, std::string &app,
|
||||
std::string &stream, int &port, std::string ¶m);
|
||||
|
||||
// Convert legacy RTMP URL format to standard format.
|
||||
// Legacy format: rtmp://ip/app/app2?vhost=xxx/stream
|
||||
// Standard format: rtmp://ip/app/app2/stream?vhost=xxx
|
||||
extern std::string srs_net_url_convert_legacy_rtmp_url(const std::string &url);
|
||||
|
||||
// Guessing stream by app and param, to make OBS happy. For example:
|
||||
// rtmp://ip/live/livestream
|
||||
// rtmp://ip/live/livestream?secret=xxx
|
||||
|
|
|
|||
|
|
@ -5218,7 +5218,7 @@ VOID TEST(ProtocolRTMPTest, RTMPRequest)
|
|||
req.stream = "livestream";
|
||||
srs_net_url_parse_tcurl("rtmp://std.ossrs.net/live#b=2",
|
||||
req.schema, req.host, req.vhost, req.app, req.stream, req.port, param);
|
||||
EXPECT_STREQ("#b=2", param.c_str());
|
||||
EXPECT_STREQ("#b=2/livestream", param.c_str());
|
||||
|
||||
param = "";
|
||||
req.stream = "livestream";
|
||||
|
|
|
|||
|
|
@ -3091,6 +3091,94 @@ VOID TEST(ProtocolRTMPTest, ParseRTMPURL)
|
|||
}
|
||||
}
|
||||
|
||||
VOID TEST(ProtocolRTMPTest, ConvertLegacyRtmpUrl)
|
||||
{
|
||||
// Test legacy format conversion: rtmp://ip/app?vhost=xxx/stream -> rtmp://ip/app/stream?vhost=xxx
|
||||
if (true) {
|
||||
string legacy_url = "rtmp://127.0.0.1:1935/live?vhost=demo.com/livestream";
|
||||
string standard_url = srs_net_url_convert_legacy_rtmp_url(legacy_url);
|
||||
EXPECT_STREQ("rtmp://127.0.0.1:1935/live/livestream?vhost=demo.com", standard_url.c_str());
|
||||
}
|
||||
|
||||
// Test with multiple query parameters
|
||||
if (true) {
|
||||
string legacy_url = "rtmp://192.168.1.100/app?vhost=test.com&token=abc/stream";
|
||||
string standard_url = srs_net_url_convert_legacy_rtmp_url(legacy_url);
|
||||
EXPECT_STREQ("rtmp://192.168.1.100/app/stream?vhost=test.com&token=abc", standard_url.c_str());
|
||||
}
|
||||
|
||||
// Test with complex stream name
|
||||
if (true) {
|
||||
string legacy_url = "rtmp://host.com/myapp?vhost=vhost.com/stream_name_123";
|
||||
string standard_url = srs_net_url_convert_legacy_rtmp_url(legacy_url);
|
||||
EXPECT_STREQ("rtmp://host.com/myapp/stream_name_123?vhost=vhost.com", standard_url.c_str());
|
||||
}
|
||||
|
||||
// Test with multiple app levels: rtmp://ip/app/app2?vhost=xxx/stream
|
||||
if (true) {
|
||||
string legacy_url = "rtmp://127.0.0.1:1935/live/sub?vhost=demo.com/livestream";
|
||||
string standard_url = srs_net_url_convert_legacy_rtmp_url(legacy_url);
|
||||
EXPECT_STREQ("rtmp://127.0.0.1:1935/live/sub/livestream?vhost=demo.com", standard_url.c_str());
|
||||
}
|
||||
|
||||
// Test with three app levels: rtmp://ip/app/app2/app3?vhost=xxx/stream
|
||||
if (true) {
|
||||
string legacy_url = "rtmp://192.168.1.100/app1/app2/app3?vhost=test.com&token=abc/stream";
|
||||
string standard_url = srs_net_url_convert_legacy_rtmp_url(legacy_url);
|
||||
EXPECT_STREQ("rtmp://192.168.1.100/app1/app2/app3/stream?vhost=test.com&token=abc", standard_url.c_str());
|
||||
}
|
||||
|
||||
// Test with multiple app levels and complex parameters
|
||||
if (true) {
|
||||
string legacy_url = "rtmp://host.com:8080/live/hls/dash?vhost=cdn.com&key=value&auth=token/my_stream_123";
|
||||
string standard_url = srs_net_url_convert_legacy_rtmp_url(legacy_url);
|
||||
EXPECT_STREQ("rtmp://host.com:8080/live/hls/dash/my_stream_123?vhost=cdn.com&key=value&auth=token", standard_url.c_str());
|
||||
}
|
||||
|
||||
// Test standard format (should remain unchanged)
|
||||
if (true) {
|
||||
string standard_url = "rtmp://127.0.0.1:1935/live/livestream?vhost=demo.com";
|
||||
string result_url = srs_net_url_convert_legacy_rtmp_url(standard_url);
|
||||
EXPECT_STREQ("rtmp://127.0.0.1:1935/live/livestream?vhost=demo.com", result_url.c_str());
|
||||
}
|
||||
|
||||
// Test URL without query string (should remain unchanged)
|
||||
if (true) {
|
||||
string simple_url = "rtmp://127.0.0.1:1935/live/livestream";
|
||||
string result_url = srs_net_url_convert_legacy_rtmp_url(simple_url);
|
||||
EXPECT_STREQ("rtmp://127.0.0.1:1935/live/livestream", result_url.c_str());
|
||||
}
|
||||
|
||||
// Test URL with query but no slash after query (should remain unchanged)
|
||||
if (true) {
|
||||
string url_no_slash = "rtmp://127.0.0.1:1935/live?vhost=demo.com";
|
||||
string result_url = srs_net_url_convert_legacy_rtmp_url(url_no_slash);
|
||||
EXPECT_STREQ("rtmp://127.0.0.1:1935/live?vhost=demo.com", result_url.c_str());
|
||||
}
|
||||
|
||||
// Test special case: malformed URL with stream appended after fragments
|
||||
// This happens when URL building process appends stream to URLs that already have fragments
|
||||
if (true) {
|
||||
string malformed_url = "rtmp://std.ossrs.net/live#b=2/livestream";
|
||||
string fixed_url = srs_net_url_convert_legacy_rtmp_url(malformed_url);
|
||||
EXPECT_STREQ("rtmp://std.ossrs.net/live#b=2/livestream", fixed_url.c_str());
|
||||
}
|
||||
|
||||
// Test special case: malformed URL with query and fragment, stream appended after
|
||||
if (true) {
|
||||
string malformed_url = "rtmp://host.com/app?a=1#b=2/stream";
|
||||
string fixed_url = srs_net_url_convert_legacy_rtmp_url(malformed_url);
|
||||
EXPECT_STREQ("rtmp://host.com/app/stream?a=1#b=2", fixed_url.c_str());
|
||||
}
|
||||
|
||||
// Test special case: malformed URL with complex fragments, stream appended after
|
||||
if (true) {
|
||||
string malformed_url = "rtmp://test.com/live?vhost=demo&token=abc#fragment1#fragment2/mystream";
|
||||
string fixed_url = srs_net_url_convert_legacy_rtmp_url(malformed_url);
|
||||
EXPECT_STREQ("rtmp://test.com/live/mystream?vhost=demo&token=abc#fragment1#fragment2", fixed_url.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
VOID TEST(ProtocolRTMPTest, GenerateURL)
|
||||
{
|
||||
if (true) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user