srs/trunk/src/utest/srs_utest_st.cpp
Winlin 0d6d36d1fb
HTTP: Rename HTTP hijack to dynamic match for better clarity. v7.0.71 (#4462)
This PR refactors the HTTP routing system by renaming "hijack"
terminology to "dynamic match" for improved code clarity and better
semantic meaning.

Interface and Class Renaming
* ISrsHttpMatchHijacker → ISrsHttpDynamicMatcher
* hijack() method → dynamic_match() method
* hijackers member variables → dynamic_matchers_

Method Renaming
* SrsHttpServeMux::hijack() → SrsHttpServeMux::add_dynamic_matcher()
* SrsHttpServeMux::unhijack() →
SrsHttpServeMux::remove_dynamic_matcher()

The new "dynamic match" terminology better reflects that this is a
legitimate routing mechanism, not a security bypass or interception.
2025-09-01 08:33:31 -04:00

609 lines
18 KiB
C++

//
// Copyright (c) 2013-2025 The SRS Authors
//
// 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>
#include <unistd.h>
using namespace std;
VOID TEST(StTest, StUtimeInMicroseconds)
{
st_utime_t st_time_1 = st_utime();
// sleep 1 microsecond
#if !defined(SRS_CYGWIN64)
usleep(1);
#endif
st_utime_t st_time_2 = st_utime();
EXPECT_GT(st_time_1, 0);
EXPECT_GT(st_time_2, 0);
EXPECT_GE(st_time_2, st_time_1);
// st_time_2 - st_time_1 should be in range of [1, 300] microseconds
EXPECT_GE(st_time_2 - st_time_1, 0);
EXPECT_LE(st_time_2 - st_time_1, 300);
}
static inline st_utime_t time_gettimeofday()
{
struct timeval tv;
gettimeofday(&tv, NULL);
return (tv.tv_sec * 1000000LL + tv.tv_usec);
}
VOID TEST(StTest, StUtimePerformance)
{
clock_t start;
int gettimeofday_elapsed_time = 0;
int st_utime_elapsed_time = 0;
// Both the st_utime(clock_gettime or gettimeofday) and gettimeofday's
// elpased time to execute is dependence on whether it is the first time be called.
// In general, the gettimeofday has better performance, but the gap between
// them is really small, maybe less than 30 clock ~ 30 microseconds.
// check st_utime first, then gettimeofday
{
start = clock();
st_utime_t t2 = st_utime();
int elapsed_time = clock() - start;
st_utime_elapsed_time += elapsed_time;
EXPECT_GT(t2, 0);
start = clock();
st_utime_t t1 = time_gettimeofday();
elapsed_time = clock() - start;
gettimeofday_elapsed_time += elapsed_time;
EXPECT_GT(t1, 0);
EXPECT_GE(gettimeofday_elapsed_time, 0);
EXPECT_GE(st_utime_elapsed_time, 0);
// pass the test, if
EXPECT_LT(gettimeofday_elapsed_time > st_utime_elapsed_time ? gettimeofday_elapsed_time - st_utime_elapsed_time : st_utime_elapsed_time - gettimeofday_elapsed_time, 30);
}
// check gettimeofday first, then st_utime
{
start = clock();
st_utime_t t1 = time_gettimeofday();
int elapsed_time = clock() - start;
gettimeofday_elapsed_time += elapsed_time;
EXPECT_GT(t1, 0);
start = clock();
st_utime_t t2 = st_utime();
elapsed_time = clock() - start;
st_utime_elapsed_time += elapsed_time;
EXPECT_GT(t2, 0);
EXPECT_GE(gettimeofday_elapsed_time, 0);
EXPECT_GE(st_utime_elapsed_time, 0);
EXPECT_LT(gettimeofday_elapsed_time > st_utime_elapsed_time ? gettimeofday_elapsed_time - st_utime_elapsed_time : st_utime_elapsed_time - gettimeofday_elapsed_time, 30);
}
// compare st_utime & gettimeofday in a loop
for (int i = 0; i < 100; i++) {
start = clock();
st_utime_t t2 = st_utime();
int elapsed_time = clock() - start;
st_utime_elapsed_time = elapsed_time;
EXPECT_GT(t2, 0);
usleep(1);
start = clock();
st_utime_t t1 = time_gettimeofday();
elapsed_time = clock() - start;
gettimeofday_elapsed_time = elapsed_time;
EXPECT_GT(t1, 0);
usleep(1);
EXPECT_GE(gettimeofday_elapsed_time, 0);
EXPECT_GE(st_utime_elapsed_time, 0);
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);
}