diff --git a/trunk/doc/CHANGELOG.md b/trunk/doc/CHANGELOG.md index 98cf04987..0050a902f 100644 --- a/trunk/doc/CHANGELOG.md +++ b/trunk/doc/CHANGELOG.md @@ -7,6 +7,7 @@ The changelog for SRS. ## SRS 7.0 Changelog +* v7.0, 2025-10-26, AI: Edge: Fix stream names with dots being incorrectly truncated in source URL generation. v7.0.109 (#4011) * v7.0, 2025-10-26, AI: HTTPS: Handle SSL_ERROR_ZERO_RETURN as graceful connection closure. v7.0.108 (#4036) * v7.0, 2025-10-26, AI: API: Add clients field to on_play/on_stop webhooks and total field to HTTP API. v7.0.107 (#4147) * v7.0, 2025-10-26, AI: WebRTC: Fix camera/microphone not released after closing publisher. v7.0.106 (#4261) diff --git a/trunk/src/core/srs_core_version7.hpp b/trunk/src/core/srs_core_version7.hpp index c6d62d463..310f76167 100644 --- a/trunk/src/core/srs_core_version7.hpp +++ b/trunk/src/core/srs_core_version7.hpp @@ -9,6 +9,6 @@ #define VERSION_MAJOR 7 #define VERSION_MINOR 0 -#define VERSION_REVISION 108 +#define VERSION_REVISION 109 #endif \ No newline at end of file diff --git a/trunk/src/protocol/srs_protocol_utility.cpp b/trunk/src/protocol/srs_protocol_utility.cpp index 390eb46c1..f9f62ede7 100644 --- a/trunk/src/protocol/srs_protocol_utility.cpp +++ b/trunk/src/protocol/srs_protocol_utility.cpp @@ -260,9 +260,21 @@ string srs_net_url_encode_sid(string vhost, string app, string stream) url += vhost; } url += "/" + app; - // Note that we ignore any extension. - SrsPath path; - url += "/" + path.filepath_filename(stream); + + // Strip only known streaming extensions, not arbitrary dots in stream names. + // This fixes issue 4011 where stream names like "WavMain.exe_rooms_290" were + // incorrectly truncated to "WavMain" because filepath_filename() stripped + // everything after any dot. + std::string stream_name = stream; + size_t pos = stream_name.rfind("."); + if (pos != string::npos) { + std::string ext = stream_name.substr(pos); + // Only strip known streaming extensions + if (ext == ".flv" || ext == ".m3u8" || ext == ".mp4" || ext == ".ts") { + stream_name = stream_name.substr(0, pos); + } + } + url += "/" + stream_name; return url; } diff --git a/trunk/src/utest/srs_utest_manual_protocol2.cpp b/trunk/src/utest/srs_utest_manual_protocol2.cpp index 9512111ef..5341bdc92 100644 --- a/trunk/src/utest/srs_utest_manual_protocol2.cpp +++ b/trunk/src/utest/srs_utest_manual_protocol2.cpp @@ -4972,6 +4972,51 @@ VOID TEST(ProtocolKbpsTest, StreamIdentify) EXPECT_STREQ("ossrs.io/live/livestream", srs_net_url_encode_sid("ossrs.io", "live", "livestream.m3u8").c_str()); } +// Reproduce bug from issue 4011: Stream names with dots get truncated incorrectly +// https://github.com/ossrs/srs/issues/4011 +// The filepath_filename() function removes everything after the LAST dot in the stream name. +// This was intended to strip extensions like .flv, but it breaks stream names that contain +// dots as part of the identifier (like WavMain.exe_rooms_290_20240402). +// This causes edge servers to treat different streams as the same source. +// +// This test FAILS to demonstrate the bug - when the bug is fixed, this test should PASS. +VOID TEST(ProtocolKbpsTest, StreamIdentifyWithDotsInName_Issue4011) +{ + // Case 1: THE ACTUAL BUG from issue 4011 + // Stream names like "WavMain.exe_rooms_290_20240402" contain a dot in ".exe" + // Expected: Each stream should have a unique source URL preserving the full name + // Actual (BUG): filepath_filename() strips everything after the dot in ".exe" + std::string url1 = srs_net_url_encode_sid("", "imlive", "WavMain.exe_rooms_290_20240402"); + std::string url2 = srs_net_url_encode_sid("", "imlive", "WavMain.exe_rooms_311_20240402"); + std::string url3 = srs_net_url_encode_sid("", "imlive", "WavMain.exe_rooms_222_20240402"); + + // EXPECTED: Each stream should preserve its full name + EXPECT_STREQ("/imlive/WavMain.exe_rooms_290_20240402", url1.c_str()); + EXPECT_STREQ("/imlive/WavMain.exe_rooms_311_20240402", url2.c_str()); + EXPECT_STREQ("/imlive/WavMain.exe_rooms_222_20240402", url3.c_str()); + + // EXPECTED: Different streams should have different URLs + EXPECT_STRNE(url1.c_str(), url2.c_str()); + EXPECT_STRNE(url1.c_str(), url3.c_str()); + EXPECT_STRNE(url2.c_str(), url3.c_str()); + + // Case 2: Stream names with multiple dots should preserve the full name + // (only strip known extensions like .flv, .m3u8, not arbitrary suffixes) + EXPECT_STREQ("/live/test.backup.stream", srs_net_url_encode_sid("", "live", "test.backup.stream").c_str()); + EXPECT_STREQ("/live/my.stream.v2", srs_net_url_encode_sid("", "live", "my.stream.v2").c_str()); + EXPECT_STREQ("/live/camera.1.hd", srs_net_url_encode_sid("", "live", "camera.1.hd").c_str()); + + // Case 3: Different streams with same prefix should be different + std::string backup1 = srs_net_url_encode_sid("", "live", "test.backup.stream"); + std::string backup2 = srs_net_url_encode_sid("", "live", "test.backup.stream2"); + EXPECT_STRNE(backup1.c_str(), backup2.c_str()); // Should be different + + // Case 4: The intended behavior - stripping ONLY known extensions + // These should work correctly (strip .flv and .m3u8) + EXPECT_STREQ("/live/livestream", srs_net_url_encode_sid("", "live", "livestream.flv").c_str()); + EXPECT_STREQ("/live/livestream", srs_net_url_encode_sid("", "live", "livestream.m3u8").c_str()); +} + VOID TEST(ProtocolHTTPTest, ParseHTTPMessage) { srs_error_t err = srs_success;