AI: HLS: Support query string in hls_key_url for JWT tokens. v7.0.120 (#4426)
This commit is contained in:
parent
0c26a3c309
commit
99970d6ba0
|
|
@ -1540,8 +1540,11 @@ vhost hls.srs.com {
|
|||
hls_key_file_path ./objs/nginx/html;
|
||||
# the key root URL, use this can support https.
|
||||
# @remark It's optional.
|
||||
# @remark Supports query string for authentication tokens (e.g., JWT).
|
||||
# Example: http://localhost:8080/?token=abc123&sig=xyz789
|
||||
# Result in m3u8: http://localhost:8080/live/livestream-0.key?token=abc123&sig=xyz789
|
||||
# Overwrite by env SRS_VHOST_HLS_HLS_KEY_URL for all vhosts.
|
||||
hls_key_url https://localhost:8080;
|
||||
hls_key_url http://localhost:8080/;
|
||||
|
||||
# Special control controls.
|
||||
###########################################
|
||||
|
|
|
|||
35
trunk/conf/hls-encrypted-query.conf
Normal file
35
trunk/conf/hls-encrypted-query.conf
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# Test config for HLS encryption with query string in hls_key_url
|
||||
# This tests the fix for issue #4426
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
|
||||
http_server {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
dir ./objs/nginx/html;
|
||||
}
|
||||
|
||||
vhost __defaultVhost__ {
|
||||
hls {
|
||||
enabled on;
|
||||
hls_fragment 10;
|
||||
hls_window 60;
|
||||
hls_path ./objs/nginx/html;
|
||||
hls_m3u8_file [app]/[stream].m3u8;
|
||||
hls_ts_file [app]/[stream]-[seq].ts;
|
||||
|
||||
# Enable AES-128 encryption
|
||||
hls_keys on;
|
||||
hls_fragments_per_key 5;
|
||||
hls_key_file [app]/[stream]-[seq].key;
|
||||
hls_key_file_path ./objs/nginx/html;
|
||||
|
||||
# Test with query string - this should now work correctly
|
||||
# Expected result in m3u8: http://localhost:8080/live/livestream-0.key?token=abc123&sig=xyz789
|
||||
hls_key_url http://localhost:8080/?token=abc123&sig=xyz789;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -7,6 +7,7 @@ The changelog for SRS.
|
|||
<a name="v7-changes"></a>
|
||||
|
||||
## SRS 7.0 Changelog
|
||||
* v7.0, 2025-11-07, AI: HLS: Support query string in hls_key_url for JWT tokens. v7.0.120 (#4426)
|
||||
* v7.0, 2025-11-07, AI: RTC: Support keep_original_ssrc to preserve SSRC and timestamps. v7.0.119 (#3850)
|
||||
* v7.0, 2025-11-05, AI: WebRTC: Report video/audio codec info and frame stats in HTTP API. v7.0.118 (#4554)
|
||||
* v7.0, 2025-11-04, AI: SRT: Report video/audio codec info and frame stats in HTTP API. v7.0.117 (#4554)
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ using namespace std;
|
|||
#include <srs_kernel_utility.hpp>
|
||||
#include <srs_protocol_amf0.hpp>
|
||||
#include <srs_protocol_format.hpp>
|
||||
#include <srs_protocol_http_stack.hpp>
|
||||
#include <srs_protocol_rtmp_stack.hpp>
|
||||
#include <srs_protocol_stream.hpp>
|
||||
|
||||
|
|
@ -45,6 +46,33 @@ using namespace std;
|
|||
// reset the piece id when deviation overflow this.
|
||||
#define SRS_JUMP_WHEN_PIECE_DEVIATION 20
|
||||
|
||||
// Build the full key URL by appending key_file to hls_key_url with proper query string handling.
|
||||
// If hls_key_url contains query string like "http://localhost:8080/?token=abc",
|
||||
// the result will be "http://localhost:8080/live/livestream-0.key?token=abc"
|
||||
// @param hls_key_url The base URL which may contain query string
|
||||
// @param key_file The key file path like "live/livestream-0.key"
|
||||
// @return The full key URL with query string properly appended
|
||||
string srs_hls_build_key_url(const string &hls_key_url, const string &key_file)
|
||||
{
|
||||
if (hls_key_url.empty()) {
|
||||
return key_file;
|
||||
}
|
||||
|
||||
// Find the query string separator
|
||||
size_t pos = hls_key_url.find("?");
|
||||
if (pos != string::npos) {
|
||||
// URL contains query string, split and rebuild
|
||||
// Example: "http://localhost:8080/?token=abc" + "live/livestream-0.key"
|
||||
// Result: "http://localhost:8080/live/livestream-0.key?token=abc"
|
||||
string base_url = hls_key_url.substr(0, pos);
|
||||
string query_string = hls_key_url.substr(pos); // Include the '?'
|
||||
return base_url + key_file + query_string;
|
||||
}
|
||||
|
||||
// No query string, simple concatenation
|
||||
return hls_key_url + key_file;
|
||||
}
|
||||
|
||||
SrsHlsSegment::SrsHlsSegment(SrsTsContext *c, SrsAudioCodecId ac, SrsVideoCodecId vc, ISrsFileWriter *w)
|
||||
{
|
||||
sequence_no_ = 0;
|
||||
|
|
@ -1107,11 +1135,7 @@ srs_error_t SrsHlsFmp4Muxer::do_refresh_m3u8_segment(SrsHlsM4sSegment *segment,
|
|||
string key_file = srs_path_build_stream(hls_key_file_, req_->vhost_, req_->app_, req_->stream_);
|
||||
key_file = srs_strings_replace(key_file, "[seq]", srs_strconv_format_int(segment->sequence_no_));
|
||||
|
||||
string key_path = key_file;
|
||||
// if key_url is not set,only use the file name
|
||||
if (!hls_key_url_.empty()) {
|
||||
key_path = hls_key_url_ + key_file;
|
||||
}
|
||||
string key_path = srs_hls_build_key_url(hls_key_url_, key_file);
|
||||
|
||||
ss << "#EXT-X-KEY:METHOD=SAMPLE-AES,URI=" << "\"" << key_path << "\",IV=0x" << hexiv << SRS_CONSTS_LF;
|
||||
}
|
||||
|
|
@ -2040,11 +2064,7 @@ srs_error_t SrsHlsMuxer::do_refresh_m3u8_segment(SrsHlsSegment *segment, std::st
|
|||
string key_file = srs_path_build_stream(hls_key_file_, req_->vhost_, req_->app_, req_->stream_);
|
||||
key_file = srs_strings_replace(key_file, "[seq]", srs_strconv_format_int(segment->sequence_no_));
|
||||
|
||||
string key_path = key_file;
|
||||
// if key_url is not set,only use the file name
|
||||
if (!hls_key_url_.empty()) {
|
||||
key_path = hls_key_url_ + key_file;
|
||||
}
|
||||
string key_path = srs_hls_build_key_url(hls_key_url_, key_file);
|
||||
|
||||
ss << "#EXT-X-KEY:METHOD=AES-128,URI=" << "\"" << key_path << "\",IV=0x" << hexiv << SRS_CONSTS_LF;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,6 @@
|
|||
|
||||
#define VERSION_MAJOR 7
|
||||
#define VERSION_MINOR 0
|
||||
#define VERSION_REVISION 119
|
||||
#define VERSION_REVISION 120
|
||||
|
||||
#endif
|
||||
|
|
@ -111,6 +111,7 @@ public:
|
|||
void reset();
|
||||
};
|
||||
|
||||
#ifdef SRS_RTSP
|
||||
// Forward declaration
|
||||
class SrsRtspConsumer;
|
||||
|
||||
|
|
@ -159,5 +160,6 @@ public:
|
|||
void set_send_error(srs_error_t err);
|
||||
void reset();
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1549,6 +1549,7 @@ void MockStatisticForRtspPlayStream::reset()
|
|||
srs_freep(on_client_error_);
|
||||
}
|
||||
|
||||
#ifdef SRS_RTSP
|
||||
// MockRtspSourceManager implementation
|
||||
MockRtspSourceManager::MockRtspSourceManager()
|
||||
{
|
||||
|
|
@ -2750,6 +2751,7 @@ VOID TEST(RtspTcpNetworkTest, WriteRtpPacket)
|
|||
// Verify the payload data (starts at offset 4)
|
||||
EXPECT_EQ(0, memcmp(rtp_packet, output + 4, kRtpPacketSize));
|
||||
}
|
||||
#endif
|
||||
|
||||
// MockDvrPlan implementation
|
||||
MockDvrPlan::MockDvrPlan()
|
||||
|
|
|
|||
|
|
@ -354,6 +354,7 @@ public:
|
|||
void reset();
|
||||
};
|
||||
|
||||
#ifdef SRS_RTSP
|
||||
// Mock ISrsRtspSourceManager for testing SrsRtspPlayStream
|
||||
class MockRtspSourceManager : public ISrsRtspSourceManager
|
||||
{
|
||||
|
|
@ -456,6 +457,7 @@ public:
|
|||
virtual void set_all_tracks_status(bool status);
|
||||
void reset();
|
||||
};
|
||||
#endif
|
||||
|
||||
// Mock ISrsDvrPlan for testing SrsDvrSegmenter
|
||||
class MockDvrPlan : public ISrsDvrPlan
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ using namespace std;
|
|||
#include <srs_utest_manual_kernel.hpp>
|
||||
#include <srs_utest_manual_protocol.hpp>
|
||||
|
||||
#ifdef SRS_GB28181
|
||||
// Mock ISrsGbMuxer implementation
|
||||
MockGbMuxer::MockGbMuxer()
|
||||
{
|
||||
|
|
@ -2294,6 +2295,7 @@ VOID TEST(GB28181Test, GoApiGbPublishSuccess)
|
|||
|
||||
srs_freep(conf);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Mock ISrsRtcNetwork implementation
|
||||
MockRtcNetworkForNetworks::MockRtcNetworkForNetworks()
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#include <srs_app_rtsp_conn.hpp>
|
||||
#endif
|
||||
|
||||
#ifdef SRS_GB28181
|
||||
// Mock ISrsGbMuxer for testing SrsGbSession
|
||||
class MockGbMuxer : public ISrsGbMuxer
|
||||
{
|
||||
|
|
@ -541,6 +542,7 @@ public:
|
|||
#endif
|
||||
void reset();
|
||||
};
|
||||
#endif
|
||||
|
||||
// Mock ISrsRtcNetwork for testing SrsRtcNetworks
|
||||
class MockRtcNetworkForNetworks : public ISrsRtcNetwork
|
||||
|
|
|
|||
|
|
@ -517,6 +517,7 @@ public:
|
|||
virtual int get_rtc_drop_for_pt(std::string vhost) { return rtc_drop_for_pt_; }
|
||||
virtual bool get_rtc_twcc_enabled(std::string vhost) { return rtc_twcc_enabled_; }
|
||||
virtual bool get_rtc_init_rate_from_sdp(std::string vhost) { return rtc_init_rate_from_sdp_; }
|
||||
virtual bool get_rtc_keep_original_ssrc(std::string vhost) { return false; }
|
||||
virtual bool get_srt_enabled() { return srt_enabled_; }
|
||||
virtual bool get_srt_enabled(std::string vhost) { return srt_enabled_; }
|
||||
virtual std::string get_srt_default_streamid() { return "#!::r=live/livestream,m=request"; }
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user