Support IPv6 for all protocols: RTMP, HTTP/HTTPS, WebRTC, SRT, RTSP. v7.0.67 (#4457)

This PR adds comprehensive IPv6 support to SRS for all major protocols,
enabling dual-stack (IPv4/IPv6) operation across the entire streaming
server.

Key Features:

* RTMP/RTMPS: IPv6 support for streaming ingestion and playback
* HTTP/HTTPS: IPv6 support for HTTP-FLV streaming and API endpoints
* WebRTC: IPv6 support for UDP/TCP media transport (WHIP/WHEP)
* SRT: IPv6 support for low-latency streaming
* RTSP: IPv6 support for standards-based streaming

For config, see `conf/console.ipv46.conf` for example.

Publish RTMP or RTMPS via IPv6:

```bash
ffmpeg -re -i ./doc/source.flv -c copy -f flv 'rtmp://[::1]:1935/live/livestream'
ffmpeg -re -i ./doc/source.flv -c copy -f flv 'rtmps://[::1]:1443/live/livestream'
```

Play RTMP or RTMPS stream via IPv6 by ffplay:

```bash
ffplay 'rtmp://[::1]:1935/live/livestream'
ffplay 'rtmps://[::1]:1443/live/livestream'
```

Play by IPv6 via HTTP streaming:
* HTTP-FLV:
[http://[::1]:8080/live/livestream.flv](http://[::1]:8080/players/srs_player.html)
* HTTPS-FLV:
[https://[::1]:8088/live/livestream.flv](https://[::1]:8088/players/srs_player.html)

To access HTTP API via IPv6:

* HTTP API: `curl 'http://[::1]:1985/api/v1/versions'`
* HTTPS API: `curl -k 'https://[::1]:1990/api/v1/versions'`

```json
{
  "code": 0,
  "data": {
    "major": 7,
    "minor": 0,
    "revision": 66,
    "version": "7.0.66"
  }
}
```

Using HTTP API, publish by IPv6 WHIP via
[HTTP](http://[::1]:8080/players/whip.html), and play by
[WHEP](http://[::1]:8080/players/whep.html)

* WHIP: `http://[::1]:1985/rtc/v1/whip/?app=live&stream=livestream`
* WHEP: `http://[::1]:1985/rtc/v1/whep/?app=live&stream=livestream`

Using HTTPS API, publish by IPv6 WHIP via
[WHIP](https://[::1]:8088/players/whip.html), and play by
[WHEP](https://[::1]:8088/players/whep.html)

* WHIP: `https://[::1]:1990/rtc/v1/whip/?app=live&stream=livestream`
* WHEP: `https://[::1]:1990/rtc/v1/whep/?app=live&stream=livestream`

Publish SRT stream by FFmpeg via IPv6:

```bash
ffmpeg -re -i ./doc/source.flv -c copy -pes_payload_size 0 -f mpegts \
  'srt://[::1]:10080?streamid=#!::r=live/livestream,m=publish'
```

Play SRT stream by ffplay via IPv6:

```bash
ffplay 'srt://[::1]:10080?streamid=#!::r=live/livestream,m=request'
```

Play RTSP stream by ffplay via IPv6:

```bash
ffplay -rtsp_transport tcp -i 'rtsp://[::1]:8554/live/livestream'
```

---------

Co-authored-by: OSSRS-AI <winlinam@gmail.com>
This commit is contained in:
Winlin 2025-08-30 08:51:04 -04:00 committed by winlin
parent 0de81e97c5
commit 35e2808f0c
20 changed files with 1273 additions and 236 deletions

View File

@ -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;
}
}

View File

@ -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;

View File

@ -7,6 +7,7 @@ The changelog for SRS.
<a name="v7-changes"></a>
## 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)

View File

@ -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<string> 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<string> 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<string> 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<string> 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<string> 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<string> 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<string> 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<string> 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<string> 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<string> SrsConfig::get_rtsp_server_listens()
{
SRS_OVERWRITE_BY_ENV_INT("srs.rtsp_server.listen"); // SRS_RTSP_SERVER_LISTEN
std::vector<string> 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<string> SrsConfig::get_rtc_server_listens()
{
SRS_OVERWRITE_BY_ENV_INT("srs.rtc_server.listen"); // SRS_RTC_SERVER_LISTEN
std::vector<string> 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<string> SrsConfig::get_rtc_server_tcp_listens()
{
SRS_OVERWRITE_BY_ENV_INT("srs.rtc_server.tcp.listen"); // SRS_RTC_SERVER_TCP_LISTEN
std::vector<string> 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<string> SrsConfig::get_http_api_listens()
{
SRS_OVERWRITE_BY_ENV_STRING("srs.http_api.listen"); // SRS_HTTP_API_LISTEN
std::vector<string> 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<string> SrsConfig::get_https_api_listens()
{
SRS_OVERWRITE_BY_ENV_STRING("srs.http_api.https.listen"); // SRS_HTTP_API_HTTPS_LISTEN
std::vector<string> 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<string> apis = get_http_api_listens();
vector<string> 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<string> SrsConfig::get_srt_listens()
{
SRS_OVERWRITE_BY_ENV_INT("srs.srt_server.listen"); // SRS_SRT_SERVER_LISTEN
std::vector<string> 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<string> SrsConfig::get_http_stream_listens()
{
SRS_OVERWRITE_BY_ENV_STRING("srs.http_server.listen"); // SRS_HTTP_SERVER_LISTEN
std::vector<string> 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<string> SrsConfig::get_https_stream_listens()
{
SRS_OVERWRITE_BY_ENV_STRING("srs.http_server.https.listen"); // SRS_HTTP_SERVER_HTTPS_LISTEN
std::vector<string> 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()

View File

@ -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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> get_https_stream_listens();
virtual std::string get_https_stream_ssl_key();
virtual std::string get_https_stream_ssl_cert();
// rtmps section

View File

@ -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;
}

View File

@ -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<string> 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<string> 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<string> 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<string> 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<string> 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<string> 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()));
}
}
}
}

View File

@ -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<string> 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<string> candidates = discover_candidates(ruc);
@ -612,7 +643,8 @@ srs_error_t SrsRtcServer::do_create_session(SrsRtcUserConfig *ruc, SrsSdp &local
}
vector<string> v = vector<string>(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.

View File

@ -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<string> http_listens = _srs_config->get_http_stream_listens();
vector<string> 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<string> 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<string> api_listens = _srs_config->get_http_api_listens();
vector<string> 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<string> 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<string> 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");
}

View File

@ -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_;

View File

@ -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<string> 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;

View File

@ -9,6 +9,6 @@
#define VERSION_MAJOR 7
#define VERSION_MINOR 0
#define VERSION_REVISION 66
#define VERSION_REVISION 67
#endif

View File

@ -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++) {

View File

@ -105,6 +105,23 @@ std::string srs_strings_join(std::vector<T> &vs, std::string separator)
return ss.str();
}
// Compare two vector with string.
template <typename T>
bool srs_strings_equal(std::vector<T> &a, std::vector<T> &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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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) {

View File

@ -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());
}
}

View File

@ -3,6 +3,10 @@
//
// SPDX-License-Identifier: MIT
//
#include <srs_core_autofree.hpp>
#include <srs_kernel_error.hpp>
#include <srs_kernel_utility.hpp>
#include <srs_protocol_st.hpp>
#include <srs_utest_st.hpp>
#include <sys/time.h>
#include <time.h>
@ -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<srs_netfd_t> 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<srs_netfd_t> 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<srs_netfd_t> 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<srs_netfd_t> 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<srs_netfd_t> 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<srs_netfd_t> 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<srs_netfd_t> 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<srs_netfd_t> 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<srs_netfd_t> 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);
}