diff --git a/trunk/conf/console.ipv46.conf b/trunk/conf/console.ipv46.conf
index 9e97d9780..508a20457 100644
--- a/trunk/conf/console.ipv46.conf
+++ b/trunk/conf/console.ipv46.conf
@@ -7,15 +7,77 @@ srs_log_tank console;
# RTMP server configuration
rtmp {
- listen 1935 [::]:1935;
+ listen 1935 [::]:1935;
+}
+rtmps {
+ enabled on;
+ listen 1443 [::]:1443;
}
http_api {
- enabled on;
- listen 1985;
+ enabled on;
+ listen 1985 [::]:1985;
+ https {
+ enabled on;
+ listen 1990 [::]:1990;
+ }
}
http_server {
- enabled on;
- listen 8080;
+ enabled on;
+ listen 8080 [::]:8080;
+ https {
+ enabled on;
+ listen 8088 [::]:8088;
+ }
+}
+rtc_server {
+ enabled on;
+
+ # @see https://ossrs.io/lts/en-us/docs/v7/doc/webrtc#config-candidate
+ candidate $CANDIDATE;
+ use_auto_detect_network_ip on;
+ #ip_family ipv4;
+ #ip_family ipv6;
+ ip_family all;
+ api_as_candidates off;
+
+ #protocol udp;
+ #protocol tcp;
+ protocol all;
+
+ # UDP port - IPv4 and IPv6
+ listen 8000 [::]:8000;
+
+ tcp {
+ enabled on;
+ listen 8000 [::]:8000;
+ }
+}
+srt_server {
+ enabled on;
+ listen 10080 [::]:10080;
+}
+rtsp_server {
+ enabled on;
+ listen 8554 [::]:8554;
}
vhost __defaultVhost__ {
+ http_remux {
+ enabled on;
+ mount [vhost]/[app]/[stream].flv;
+ }
+ rtsp {
+ enabled on;
+ rtmp_to_rtsp on;
+ }
+ rtc {
+ enabled on;
+ # @see https://ossrs.io/lts/en-us/docs/v7/doc/webrtc#rtmp-to-rtc
+ rtmp_to_rtc on;
+ # @see https://ossrs.io/lts/en-us/docs/v7/doc/webrtc#rtc-to-rtmp
+ rtc_to_rtmp on;
+ }
+ srt {
+ enabled on;
+ srt_to_rtmp on;
+ }
}
diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf
index e45d823e7..0f3d3551f 100644
--- a/trunk/conf/full.conf
+++ b/trunk/conf/full.conf
@@ -168,9 +168,9 @@ tcmalloc_release_rate 0.8;
#############################################################################################
# the rtmp server section
rtmp {
- # the rtmp listen ports, split by space, each listen entry is <[ip:]port>
- # for example, 192.168.1.100:1935 10.10.10.100:1935
- # where the ip is optional, default to 0.0.0.0, that is 1935 equals to 0.0.0.0:1935
+ # The rtmp listen endpoints, split by space, each listen entry is <[ip:]port>. The ip can be
+ # ipv4, ipv6, or both. For example:
+ # listen 1935 [::]:1935 192.168.1.100:1935 10.10.10.100:1935;
# Overwrite by env SRS_LISTEN
listen 1935;
# the default chunk size is 128, max is 65536,
@@ -188,7 +188,9 @@ rtmps {
# Overwrite by env SRS_RTMPS_ENABLED
# default: off
enabled on;
- # The rtmps listen port
+ # The rtmps listen endpoints, each with format as <[ip:]port>. The ip can be either ipv4 or ipv6,
+ # or both. For example:
+ # listen 1443 [::]:1443;
# Overwrite by env SRS_RTMPS_LISTEN
listen 1443;
# The SSL private key file, generated by:
@@ -410,10 +412,9 @@ http_api {
# Overwrite by env SRS_HTTP_API_ENABLED
# default: off
enabled on;
- # The http api listen entry is <[ip:]port>, For example, 192.168.1.100:8080, where the ip is optional, default to
- # 0.0.0.0, that is 8080 equals to 0.0.0.0:8080.
- # Note that you're able to use a dedicated port for HTTP API, such as 1985, to be different with HTTP server. In
- # this situation, you you must also set another HTTPS API port.
+ # The http api listen endpoints, each is format as <[ip:]port>. The ip can be either ipv4
+ # or ipv6, or both. For example:
+ # listen 1985 [::]:1985 192.168.1.100:1985 10.10.10.100:1985;
# Overwrite by env SRS_HTTP_API_LISTEN
# Default: 1985
listen 8080;
@@ -458,9 +459,9 @@ http_api {
# Overwrite by env SRS_HTTP_API_HTTPS_ENABLED
# default: off
enabled on;
- # The listen endpoint for HTTPS API.
- # Note that you're able to use a dedicated port for HTTPS API, such as 1990, and the HTTP API should not be
- # the same of HTTP server(8080) neither.
+ # The listen endpoint for HTTPS API, each with format as <[ip:]port>. The ip can be either ipv4 or ipv6,
+ # or both. For example:
+ # listen 1990 [::]:1990 192.168.1.100:1990 10.10.10.100:1990;
# Overwrite by env SRS_HTTP_API_HTTPS_LISTEN
# Default: 1990
listen 8088;
@@ -491,10 +492,9 @@ http_server {
# Overwrite by env SRS_HTTP_SERVER_ENABLED
# default: off
enabled on;
- # the http streaming listen entry is <[ip:]port>
- # for example, 192.168.1.100:8080
- # where the ip is optional, default to 0.0.0.0, that is 8080 equals to 0.0.0.0:8080
- # @remark, if use lower port, for instance 80, user must start srs by root.
+ # The http streaming listen endpoints, each with format as <[ip:]port>. The ip can be either ipv4 or ipv6,
+ # or both. For example:
+ # listen 8080 [::]:8080 192.168.1.100:8080 10.10.10.100:8080;
# Overwrite by env SRS_HTTP_SERVER_LISTEN
# default: 8080
listen 8080;
@@ -514,7 +514,9 @@ http_server {
# Overwrite by env SRS_HTTP_SERVER_HTTPS_ENABLED
# default: off
enabled on;
- # The listen endpoint for HTTPS Streaming.
+ # The listen endpoint for HTTPS Streaming, each with format as <[ip:]port>. The ip can be either ipv4 or ipv6,
+ # or both. For example:
+ # listen 8088 [::]:8088 192.168.1.100:8088 10.10.10.100:8088;
# Overwrite by env SRS_HTTP_SERVER_HTTPS_LISTEN
# default: 8088
listen 8088;
@@ -653,7 +655,9 @@ srt_server {
# Overwrite by env SRS_SRT_SERVER_ENABLED
# default: off
enabled on;
- # The UDP listen port for SRT.
+ # The UDP listen endpoints for SRT, each with format as <[ip:]port>. The ip can be either ipv4 or ipv6,
+ # or both. For example:
+ # listen 10080 [::]:10080 192.168.1.100:10080 10.10.10.100:10080;
# Overwrite by env SRS_SRT_SERVER_LISTEN
listen 10080;
# For detail parameters, please read wiki:
@@ -754,7 +758,9 @@ rtc_server {
# Overwrite by env SRS_RTC_SERVER_ENABLED
# default: off
enabled on;
- # The udp listen port, we will reuse it for connections.
+ # The udp listen endpoints, we will reuse it for connections, each with format as <[ip:]port>.
+ # The ip can be either ipv4 or ipv6, or both. For example:
+ # listen 8000 [::]:8000 192.168.1.100:8000 10.10.10.100:8000;
# Overwrite by env SRS_RTC_SERVER_LISTEN
# default: 8000
listen 8000;
@@ -765,8 +771,11 @@ rtc_server {
# Overwrite by env SRS_RTC_SERVER_TCP_ENABLED
# Default: off
enabled off;
- # The TCP listen port for WebRTC. Highly recommend is some normally used ports, such as TCP/80, TCP/443,
- # TCP/8000, TCP/8080 etc. However SRS default to TCP/8000 corresponding to UDP/8000.
+ # The TCP listen endpoints for WebRTC, each with format as <[ip:]port>. The ip can be either ipv4 or ipv6,
+ # or both. For example:
+ # listen 8000 [::]:8000 192.168.1.100:8000 10.10.10.100:8000;
+ # Highly recommend is some normally used ports, such as TCP/80, TCP/443, TCP/8000, TCP/8080 etc.
+ # However SRS default to TCP/8000 corresponding to UDP/8000.
# Overwrite by env SRS_RTC_SERVER_TCP_LISTEN
# Default: 8000
listen 8000;
@@ -1544,7 +1553,9 @@ rtsp_server {
# Overwrite by env SRS_RTSP_SERVER_ENABLED
# default: off
enabled on;
- # The listen port for RTSP server.
+ # The listen endpoints for RTSP server, each with format as <[ip:]port>. The ip can be
+ # either ipv4 or ipv6, or both. For example:
+ # listen 554 [::]:554 192.168.1.100:554 10.10.10.100:554;
# Overwrite by env SRS_RTSP_SERVER_LISTEN
# default: 554
listen 8554;
diff --git a/trunk/doc/CHANGELOG.md b/trunk/doc/CHANGELOG.md
index 5d919bf42..873295cdc 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-08-29, Merge [#4457](https://github.com/ossrs/srs/pull/4457): Support IPv6 for all protocols: RTMP, HTTP/HTTPS, WebRTC, SRT, RTSP. v7.0.67 (#4457)
* v7.0, 2025-08-28, Merge [#4456](https://github.com/ossrs/srs/pull/4456): AI: Remove cloud CLS and APM. v7.0.66 (#4456)
* v7.0, 2025-08-27, Merge [#4455](https://github.com/ossrs/srs/pull/4455): Gather utility functions to kernel or protocol. v7.0.65 (#4455)
* v7.0, 2025-08-27, Merge [#4454](https://github.com/ossrs/srs/pull/4454): AI: Config: Move RTMP configs to rtmp{} section. v7.0.64 (#4454)
diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp
index 67750cd90..c7def53b6 100644
--- a/trunk/src/app/srs_app_config.cpp
+++ b/trunk/src/app/srs_app_config.cpp
@@ -2107,7 +2107,7 @@ srs_error_t SrsConfig::raw_to_json(SrsJsonObject *obj)
obj->set("http_api", sobj);
sobj->set("enabled", SrsJsonAny::boolean(get_http_api_enabled()));
- sobj->set("listen", SrsJsonAny::str(get_http_api_listen().c_str()));
+ sobj->set("listen", SrsJsonAny::str(get_http_api_listens().at(0).c_str()));
sobj->set("crossdomain", SrsJsonAny::boolean(get_http_api_crossdomain()));
SrsJsonObject *ssobj = SrsJsonAny::object();
@@ -2477,18 +2477,8 @@ srs_error_t SrsConfig::check_normal_config()
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "listen requires params");
}
for (int i = 0; i < (int)listens.size(); i++) {
- int port;
- string ip;
- srs_net_split_for_listener(listens[i], ip, port);
-
- // check ip
- if (!srs_net_is_valid_ip(ip)) {
- return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "listen.ip=%s is invalid", ip.c_str());
- }
-
- // check port
- if (port <= 0) {
- return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "listen.port=%d is invalid", port);
+ if (!srs_net_is_valid_endpoint(listens[i])) {
+ return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "rtmp.listen=%s is invalid", listens[i].c_str());
}
}
}
@@ -2536,22 +2526,39 @@ srs_error_t SrsConfig::check_normal_config()
// Check HTTP API and server.
////////////////////////////////////////////////////////////////////////
if (true) {
- string api = get_http_api_listen();
- string server = get_http_stream_listen();
- if (api.empty()) {
+ vector apis = get_http_api_listens();
+ if (apis.empty()) {
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "http_api.listen requires params");
}
- if (server.empty()) {
- return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "http_server.listen requires params");
+ for (int i = 0; i < (int)apis.size(); i++) {
+ if (!srs_net_is_valid_endpoint(apis[i])) {
+ return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "http_api.listen=%s is invalid", apis[i].c_str());
+ }
}
- string apis = get_https_api_listen();
- string servers = get_https_stream_listen();
- if (api == server && apis != servers) {
- return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "for same http, https api(%s) != server(%s)", apis.c_str(), servers.c_str());
+ vector servers = get_http_stream_listens();
+ if (servers.empty()) {
+ return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "http_server.listen requires params");
}
- if (apis == servers && api != server) {
- return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "for same https, http api(%s) != server(%s)", api.c_str(), server.c_str());
+ for (int i = 0; i < (int)servers.size(); i++) {
+ if (!srs_net_is_valid_endpoint(servers[i])) {
+ return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "http_server.listen=%s is invalid", servers[i].c_str());
+ }
+ }
+
+ vector sapis = get_https_api_listens();
+ for (int i = 0; i < (int)sapis.size(); i++) {
+ if (!srs_net_is_valid_endpoint(sapis[i])) {
+ return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "http_api.https.listen=%s is invalid", sapis[i].c_str());
+ }
+ }
+
+ vector sservers = get_https_stream_listens();
+ if (srs_strings_equal(apis, servers) && !srs_strings_equal(sapis, sservers)) {
+ return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "for same http, https api(%s) != server(%s)", srs_strings_join(sapis, ",").c_str(), srs_strings_join(sservers, ",").c_str());
+ }
+ if (srs_strings_equal(sapis, sservers) && !srs_strings_equal(apis, servers)) {
+ return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "for same https, http api(%s) != server(%s)", srs_strings_join(apis, ",").c_str(), srs_strings_join(servers, ",").c_str());
}
if (get_https_api_enabled() && !get_http_api_enabled()) {
@@ -2560,6 +2567,65 @@ srs_error_t SrsConfig::check_normal_config()
if (get_https_stream_enabled() && !get_http_stream_enabled()) {
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "https stream depends on http");
}
+
+ // Validate HTTPS stream listen addresses
+ if (get_https_stream_enabled()) {
+ vector https_listens = get_https_stream_listens();
+ for (int i = 0; i < (int)https_listens.size(); i++) {
+ if (!srs_net_is_valid_endpoint(https_listens[i])) {
+ return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "https_server.listen=%s is invalid", https_listens[i].c_str());
+ }
+ }
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+ // Check RTSP server.
+ ////////////////////////////////////////////////////////////////////////
+ if (get_rtsp_server_enabled()) {
+ // Validate RTSP server listen addresses
+ vector rtsp_listens = get_rtsp_server_listens();
+ for (int i = 0; i < (int)rtsp_listens.size(); i++) {
+ if (!srs_net_is_valid_endpoint(rtsp_listens[i])) {
+ return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "rtsp_server.listen=%s is invalid", rtsp_listens[i].c_str());
+ }
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+ // Check RTC server.
+ ////////////////////////////////////////////////////////////////////////
+ if (get_rtc_server_enabled()) {
+ // Validate RTC server listen addresses
+ vector rtc_listens = get_rtc_server_listens();
+ for (int i = 0; i < (int)rtc_listens.size(); i++) {
+ if (!srs_net_is_valid_endpoint(rtc_listens[i])) {
+ return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "rtc_server.listen=%s is invalid", rtc_listens[i].c_str());
+ }
+ }
+
+ // Validate RTC server TCP listen addresses
+ if (get_rtc_server_tcp_enabled()) {
+ vector rtc_tcp_listens = get_rtc_server_tcp_listens();
+ for (int i = 0; i < (int)rtc_tcp_listens.size(); i++) {
+ if (!srs_net_is_valid_endpoint(rtc_tcp_listens[i])) {
+ return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "rtc_server.tcp.listen=%s is invalid", rtc_tcp_listens[i].c_str());
+ }
+ }
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+ // Check SRT server.
+ ////////////////////////////////////////////////////////////////////////
+ if (get_srt_enabled()) {
+ // Validate SRT server listen addresses
+ vector srt_listens = get_srt_listens();
+ for (int i = 0; i < (int)srt_listens.size(); i++) {
+ if (!srs_net_is_valid_endpoint(srt_listens[i])) {
+ return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "srt_server.listen=%s is invalid", srt_listens[i].c_str());
+ }
+ }
}
////////////////////////////////////////////////////////////////////////
@@ -3735,24 +3801,38 @@ bool SrsConfig::get_rtsp_server_enabled(SrsConfDirective *conf)
return SRS_CONF_PREFER_FALSE(conf->arg0());
}
-int SrsConfig::get_rtsp_server_listen()
+vector SrsConfig::get_rtsp_server_listens()
{
- SRS_OVERWRITE_BY_ENV_INT("srs.rtsp_server.listen"); // SRS_RTSP_SERVER_LISTEN
+ std::vector listens;
+
+ if (!srs_getenv("srs.rtsp_server.listen").empty()) { // SRS_RTSP_SERVER_LISTEN
+ return srs_strings_split(srs_getenv("srs.rtsp_server.listen"), " ");
+ }
+
+ static string DEFAULT = "554";
SrsConfDirective *conf = root->get("rtsp_server");
-
- static int DEFAULT = 554;
-
if (!conf) {
- return DEFAULT;
+ listens.push_back(DEFAULT);
+ return listens;
}
conf = conf->get("listen");
- if (!conf || conf->arg0().empty()) {
- return DEFAULT;
+ if (!conf) {
+ listens.push_back(DEFAULT);
+ return listens;
}
- return ::atoi(conf->arg0().c_str());
+ for (int i = 0; i < (int)conf->args.size(); i++) {
+ listens.push_back(conf->args.at(i));
+ }
+
+ // If no arguments, use default
+ if (listens.empty()) {
+ listens.push_back(DEFAULT);
+ }
+
+ return listens;
}
SrsConfDirective *SrsConfig::get_rtsp(string vhost)
@@ -3825,23 +3905,36 @@ bool SrsConfig::get_rtc_server_enabled(SrsConfDirective *conf)
return SRS_CONF_PREFER_FALSE(conf->arg0());
}
-int SrsConfig::get_rtc_server_listen()
+vector SrsConfig::get_rtc_server_listens()
{
- SRS_OVERWRITE_BY_ENV_INT("srs.rtc_server.listen"); // SRS_RTC_SERVER_LISTEN
+ std::vector listens;
- static int DEFAULT = 8000;
+ if (!srs_getenv("srs.rtc_server.listen").empty()) { // SRS_RTC_SERVER_LISTEN
+ return srs_strings_split(srs_getenv("srs.rtc_server.listen"), " ");
+ }
SrsConfDirective *conf = root->get("rtc_server");
if (!conf) {
- return DEFAULT;
+ listens.push_back("8000");
+ return listens;
}
conf = conf->get("listen");
- if (!conf || conf->arg0().empty()) {
- return DEFAULT;
+ if (!conf) {
+ listens.push_back("8000");
+ return listens;
}
- return ::atoi(conf->arg0().c_str());
+ for (int i = 0; i < (int)conf->args.size(); i++) {
+ listens.push_back(conf->args.at(i));
+ }
+
+ // If no arguments, use default
+ if (listens.empty()) {
+ listens.push_back("8000");
+ }
+
+ return listens;
}
std::string SrsConfig::get_rtc_server_candidates()
@@ -3979,28 +4072,44 @@ bool SrsConfig::get_rtc_server_tcp_enabled()
return SRS_CONF_PREFER_FALSE(conf->arg0());
}
-int SrsConfig::get_rtc_server_tcp_listen()
+vector SrsConfig::get_rtc_server_tcp_listens()
{
- SRS_OVERWRITE_BY_ENV_INT("srs.rtc_server.tcp.listen"); // SRS_RTC_SERVER_TCP_LISTEN
+ std::vector listens;
- static int DEFAULT = 8000;
+ if (!srs_getenv("srs.rtc_server.tcp.listen").empty()) { // SRS_RTC_SERVER_TCP_LISTEN
+ return srs_strings_split(srs_getenv("srs.rtc_server.tcp.listen"), " ");
+ }
+
+ static string DEFAULT = "8000";
SrsConfDirective *conf = root->get("rtc_server");
if (!conf) {
- return DEFAULT;
+ listens.push_back(DEFAULT);
+ return listens;
}
conf = conf->get("tcp");
if (!conf) {
- return DEFAULT;
+ listens.push_back(DEFAULT);
+ return listens;
}
conf = conf->get("listen");
- if (!conf || conf->arg0().empty()) {
- return DEFAULT;
+ if (!conf) {
+ listens.push_back(DEFAULT);
+ return listens;
}
- return ::atoi(conf->arg0().c_str());
+ for (int i = 0; i < (int)conf->args.size(); i++) {
+ listens.push_back(conf->args.at(i));
+ }
+
+ // If no arguments, use default
+ if (listens.empty()) {
+ listens.push_back(DEFAULT);
+ }
+
+ return listens;
}
std::string SrsConfig::get_rtc_server_protocol()
@@ -7464,24 +7573,38 @@ bool SrsConfig::get_http_api_enabled(SrsConfDirective *conf)
return SRS_CONF_PREFER_FALSE(conf->arg0());
}
-string SrsConfig::get_http_api_listen()
+vector SrsConfig::get_http_api_listens()
{
- SRS_OVERWRITE_BY_ENV_STRING("srs.http_api.listen"); // SRS_HTTP_API_LISTEN
+ std::vector listens;
+
+ if (!srs_getenv("srs.http_api.listen").empty()) { // SRS_HTTP_API_LISTEN
+ return srs_strings_split(srs_getenv("srs.http_api.listen"), " ");
+ }
static string DEFAULT = "1985";
SrsConfDirective *conf = root->get("http_api");
-
if (!conf) {
- return DEFAULT;
+ listens.push_back(DEFAULT);
+ return listens;
}
conf = conf->get("listen");
- if (!conf || conf->arg0().empty()) {
- return DEFAULT;
+ if (!conf) {
+ listens.push_back(DEFAULT);
+ return listens;
}
- return conf->arg0();
+ for (int i = 0; i < (int)conf->args.size(); i++) {
+ listens.push_back(conf->args.at(i));
+ }
+
+ // If no arguments, use default
+ if (listens.empty()) {
+ listens.push_back(DEFAULT);
+ }
+
+ return listens;
}
bool SrsConfig::get_http_api_crossdomain()
@@ -7664,28 +7787,48 @@ bool SrsConfig::get_https_api_enabled()
return SRS_CONF_PREFER_FALSE(conf->arg0());
}
-string SrsConfig::get_https_api_listen()
+vector SrsConfig::get_https_api_listens()
{
- SRS_OVERWRITE_BY_ENV_STRING("srs.http_api.https.listen"); // SRS_HTTP_API_HTTPS_LISTEN
+ std::vector listens;
- // We should not use static default, because we need to reset for different use scenarios.
- string DEFAULT = "1990";
- // Follow the HTTPS server if config HTTP API as the same of HTTP server.
- if (get_http_api_listen() == get_http_stream_listen()) {
- DEFAULT = get_https_stream_listen();
+ if (!srs_getenv("srs.http_api.https.listen").empty()) { // SRS_HTTP_API_HTTPS_LISTEN
+ return srs_strings_split(srs_getenv("srs.http_api.https.listen"), " ");
}
+ // If HTTP API uses the same port to HTTP server, then HTTPS API should
+ // always default to the same port to HTTPS server.
+ if (true) {
+ vector apis = get_http_api_listens();
+ vector servers = get_http_stream_listens();
+ if (srs_strings_equal(apis, servers)) {
+ return get_https_stream_listens();
+ }
+ }
+
+ static string DEFAULT = "1990";
+
SrsConfDirective *conf = get_https_api();
if (!conf) {
- return DEFAULT;
+ listens.push_back(DEFAULT);
+ return listens;
}
conf = conf->get("listen");
if (!conf) {
- return DEFAULT;
+ listens.push_back(DEFAULT);
+ return listens;
}
- return conf->arg0();
+ for (int i = 0; i < (int)conf->args.size(); i++) {
+ listens.push_back(conf->args.at(i));
+ }
+
+ // If no arguments, use default
+ if (listens.empty()) {
+ listens.push_back(DEFAULT);
+ }
+
+ return listens;
}
string SrsConfig::get_https_api_ssl_key()
@@ -7745,21 +7888,38 @@ bool SrsConfig::get_srt_enabled()
return SRS_CONF_PREFER_FALSE(conf->arg0());
}
-unsigned short SrsConfig::get_srt_listen_port()
+vector SrsConfig::get_srt_listens()
{
- SRS_OVERWRITE_BY_ENV_INT("srs.srt_server.listen"); // SRS_SRT_SERVER_LISTEN
+ std::vector listens;
+
+ if (!srs_getenv("srs.srt_server.listen").empty()) { // SRS_SRT_SERVER_LISTEN
+ return srs_strings_split(srs_getenv("srs.srt_server.listen"), " ");
+ }
+
+ static string DEFAULT = "10080";
- static unsigned short DEFAULT = 10080;
SrsConfDirective *conf = root->get("srt_server");
if (!conf) {
- return DEFAULT;
+ listens.push_back(DEFAULT);
+ return listens;
}
conf = conf->get("listen");
- if (!conf || conf->arg0().empty()) {
- return DEFAULT;
+ if (!conf) {
+ listens.push_back(DEFAULT);
+ return listens;
}
- return (unsigned short)atoi(conf->arg0().c_str());
+
+ for (int i = 0; i < (int)conf->args.size(); i++) {
+ listens.push_back(conf->args.at(i));
+ }
+
+ // If no arguments, use default
+ if (listens.empty()) {
+ listens.push_back(DEFAULT);
+ }
+
+ return listens;
}
int64_t SrsConfig::get_srto_maxbw()
@@ -8104,23 +8264,38 @@ bool SrsConfig::get_http_stream_enabled(SrsConfDirective *conf)
return SRS_CONF_PREFER_FALSE(conf->arg0());
}
-string SrsConfig::get_http_stream_listen()
+vector SrsConfig::get_http_stream_listens()
{
- SRS_OVERWRITE_BY_ENV_STRING("srs.http_server.listen"); // SRS_HTTP_SERVER_LISTEN
+ std::vector listens;
+
+ if (!srs_getenv("srs.http_server.listen").empty()) { // SRS_HTTP_SERVER_LISTEN
+ return srs_strings_split(srs_getenv("srs.http_server.listen"), " ");
+ }
static string DEFAULT = "8080";
SrsConfDirective *conf = root->get("http_server");
if (!conf) {
- return DEFAULT;
+ listens.push_back(DEFAULT);
+ return listens;
}
conf = conf->get("listen");
- if (!conf || conf->arg0().empty()) {
- return DEFAULT;
+ if (!conf) {
+ listens.push_back(DEFAULT);
+ return listens;
}
- return conf->arg0();
+ for (int i = 0; i < (int)conf->args.size(); i++) {
+ listens.push_back(conf->args.at(i));
+ }
+
+ // If no arguments, use default
+ if (listens.empty()) {
+ listens.push_back(DEFAULT);
+ }
+
+ return listens;
}
string SrsConfig::get_http_stream_dir()
@@ -8190,23 +8365,38 @@ bool SrsConfig::get_https_stream_enabled()
return SRS_CONF_PREFER_FALSE(conf->arg0());
}
-string SrsConfig::get_https_stream_listen()
+vector SrsConfig::get_https_stream_listens()
{
- SRS_OVERWRITE_BY_ENV_STRING("srs.http_server.https.listen"); // SRS_HTTP_SERVER_HTTPS_LISTEN
+ std::vector listens;
+
+ if (!srs_getenv("srs.http_server.https.listen").empty()) { // SRS_HTTP_SERVER_HTTPS_LISTEN
+ return srs_strings_split(srs_getenv("srs.http_server.https.listen"), " ");
+ }
static string DEFAULT = "8088";
SrsConfDirective *conf = get_https_stream();
if (!conf) {
- return DEFAULT;
+ listens.push_back(DEFAULT);
+ return listens;
}
conf = conf->get("listen");
if (!conf) {
- return DEFAULT;
+ listens.push_back(DEFAULT);
+ return listens;
}
- return conf->arg0();
+ for (int i = 0; i < (int)conf->args.size(); i++) {
+ listens.push_back(conf->args.at(i));
+ }
+
+ // If no arguments, use default
+ if (listens.empty()) {
+ listens.push_back(DEFAULT);
+ }
+
+ return listens;
}
string SrsConfig::get_https_stream_ssl_key()
diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp
index 5c8a0c4a1..4b2a42c9f 100644
--- a/trunk/src/app/srs_app_config.hpp
+++ b/trunk/src/app/srs_app_config.hpp
@@ -513,7 +513,8 @@ public:
public:
virtual bool get_rtsp_server_enabled();
virtual bool get_rtsp_server_enabled(SrsConfDirective *conf);
- virtual int get_rtsp_server_listen();
+ // Get the rtsp server listen addresses, support IPv4 and IPv6.
+ virtual std::vector get_rtsp_server_listens();
public:
SrsConfDirective *get_rtsp(std::string vhost);
@@ -524,14 +525,16 @@ public:
public:
virtual bool get_rtc_server_enabled();
virtual bool get_rtc_server_enabled(SrsConfDirective *conf);
- virtual int get_rtc_server_listen();
+ // Get the rtc server listen addresses, support IPv4 and IPv6.
+ virtual std::vector get_rtc_server_listens();
virtual std::string get_rtc_server_candidates();
virtual bool get_api_as_candidates();
virtual bool get_resolve_api_domain();
virtual bool get_keep_api_domain();
virtual bool get_use_auto_detect_network_ip();
virtual bool get_rtc_server_tcp_enabled();
- virtual int get_rtc_server_tcp_listen();
+ // Get the rtc server tcp listen addresses, support IPv4 and IPv6.
+ virtual std::vector get_rtc_server_tcp_listens();
virtual std::string get_rtc_server_protocol();
virtual std::string get_rtc_server_ip_family();
virtual bool get_rtc_server_ecdsa();
@@ -689,8 +692,8 @@ public:
public:
// Whether the srt sevice enabled
virtual bool get_srt_enabled();
- // Get the srt service listen port
- virtual unsigned short get_srt_listen_port();
+ // Get the srt service listen addresses, support IPv4 and IPv6.
+ virtual std::vector get_srt_listens();
// Get the srt SRTO_MAXBW, max bandwith, default is -1.
virtual int64_t get_srto_maxbw();
// Get the srt SRTO_MSS, Maximum Segment Size, default is 1500.
@@ -1060,8 +1063,8 @@ private:
public:
// Whether http api enabled.
virtual bool get_http_api_enabled();
- // Get the http api listen port.
- virtual std::string get_http_api_listen();
+ // Get the http api listen addresses, support IPv4 and IPv6.
+ virtual std::vector get_http_api_listens();
// Whether enable crossdomain for http api.
virtual bool get_http_api_crossdomain();
// Whether enable the HTTP RAW API.
@@ -1084,7 +1087,7 @@ private:
public:
virtual bool get_https_api_enabled();
- virtual std::string get_https_api_listen();
+ virtual std::vector get_https_api_listens();
virtual std::string get_https_api_ssl_key();
virtual std::string get_https_api_ssl_cert();
// http stream section
@@ -1096,8 +1099,8 @@ public:
// Whether http stream enabled.
// TODO: FIXME: rename to http_static.
virtual bool get_http_stream_enabled();
- // Get the http stream listen port.
- virtual std::string get_http_stream_listen();
+ // Get the http stream listen addresses, support IPv4 and IPv6.
+ virtual std::vector get_http_stream_listens();
// Get the http stream root dir.
virtual std::string get_http_stream_dir();
// Whether enable crossdomain for http static and stream server.
@@ -1108,7 +1111,8 @@ private:
public:
virtual bool get_https_stream_enabled();
- virtual std::string get_https_stream_listen();
+ // Get the https stream listen addresses, support IPv4 and IPv6.
+ virtual std::vector get_https_stream_listens();
virtual std::string get_https_stream_ssl_key();
virtual std::string get_https_stream_ssl_cert();
// rtmps section
diff --git a/trunk/src/app/srs_app_coworkers.cpp b/trunk/src/app/srs_app_coworkers.cpp
index be78576c1..3b4501c42 100644
--- a/trunk/src/app/srs_app_coworkers.cpp
+++ b/trunk/src/app/srs_app_coworkers.cpp
@@ -84,7 +84,7 @@ SrsJsonAny *SrsCoWorkers::dumps(string vhost, string coworker, string app, strin
}
// The backend API endpoint.
- string backend = _srs_config->get_http_api_listen();
+ string backend = _srs_config->get_http_api_listens().at(0);
if (backend.find(":") == string::npos) {
backend = service_ip + ":" + backend;
}
diff --git a/trunk/src/app/srs_app_heartbeat.cpp b/trunk/src/app/srs_app_heartbeat.cpp
index d8024971b..5d2da3b2e 100644
--- a/trunk/src/app/srs_app_heartbeat.cpp
+++ b/trunk/src/app/srs_app_heartbeat.cpp
@@ -97,8 +97,10 @@ srs_error_t SrsHttpHeartbeat::do_heartbeat()
SrsJsonArray *o = SrsJsonAny::array();
obj->set("http", o);
- string endpoint = _srs_config->get_http_stream_listen();
- o->append(SrsJsonAny::str(endpoint.c_str()));
+ vector endpoints = _srs_config->get_http_stream_listens();
+ for (int i = 0; i < (int)endpoints.size(); i++) {
+ o->append(SrsJsonAny::str(endpoints.at(i).c_str()));
+ }
}
// For HTTP API listen endpoints.
@@ -106,8 +108,10 @@ srs_error_t SrsHttpHeartbeat::do_heartbeat()
SrsJsonArray *o = SrsJsonAny::array();
obj->set("api", o);
- string endpoint = _srs_config->get_http_api_listen();
- o->append(SrsJsonAny::str(endpoint.c_str()));
+ vector endpoints = _srs_config->get_http_api_listens();
+ for (int i = 0; i < (int)endpoints.size(); i++) {
+ o->append(SrsJsonAny::str(endpoints.at(i).c_str()));
+ }
}
// For SRT listen endpoints.
@@ -115,8 +119,10 @@ srs_error_t SrsHttpHeartbeat::do_heartbeat()
SrsJsonArray *o = SrsJsonAny::array();
obj->set("srt", o);
- uint16_t endpoint = _srs_config->get_srt_listen_port();
- o->append(SrsJsonAny::str(srs_fmt_sprintf("udp://0.0.0.0:%d", endpoint).c_str()));
+ vector endpoints = _srs_config->get_srt_listens();
+ for (int i = 0; i < (int)endpoints.size(); i++) {
+ o->append(SrsJsonAny::str(endpoints.at(i).c_str()));
+ }
}
// For RTSP listen endpoints.
@@ -124,8 +130,10 @@ srs_error_t SrsHttpHeartbeat::do_heartbeat()
SrsJsonArray *o = SrsJsonAny::array();
obj->set("rtsp", o);
- int endpoint = _srs_config->get_rtsp_server_listen();
- o->append(SrsJsonAny::str(srs_fmt_sprintf("rtsp://0.0.0.0:%d", endpoint).c_str()));
+ vector endpoints = _srs_config->get_rtsp_server_listens();
+ for (int i = 0; i < (int)endpoints.size(); i++) {
+ o->append(SrsJsonAny::str(endpoints.at(i).c_str()));
+ }
}
// For WebRTC listen endpoints.
@@ -133,12 +141,18 @@ srs_error_t SrsHttpHeartbeat::do_heartbeat()
SrsJsonArray *o = SrsJsonAny::array();
obj->set("rtc", o);
- int endpoint = _srs_config->get_rtc_server_listen();
- o->append(SrsJsonAny::str(srs_fmt_sprintf("udp://0.0.0.0:%d", endpoint).c_str()));
+ vector endpoints = _srs_config->get_rtc_server_listens();
+ for (int i = 0; i < (int)endpoints.size(); i++) {
+ string endpoint = srs_fmt_sprintf("udp://%s", endpoints.at(i).c_str());
+ o->append(SrsJsonAny::str(endpoint.c_str()));
+ }
if (_srs_config->get_rtc_server_tcp_enabled()) {
- endpoint = _srs_config->get_rtc_server_tcp_listen();
- o->append(SrsJsonAny::str(srs_fmt_sprintf("tcp://0.0.0.0:%d", endpoint).c_str()));
+ vector endpoints = _srs_config->get_rtc_server_tcp_listens();
+ for (int i = 0; i < (int)endpoints.size(); i++) {
+ string endpoint = srs_fmt_sprintf("tcp://%s", endpoints.at(i).c_str());
+ o->append(SrsJsonAny::str(endpoint.c_str()));
+ }
}
}
}
diff --git a/trunk/src/app/srs_app_rtc_server.cpp b/trunk/src/app/srs_app_rtc_server.cpp
index 5b6dfc034..1a4d86792 100644
--- a/trunk/src/app/srs_app_rtc_server.cpp
+++ b/trunk/src/app/srs_app_rtc_server.cpp
@@ -366,25 +366,43 @@ srs_error_t SrsRtcServer::listen_udp()
return err;
}
- int port = _srs_config->get_rtc_server_listen();
- if (port <= 0) {
- return srs_error_new(ERROR_RTC_PORT, "invalid port=%d", port);
+ // Check protocol setting - only create UDP listeners if protocol allows UDP
+ string protocol = _srs_config->get_rtc_server_protocol();
+ if (protocol == "tcp") {
+ // When protocol is "tcp", don't create UDP listeners
+ return err;
}
- string ip = srs_net_address_any();
+ vector rtc_listens = _srs_config->get_rtc_server_listens();
+ if (rtc_listens.empty()) {
+ return srs_error_new(ERROR_RTC_PORT, "empty rtc listen");
+ }
+
+ // There should be no listeners before listening.
srs_assert(listeners.empty());
+ // For each listen address, create multiple listeners based on reuseport setting
int nn_listeners = _srs_config->get_rtc_server_reuseport();
- for (int i = 0; i < nn_listeners; i++) {
- SrsUdpMuxListener *listener = new SrsUdpMuxListener(this, ip, port);
+ for (int j = 0; j < (int)rtc_listens.size(); j++) {
+ string ip;
+ int port;
+ srs_net_split_for_listener(rtc_listens[j], ip, port);
- if ((err = listener->listen()) != srs_success) {
- srs_freep(listener);
- return srs_error_wrap(err, "listen %s:%d", ip.c_str(), port);
+ if (port <= 0) {
+ return srs_error_new(ERROR_RTC_PORT, "invalid port=%d", port);
}
- srs_trace("rtc listen at udp://%s:%d, fd=%d", ip.c_str(), port, listener->fd());
- listeners.push_back(listener);
+ for (int i = 0; i < nn_listeners; i++) {
+ SrsUdpMuxListener *listener = new SrsUdpMuxListener(this, ip, port);
+
+ if ((err = listener->listen()) != srs_success) {
+ srs_freep(listener);
+ return srs_error_wrap(err, "listen %s:%d", ip.c_str(), port);
+ }
+
+ srs_trace("WebRTC listen at udp://%s:%d, fd=%d", ip.c_str(), port, listener->fd());
+ listeners.push_back(listener);
+ }
}
return err;
@@ -589,8 +607,21 @@ srs_error_t SrsRtcServer::do_create_session(SrsRtcUserConfig *ruc, SrsSdp &local
// We allows to mock the eip of server.
if (true) {
- int udp_port = _srs_config->get_rtc_server_listen();
- int tcp_port = _srs_config->get_rtc_server_tcp_listen();
+ // TODO: Support multiple listen ports.
+ int udp_port = 0;
+ if (true) {
+ string udp_host;
+ string udp_hostport = _srs_config->get_rtc_server_listens().at(0);
+ srs_net_split_for_listener(udp_hostport, udp_host, udp_port);
+ }
+
+ int tcp_port = 0;
+ if (true) {
+ string tcp_host;
+ string tcp_hostport = _srs_config->get_rtc_server_tcp_listens().at(0);
+ srs_net_split_for_listener(tcp_hostport, tcp_host, tcp_port);
+ }
+
string protocol = _srs_config->get_rtc_server_protocol();
set candidates = discover_candidates(ruc);
@@ -612,7 +643,8 @@ srs_error_t SrsRtcServer::do_create_session(SrsRtcUserConfig *ruc, SrsSdp &local
}
vector v = vector(candidates.begin(), candidates.end());
- srs_trace("RTC: Use candidates %s, protocol=%s", srs_strings_join(v, ", ").c_str(), protocol.c_str());
+ srs_trace("RTC: Use candidates %s, protocol=%s, tcp_port=%d, udp_port=%d",
+ srs_strings_join(v, ", ").c_str(), protocol.c_str(), tcp_port, udp_port);
}
// Setup the negotiate DTLS by config.
diff --git a/trunk/src/app/srs_app_server.cpp b/trunk/src/app/srs_app_server.cpp
index eacd7ae92..89edba798 100644
--- a/trunk/src/app/srs_app_server.cpp
+++ b/trunk/src/app/srs_app_server.cpp
@@ -340,13 +340,13 @@ SrsServer::SrsServer()
rtmp_listener_ = new SrsMultipleTcpListeners(this);
rtmps_listener_ = new SrsMultipleTcpListeners(this);
- api_listener_ = new SrsTcpListener(this);
- apis_listener_ = new SrsTcpListener(this);
- http_listener_ = new SrsTcpListener(this);
- https_listener_ = new SrsTcpListener(this);
- webrtc_listener_ = new SrsTcpListener(this);
+ api_listener_ = new SrsMultipleTcpListeners(this);
+ apis_listener_ = new SrsMultipleTcpListeners(this);
+ http_listener_ = new SrsMultipleTcpListeners(this);
+ https_listener_ = new SrsMultipleTcpListeners(this);
+ webrtc_listener_ = new SrsMultipleTcpListeners(this);
#ifdef SRS_RTSP
- rtsp_listener_ = new SrsTcpListener(this);
+ rtsp_listener_ = new SrsMultipleTcpListeners(this);
#endif
stream_caster_flv_listener_ = new SrsHttpFlvListener();
stream_caster_mpegts_ = new SrsUdpCasterListener();
@@ -510,28 +510,28 @@ srs_error_t SrsServer::initialize()
_srs_config->subscribe(this);
bool stream = _srs_config->get_http_stream_enabled();
- string http_listen = _srs_config->get_http_stream_listen();
- string https_listen = _srs_config->get_https_stream_listen();
+ vector http_listens = _srs_config->get_http_stream_listens();
+ vector https_listens = _srs_config->get_https_stream_listens();
bool rtc = _srs_config->get_rtc_server_enabled();
bool rtc_tcp = _srs_config->get_rtc_server_tcp_enabled();
- string rtc_listen = srs_strconv_format_int(_srs_config->get_rtc_server_tcp_listen());
+ vector rtc_listens = _srs_config->get_rtc_server_tcp_listens();
// If enabled and listen is the same value, resue port for WebRTC over TCP.
- if (stream && rtc && rtc_tcp && http_listen == rtc_listen) {
- srs_trace("WebRTC tcp=%s reuses http=%s server", rtc_listen.c_str(), http_listen.c_str());
+ if (stream && rtc && rtc_tcp && srs_strings_equal(http_listens, rtc_listens)) {
+ srs_trace("WebRTC tcp=%s reuses http=%s server", srs_strings_join(rtc_listens, ",").c_str(), srs_strings_join(http_listens, ",").c_str());
reuse_rtc_over_server_ = true;
}
- if (stream && rtc && rtc_tcp && https_listen == rtc_listen) {
- srs_trace("WebRTC tcp=%s reuses https=%s server", rtc_listen.c_str(), https_listen.c_str());
+ if (stream && rtc && rtc_tcp && srs_strings_equal(https_listens, rtc_listens)) {
+ srs_trace("WebRTC tcp=%s reuses https=%s server", srs_strings_join(rtc_listens, ",").c_str(), srs_strings_join(https_listens, ",").c_str());
reuse_rtc_over_server_ = true;
}
// If enabled and the listen is the same value, reuse port.
bool api = _srs_config->get_http_api_enabled();
- string api_listen = _srs_config->get_http_api_listen();
- string apis_listen = _srs_config->get_https_api_listen();
- if (stream && api && api_listen == http_listen && apis_listen == https_listen) {
- srs_trace("API reuses http=%s and https=%s server", http_listen.c_str(), https_listen.c_str());
+ vector api_listens = _srs_config->get_http_api_listens();
+ vector apis_listens = _srs_config->get_https_api_listens();
+ if (stream && api && srs_strings_equal(api_listens, http_listens) && srs_strings_equal(apis_listens, https_listens)) {
+ srs_trace("API reuses http=%s and https=%s server", srs_strings_join(http_listens, ",").c_str(), srs_strings_join(https_listens, ",").c_str());
reuse_api_over_server_ = true;
}
@@ -608,9 +608,10 @@ srs_error_t SrsServer::listen()
// Create HTTP API listener.
if (_srs_config->get_http_api_enabled()) {
if (reuse_api_over_server_) {
- srs_trace("HTTP-API: Reuse listen to http server %s", _srs_config->get_http_stream_listen().c_str());
+ vector listens = _srs_config->get_http_stream_listens();
+ srs_trace("HTTP-API: Reuse listen to http server %s", srs_strings_join(listens, ",").c_str());
} else {
- api_listener_->set_endpoint(_srs_config->get_http_api_listen())->set_label("HTTP-API");
+ api_listener_->add(_srs_config->get_http_api_listens())->set_label("HTTP-API");
if ((err = api_listener_->listen()) != srs_success) {
return srs_error_wrap(err, "http api listen");
}
@@ -620,9 +621,10 @@ srs_error_t SrsServer::listen()
// Create HTTPS API listener.
if (_srs_config->get_https_api_enabled()) {
if (reuse_api_over_server_) {
- srs_trace("HTTPS-API: Reuse listen to http server %s", _srs_config->get_https_stream_listen().c_str());
+ vector listens = _srs_config->get_https_stream_listens();
+ srs_trace("HTTPS-API: Reuse listen to http server %s", srs_strings_join(listens, ",").c_str());
} else {
- apis_listener_->set_endpoint(_srs_config->get_https_api_listen())->set_label("HTTPS-API");
+ apis_listener_->add(_srs_config->get_https_api_listens())->set_label("HTTPS-API");
if ((err = apis_listener_->listen()) != srs_success) {
return srs_error_wrap(err, "https api listen");
}
@@ -631,7 +633,7 @@ srs_error_t SrsServer::listen()
// Create HTTP server listener.
if (_srs_config->get_http_stream_enabled()) {
- http_listener_->set_endpoint(_srs_config->get_http_stream_listen())->set_label("HTTP-Server");
+ http_listener_->add(_srs_config->get_http_stream_listens())->set_label("HTTP-Server");
if ((err = http_listener_->listen()) != srs_success) {
return srs_error_wrap(err, "http server listen");
}
@@ -639,15 +641,16 @@ srs_error_t SrsServer::listen()
// Create HTTPS server listener.
if (_srs_config->get_https_stream_enabled()) {
- https_listener_->set_endpoint(_srs_config->get_https_stream_listen())->set_label("HTTPS-Server");
+ https_listener_->add(_srs_config->get_https_stream_listens())->set_label("HTTPS-Server");
if ((err = https_listener_->listen()) != srs_success) {
return srs_error_wrap(err, "https server listen");
}
}
// Start WebRTC over TCP listener.
- if (!reuse_rtc_over_server_ && _srs_config->get_rtc_server_tcp_enabled()) {
- webrtc_listener_->set_endpoint(srs_strconv_format_int(_srs_config->get_rtc_server_tcp_listen()))->set_label("WebRTC");
+ string protocol = _srs_config->get_rtc_server_protocol();
+ if (!reuse_rtc_over_server_ && protocol != "udp" && _srs_config->get_rtc_server_tcp_enabled()) {
+ webrtc_listener_->add(_srs_config->get_rtc_server_tcp_listens())->set_label("WebRTC");
if ((err = webrtc_listener_->listen()) != srs_success) {
return srs_error_wrap(err, "webrtc tcp listen");
}
@@ -656,7 +659,7 @@ srs_error_t SrsServer::listen()
#ifdef SRS_RTSP
// Start RTSP listener. RTC is a critical dependency.
if (_srs_config->get_rtsp_server_enabled()) {
- rtsp_listener_->set_endpoint(srs_strconv_format_int(_srs_config->get_rtsp_server_listen()))->set_label("RTSP");
+ rtsp_listener_->add(_srs_config->get_rtsp_server_listens())->set_label("RTSP");
if ((err = rtsp_listener_->listen()) != srs_success) {
return srs_error_wrap(err, "rtsp listen");
}
diff --git a/trunk/src/app/srs_app_server.hpp b/trunk/src/app/srs_app_server.hpp
index 97e47c1e0..1d4c63514 100644
--- a/trunk/src/app/srs_app_server.hpp
+++ b/trunk/src/app/srs_app_server.hpp
@@ -131,20 +131,20 @@ private:
// RTMPS stream listeners, over TCP.
SrsMultipleTcpListeners *rtmps_listener_;
// HTTP API listener, over TCP. Please note that it might reuse with stream listener.
- SrsTcpListener *api_listener_;
+ SrsMultipleTcpListeners *api_listener_;
// HTTPS API listener, over TCP. Please note that it might reuse with stream listener.
- SrsTcpListener *apis_listener_;
+ SrsMultipleTcpListeners *apis_listener_;
// HTTP server listener, over TCP. Please note that request of both HTTP static and stream are served by this
// listener, and it might be reused by HTTP API and WebRTC TCP.
- SrsTcpListener *http_listener_;
+ SrsMultipleTcpListeners *http_listener_;
// HTTPS server listener, over TCP. Please note that request of both HTTP static and stream are served by this
// listener, and it might be reused by HTTP API and WebRTC TCP.
- SrsTcpListener *https_listener_;
+ SrsMultipleTcpListeners *https_listener_;
// WebRTC over TCP listener. Please note that there is always a UDP listener by RTC server.
- SrsTcpListener *webrtc_listener_;
+ SrsMultipleTcpListeners *webrtc_listener_;
#ifdef SRS_RTSP
// RTSP listener, over TCP.
- SrsTcpListener *rtsp_listener_;
+ SrsMultipleTcpListeners *rtsp_listener_;
#endif
// Stream Caster for push over HTTP-FLV.
SrsHttpFlvListener *stream_caster_flv_listener_;
diff --git a/trunk/src/app/srs_app_srt_server.cpp b/trunk/src/app/srs_app_srt_server.cpp
index 070b5c673..5913625d2 100644
--- a/trunk/src/app/srs_app_srt_server.cpp
+++ b/trunk/src/app/srs_app_srt_server.cpp
@@ -195,16 +195,28 @@ srs_error_t SrsSrtServer::listen_srt_mpegts()
// Close all listener for SRT if exists.
close_listeners();
- // Start a listener for SRT, we might need multiple listeners in the future.
- SrsSrtAcceptor *acceptor = new SrsSrtAcceptor(this);
- acceptors_.push_back(acceptor);
+ // Start listeners for SRT, support multiple addresses including IPv6.
+ vector srt_listens = _srs_config->get_srt_listens();
+ for (int i = 0; i < (int)srt_listens.size(); i++) {
+ SrsSrtAcceptor *acceptor = new SrsSrtAcceptor(this);
- int port;
- string ip;
- srs_net_split_for_listener(srs_strconv_format_int(_srs_config->get_srt_listen_port()), ip, port);
+ int port;
+ string ip;
+ srs_net_split_for_listener(srt_listens[i], ip, port);
- if ((err = acceptor->listen(ip, port)) != srs_success) {
- return srs_error_wrap(err, "srt listen %s:%d", ip.c_str(), port);
+ if ((err = acceptor->listen(ip, port)) != srs_success) {
+ srs_freep(acceptor);
+ srs_warn("srt listen %s:%d failed, err=%s", ip.c_str(), port, srs_error_desc(err).c_str());
+ srs_error_reset(err);
+ continue;
+ }
+
+ acceptors_.push_back(acceptor);
+ }
+
+ // Check if at least one listener succeeded
+ if (acceptors_.empty()) {
+ return srs_error_new(ERROR_SOCKET_LISTEN, "no srt listeners available");
}
return err;
diff --git a/trunk/src/core/srs_core_version7.hpp b/trunk/src/core/srs_core_version7.hpp
index 238874436..3d3c38b41 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 66
+#define VERSION_REVISION 67
#endif
\ No newline at end of file
diff --git a/trunk/src/kernel/srs_kernel_utility.cpp b/trunk/src/kernel/srs_kernel_utility.cpp
index 24447bd37..1653b9e01 100644
--- a/trunk/src/kernel/srs_kernel_utility.cpp
+++ b/trunk/src/kernel/srs_kernel_utility.cpp
@@ -855,6 +855,27 @@ bool srs_net_is_valid_ip(string ip)
return false;
}
+bool srs_net_is_valid_endpoint(std::string endpoint)
+{
+ if (endpoint.empty()) {
+ return false;
+ }
+
+ string host;
+ int port = 0;
+ srs_net_split_for_listener(endpoint, host, port);
+
+ if (!srs_net_is_valid_ip(host)) {
+ return false;
+ }
+
+ if (port <= 0) {
+ return false;
+ }
+
+ return true;
+}
+
bool srs_net_is_ipv4(string domain)
{
for (int i = 0; i < (int)domain.length(); i++) {
diff --git a/trunk/src/kernel/srs_kernel_utility.hpp b/trunk/src/kernel/srs_kernel_utility.hpp
index 88788f902..df8cc4976 100644
--- a/trunk/src/kernel/srs_kernel_utility.hpp
+++ b/trunk/src/kernel/srs_kernel_utility.hpp
@@ -105,6 +105,23 @@ std::string srs_strings_join(std::vector &vs, std::string separator)
return ss.str();
}
+// Compare two vector with string.
+template
+bool srs_strings_equal(std::vector &a, std::vector &b)
+{
+ if (a.size() != b.size()) {
+ return false;
+ }
+
+ for (int i = 0; i < (int)a.size(); i++) {
+ if (a.at(i) != b.at(i)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
// Compare the memory in bytes.
// @return true if completely equal; otherwise, false.
extern bool srs_bytes_equal(void *pa, void *pb, int size);
@@ -174,6 +191,9 @@ extern std::string srs_net_address_any();
// Check whether the ip is valid.
extern bool srs_net_is_valid_ip(std::string ip);
+// Check whether the endpoint is valid.
+extern bool srs_net_is_valid_endpoint(std::string endpoint);
+
// Whether domain is an IPv4 address.
extern bool srs_net_is_ipv4(std::string domain);
diff --git a/trunk/src/protocol/srs_protocol_srt.cpp b/trunk/src/protocol/srs_protocol_srt.cpp
index 0acc38439..399078932 100644
--- a/trunk/src/protocol/srs_protocol_srt.cpp
+++ b/trunk/src/protocol/srs_protocol_srt.cpp
@@ -55,6 +55,15 @@ static srs_error_t do_srs_srt_listen(srs_srt_t srt_fd, addrinfo *r)
return srs_error_wrap(err, "nonblock");
}
+ // Set IPv6 options for IPv6 addresses
+ if (r->ai_family == AF_INET6) {
+ // Set SRTO_IPV6ONLY to 1 for IPv6-only behavior to avoid conflicts with IPv4 wildcard binding
+ int ipv6only = 1; // IPv6-only to avoid conflicts
+ if (srt_setsockflag(srt_fd, SRTO_IPV6ONLY, &ipv6only, sizeof(ipv6only)) == SRT_ERROR) {
+ return srs_error_new(ERROR_SOCKET_BIND, "srt set SRTO_IPV6ONLY=%d failed, err=%s", ipv6only, srt_getlasterror_str());
+ }
+ }
+
if (srt_bind(srt_fd, r->ai_addr, r->ai_addrlen) == -1) {
return srs_error_new(ERROR_SOCKET_BIND, "bind");
}
@@ -204,7 +213,7 @@ srs_error_t srs_srt_listen(srs_srt_t srt_fd, std::string ip, int port)
addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
+ hints.ai_socktype = SOCK_DGRAM; // SRT is UDP-based
hints.ai_flags = AI_NUMERICHOST;
addrinfo *r_raw = NULL;
@@ -764,7 +773,7 @@ srs_error_t SrsSrtSocket::accept(srs_srt_t *client_srt_fd)
srs_error_t err = srs_success;
while (true) {
- sockaddr_in inaddr;
+ sockaddr_storage inaddr; // Use sockaddr_storage to support both IPv4 and IPv6
int addrlen = sizeof(inaddr);
// @see https://github.com/Haivision/srt/blob/master/docs/API/API-functions.md#srt_accept
srs_srt_t srt_fd = srt_accept(srt_fd_, (sockaddr *)&inaddr, &addrlen);
diff --git a/trunk/src/protocol/srs_protocol_st.cpp b/trunk/src/protocol/srs_protocol_st.cpp
index ada71e57a..6d741ea06 100644
--- a/trunk/src/protocol/srs_protocol_st.cpp
+++ b/trunk/src/protocol/srs_protocol_st.cpp
@@ -123,6 +123,13 @@ void srs_close_stfd(srs_netfd_t &stfd)
}
}
+void srs_close_stfd_ptr(srs_netfd_t *stfd)
+{
+ if (stfd && *stfd) {
+ srs_close_stfd(*stfd);
+ }
+}
+
srs_error_t srs_fd_closeexec(int fd)
{
int flags = fcntl(fd, F_GETFD);
diff --git a/trunk/src/protocol/srs_protocol_st.hpp b/trunk/src/protocol/srs_protocol_st.hpp
index bab3b40d5..c9367b32e 100644
--- a/trunk/src/protocol/srs_protocol_st.hpp
+++ b/trunk/src/protocol/srs_protocol_st.hpp
@@ -33,6 +33,7 @@ extern void srs_st_destroy(void);
// Close the netfd, and close the underlayer fd.
// @remark when close, user must ensure io completed.
extern void srs_close_stfd(srs_netfd_t &stfd);
+extern void srs_close_stfd_ptr(srs_netfd_t *stfd);
// Set the FD_CLOEXEC of FD.
extern srs_error_t srs_fd_closeexec(int fd);
diff --git a/trunk/src/utest/srs_utest_config.cpp b/trunk/src/utest/srs_utest_config.cpp
index cbbf1eae4..d98a7cd85 100644
--- a/trunk/src/utest/srs_utest_config.cpp
+++ b/trunk/src/utest/srs_utest_config.cpp
@@ -3729,43 +3729,57 @@ VOID TEST(ConfigMainTest, CheckHttpListen)
if (true) {
MockSrsConfig conf;
- HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_api{enabled on;listen xxx;}http_server{enabled on;listen xxx;}"));
+ HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_api{enabled on;listen 1234;}http_server{enabled on;listen 2345;}"));
EXPECT_TRUE(conf.get_http_stream_enabled());
- EXPECT_STREQ("xxx", conf.get_http_stream_listen().c_str());
- EXPECT_STREQ("xxx", conf.get_http_api_listen().c_str());
+ EXPECT_EQ(1, (int)conf.get_http_stream_listens().size());
+ EXPECT_STREQ("2345", conf.get_http_stream_listens().at(0).c_str());
+ EXPECT_EQ(1, (int)conf.get_http_api_listens().size());
+ EXPECT_STREQ("1234", conf.get_http_api_listens().at(0).c_str());
EXPECT_TRUE(conf.get_http_stream_crossdomain());
}
if (true) {
MockSrsConfig conf;
- HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_api{enabled on;listen xxx;}http_server{enabled on;listen xxx;}"));
+ HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_api{enabled on;listen 1234;}http_server{enabled on;listen 2345;}"));
EXPECT_TRUE(conf.get_http_stream_enabled());
- EXPECT_STREQ("xxx", conf.get_http_stream_listen().c_str());
- EXPECT_STREQ("8088", conf.get_https_stream_listen().c_str());
- EXPECT_STREQ("xxx", conf.get_http_api_listen().c_str());
- EXPECT_STREQ("8088", conf.get_https_api_listen().c_str());
+ EXPECT_EQ(1, (int)conf.get_http_stream_listens().size());
+ EXPECT_STREQ("2345", conf.get_http_stream_listens().at(0).c_str());
+ EXPECT_EQ(1, (int)conf.get_https_stream_listens().size());
+ EXPECT_STREQ("8088", conf.get_https_stream_listens().at(0).c_str());
+ EXPECT_EQ(1, (int)conf.get_http_api_listens().size());
+ EXPECT_STREQ("1234", conf.get_http_api_listens().at(0).c_str());
+ EXPECT_EQ(1, (int)conf.get_https_api_listens().size());
+ EXPECT_STREQ("1990", conf.get_https_api_listens().at(0).c_str());
EXPECT_TRUE(conf.get_http_stream_crossdomain());
}
if (true) {
MockSrsConfig conf;
- HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_api{enabled on;listen mmm;https{enabled on;listen zzz;}}http_server{enabled on;listen xxx;https{enabled on;listen yyy;}}"));
+ HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_api{enabled on;listen 1234;https{enabled on;listen 2345;}}http_server{enabled on;listen 3456;https{enabled on;listen 4567;}}"));
EXPECT_TRUE(conf.get_http_stream_enabled());
- EXPECT_STREQ("xxx", conf.get_http_stream_listen().c_str());
- EXPECT_STREQ("yyy", conf.get_https_stream_listen().c_str());
- EXPECT_STREQ("mmm", conf.get_http_api_listen().c_str());
- EXPECT_STREQ("zzz", conf.get_https_api_listen().c_str());
+ EXPECT_EQ(1, (int)conf.get_http_stream_listens().size());
+ EXPECT_STREQ("3456", conf.get_http_stream_listens().at(0).c_str());
+ EXPECT_EQ(1, (int)conf.get_https_stream_listens().size());
+ EXPECT_STREQ("4567", conf.get_https_stream_listens().at(0).c_str());
+ EXPECT_EQ(1, (int)conf.get_http_api_listens().size());
+ EXPECT_STREQ("1234", conf.get_http_api_listens().at(0).c_str());
+ EXPECT_EQ(1, (int)conf.get_https_api_listens().size());
+ EXPECT_STREQ("2345", conf.get_https_api_listens().at(0).c_str());
EXPECT_TRUE(conf.get_http_stream_crossdomain());
}
if (true) {
MockSrsConfig conf;
- HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_api{enabled on;listen xxx;https{enabled on;listen yyy;}}http_server{enabled on;listen xxx;https{enabled on;listen yyy;}}"));
+ HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_api{enabled on;listen 1234;https{enabled on;listen 2345;}}http_server{enabled on;listen 3456;https{enabled on;listen 5678;}}"));
EXPECT_TRUE(conf.get_http_stream_enabled());
- EXPECT_STREQ("xxx", conf.get_http_stream_listen().c_str());
- EXPECT_STREQ("yyy", conf.get_https_stream_listen().c_str());
- EXPECT_STREQ("xxx", conf.get_http_api_listen().c_str());
- EXPECT_STREQ("yyy", conf.get_https_api_listen().c_str());
+ EXPECT_EQ(1, (int)conf.get_http_stream_listens().size());
+ EXPECT_STREQ("3456", conf.get_http_stream_listens().at(0).c_str());
+ EXPECT_EQ(1, (int)conf.get_https_stream_listens().size());
+ EXPECT_STREQ("5678", conf.get_https_stream_listens().at(0).c_str());
+ EXPECT_EQ(1, (int)conf.get_http_api_listens().size());
+ EXPECT_STREQ("1234", conf.get_http_api_listens().at(0).c_str());
+ EXPECT_EQ(1, (int)conf.get_https_api_listens().size());
+ EXPECT_STREQ("2345", conf.get_https_api_listens().at(0).c_str());
EXPECT_TRUE(conf.get_http_stream_crossdomain());
}
@@ -3882,9 +3896,10 @@ VOID TEST(ConfigMainTest, CheckVhostConfig5)
if (true) {
MockSrsConfig conf;
- HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_api{enabled on;listen xxx;crossdomain off;auth {enabled on;username admin;password 123456;}raw_api {enabled on;allow_reload on;allow_query on;allow_update on;}}"));
+ HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_api{enabled on;listen 1234;crossdomain off;auth {enabled on;username admin;password 123456;}raw_api {enabled on;allow_reload on;allow_query on;allow_update on;}}"));
EXPECT_TRUE(conf.get_http_api_enabled());
- EXPECT_STREQ("xxx", conf.get_http_api_listen().c_str());
+ EXPECT_EQ(1, (int)conf.get_http_api_listens().size());
+ EXPECT_STREQ("1234", conf.get_http_api_listens().at(0).c_str());
EXPECT_FALSE(conf.get_http_api_crossdomain());
EXPECT_TRUE(conf.get_raw_api());
EXPECT_TRUE(conf.get_raw_api_allow_reload());
@@ -3897,9 +3912,10 @@ VOID TEST(ConfigMainTest, CheckVhostConfig5)
if (true) {
MockSrsConfig conf;
- HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_server{enabled on;listen xxx;dir xxx2;crossdomain on;}"));
+ HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_server{enabled on;listen 1234;dir xxx2;crossdomain on;}"));
EXPECT_TRUE(conf.get_http_stream_enabled());
- EXPECT_STREQ("xxx", conf.get_http_stream_listen().c_str());
+ EXPECT_EQ(1, (int)conf.get_http_stream_listens().size());
+ EXPECT_STREQ("1234", conf.get_http_stream_listens().at(0).c_str());
EXPECT_STREQ("xxx2", conf.get_http_stream_dir().c_str());
EXPECT_TRUE(conf.get_http_stream_crossdomain());
}
@@ -3955,7 +3971,7 @@ VOID TEST(ConfigMainTest, CheckIncludeConfig)
MockSrsConfig conf;
conf.mock_include("./conf/include_test/include.conf", "rtmp{listen 1935;}include ./conf/include_test/include_1.conf;");
- conf.mock_include("./conf/include_test/include_1.conf", "max_connections 1000;daemon off;srs_log_tank console;http_server {enabled on;listen xxx;dir xxx2;}vhost ossrs.net {hls {enabled on;hls_path xxx;hls_m3u8_file xxx1;hls_ts_file xxx2;hls_fragment 10;hls_window 60;}}");
+ conf.mock_include("./conf/include_test/include_1.conf", "max_connections 1000;daemon off;srs_log_tank console;http_server {enabled on;listen 1234;dir xxx2;}vhost ossrs.net {hls {enabled on;hls_path xxx;hls_m3u8_file xxx1;hls_ts_file xxx2;hls_fragment 10;hls_window 60;}}");
HELPER_ASSERT_SUCCESS(conf.parse("include ./conf/include_test/include.conf;"));
@@ -3966,7 +3982,8 @@ VOID TEST(ConfigMainTest, CheckIncludeConfig)
EXPECT_FALSE(conf.get_log_tank_file());
EXPECT_TRUE(conf.get_http_stream_enabled());
- EXPECT_STREQ("xxx", conf.get_http_stream_listen().c_str());
+ EXPECT_EQ(1, (int)conf.get_http_stream_listens().size());
+ EXPECT_STREQ("1234", conf.get_http_stream_listens().at(0).c_str());
EXPECT_STREQ("xxx2", conf.get_http_stream_dir().c_str());
EXPECT_TRUE(conf.get_hls_enabled("ossrs.net"));
@@ -3980,7 +3997,7 @@ VOID TEST(ConfigMainTest, CheckIncludeConfig)
if (true) {
MockSrsConfig conf;
- conf.mock_include("./conf/include_test/include_1.conf", "max_connections 1000;daemon off;srs_log_tank console;http_server {enabled on;listen xxx;dir xxx2;}vhost ossrs.net {hls {enabled on;hls_path xxx;hls_m3u8_file xxx1;hls_ts_file xxx2;hls_fragment 10;hls_window 60;}}");
+ conf.mock_include("./conf/include_test/include_1.conf", "max_connections 1000;daemon off;srs_log_tank console;http_server {enabled on;listen 1234;dir xxx2;}vhost ossrs.net {hls {enabled on;hls_path xxx;hls_m3u8_file xxx1;hls_ts_file xxx2;hls_fragment 10;hls_window 60;}}");
HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "include ./conf/include_test/include_1.conf;"));
@@ -3991,7 +4008,8 @@ VOID TEST(ConfigMainTest, CheckIncludeConfig)
EXPECT_FALSE(conf.get_log_tank_file());
EXPECT_TRUE(conf.get_http_stream_enabled());
- EXPECT_STREQ("xxx", conf.get_http_stream_listen().c_str());
+ EXPECT_EQ(1, (int)conf.get_http_stream_listens().size());
+ EXPECT_STREQ("1234", conf.get_http_stream_listens().at(0).c_str());
EXPECT_STREQ("xxx2", conf.get_http_stream_dir().c_str());
EXPECT_TRUE(conf.get_hls_enabled("ossrs.net"));
@@ -4005,7 +4023,7 @@ VOID TEST(ConfigMainTest, CheckIncludeConfig)
if (true) {
MockSrsConfig conf;
- conf.mock_include("./conf/include_test/include_2.conf", "listen 1935;max_connections 1000;daemon off;srs_log_tank console;http_server {enabled on;listen xxx;dir xxx2;}vhost ossrs.net {include ./conf/include_test/include_3.conf;}");
+ conf.mock_include("./conf/include_test/include_2.conf", "listen 1935;max_connections 1000;daemon off;srs_log_tank console;http_server {enabled on;listen 1234;dir xxx2;}vhost ossrs.net {include ./conf/include_test/include_3.conf;}");
conf.mock_include("./conf/include_test/include_3.conf", "hls {enabled on;hls_path xxx;hls_m3u8_file xxx1;hls_ts_file xxx2;hls_fragment 10;hls_window 60;}");
HELPER_ASSERT_SUCCESS(conf.parse("include ./conf/include_test/include_2.conf;"));
@@ -4017,7 +4035,8 @@ VOID TEST(ConfigMainTest, CheckIncludeConfig)
EXPECT_FALSE(conf.get_log_tank_file());
EXPECT_TRUE(conf.get_http_stream_enabled());
- EXPECT_STREQ("xxx", conf.get_http_stream_listen().c_str());
+ EXPECT_EQ(1, (int)conf.get_http_stream_listens().size());
+ EXPECT_STREQ("1234", conf.get_http_stream_listens().at(0).c_str());
EXPECT_STREQ("xxx2", conf.get_http_stream_dir().c_str());
EXPECT_TRUE(conf.get_hls_enabled("ossrs.net"));
@@ -4045,7 +4064,8 @@ VOID TEST(ConfigMainTest, CheckIncludeConfig)
EXPECT_FALSE(conf.get_log_tank_file());
EXPECT_TRUE(conf.get_http_stream_enabled());
- EXPECT_STREQ("8080", conf.get_http_stream_listen().c_str());
+ EXPECT_EQ(1, (int)conf.get_http_stream_listens().size());
+ EXPECT_STREQ("8080", conf.get_http_stream_listens().at(0).c_str());
EXPECT_STREQ("xxx2", conf.get_http_stream_dir().c_str());
EXPECT_TRUE(conf.get_hls_enabled("ossrs.net"));
@@ -4056,7 +4076,8 @@ VOID TEST(ConfigMainTest, CheckIncludeConfig)
EXPECT_EQ(60 * SRS_UTIME_SECONDS, conf.get_hls_window("ossrs.net"));
EXPECT_TRUE(conf.get_http_api_enabled());
- EXPECT_STREQ("1985", conf.get_http_api_listen().c_str());
+ EXPECT_EQ(1, (int)conf.get_http_api_listens().size());
+ EXPECT_STREQ("1985", conf.get_http_api_listens().at(0).c_str());
}
if (true) {
@@ -4064,8 +4085,8 @@ VOID TEST(ConfigMainTest, CheckIncludeConfig)
conf.mock_include("./conf/include_test/include_3.conf", "hls {enabled on;hls_path xxx;hls_m3u8_file xxx1;hls_ts_file xxx2;hls_fragment 10;hls_window 60;}");
conf.mock_include("./conf/include_test/include_4.conf", "listen 1935;max_connections 1000;daemon off;srs_log_tank console;include ./conf/include_test/include_5.conf ./conf/include_test/include_6.conf;vhost ossrs.net {include ./conf/include_test/include_3.conf;}");
- conf.mock_include("./conf/include_test/include_5.conf", "http_server {enabled on;listen xxx;dir xxx2;}");
- conf.mock_include("./conf/include_test/include_6.conf", "http_api {enabled on;listen yyy;}");
+ conf.mock_include("./conf/include_test/include_5.conf", "http_server {enabled on;listen 1234;dir xxx2;}");
+ conf.mock_include("./conf/include_test/include_6.conf", "http_api {enabled on;listen 2345;}");
HELPER_ASSERT_SUCCESS(conf.parse("include ./conf/include_test/include_4.conf;"));
@@ -4076,11 +4097,13 @@ VOID TEST(ConfigMainTest, CheckIncludeConfig)
EXPECT_FALSE(conf.get_log_tank_file());
EXPECT_TRUE(conf.get_http_stream_enabled());
- EXPECT_STREQ("xxx", conf.get_http_stream_listen().c_str());
+ EXPECT_EQ(1, (int)conf.get_http_stream_listens().size());
+ EXPECT_STREQ("1234", conf.get_http_stream_listens().at(0).c_str());
EXPECT_STREQ("xxx2", conf.get_http_stream_dir().c_str());
EXPECT_TRUE(conf.get_http_api_enabled());
- EXPECT_STREQ("yyy", conf.get_http_api_listen().c_str());
+ EXPECT_EQ(1, (int)conf.get_http_api_listens().size());
+ EXPECT_STREQ("2345", conf.get_http_api_listens().at(0).c_str());
EXPECT_TRUE(conf.get_hls_enabled("ossrs.net"));
EXPECT_STREQ("xxx", conf.get_hls_path("ossrs.net").c_str());
@@ -4095,8 +4118,8 @@ VOID TEST(ConfigMainTest, CheckIncludeConfig)
conf.mock_include("./conf/include_test/include_3.conf", "hls {enabled on;hls_path xxx;hls_m3u8_file xxx1;hls_ts_file xxx2;hls_fragment 10;hls_window 60;}");
conf.mock_include("./conf/include_test/include_4.conf", "listen 1935;max_connections 1000;daemon off;srs_log_tank console;include ./conf/include_test/include_5.conf ./conf/include_test/include_6.conf;vhost ossrs.net {include ./conf/include_test/include_3.conf ./conf/include_test/include_7.conf;}");
- conf.mock_include("./conf/include_test/include_5.conf", "http_server {enabled on;listen xxx;dir xxx2;}");
- conf.mock_include("./conf/include_test/include_6.conf", "http_api {enabled on;listen yyy;}");
+ conf.mock_include("./conf/include_test/include_5.conf", "http_server {enabled on;listen 1234;dir xxx2;}");
+ conf.mock_include("./conf/include_test/include_6.conf", "http_api {enabled on;listen 2345;}");
conf.mock_include("./conf/include_test/include_7.conf", "dash{enabled on;dash_fragment 10;dash_update_period 10;dash_timeshift 10;dash_path xxx;dash_mpd_file xxx2;}");
HELPER_ASSERT_SUCCESS(conf.parse("include ./conf/include_test/include_4.conf;"));
@@ -4108,11 +4131,13 @@ VOID TEST(ConfigMainTest, CheckIncludeConfig)
EXPECT_FALSE(conf.get_log_tank_file());
EXPECT_TRUE(conf.get_http_stream_enabled());
- EXPECT_STREQ("xxx", conf.get_http_stream_listen().c_str());
+ EXPECT_EQ(1, (int)conf.get_http_stream_listens().size());
+ EXPECT_STREQ("1234", conf.get_http_stream_listens().at(0).c_str());
EXPECT_STREQ("xxx2", conf.get_http_stream_dir().c_str());
EXPECT_TRUE(conf.get_http_api_enabled());
- EXPECT_STREQ("yyy", conf.get_http_api_listen().c_str());
+ EXPECT_EQ(1, (int)conf.get_http_api_listens().size());
+ EXPECT_STREQ("2345", conf.get_http_api_listens().at(0).c_str());
EXPECT_TRUE(conf.get_hls_enabled("ossrs.net"));
EXPECT_STREQ("xxx", conf.get_hls_path("ossrs.net").c_str());
@@ -4344,7 +4369,8 @@ VOID TEST(ConfigEnvTest, CheckEnvValuesHttpApi)
EXPECT_TRUE(conf.get_http_api_enabled());
SrsSetEnvConfig(conf, http_api_listen, "SRS_HTTP_API_LISTEN", "xxx");
- EXPECT_STREQ("xxx", conf.get_http_api_listen().c_str());
+ EXPECT_EQ(1, (int)conf.get_http_api_listens().size());
+ EXPECT_STREQ("xxx", conf.get_http_api_listens().at(0).c_str());
SrsSetEnvConfig(conf, http_api_crossdomain, "SRS_HTTP_API_CROSSDOMAIN", "off");
EXPECT_FALSE(conf.get_http_api_crossdomain());
@@ -4376,7 +4402,8 @@ VOID TEST(ConfigEnvTest, CheckEnvValuesHttpApi)
EXPECT_TRUE(conf.get_https_api_enabled());
SrsSetEnvConfig(conf, https_api_listen, "SRS_HTTP_API_HTTPS_LISTEN", "xxx");
- EXPECT_STREQ("xxx", conf.get_https_api_listen().c_str());
+ EXPECT_EQ(1, (int)conf.get_https_api_listens().size());
+ EXPECT_STREQ("xxx", conf.get_https_api_listens().at(0).c_str());
SrsSetEnvConfig(conf, https_api_ssl_key, "SRS_HTTP_API_HTTPS_KEY", "xxx2");
EXPECT_STREQ("xxx2", conf.get_https_api_ssl_key().c_str());
@@ -4395,7 +4422,8 @@ VOID TEST(ConfigEnvTest, CheckEnvValuesHttpServer)
EXPECT_TRUE(conf.get_http_stream_enabled());
SrsSetEnvConfig(conf, http_stream_listen, "SRS_HTTP_SERVER_LISTEN", "xxx");
- EXPECT_STREQ("xxx", conf.get_http_stream_listen().c_str());
+ EXPECT_EQ(1, (int)conf.get_http_stream_listens().size());
+ EXPECT_STREQ("xxx", conf.get_http_stream_listens().at(0).c_str());
SrsSetEnvConfig(conf, http_stream_dir, "SRS_HTTP_SERVER_DIR", "xxx2");
EXPECT_STREQ("xxx2", conf.get_http_stream_dir().c_str());
@@ -4411,7 +4439,8 @@ VOID TEST(ConfigEnvTest, CheckEnvValuesHttpServer)
EXPECT_TRUE(conf.get_https_stream_enabled());
SrsSetEnvConfig(conf, https_stream_listen, "SRS_HTTP_SERVER_HTTPS_LISTEN", "xxx");
- EXPECT_STREQ("xxx", conf.get_https_stream_listen().c_str());
+ EXPECT_EQ(1, (int)conf.get_https_stream_listens().size());
+ EXPECT_STREQ("xxx", conf.get_https_stream_listens().at(0).c_str());
SrsSetEnvConfig(conf, https_stream_ssl_key, "SRS_HTTP_SERVER_HTTPS_KEY", "xxx2");
EXPECT_STREQ("xxx2", conf.get_https_stream_ssl_key().c_str());
@@ -4430,7 +4459,8 @@ VOID TEST(ConfigEnvTest, CheckEnvValuesSrtServer)
EXPECT_TRUE(conf.get_srt_enabled());
SrsSetEnvConfig(conf, srt_listen_port, "SRS_SRT_SERVER_LISTEN", "10000");
- EXPECT_EQ(10000, conf.get_srt_listen_port());
+ EXPECT_EQ(1, (int)conf.get_srt_listens().size());
+ EXPECT_STREQ("10000", conf.get_srt_listens().at(0).c_str());
SrsSetEnvConfig(conf, srto_maxbw, "SRS_SRT_SERVER_MAXBW", "1000000000");
EXPECT_EQ(1000000000, conf.get_srto_maxbw());
@@ -4501,7 +4531,8 @@ VOID TEST(ConfigEnvTest, CheckEnvValuesRtspServer)
EXPECT_TRUE(conf.get_rtsp_server_enabled());
SrsSetEnvConfig(conf, rtsp_server_listen, "SRS_RTSP_SERVER_LISTEN", "554");
- EXPECT_EQ(554, conf.get_rtsp_server_listen());
+ EXPECT_EQ(1, (int)conf.get_rtsp_server_listens().size());
+ EXPECT_STREQ("554", conf.get_rtsp_server_listens().at(0).c_str());
}
}
@@ -4514,7 +4545,8 @@ VOID TEST(ConfigEnvTest, CheckEnvValuesRtcServer)
EXPECT_TRUE(conf.get_rtc_server_enabled());
SrsSetEnvConfig(conf, rtc_server_listen, "SRS_RTC_SERVER_LISTEN", "8080");
- EXPECT_EQ(8080, conf.get_rtc_server_listen());
+ EXPECT_EQ(1, (int)conf.get_rtc_server_listens().size());
+ EXPECT_STREQ("8080", conf.get_rtc_server_listens().at(0).c_str());
SrsSetEnvConfig(conf, rtc_server_protocol, "SRS_RTC_SERVER_PROTOCOL", "xxx");
EXPECT_STREQ("xxx", conf.get_rtc_server_protocol().c_str());
@@ -4557,7 +4589,8 @@ VOID TEST(ConfigEnvTest, CheckEnvValuesRtcServer)
EXPECT_TRUE(conf.get_rtc_server_tcp_enabled());
SrsSetEnvConfig(conf, get_rtc_server_tcp_listen, "SRS_RTC_SERVER_TCP_LISTEN", "8080");
- EXPECT_EQ(8080, conf.get_rtc_server_tcp_listen());
+ EXPECT_EQ(1, (int)conf.get_rtc_server_tcp_listens().size());
+ EXPECT_STREQ("8080", conf.get_rtc_server_tcp_listens().at(0).c_str());
}
if (true) {
diff --git a/trunk/src/utest/srs_utest_config2.cpp b/trunk/src/utest/srs_utest_config2.cpp
index 6a88d4dea..c803e1345 100644
--- a/trunk/src/utest/srs_utest_config2.cpp
+++ b/trunk/src/utest/srs_utest_config2.cpp
@@ -44,3 +44,122 @@ VOID TEST(ConfigMainTest, CheckIncludeEmptyConfig)
EXPECT_EQ(1, (int)conf.get_listens().size());
}
}
+
+VOID TEST(ConfigMainTest, CheckHttpListenFollow)
+{
+ srs_error_t err;
+
+ if (true) {
+ MockSrsConfig conf;
+ HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_api{enabled on;https{enabled on;}}http_server{enabled on;listen 1985;https{enabled on;listen 4567;}}"));
+ EXPECT_TRUE(conf.get_http_stream_enabled());
+
+ // If http API use same port to HTTP server.
+ EXPECT_EQ(1, (int)conf.get_http_stream_listens().size());
+ EXPECT_STREQ("1985", conf.get_http_stream_listens().at(0).c_str());
+ EXPECT_EQ(1, (int)conf.get_http_api_listens().size());
+ EXPECT_STREQ("1985", conf.get_http_api_listens().at(0).c_str());
+
+ // Then HTTPS API should use the same port to HTTPS server.
+ EXPECT_EQ(1, (int)conf.get_https_stream_listens().size());
+ EXPECT_STREQ("4567", conf.get_https_stream_listens().at(0).c_str());
+ EXPECT_EQ(1, (int)conf.get_https_api_listens().size());
+ EXPECT_STREQ("4567", conf.get_https_api_listens().at(0).c_str());
+ EXPECT_TRUE(conf.get_http_stream_crossdomain());
+ }
+
+ if (true) {
+ MockSrsConfig conf;
+ HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_api{enabled on;listen 8080;https{enabled on;}}http_server{enabled on;https{enabled on;listen 4567;}}"));
+ EXPECT_TRUE(conf.get_http_stream_enabled());
+
+ // If http API use same port to HTTP server.
+ EXPECT_EQ(1, (int)conf.get_http_stream_listens().size());
+ EXPECT_STREQ("8080", conf.get_http_stream_listens().at(0).c_str());
+ EXPECT_EQ(1, (int)conf.get_http_api_listens().size());
+ EXPECT_STREQ("8080", conf.get_http_api_listens().at(0).c_str());
+
+ // Then HTTPS API should use the same port to HTTPS server.
+ EXPECT_EQ(1, (int)conf.get_https_stream_listens().size());
+ EXPECT_STREQ("4567", conf.get_https_stream_listens().at(0).c_str());
+ EXPECT_EQ(1, (int)conf.get_https_api_listens().size());
+ EXPECT_STREQ("4567", conf.get_https_api_listens().at(0).c_str());
+ EXPECT_TRUE(conf.get_http_stream_crossdomain());
+ }
+
+ if (true) {
+ MockSrsConfig conf;
+ HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_api{enabled on;listen 8080;https{enabled on;}}http_server{enabled on;https{enabled on;}}"));
+ EXPECT_TRUE(conf.get_http_stream_enabled());
+
+ // If http API use same port to HTTP server.
+ EXPECT_EQ(1, (int)conf.get_http_stream_listens().size());
+ EXPECT_STREQ("8080", conf.get_http_stream_listens().at(0).c_str());
+ EXPECT_EQ(1, (int)conf.get_http_api_listens().size());
+ EXPECT_STREQ("8080", conf.get_http_api_listens().at(0).c_str());
+
+ // Then HTTPS API should use the same port to HTTPS server.
+ EXPECT_EQ(1, (int)conf.get_https_stream_listens().size());
+ EXPECT_STREQ("8088", conf.get_https_stream_listens().at(0).c_str());
+ EXPECT_EQ(1, (int)conf.get_https_api_listens().size());
+ EXPECT_STREQ("8088", conf.get_https_api_listens().at(0).c_str());
+ EXPECT_TRUE(conf.get_http_stream_crossdomain());
+ }
+
+ if (true) {
+ MockSrsConfig conf;
+ HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_api{enabled on;https{enabled on;}}http_server{enabled on;listen 1985;https{enabled on;}}"));
+ EXPECT_TRUE(conf.get_http_stream_enabled());
+
+ // If http API use same port to HTTP server.
+ EXPECT_EQ(1, (int)conf.get_http_stream_listens().size());
+ EXPECT_STREQ("1985", conf.get_http_stream_listens().at(0).c_str());
+ EXPECT_EQ(1, (int)conf.get_http_api_listens().size());
+ EXPECT_STREQ("1985", conf.get_http_api_listens().at(0).c_str());
+
+ // Then HTTPS API should use the same port to HTTPS server.
+ EXPECT_EQ(1, (int)conf.get_https_stream_listens().size());
+ EXPECT_STREQ("8088", conf.get_https_stream_listens().at(0).c_str());
+ EXPECT_EQ(1, (int)conf.get_https_api_listens().size());
+ EXPECT_STREQ("8088", conf.get_https_api_listens().at(0).c_str());
+ EXPECT_TRUE(conf.get_http_stream_crossdomain());
+ }
+
+ if (true) {
+ MockSrsConfig conf;
+ HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_api{enabled on;listen 1234;https{enabled on;}}http_server{enabled on;listen 1234;https{enabled on;}}"));
+ EXPECT_TRUE(conf.get_http_stream_enabled());
+
+ // If http API use same port to HTTP server.
+ EXPECT_EQ(1, (int)conf.get_http_stream_listens().size());
+ EXPECT_STREQ("1234", conf.get_http_stream_listens().at(0).c_str());
+ EXPECT_EQ(1, (int)conf.get_http_api_listens().size());
+ EXPECT_STREQ("1234", conf.get_http_api_listens().at(0).c_str());
+
+ // Then HTTPS API should use the same port to HTTPS server.
+ EXPECT_EQ(1, (int)conf.get_https_stream_listens().size());
+ EXPECT_STREQ("8088", conf.get_https_stream_listens().at(0).c_str());
+ EXPECT_EQ(1, (int)conf.get_https_api_listens().size());
+ EXPECT_STREQ("8088", conf.get_https_api_listens().at(0).c_str());
+ EXPECT_TRUE(conf.get_http_stream_crossdomain());
+ }
+
+ if (true) {
+ MockSrsConfig conf;
+ HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_api{enabled on;listen 1234;https{enabled on;}}http_server{enabled on;listen 1234;https{enabled on;listen 4567;}}"));
+ EXPECT_TRUE(conf.get_http_stream_enabled());
+
+ // If http API use same port to HTTP server.
+ EXPECT_EQ(1, (int)conf.get_http_stream_listens().size());
+ EXPECT_STREQ("1234", conf.get_http_stream_listens().at(0).c_str());
+ EXPECT_EQ(1, (int)conf.get_http_api_listens().size());
+ EXPECT_STREQ("1234", conf.get_http_api_listens().at(0).c_str());
+
+ // Then HTTPS API should use the same port to HTTPS server.
+ EXPECT_EQ(1, (int)conf.get_https_stream_listens().size());
+ EXPECT_STREQ("4567", conf.get_https_stream_listens().at(0).c_str());
+ EXPECT_EQ(1, (int)conf.get_https_api_listens().size());
+ EXPECT_STREQ("4567", conf.get_https_api_listens().at(0).c_str());
+ EXPECT_TRUE(conf.get_http_stream_crossdomain());
+ }
+}
diff --git a/trunk/src/utest/srs_utest_st.cpp b/trunk/src/utest/srs_utest_st.cpp
index 1ddf55a21..f0428f21c 100644
--- a/trunk/src/utest/srs_utest_st.cpp
+++ b/trunk/src/utest/srs_utest_st.cpp
@@ -3,6 +3,10 @@
//
// SPDX-License-Identifier: MIT
//
+#include
+#include
+#include
+#include
#include
#include
#include
@@ -108,3 +112,497 @@ VOID TEST(StTest, StUtimePerformance)
EXPECT_LT(gettimeofday_elapsed_time > st_utime_elapsed_time ? gettimeofday_elapsed_time - st_utime_elapsed_time : st_utime_elapsed_time - gettimeofday_elapsed_time, 30);
}
}
+
+// Test IPv4 and IPv6 socket functions
+VOID TEST(StSocketTest, IPv4TcpListen)
+{
+ srs_error_t err;
+
+ srs_netfd_t fd = NULL;
+ HELPER_EXPECT_SUCCESS(srs_tcp_listen("127.0.0.1", 0, &fd));
+ EXPECT_TRUE(fd != NULL);
+
+ // Get the actual port that was assigned
+ int actual_fd = srs_netfd_fileno(fd);
+ EXPECT_GT(actual_fd, 0);
+
+ srs_close_stfd(fd);
+}
+
+VOID TEST(StSocketTest, IPv4UdpListen)
+{
+ srs_error_t err;
+
+ srs_netfd_t fd = NULL;
+ HELPER_EXPECT_SUCCESS(srs_udp_listen("127.0.0.1", 0, &fd));
+ EXPECT_TRUE(fd != NULL);
+
+ // Get the actual port that was assigned
+ int actual_fd = srs_netfd_fileno(fd);
+ EXPECT_GT(actual_fd, 0);
+
+ srs_close_stfd(fd);
+}
+
+VOID TEST(StSocketTest, IPv6TcpListen)
+{
+ srs_error_t err;
+
+ srs_netfd_t fd = NULL;
+ HELPER_EXPECT_SUCCESS(srs_udp_listen("::1", 0, &fd));
+
+ int actual_fd = srs_netfd_fileno(fd);
+ EXPECT_GT(actual_fd, 0);
+
+ srs_close_stfd(fd);
+}
+
+VOID TEST(StSocketTest, IPv6UdpListen)
+{
+ srs_error_t err;
+
+ srs_netfd_t fd = NULL;
+ HELPER_EXPECT_SUCCESS(srs_udp_listen("::1", 0, &fd));
+
+ int actual_fd = srs_netfd_fileno(fd);
+ EXPECT_GT(actual_fd, 0);
+
+ srs_close_stfd(fd);
+}
+
+VOID TEST(StSocketTest, IPv6AnyAddressTcpListen)
+{
+ srs_error_t err;
+
+ srs_netfd_t fd = NULL;
+ HELPER_EXPECT_SUCCESS(srs_tcp_listen("::", 0, &fd));
+
+ int actual_fd = srs_netfd_fileno(fd);
+ EXPECT_GT(actual_fd, 0);
+
+ srs_close_stfd(fd);
+}
+
+VOID TEST(StSocketTest, IPv6AnyAddressUdpListen)
+{
+ srs_error_t err;
+
+ srs_netfd_t fd = NULL;
+ HELPER_EXPECT_SUCCESS(srs_udp_listen("::", 0, &fd));
+
+ int actual_fd = srs_netfd_fileno(fd);
+ EXPECT_GT(actual_fd, 0);
+
+ srs_close_stfd(fd);
+}
+
+VOID TEST(StSocketTest, IPv4AnyAddressTcpListen)
+{
+ srs_error_t err;
+
+ srs_netfd_t fd = NULL;
+ HELPER_EXPECT_SUCCESS(srs_tcp_listen("0.0.0.0", 0, &fd));
+ EXPECT_TRUE(fd != NULL);
+
+ int actual_fd = srs_netfd_fileno(fd);
+ EXPECT_GT(actual_fd, 0);
+
+ srs_close_stfd(fd);
+}
+
+VOID TEST(StSocketTest, IPv4AnyAddressUdpListen)
+{
+ srs_error_t err;
+
+ srs_netfd_t fd = NULL;
+ HELPER_EXPECT_SUCCESS(srs_udp_listen("0.0.0.0", 0, &fd));
+ EXPECT_TRUE(fd != NULL);
+
+ int actual_fd = srs_netfd_fileno(fd);
+ EXPECT_GT(actual_fd, 0);
+
+ srs_close_stfd(fd);
+}
+
+VOID TEST(StSocketTest, InvalidAddressTcpListen)
+{
+ srs_error_t err;
+
+ srs_netfd_t fd = NULL;
+ HELPER_EXPECT_FAILED(srs_tcp_listen("999.999.999.999", 8080, &fd));
+ EXPECT_TRUE(fd == NULL);
+
+ HELPER_EXPECT_FAILED(srs_tcp_listen("::gggg", 8080, &fd));
+ EXPECT_TRUE(fd == NULL);
+}
+
+VOID TEST(StSocketTest, InvalidAddressUdpListen)
+{
+ srs_error_t err;
+
+ srs_netfd_t fd = NULL;
+ HELPER_EXPECT_FAILED(srs_udp_listen("999.999.999.999", 8080, &fd));
+ EXPECT_TRUE(fd == NULL);
+
+ HELPER_EXPECT_FAILED(srs_udp_listen("::gggg", 8080, &fd));
+ EXPECT_TRUE(fd == NULL);
+}
+
+VOID TEST(StSocketTest, PortRangeTcpListen)
+{
+ srs_error_t err;
+
+ // Test valid port ranges
+ srs_netfd_t fd = NULL;
+ HELPER_EXPECT_SUCCESS(srs_tcp_listen("127.0.0.1", 1024, &fd));
+ EXPECT_TRUE(fd != NULL);
+ srs_close_stfd(fd);
+
+ fd = NULL;
+ HELPER_EXPECT_SUCCESS(srs_tcp_listen("127.0.0.1", 65535, &fd));
+ EXPECT_TRUE(fd != NULL);
+ srs_close_stfd(fd);
+
+ // Test port 0 (system assigns available port)
+ fd = NULL;
+ HELPER_EXPECT_SUCCESS(srs_tcp_listen("127.0.0.1", 0, &fd));
+ EXPECT_TRUE(fd != NULL);
+ srs_close_stfd(fd);
+}
+
+VOID TEST(StSocketTest, PortRangeUdpListen)
+{
+ srs_error_t err;
+
+ // Test valid port ranges
+ srs_netfd_t fd = NULL;
+ HELPER_EXPECT_SUCCESS(srs_udp_listen("127.0.0.1", 1024, &fd));
+ EXPECT_TRUE(fd != NULL);
+ srs_close_stfd(fd);
+
+ fd = NULL;
+ HELPER_EXPECT_SUCCESS(srs_udp_listen("127.0.0.1", 65535, &fd));
+ EXPECT_TRUE(fd != NULL);
+ srs_close_stfd(fd);
+
+ // Test port 0 (system assigns available port)
+ fd = NULL;
+ HELPER_EXPECT_SUCCESS(srs_udp_listen("127.0.0.1", 0, &fd));
+ EXPECT_TRUE(fd != NULL);
+ srs_close_stfd(fd);
+}
+
+VOID TEST(StSocketTest, TcpConnectIPv4)
+{
+ srs_error_t err;
+
+ // Test connecting to a well-known service (should fail but test address resolution)
+ srs_netfd_t fd = NULL;
+ HELPER_EXPECT_FAILED(srs_tcp_connect("127.0.0.1", 1, SRS_UTIME_SECONDS, &fd));
+ EXPECT_TRUE(fd == NULL);
+}
+
+VOID TEST(StSocketTest, TcpConnectIPv6)
+{
+ srs_error_t err;
+
+ // Test connecting to IPv6 loopback (should fail but test address resolution)
+ srs_netfd_t fd = NULL;
+ HELPER_EXPECT_FAILED(srs_tcp_connect("::1", 1, SRS_UTIME_SECONDS, &fd));
+ EXPECT_TRUE(fd == NULL);
+}
+
+VOID TEST(StSocketTest, TcpConnectInvalidAddress)
+{
+ srs_error_t err;
+
+ // Test connecting to invalid address
+ srs_netfd_t fd = NULL;
+ HELPER_EXPECT_FAILED(srs_tcp_connect("999.999.999.999", 80, SRS_UTIME_SECONDS, &fd));
+ EXPECT_TRUE(fd == NULL);
+
+ // Test connecting to invalid IPv6 address
+ fd = NULL;
+ HELPER_EXPECT_FAILED(srs_tcp_connect("::gggg", 80, SRS_UTIME_SECONDS, &fd));
+ EXPECT_TRUE(fd == NULL);
+}
+
+// Test address utility functions
+VOID TEST(StSocketTest, AddressUtilityFunctions)
+{
+ // Test srs_net_address_any() function
+ string any_addr = srs_net_address_any();
+ EXPECT_TRUE(any_addr == "0.0.0.0" || any_addr == "::");
+
+ // Test srs_net_is_valid_ip() function
+ EXPECT_TRUE(srs_net_is_valid_ip("127.0.0.1"));
+ EXPECT_TRUE(srs_net_is_valid_ip("0.0.0.0"));
+ EXPECT_TRUE(srs_net_is_valid_ip("192.168.1.1"));
+
+ // Test IPv6 addresses if supported
+ EXPECT_TRUE(srs_net_is_valid_ip("::1"));
+ EXPECT_TRUE(srs_net_is_valid_ip("::"));
+ EXPECT_TRUE(srs_net_is_valid_ip("2001:db8::1"));
+
+ // Test invalid addresses
+ EXPECT_FALSE(srs_net_is_valid_ip("999.999.999.999"));
+ EXPECT_FALSE(srs_net_is_valid_ip("::gggg"));
+ EXPECT_FALSE(srs_net_is_valid_ip("invalid"));
+ EXPECT_FALSE(srs_net_is_valid_ip(""));
+}
+
+VOID TEST(StSocketTest, AddressParsingForListener)
+{
+ string ip;
+ int port;
+
+ // Test IPv4 address:port parsing
+ srs_net_split_for_listener("192.168.1.1:8080", ip, port);
+ EXPECT_EQ(ip, "192.168.1.1");
+ EXPECT_EQ(port, 8080);
+
+ // Test IPv4 address without port (should use any address)
+ srs_net_split_for_listener("8080", ip, port);
+ EXPECT_TRUE(ip == "0.0.0.0" || ip == "::"); // Should be any address
+ EXPECT_EQ(port, 8080);
+
+ // Test IPv6 address in RFC 2732 format [address]:port
+ srs_net_split_for_listener("[::1]:8080", ip, port);
+ EXPECT_EQ(ip, "::1");
+ EXPECT_EQ(port, 8080);
+
+ // Test IPv6 address in RFC 2732 format [address]:port with full address
+ srs_net_split_for_listener("[2001:db8::1]:1935", ip, port);
+ EXPECT_EQ(ip, "2001:db8::1");
+ EXPECT_EQ(port, 1935);
+
+ // Test IPv6 any address
+ srs_net_split_for_listener("[::]:8080", ip, port);
+ EXPECT_EQ(ip, "::");
+ EXPECT_EQ(port, 8080);
+
+ // Test localhost
+ srs_net_split_for_listener("localhost:8080", ip, port);
+ EXPECT_EQ(ip, "localhost");
+ EXPECT_EQ(port, 8080);
+}
+
+VOID TEST(StSocketTest, IPv4Detection)
+{
+ // Test srs_net_is_ipv4() function
+ EXPECT_TRUE(srs_net_is_ipv4("127.0.0.1"));
+ EXPECT_TRUE(srs_net_is_ipv4("0.0.0.0"));
+ EXPECT_TRUE(srs_net_is_ipv4("192.168.1.1"));
+ EXPECT_TRUE(srs_net_is_ipv4("255.255.255.255"));
+
+ // Test non-IPv4 addresses
+ EXPECT_FALSE(srs_net_is_ipv4("::1"));
+ EXPECT_FALSE(srs_net_is_ipv4("2001:db8::1"));
+ EXPECT_FALSE(srs_net_is_ipv4("localhost"));
+ EXPECT_FALSE(srs_net_is_ipv4("example.com"));
+
+ // Test empty string - the function might treat it as IPv4 format, so check actual behavior
+ bool empty_result = srs_net_is_ipv4("");
+ // Just verify the function doesn't crash, behavior may vary
+ EXPECT_TRUE(empty_result == true || empty_result == false);
+
+ // Test invalid IPv4 (but still numeric format)
+ EXPECT_TRUE(srs_net_is_ipv4("999.999.999.999")); // Invalid but IPv4 format
+}
+
+VOID TEST(StSocketTest, DualStackBehavior)
+{
+ srs_error_t err;
+
+ // Test that we can create both IPv4 and IPv6 sockets on the same port
+ // This tests the underlying dual-stack behavior
+
+ srs_netfd_t ipv4_fd = NULL;
+ srs_netfd_t ipv6_fd = NULL;
+
+ // Create IPv4 socket first
+ HELPER_EXPECT_SUCCESS(srs_tcp_listen("127.0.0.1", 0, &ipv4_fd));
+ EXPECT_TRUE(ipv4_fd != NULL);
+ srs_close_stfd(ipv4_fd);
+
+ // Try to create IPv6 socket (may fail if IPv6 not supported)
+ HELPER_EXPECT_SUCCESS(srs_tcp_listen("::1", 0, &ipv6_fd));
+ EXPECT_TRUE(ipv6_fd != NULL);
+ srs_close_stfd(ipv6_fd);
+}
+
+VOID TEST(StSocketTest, SocketFileDescriptorValidation)
+{
+ srs_error_t err;
+
+ // Test that created sockets have valid file descriptors
+ srs_netfd_t tcp_fd = NULL;
+ HELPER_EXPECT_SUCCESS(srs_tcp_listen("127.0.0.1", 0, &tcp_fd));
+ EXPECT_TRUE(tcp_fd != NULL);
+
+ int raw_fd = srs_netfd_fileno(tcp_fd);
+ EXPECT_GT(raw_fd, 0); // Valid file descriptor should be > 0
+
+ srs_close_stfd(tcp_fd);
+
+ // Test UDP socket
+ srs_netfd_t udp_fd = NULL;
+ HELPER_EXPECT_SUCCESS(srs_udp_listen("127.0.0.1", 0, &udp_fd));
+ EXPECT_TRUE(udp_fd != NULL);
+
+ raw_fd = srs_netfd_fileno(udp_fd);
+ EXPECT_GT(raw_fd, 0); // Valid file descriptor should be > 0
+
+ srs_close_stfd(udp_fd);
+}
+
+VOID TEST(StSocketTest, MultipleSocketsOnDifferentPorts)
+{
+ srs_error_t err;
+
+ // Test creating multiple sockets on different ports
+ vector sockets;
+
+ for (int i = 0; i < 5; i++) {
+ srs_netfd_t fd = NULL;
+ HELPER_EXPECT_SUCCESS(srs_tcp_listen("127.0.0.1", 0, &fd)); // Port 0 = system assigns
+ EXPECT_TRUE(fd != NULL);
+
+ int raw_fd = srs_netfd_fileno(fd);
+ EXPECT_GT(raw_fd, 0);
+
+ sockets.push_back(fd);
+ }
+
+ // Clean up all sockets
+ for (size_t i = 0; i < sockets.size(); i++) {
+ srs_close_stfd(sockets[i]);
+ }
+}
+
+VOID TEST(StSocketTest, RandomPortTcpListenAndConnect)
+{
+ srs_error_t err;
+
+ // Generate random port in range [30000, 60000]
+ int random_port = 30000 + (srs_rand_integer() % (60000 - 30000 + 1));
+ EXPECT_GE(random_port, 30000);
+ EXPECT_LE(random_port, 60000);
+
+ // Create TCP listener on the random port
+ srs_netfd_t listen_fd = NULL;
+ HELPER_EXPECT_SUCCESS(srs_tcp_listen("127.0.0.1", random_port, &listen_fd));
+ EXPECT_TRUE(listen_fd != NULL);
+
+ // Use SrsUniquePtr to automatically close the socket when it goes out of scope.
+ SrsUniquePtr listen_fd_ptr(&listen_fd, srs_close_stfd_ptr);
+
+ int actual_fd = srs_netfd_fileno(listen_fd);
+ EXPECT_GT(actual_fd, 0);
+
+ // Try to connect to the listening port
+ srs_netfd_t connect_fd = NULL;
+ HELPER_EXPECT_SUCCESS(srs_tcp_connect("127.0.0.1", random_port, SRS_UTIME_SECONDS, &connect_fd));
+ SrsUniquePtr connect_fd_ptr(&connect_fd, srs_close_stfd_ptr);
+
+ int connect_actual_fd = srs_netfd_fileno(connect_fd);
+ EXPECT_GT(connect_actual_fd, 0);
+
+ // Verify they are different file descriptors
+ EXPECT_NE(actual_fd, connect_actual_fd);
+}
+
+VOID TEST(StSocketTest, RandomPortTcpListenAndConnectIPv6)
+{
+ srs_error_t err;
+
+ // Generate random port in range [30000, 60000]
+ int random_port = 30000 + (srs_rand_integer() % (60000 - 30000 + 1));
+ EXPECT_GE(random_port, 30000);
+ EXPECT_LE(random_port, 60000);
+
+ // Create TCP listener on IPv6 loopback with the random port
+ srs_netfd_t listen_fd = NULL;
+ HELPER_EXPECT_SUCCESS(srs_tcp_listen("::1", random_port, &listen_fd));
+ EXPECT_TRUE(listen_fd != NULL);
+ SrsUniquePtr listen_fd_ptr(&listen_fd, srs_close_stfd_ptr);
+
+ int actual_fd = srs_netfd_fileno(listen_fd);
+ EXPECT_GT(actual_fd, 0);
+
+ // Try to connect to the listening port
+ srs_netfd_t connect_fd = NULL;
+ HELPER_EXPECT_SUCCESS(srs_tcp_connect("::1", random_port, SRS_UTIME_SECONDS, &connect_fd));
+ EXPECT_TRUE(connect_fd != NULL);
+ SrsUniquePtr connect_fd_ptr(&connect_fd, srs_close_stfd_ptr);
+
+ int connect_actual_fd = srs_netfd_fileno(connect_fd);
+ EXPECT_GT(connect_actual_fd, 0);
+
+ // Verify they are different file descriptors
+ EXPECT_NE(actual_fd, connect_actual_fd);
+}
+
+VOID TEST(StSocketTest, RandomPortUdpListenIPv4)
+{
+ srs_error_t err;
+
+ // Generate random port in range [30000, 60000]
+ int random_port = 30000 + (srs_rand_integer() % (60000 - 30000 + 1));
+ EXPECT_GE(random_port, 30000);
+ EXPECT_LE(random_port, 60000);
+
+ // Create UDP listener on IPv4 loopback with the random port
+ srs_netfd_t listen_fd = NULL;
+ HELPER_EXPECT_SUCCESS(srs_udp_listen("127.0.0.1", random_port, &listen_fd));
+ EXPECT_TRUE(listen_fd != NULL);
+ SrsUniquePtr listen_fd_ptr(&listen_fd, srs_close_stfd_ptr);
+
+ int actual_fd = srs_netfd_fileno(listen_fd);
+ EXPECT_GT(actual_fd, 0);
+
+ // For UDP, we can create another socket and bind to a different port
+ // to simulate client behavior (UDP is connectionless)
+ srs_netfd_t client_fd = NULL;
+ HELPER_EXPECT_SUCCESS(srs_udp_listen("127.0.0.1", 0, &client_fd)); // Port 0 = system assigns
+ EXPECT_TRUE(client_fd != NULL);
+ SrsUniquePtr client_fd_ptr(&client_fd, srs_close_stfd_ptr);
+
+ int client_actual_fd = srs_netfd_fileno(client_fd);
+ EXPECT_GT(client_actual_fd, 0);
+
+ // Verify they are different file descriptors
+ EXPECT_NE(actual_fd, client_actual_fd);
+}
+
+VOID TEST(StSocketTest, RandomPortUdpListenIPv6)
+{
+ srs_error_t err;
+
+ // Generate random port in range [30000, 60000]
+ int random_port = 30000 + (srs_rand_integer() % (60000 - 30000 + 1));
+ EXPECT_GE(random_port, 30000);
+ EXPECT_LE(random_port, 60000);
+
+ // Create UDP listener on IPv6 loopback with the random port
+ srs_netfd_t listen_fd = NULL;
+ HELPER_EXPECT_SUCCESS(srs_udp_listen("::1", random_port, &listen_fd));
+ EXPECT_TRUE(listen_fd != NULL);
+ SrsUniquePtr listen_fd_ptr(&listen_fd, srs_close_stfd_ptr);
+
+ int actual_fd = srs_netfd_fileno(listen_fd);
+ EXPECT_GT(actual_fd, 0);
+
+ // For UDP, we can create another socket and bind to a different port
+ // to simulate client behavior (UDP is connectionless)
+ srs_netfd_t client_fd = NULL;
+ HELPER_EXPECT_SUCCESS(srs_udp_listen("::1", 0, &client_fd));
+ EXPECT_TRUE(client_fd != NULL);
+ SrsUniquePtr client_fd_ptr(&client_fd, srs_close_stfd_ptr);
+
+ int client_actual_fd = srs_netfd_fileno(client_fd);
+ EXPECT_GT(client_actual_fd, 0);
+
+ // Verify they are different file descriptors
+ EXPECT_NE(actual_fd, client_actual_fd);
+}