From 845e0287c018e7a83b70de0df831c4ec1e9ea742 Mon Sep 17 00:00:00 2001 From: Winlin Date: Mon, 20 Oct 2025 08:03:07 -0400 Subject: [PATCH] Forward: Reject RTMPS destinations with clear error message. v7.0.100 (#4537) SRS forward feature only supports plain RTMP protocol, not RTMPS (RTMP over SSL/TLS). This is by design - SRS SSL is server-side only (accepting connections), not client-side (initiating connections). The forward feature uses SrsSimpleRtmpClient which has no SSL handshake or encryption capabilities for outgoing connections. Changes: 1. Add RTMPS URL detection in SrsForwarder::initialize() 2. Return ERROR_NOT_SUPPORTED error when RTMPS destination is detected 3. Add unit test to verify RTMPS URLs are properly rejected 4. Add FAQ section to .augment-guidelines explaining the limitation For users who need to forward to RTMPS destinations (e.g., AWS IVS), the recommended solution is to use FFmpeg with SRS HTTP Hooks: - on_publish event: Automatically start FFmpeg to relay stream to RTMPS destination - on_unpublish event: Automatically stop FFmpeg process when stream ends This provides a fully automated, production-ready RTMPS relay solution without adding complexity to SRS core. Related: #4536 --------- Co-authored-by: OSSRS-AI --- .augment-guidelines | 17 ++++++++++++++- trunk/doc/CHANGELOG.md | 1 + trunk/src/app/srs_app_forward.cpp | 6 ++++++ trunk/src/core/srs_core_version7.hpp | 2 +- trunk/src/utest/srs_utest_ai14.cpp | 32 ++++++++++++++++++++++++++++ 5 files changed, 56 insertions(+), 2 deletions(-) diff --git a/.augment-guidelines b/.augment-guidelines index c8223c089..f73db8bbd 100644 --- a/.augment-guidelines +++ b/.augment-guidelines @@ -893,6 +893,21 @@ documentation: description: | SRS documentation source files are located in the 3rdparty/srs-docs directory usage: | - When looking for documentation or need to update docs, check this directory for markdown + When looking for documentation or need to update docs, check this directory for markdown files and documentation structure. Do not search ossrs.io or ossrs.net for documentation, use the local files instead. +faq: + rtmps_forward: + question: "Why doesn't SRS support RTMPS in the forward feature? Why does SRS only support RTMPS server but not forwarding to RTMPS endpoints?" + + answer: | + SRS doesn't support RTMPS (RTMP over SSL/TLS) in the forward feature because: + 1. SRS SSL is designed for server-side only (accepting connections), not client-side (initiating connections) + 2. Forward feature uses SrsSimpleRtmpClient which only supports plain RTMP protocol + 3. Adding RTMPS client support would add significant complexity for a rare use case + + Recommended solution: Use FFmpeg with SRS HTTP Hooks + - on_publish event: Automatically start FFmpeg to relay stream to RTMPS destination (e.g., AWS IVS) + - on_unpublish event: Automatically stop FFmpeg process when stream ends + - This provides fully automated, production-ready RTMPS relay without adding complexity to SRS core + diff --git a/trunk/doc/CHANGELOG.md b/trunk/doc/CHANGELOG.md index 740a7f373..04f8cdf7e 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-20, Merge [#4537](https://github.com/ossrs/srs/pull/4537): Forward: Reject RTMPS destinations with clear error message. v7.0.100 (#4537) * v7.0, 2025-10-17, Merge [#4534](https://github.com/ossrs/srs/pull/4534): HLS: Fix a iterator bug in hls_ctx cleanup function. v7.0.99 (#4534) * v7.0, 2025-10-16, Merge [#4530](https://github.com/ossrs/srs/pull/4530): fix crash issue caused by reload configuration file. v7.0.98 (#4530) * v7.0, 2025-10-15, Merge [#4520](https://github.com/ossrs/srs/pull/4520): srs_app_rtc_conn: fix illegal memory access. v7.0.97 (#4520) diff --git a/trunk/src/app/srs_app_forward.cpp b/trunk/src/app/srs_app_forward.cpp index f7b5c3862..f7962001e 100644 --- a/trunk/src/app/srs_app_forward.cpp +++ b/trunk/src/app/srs_app_forward.cpp @@ -75,6 +75,12 @@ srs_error_t SrsForwarder::initialize(ISrsRequest *r, string ep) // the ep(endpoint) to forward to ep_forward_ = ep; + // Check if the forward destination is RTMPS URL + // SRS forward only supports plain RTMP protocol, not RTMPS (RTMP over SSL/TLS) + if (ep_forward_.find("rtmps://") != string::npos) { + return srs_error_new(ERROR_NOT_SUPPORTED, "forward does not support RTMPS destination=%s", ep_forward_.c_str()); + } + // Remember the source context id. source_cid_ = _srs_context->get_id(); diff --git a/trunk/src/core/srs_core_version7.hpp b/trunk/src/core/srs_core_version7.hpp index d5ffe5c74..3d199c7ba 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 99 +#define VERSION_REVISION 100 #endif \ No newline at end of file diff --git a/trunk/src/utest/srs_utest_ai14.cpp b/trunk/src/utest/srs_utest_ai14.cpp index b4393e672..6146726bb 100644 --- a/trunk/src/utest/srs_utest_ai14.cpp +++ b/trunk/src/utest/srs_utest_ai14.cpp @@ -2892,6 +2892,38 @@ VOID TEST(AppOriginHubTest, CreateBackendForwardersTypicalScenario) srs_freep(mock_hooks); } +// Unit test for SrsForwarder RTMPS detection +VOID TEST(AppForwarderTest, RejectRtmpsDestination) +{ + srs_error_t err; + + // Create mock origin hub + MockLiveSourceForOriginHub *mock_source = new MockLiveSourceForOriginHub(); + SrsUniquePtr hub(new SrsOriginHub()); + hub->source_ = mock_source; + + // Create forwarder + SrsUniquePtr forwarder(new SrsForwarder(hub.get())); + + // Create mock request + SrsUniquePtr req(new MockHlsRequest()); + + // Test the hostport spliting + std::string server; + int port = SRS_CONSTS_RTMP_DEFAULT_PORT; + srs_net_split_hostport("rtmps://fake.demo.ossrs.io:443/app", server, port); + EXPECT_STREQ("rtmps://fake.demo.ossrs.io:443/app", server.c_str()); + + // Test 1: RTMPS URL should be rejected + HELPER_ASSERT_FAILED(forwarder->initialize(req.get(), "rtmps://fake.demo.ossrs.io:443/app")); + + // Test 2: Plain RTMP URL should be accepted + HELPER_EXPECT_SUCCESS(forwarder->initialize(req.get(), "127.0.0.1:1935")); + + // Cleanup + srs_freep(mock_source); +} + VOID TEST(SrsLiveSourceTest, OnAggregateSelectionTypical) { srs_error_t err;