AI: Add workflow test for HTTP conn

This commit is contained in:
OSSRS-AI 2025-10-19 14:30:25 -04:00 committed by winlin
parent 35d0e3d7c7
commit ce7ac11eae
17 changed files with 771 additions and 439 deletions

2
trunk/configure vendored
View File

@ -382,7 +382,7 @@ if [[ $SRS_UTEST == YES ]]; then
"srs_utest_source_lock" "srs_utest_stream_token" "srs_utest_rtc_recv_track" "srs_utest_st2"
"srs_utest_hevc_structs" "srs_utest_coworkers" "srs_utest_pithy_print" "srs_utest_protocol3"
"srs_utest_app" "srs_utest_mock" "srs_utest_rtc_playstream" "srs_utest_rtc_publishstream"
"srs_utest_rtc_conn" "srs_utest_rtmp_conn" "srs_utest_srt_conn")
"srs_utest_rtc_conn" "srs_utest_rtmp_conn" "srs_utest_srt_conn" "srs_utest_http_conn")
MODULE_FILES+=("srs_utest_ai01" "srs_utest_ai02" "srs_utest_ai03" "srs_utest_ai04" "srs_utest_ai05"
"srs_utest_ai06" "srs_utest_ai07" "srs_utest_ai08" "srs_utest_ai09" "srs_utest_ai10" "srs_utest_ai11"
"srs_utest_ai12" "srs_utest_ai13" "srs_utest_ai14" "srs_utest_ai15" "srs_utest_ai16" "srs_utest_ai17"

View File

@ -34,6 +34,7 @@
#include <srs_protocol_http_client.hpp>
#include <srs_protocol_st.hpp>
#include <srs_protocol_utility.hpp>
#include <srs_protocol_http_conn.hpp>
ISrsAppFactory::ISrsAppFactory()
{
@ -206,6 +207,11 @@ ISrsRtcPlayStream *SrsAppFactory::create_rtc_play_stream(ISrsExecRtcAsyncTask *e
return new SrsRtcPlayStream(exec, expire, sender, cid);
}
ISrsHttpResponseWriter *SrsAppFactory::create_http_response_writer(ISrsProtocolReadWriter *io)
{
return new SrsHttpResponseWriter(io);
}
ISrsCoroutine *SrsAppFactory::create_coroutine(const std::string &name, ISrsCoroutineHandler *handler, SrsContextId cid)
{
return kernel_factory_->create_coroutine(name, handler, cid);

View File

@ -48,6 +48,8 @@ class ISrsRtcPacketReceiver;
class ISrsExpire;
class ISrsRtcPlayStream;
class ISrsRtcPacketSender;
class ISrsHttpResponseWriter;
class ISrsProtocolReadWriter;
// The factory to create app objects.
class ISrsAppFactory : public ISrsKernelFactory
@ -90,6 +92,7 @@ public:
virtual ISrsProtocolUtility *create_protocol_utility() = 0;
virtual ISrsRtcPublishStream *create_rtc_publish_stream(ISrsExecRtcAsyncTask *exec, ISrsExpire *expire, ISrsRtcPacketReceiver *receiver, const SrsContextId &cid) = 0;
virtual ISrsRtcPlayStream *create_rtc_play_stream(ISrsExecRtcAsyncTask *exec, ISrsExpire *expire, ISrsRtcPacketSender *sender, const SrsContextId &cid) = 0;
virtual ISrsHttpResponseWriter *create_http_response_writer(ISrsProtocolReadWriter *io) = 0;
};
// The factory to create app objects.
@ -137,6 +140,7 @@ public:
virtual ISrsProtocolUtility *create_protocol_utility();
virtual ISrsRtcPublishStream *create_rtc_publish_stream(ISrsExecRtcAsyncTask *exec, ISrsExpire *expire, ISrsRtcPacketReceiver *receiver, const SrsContextId &cid);
virtual ISrsRtcPlayStream *create_rtc_play_stream(ISrsExecRtcAsyncTask *exec, ISrsExpire *expire, ISrsRtcPacketSender *sender, const SrsContextId &cid);
virtual ISrsHttpResponseWriter *create_http_response_writer(ISrsProtocolReadWriter *io);
public:
virtual ISrsCoroutine *create_coroutine(const std::string &name, ISrsCoroutineHandler *handler, SrsContextId cid);

View File

@ -40,6 +40,7 @@ using namespace std;
#include <srs_protocol_rtmp_stack.hpp>
#include <srs_protocol_stream.hpp>
#include <srs_protocol_utility.hpp>
#include <srs_app_factory.hpp>
ISrsHttpConnOwner::ISrsHttpConnOwner()
{
@ -75,6 +76,7 @@ SrsHttpConn::SrsHttpConn(ISrsHttpConnOwner *handler, ISrsProtocolReadWriter *fd,
trd_ = new SrsSTCoroutine("http", this, _srs_context->get_id());
config_ = _srs_config;
app_factory_ = _srs_app_factory;
}
SrsHttpConn::~SrsHttpConn()
@ -89,6 +91,7 @@ SrsHttpConn::~SrsHttpConn()
srs_freep(delta_);
config_ = NULL;
app_factory_ = NULL;
}
std::string SrsHttpConn::desc()
@ -199,8 +202,11 @@ srs_error_t SrsHttpConn::process_requests(ISrsRequest **preq)
srs_assert(req_raw);
SrsUniquePtr<ISrsHttpMessage> req(req_raw);
// It must be a owner setter interface.
ISrsHttpMessageOwnerSetter *hreq = dynamic_cast<ISrsHttpMessageOwnerSetter *>(req.get());
srs_assert(hreq);
// Attach owner connection to message.
SrsHttpMessage *hreq = (SrsHttpMessage *)req.get();
hreq->set_connection(this);
// copy request to last request object.
@ -208,18 +214,18 @@ srs_error_t SrsHttpConn::process_requests(ISrsRequest **preq)
*preq = hreq->to_request(hreq->host());
// may should discard the body.
SrsHttpResponseWriter writer(skt_);
if ((err = handler_->on_http_message(req.get(), &writer)) != srs_success) {
SrsUniquePtr<ISrsHttpResponseWriter> writer(app_factory_->create_http_response_writer(skt_));
if ((err = handler_->on_http_message(req.get(), writer.get())) != srs_success) {
return srs_error_wrap(err, "on http message");
}
// ok, handle http request.
if ((err = process_request(&writer, req.get(), req_id)) != srs_success) {
if ((err = process_request(writer.get(), req.get(), req_id)) != srs_success) {
return srs_error_wrap(err, "process request=%d", req_id);
}
// After the request is processed.
if ((err = handler_->on_message_done(req.get(), &writer)) != srs_success) {
if ((err = handler_->on_message_done(req.get(), writer.get())) != srs_success) {
return srs_error_wrap(err, "on message done");
}

View File

@ -49,6 +49,7 @@ class ISrsSslConnection;
class ISrsHttpConn;
class ISrsAppConfig;
class ISrsStatistic;
class ISrsAppFactory;
// The owner of HTTP connection.
class ISrsHttpConnOwner
@ -98,6 +99,7 @@ class SrsHttpConn : public ISrsHttpConn
// clang-format off
SRS_DECLARE_PRIVATE: // clang-format on
ISrsAppConfig *config_;
ISrsAppFactory *app_factory_;
// clang-format off
SRS_DECLARE_PROTECTED: // clang-format on

View File

@ -301,6 +301,14 @@ int SrsHttpParser::on_body(llhttp_t *parser, const char *at, size_t length)
return 0;
}
ISrsHttpMessageOwnerSetter::ISrsHttpMessageOwnerSetter()
{
}
ISrsHttpMessageOwnerSetter::~ISrsHttpMessageOwnerSetter()
{
}
SrsHttpMessage::SrsHttpMessage(ISrsReader *reader, SrsFastStream *buffer)
{
owner_conn_ = NULL;

View File

@ -100,13 +100,25 @@ SRS_DECLARE_PRIVATE: // clang-format on
static int on_body(llhttp_t *parser, const char *at, size_t length);
};
// The interface for setting the connection owner.
class ISrsHttpMessageOwnerSetter : public ISrsHttpMessage
{
public:
ISrsHttpMessageOwnerSetter();
virtual ~ISrsHttpMessageOwnerSetter();
public:
virtual void set_connection(ISrsConnection *conn) = 0;
virtual ISrsRequest *to_request(std::string vhost) = 0;
};
// A Request represents an HTTP request received by a server
// or to be sent by a client.
//
// The field semantics differ slightly between client and server
// usage. In addition to the notes on the fields below, see the
// documentation for Request.Write and RoundTripper.
class SrsHttpMessage : public ISrsHttpMessage
class SrsHttpMessage : public ISrsHttpMessageOwnerSetter
{
// clang-format off
SRS_DECLARE_PRIVATE: // clang-format on

View File

@ -230,29 +230,6 @@ VOID TEST(SrsServerTest, ListenRtmpSuccess)
EXPECT_TRUE(server.get() != NULL);
}
MockHttpServeMux::MockHttpServeMux()
{
handle_count_ = 0;
}
MockHttpServeMux::~MockHttpServeMux()
{
}
srs_error_t MockHttpServeMux::handle(std::string pattern, ISrsHttpHandler *handler)
{
handle_count_++;
patterns_.push_back(pattern);
// Free the handler since we're not actually using it
srs_freep(handler);
return srs_success;
}
srs_error_t MockHttpServeMux::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessage *r)
{
return srs_success;
}
// Test SrsServer::http_handle() method to verify proper HTTP API handler registration.
// This test covers the major use scenario of registering all HTTP API endpoints including
// root API, versioning, summaries, rusages, stats, meminfos, authors, features, vhosts,

View File

@ -69,22 +69,6 @@ public:
virtual std::string get_exporter_listen();
};
// Mock ISrsCommonHttpHandler for testing SrsServer::http_handle()
class MockHttpServeMux : public ISrsCommonHttpHandler
{
public:
int handle_count_;
std::vector<std::string> patterns_;
public:
MockHttpServeMux();
virtual ~MockHttpServeMux();
public:
virtual srs_error_t handle(std::string pattern, ISrsHttpHandler *handler);
virtual srs_error_t serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessage *r);
};
// Mock ISrsLog for testing SrsServer::on_signal()
class MockLogForSignal : public ISrsLog
{

View File

@ -776,93 +776,6 @@ VOID TEST(BufferWriterTest, WriteToHttpResponse)
buffer_writer->close();
}
// Old mock implementations for backward compatibility
MockHttpxConn::MockHttpxConn()
{
enable_stat_ = false;
}
MockHttpxConn::~MockHttpxConn()
{
}
void MockHttpxConn::set_enable_stat(bool v)
{
enable_stat_ = v;
}
srs_error_t MockHttpxConn::on_start()
{
return srs_success;
}
srs_error_t MockHttpxConn::on_http_message(ISrsHttpMessage *r, ISrsHttpResponseWriter *w)
{
return srs_success;
}
srs_error_t MockHttpxConn::on_message_done(ISrsHttpMessage *r, ISrsHttpResponseWriter *w)
{
return srs_success;
}
srs_error_t MockHttpxConn::on_conn_done(srs_error_t r0)
{
return r0;
}
MockHttpConn::MockHttpConn()
{
handler_ = new MockHttpxConn();
remote_ip_ = "127.0.0.1";
}
MockHttpConn::~MockHttpConn()
{
srs_freep(handler_);
}
std::string MockHttpConn::remote_ip()
{
return remote_ip_;
}
const SrsContextId &MockHttpConn::get_id()
{
static SrsContextId id;
return id;
}
std::string MockHttpConn::desc()
{
return "MockHttpConn";
}
void MockHttpConn::expire()
{
}
ISrsHttpConnOwner *MockHttpConn::handler()
{
return handler_;
}
MockHttpMessage::MockHttpMessage() : SrsHttpMessage()
{
mock_conn_ = new MockHttpConn();
set_connection(mock_conn_);
}
MockHttpMessage::~MockHttpMessage()
{
srs_freep(mock_conn_);
}
std::string MockHttpMessage::path()
{
return "/live/stream.flv";
}
// New mock implementations for SrsLiveStream testing
MockHttpxConnForLiveStream::MockHttpxConnForLiveStream() : SrsHttpxConn(NULL, NULL, NULL, "127.0.0.1", 1935, "", "")
{

View File

@ -52,57 +52,6 @@ public:
virtual srs_error_t update_auth(ISrsRequest *r);
};
// Mock SrsHttpxConn for testing SrsLiveStream (old version for backward compatibility)
class MockHttpxConn : public ISrsHttpConnOwner
{
public:
bool enable_stat_;
public:
MockHttpxConn();
virtual ~MockHttpxConn();
public:
virtual void set_enable_stat(bool v);
virtual srs_error_t on_start();
virtual srs_error_t on_http_message(ISrsHttpMessage *r, ISrsHttpResponseWriter *w);
virtual srs_error_t on_message_done(ISrsHttpMessage *r, ISrsHttpResponseWriter *w);
virtual srs_error_t on_conn_done(srs_error_t r0);
};
// Mock SrsHttpConn for testing SrsLiveStream (old version for backward compatibility)
class MockHttpConn : public ISrsConnection, public ISrsExpire
{
public:
MockHttpxConn *handler_;
std::string remote_ip_;
public:
MockHttpConn();
virtual ~MockHttpConn();
public:
virtual std::string remote_ip();
virtual const SrsContextId &get_id();
virtual std::string desc();
virtual void expire();
virtual ISrsHttpConnOwner *handler();
};
// Mock SrsHttpMessage for testing SrsLiveStream (old version for backward compatibility)
class MockHttpMessage : public SrsHttpMessage
{
public:
MockHttpConn *mock_conn_;
public:
MockHttpMessage();
virtual ~MockHttpMessage();
public:
virtual std::string path();
};
// Mock SrsHttpxConn for testing SrsLiveStream - inherits from real SrsHttpxConn
class MockHttpxConnForLiveStream : public SrsHttpxConn
{

View File

@ -1434,185 +1434,6 @@ VOID TEST(AppCasterFlvTest, ResourceManagerDelegation)
srs_freep(real_manager);
}
// Mock ISrsHttpConnOwner implementation for testing SrsHttpConn::cycle()
MockHttpConnOwnerForCycle::MockHttpConnOwnerForCycle()
{
on_start_called_ = false;
on_http_message_called_ = false;
on_message_done_called_ = false;
on_conn_done_called_ = false;
on_start_error_ = srs_success;
on_http_message_error_ = srs_success;
on_message_done_error_ = srs_success;
on_conn_done_error_ = srs_success;
}
MockHttpConnOwnerForCycle::~MockHttpConnOwnerForCycle()
{
srs_freep(on_start_error_);
srs_freep(on_http_message_error_);
srs_freep(on_message_done_error_);
srs_freep(on_conn_done_error_);
}
srs_error_t MockHttpConnOwnerForCycle::on_start()
{
on_start_called_ = true;
if (on_start_error_ != srs_success) {
return srs_error_copy(on_start_error_);
}
return srs_success;
}
srs_error_t MockHttpConnOwnerForCycle::on_http_message(ISrsHttpMessage *r, ISrsHttpResponseWriter *w)
{
on_http_message_called_ = true;
if (on_http_message_error_ != srs_success) {
return srs_error_copy(on_http_message_error_);
}
return srs_success;
}
srs_error_t MockHttpConnOwnerForCycle::on_message_done(ISrsHttpMessage *r, ISrsHttpResponseWriter *w)
{
on_message_done_called_ = true;
if (on_message_done_error_ != srs_success) {
return srs_error_copy(on_message_done_error_);
}
return srs_success;
}
srs_error_t MockHttpConnOwnerForCycle::on_conn_done(srs_error_t r0)
{
on_conn_done_called_ = true;
if (on_conn_done_error_ != srs_success) {
srs_freep(r0);
return srs_error_copy(on_conn_done_error_);
}
return r0;
}
void MockHttpConnOwnerForCycle::reset()
{
on_start_called_ = false;
on_http_message_called_ = false;
on_message_done_called_ = false;
on_conn_done_called_ = false;
srs_freep(on_start_error_);
srs_freep(on_http_message_error_);
srs_freep(on_message_done_error_);
srs_freep(on_conn_done_error_);
}
// Mock ISrsHttpParser implementation for testing SrsHttpConn::cycle()
MockHttpParserForCycle::MockHttpParserForCycle()
{
initialize_called_ = false;
parse_message_called_ = false;
initialize_error_ = srs_success;
parse_message_error_ = srs_success;
mock_message_ = NULL;
}
MockHttpParserForCycle::~MockHttpParserForCycle()
{
srs_freep(initialize_error_);
srs_freep(parse_message_error_);
}
srs_error_t MockHttpParserForCycle::initialize(enum llhttp_type type)
{
initialize_called_ = true;
if (initialize_error_ != srs_success) {
return srs_error_copy(initialize_error_);
}
return srs_success;
}
void MockHttpParserForCycle::set_jsonp(bool allow_jsonp)
{
}
srs_error_t MockHttpParserForCycle::parse_message(ISrsReader *reader, ISrsHttpMessage **ppmsg)
{
parse_message_called_ = true;
if (parse_message_error_ != srs_success) {
return srs_error_copy(parse_message_error_);
}
*ppmsg = mock_message_;
return srs_success;
}
void MockHttpParserForCycle::reset()
{
initialize_called_ = false;
parse_message_called_ = false;
srs_freep(initialize_error_);
srs_freep(parse_message_error_);
mock_message_ = NULL;
}
// Mock ISrsProtocolReadWriter implementation for testing SrsHttpConn::cycle()
MockProtocolReadWriterForCycle::MockProtocolReadWriterForCycle()
{
recv_timeout_ = SRS_UTIME_NO_TIMEOUT;
send_timeout_ = SRS_UTIME_NO_TIMEOUT;
}
MockProtocolReadWriterForCycle::~MockProtocolReadWriterForCycle()
{
}
srs_error_t MockProtocolReadWriterForCycle::read_fully(void *buf, size_t size, ssize_t *nread)
{
return srs_success;
}
srs_error_t MockProtocolReadWriterForCycle::read(void *buf, size_t size, ssize_t *nread)
{
return srs_success;
}
void MockProtocolReadWriterForCycle::set_recv_timeout(srs_utime_t tm)
{
recv_timeout_ = tm;
}
srs_utime_t MockProtocolReadWriterForCycle::get_recv_timeout()
{
return recv_timeout_;
}
int64_t MockProtocolReadWriterForCycle::get_recv_bytes()
{
return 0;
}
srs_error_t MockProtocolReadWriterForCycle::write(void *buf, size_t size, ssize_t *nwrite)
{
return srs_success;
}
void MockProtocolReadWriterForCycle::set_send_timeout(srs_utime_t tm)
{
send_timeout_ = tm;
}
srs_utime_t MockProtocolReadWriterForCycle::get_send_timeout()
{
return send_timeout_;
}
int64_t MockProtocolReadWriterForCycle::get_send_bytes()
{
return 0;
}
srs_error_t MockProtocolReadWriterForCycle::writev(const iovec *iov, int iov_size, ssize_t *nwrite)
{
return srs_success;
}
// Mock ISrsCoroutine implementation for testing SrsHttpConn::cycle()
MockCoroutineForCycle::MockCoroutineForCycle()
{
@ -1699,15 +1520,15 @@ VOID TEST(HttpConnTest, CycleSuccessWithSingleRequest)
srs_error_t err;
// Create mock dependencies
MockHttpConnOwnerForCycle *mock_handler = new MockHttpConnOwnerForCycle();
MockProtocolReadWriterForCycle *mock_skt = new MockProtocolReadWriterForCycle();
MockHttpxConn *mock_handler = new MockHttpxConn();
MockProtocolReadWriter *mock_skt = new MockProtocolReadWriter();
SrsHttpServeMux *mock_mux = new SrsHttpServeMux();
// Create SrsHttpConn
SrsUniquePtr<SrsHttpConn> conn(new SrsHttpConn(mock_handler, mock_skt, mock_mux, "127.0.0.1", 8080));
// Create mock parser and coroutine
MockHttpParserForCycle *mock_parser = new MockHttpParserForCycle();
MockHttpParser *mock_parser = new MockHttpParser();
MockCoroutineForCycle *mock_trd = new MockCoroutineForCycle();
// Create mock message - will be freed by SrsHttpConn::process_requests()
@ -1717,7 +1538,8 @@ VOID TEST(HttpConnTest, CycleSuccessWithSingleRequest)
mock_message->is_keep_alive_ = false;
// Configure mock parser to return our mock message
mock_parser->mock_message_ = mock_message;
mock_parser->messages_.push_back(mock_message);
mock_parser->cond_->signal();
// Inject mock dependencies into SrsHttpConn
conn->parser_ = mock_parser;

View File

@ -478,76 +478,6 @@ public:
virtual void expire();
};
// Mock ISrsHttpConnOwner for testing SrsHttpConn::cycle()
class MockHttpConnOwnerForCycle : public ISrsHttpConnOwner
{
public:
bool on_start_called_;
bool on_http_message_called_;
bool on_message_done_called_;
bool on_conn_done_called_;
srs_error_t on_start_error_;
srs_error_t on_http_message_error_;
srs_error_t on_message_done_error_;
srs_error_t on_conn_done_error_;
public:
MockHttpConnOwnerForCycle();
virtual ~MockHttpConnOwnerForCycle();
public:
virtual srs_error_t on_start();
virtual srs_error_t on_http_message(ISrsHttpMessage *r, ISrsHttpResponseWriter *w);
virtual srs_error_t on_message_done(ISrsHttpMessage *r, ISrsHttpResponseWriter *w);
virtual srs_error_t on_conn_done(srs_error_t r0);
void reset();
};
// Mock ISrsHttpParser for testing SrsHttpConn::cycle()
class MockHttpParserForCycle : public ISrsHttpParser
{
public:
bool initialize_called_;
bool parse_message_called_;
srs_error_t initialize_error_;
srs_error_t parse_message_error_;
ISrsHttpMessage *mock_message_;
public:
MockHttpParserForCycle();
virtual ~MockHttpParserForCycle();
public:
virtual srs_error_t initialize(enum llhttp_type type);
virtual void set_jsonp(bool allow_jsonp);
virtual srs_error_t parse_message(ISrsReader *reader, ISrsHttpMessage **ppmsg);
void reset();
};
// Mock ISrsProtocolReadWriter for testing SrsHttpConn::cycle()
class MockProtocolReadWriterForCycle : public ISrsProtocolReadWriter
{
public:
srs_utime_t recv_timeout_;
srs_utime_t send_timeout_;
public:
MockProtocolReadWriterForCycle();
virtual ~MockProtocolReadWriterForCycle();
public:
virtual srs_error_t read_fully(void *buf, size_t size, ssize_t *nread);
virtual srs_error_t read(void *buf, size_t size, ssize_t *nread);
virtual void set_recv_timeout(srs_utime_t tm);
virtual srs_utime_t get_recv_timeout();
virtual int64_t get_recv_bytes();
virtual srs_error_t write(void *buf, size_t size, ssize_t *nwrite);
virtual void set_send_timeout(srs_utime_t tm);
virtual srs_utime_t get_send_timeout();
virtual int64_t get_send_bytes();
virtual srs_error_t writev(const iovec *iov, int iov_size, ssize_t *nwrite);
};
// Mock ISrsCoroutine for testing SrsHttpConn::cycle()
class MockCoroutineForCycle : public ISrsCoroutine
{

View File

@ -0,0 +1,162 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2013-2025 Winlin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <srs_utest_http_conn.hpp>
#include <srs_app_http_conn.hpp>
#include <srs_protocol_conn.hpp>
#include <srs_protocol_http_conn.hpp>
#include <srs_protocol_http_stack.hpp>
#include <srs_protocol_st.hpp>
#include <srs_utest_ai13.hpp>
#include <srs_utest_ai15.hpp>
#include <srs_utest_ai19.hpp>
#include <srs_utest_http.hpp>
#include <srs_utest_mock.hpp>
#include <srs_utest_service.hpp>
// This test is used to verify the basic workflow of the HTTP connection.
// It's finished with the help of AI, but each step is manually designed
// and verified. So this is not dominated by AI, but by humanbeing.
VOID TEST(HttpConnTest, ManuallyVerifyBasicWorkflowForHttpRequest)
{
srs_error_t err;
// Create mock objects for dependencies
SrsUniquePtr<MockAppConfig> mock_config(new MockAppConfig());
SrsUniquePtr<MockAppFactory> mock_app_factory(new MockAppFactory());
SrsUniquePtr<MockHttpxConn> mock_handler(new MockHttpxConn());
MockProtocolReadWriter *mock_io = new MockProtocolReadWriter();
MockHttpParser *mock_parser = new MockHttpParser();
SrsUniquePtr<MockHttpServeMux> mock_http_mux(new MockHttpServeMux());
// Setup mock config
mock_config->default_vhost_ = new SrsConfDirective();
mock_config->default_vhost_->name_ = "vhost";
mock_config->default_vhost_->args_.push_back("__defaultVhost__");
// Create SrsHttpConn - it takes ownership of mock_io
SrsUniquePtr<SrsHttpConn> conn(new SrsHttpConn(mock_handler.get(), mock_io, mock_http_mux.get(), "192.168.1.100", 8080));
// Inject mock dependencies into private fields
conn->config_ = mock_config.get();
conn->app_factory_ = mock_app_factory.get();
srs_freep(conn->parser_);
conn->parser_ = mock_parser;
// Start the HTTP connection
HELPER_EXPECT_SUCCESS(conn->start());
// Wait for coroutine to start
srs_usleep(1 * SRS_UTIME_MILLISECONDS);
// Verify that http_mux->handle() was called
EXPECT_TRUE(mock_parser->initialize_called_);
EXPECT_TRUE(mock_handler->on_start_called_);
EXPECT_TRUE(mock_parser->parse_message_called_);
// Create a mock HTTP message for the parser to return
// Note: The parser will take ownership of this message
MockHttpMessage *mock_message = new MockHttpMessage();
mock_message->set_url("/api/v1/versions", false);
mock_parser->messages_.push_back(mock_message);
mock_parser->cond_->signal();
// Wait for message processing
srs_usleep(1 * SRS_UTIME_MILLISECONDS);
// Verify that http_mux->handle() and serve_http() were called
EXPECT_EQ(1, mock_http_mux->serve_http_count_);
}
// This test is used to verify the basic workflow of the HTTPx connection.
// It's finished with the help of AI, but each step is manually designed
// and verified. So this is not dominated by AI, but by humanbeing.
VOID TEST(HttpConnTest, ManuallyVerifyBasicWorkflowForHttpxRequest)
{
srs_error_t err;
// Create mock objects for dependencies
SrsUniquePtr<MockAppConfig> mock_config(new MockAppConfig());
SrsUniquePtr<MockAppFactory> mock_app_factory(new MockAppFactory());
MockProtocolReadWriter *mock_io = new MockProtocolReadWriter();
MockHttpParser *mock_parser = new MockHttpParser();
SrsUniquePtr<MockHttpServeMux> mock_http_mux(new MockHttpServeMux());
MockSslConnection *mock_ssl = new MockSslConnection();
// Setup mock config
mock_config->default_vhost_ = new SrsConfDirective();
mock_config->default_vhost_->name_ = "vhost";
mock_config->default_vhost_->args_.push_back("__defaultVhost__");
// Create mock resource manager
SrsUniquePtr<MockConnectionManager> mock_manager(new MockConnectionManager());
// Create SrsHttpxConn - it takes ownership of mock_io
// Set key and cert to empty to disable SSL
SrsUniquePtr<SrsHttpxConn> conn(new SrsHttpxConn(mock_manager.get(), mock_io, mock_http_mux.get(), "192.168.1.100", 8080, "", ""));
// Inject mock dependencies into private fields
conn->config_ = mock_config.get();
// Inject mock SSL connection
srs_freep(conn->ssl_);
conn->ssl_ = mock_ssl;
// Access the internal SrsHttpConn through conn_ field (cast from ISrsHttpConn* to SrsHttpConn*)
SrsHttpConn *http_conn = new SrsHttpConn(conn.get(), mock_ssl, mock_http_mux.get(), "192.168.1.100", 8080);
http_conn->config_ = mock_config.get();
http_conn->app_factory_ = mock_app_factory.get();
srs_freep(http_conn->parser_);
http_conn->parser_ = mock_parser;
// Inject the mock SrsHttpConn into conn_ field
srs_freep(conn->conn_);
conn->conn_ = http_conn;
// Start the HTTPx connection
HELPER_EXPECT_SUCCESS(conn->start());
// Wait for coroutine to start
srs_usleep(1 * SRS_UTIME_MILLISECONDS);
// Verify that parser->initialize() was called
EXPECT_TRUE(mock_parser->initialize_called_);
EXPECT_TRUE(mock_parser->parse_message_called_);
// Verify HTTPS handshake was called
EXPECT_TRUE(mock_ssl->handshake_called_);
// Create a mock HTTP message for the parser to return
// Note: The parser will take ownership of this message
MockHttpMessage *mock_message = new MockHttpMessage();
mock_message->set_url("/api/v1/versions", false);
mock_parser->messages_.push_back(mock_message);
mock_parser->cond_->signal();
// Wait for message processing
srs_usleep(1 * SRS_UTIME_MILLISECONDS);
// Verify that http_mux->serve_http() was called
EXPECT_EQ(1, mock_http_mux->serve_http_count_);
}

View File

@ -0,0 +1,32 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2013-2025 Winlin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef SRS_UTEST_HTTP_CONN_HPP
#define SRS_UTEST_HTTP_CONN_HPP
/*
#include <srs_utest_http_conn.hpp>
*/
#include <srs_utest.hpp>
#endif

View File

@ -1565,6 +1565,67 @@ void MockRtmpServer::reset()
auto_response_value_ = true;
}
// Mock ISrsProtocolReadWriter implementation for testing SrsHttpConn::cycle()
MockProtocolReadWriter::MockProtocolReadWriter()
{
recv_timeout_ = SRS_UTIME_NO_TIMEOUT;
send_timeout_ = SRS_UTIME_NO_TIMEOUT;
}
MockProtocolReadWriter::~MockProtocolReadWriter()
{
}
srs_error_t MockProtocolReadWriter::read_fully(void *buf, size_t size, ssize_t *nread)
{
return srs_success;
}
srs_error_t MockProtocolReadWriter::read(void *buf, size_t size, ssize_t *nread)
{
return srs_success;
}
void MockProtocolReadWriter::set_recv_timeout(srs_utime_t tm)
{
recv_timeout_ = tm;
}
srs_utime_t MockProtocolReadWriter::get_recv_timeout()
{
return recv_timeout_;
}
int64_t MockProtocolReadWriter::get_recv_bytes()
{
return 0;
}
srs_error_t MockProtocolReadWriter::write(void *buf, size_t size, ssize_t *nwrite)
{
return srs_success;
}
void MockProtocolReadWriter::set_send_timeout(srs_utime_t tm)
{
send_timeout_ = tm;
}
srs_utime_t MockProtocolReadWriter::get_send_timeout()
{
return send_timeout_;
}
int64_t MockProtocolReadWriter::get_send_bytes()
{
return 0;
}
srs_error_t MockProtocolReadWriter::writev(const iovec *iov, int iov_size, ssize_t *nwrite)
{
return srs_success;
}
MockRtmpTransport::MockRtmpTransport()
{
}
@ -1585,7 +1646,7 @@ int MockRtmpTransport::osfd()
ISrsProtocolReadWriter *MockRtmpTransport::io()
{
return this;
return &mock_io_;
}
srs_error_t MockRtmpTransport::handshake()
@ -1656,6 +1717,90 @@ srs_error_t MockRtmpTransport::writev(const iovec *iov, int iov_size, ssize_t *n
return srs_success;
}
// Mock ISrsSslConnection implementation for testing HTTPS connections
MockSslConnection::MockSslConnection()
{
handshake_called_ = false;
handshake_error_ = srs_success;
recv_timeout_ = SRS_UTIME_NO_TIMEOUT;
send_timeout_ = SRS_UTIME_NO_TIMEOUT;
recv_bytes_ = 0;
send_bytes_ = 0;
}
MockSslConnection::~MockSslConnection()
{
srs_freep(handshake_error_);
}
srs_error_t MockSslConnection::handshake(std::string key_file, std::string crt_file)
{
handshake_called_ = true;
return srs_error_copy(handshake_error_);
}
void MockSslConnection::set_recv_timeout(srs_utime_t tm)
{
recv_timeout_ = tm;
}
srs_utime_t MockSslConnection::get_recv_timeout()
{
return recv_timeout_;
}
srs_error_t MockSslConnection::read_fully(void *buf, size_t size, ssize_t *nread)
{
return srs_error_new(ERROR_NOT_SUPPORTED, "mock ssl read_fully");
}
int64_t MockSslConnection::get_recv_bytes()
{
return recv_bytes_;
}
int64_t MockSslConnection::get_send_bytes()
{
return send_bytes_;
}
srs_error_t MockSslConnection::read(void *buf, size_t size, ssize_t *nread)
{
return srs_error_new(ERROR_NOT_SUPPORTED, "mock ssl read");
}
void MockSslConnection::set_send_timeout(srs_utime_t tm)
{
send_timeout_ = tm;
}
srs_utime_t MockSslConnection::get_send_timeout()
{
return send_timeout_;
}
srs_error_t MockSslConnection::write(void *buf, size_t size, ssize_t *nwrite)
{
*nwrite = size;
send_bytes_ += size;
return srs_success;
}
srs_error_t MockSslConnection::writev(const iovec *iov, int iov_size, ssize_t *nwrite)
{
return srs_error_new(ERROR_NOT_SUPPORTED, "mock ssl writev");
}
void MockSslConnection::reset()
{
handshake_called_ = false;
srs_freep(handshake_error_);
recv_timeout_ = SRS_UTIME_NO_TIMEOUT;
send_timeout_ = SRS_UTIME_NO_TIMEOUT;
recv_bytes_ = 0;
send_bytes_ = 0;
}
// Mock ISrsProtocolReadWriter implementation for SrsSrtRecvThread
MockSrtConnection::MockSrtConnection()
{
@ -1769,3 +1914,221 @@ srs_error_t MockSrtConnection::get_stats(SrsSrtStat &stat)
{
return srs_success;
}
// Mock ISrsHttpParser implementation for testing SrsHttpConn::cycle()
MockHttpParser::MockHttpParser()
{
initialize_called_ = false;
parse_message_called_ = false;
initialize_error_ = srs_success;
parse_message_error_ = srs_success;
cond_ = new SrsCond();
}
MockHttpParser::~MockHttpParser()
{
reset();
srs_freep(cond_);
}
srs_error_t MockHttpParser::initialize(enum llhttp_type type)
{
initialize_called_ = true;
if (initialize_error_ != srs_success) {
return srs_error_copy(initialize_error_);
}
return srs_success;
}
void MockHttpParser::set_jsonp(bool allow_jsonp)
{
}
srs_error_t MockHttpParser::parse_message(ISrsReader *reader, ISrsHttpMessage **ppmsg)
{
parse_message_called_ = true;
if (messages_.empty()) {
cond_->wait();
}
if (parse_message_error_ != srs_success) {
return srs_error_copy(parse_message_error_);
}
if (messages_.empty()) {
return srs_error_new(ERROR_SOCKET_READ, "mock http parser no message");
}
ISrsHttpMessage *msg = messages_.front();
messages_.erase(messages_.begin());
*ppmsg = msg;
return srs_success;
}
void MockHttpParser::reset()
{
initialize_called_ = false;
parse_message_called_ = false;
srs_freep(initialize_error_);
srs_freep(parse_message_error_);
for (vector<ISrsHttpMessage *>::iterator it = messages_.begin(); it != messages_.end(); ++it) {
ISrsHttpMessage *msg = *it;
srs_freep(msg);
}
messages_.clear();
}
// Old mock implementations for backward compatibility
MockHttpxConn::MockHttpxConn()
{
enable_stat_ = false;
on_start_called_ = false;
on_http_message_called_ = false;
on_message_done_called_ = false;
on_conn_done_called_ = false;
on_start_error_ = srs_success;
on_http_message_error_ = srs_success;
on_message_done_error_ = srs_success;
on_conn_done_error_ = srs_success;
}
MockHttpxConn::~MockHttpxConn()
{
srs_freep(on_start_error_);
srs_freep(on_http_message_error_);
srs_freep(on_message_done_error_);
srs_freep(on_conn_done_error_);
}
void MockHttpxConn::set_enable_stat(bool v)
{
enable_stat_ = v;
}
srs_error_t MockHttpxConn::on_start()
{
on_start_called_ = true;
if (on_start_error_ != srs_success) {
return srs_error_copy(on_start_error_);
}
return srs_success;
}
srs_error_t MockHttpxConn::on_http_message(ISrsHttpMessage *r, ISrsHttpResponseWriter *w)
{
on_http_message_called_ = true;
if (on_http_message_error_ != srs_success) {
return srs_error_copy(on_http_message_error_);
}
return srs_success;
}
srs_error_t MockHttpxConn::on_message_done(ISrsHttpMessage *r, ISrsHttpResponseWriter *w)
{
on_message_done_called_ = true;
if (on_message_done_error_ != srs_success) {
return srs_error_copy(on_message_done_error_);
}
return srs_success;
}
srs_error_t MockHttpxConn::on_conn_done(srs_error_t r0)
{
on_conn_done_called_ = true;
if (on_conn_done_error_ != srs_success) {
srs_freep(r0);
return srs_error_copy(on_conn_done_error_);
}
return r0;
}
void MockHttpxConn::reset()
{
on_start_called_ = false;
on_http_message_called_ = false;
on_message_done_called_ = false;
on_conn_done_called_ = false;
srs_freep(on_start_error_);
srs_freep(on_http_message_error_);
srs_freep(on_message_done_error_);
srs_freep(on_conn_done_error_);
}
MockHttpConn::MockHttpConn()
{
handler_ = new MockHttpxConn();
remote_ip_ = "127.0.0.1";
}
MockHttpConn::~MockHttpConn()
{
srs_freep(handler_);
}
std::string MockHttpConn::remote_ip()
{
return remote_ip_;
}
const SrsContextId &MockHttpConn::get_id()
{
static SrsContextId id;
return id;
}
std::string MockHttpConn::desc()
{
return "MockHttpConn";
}
void MockHttpConn::expire()
{
}
ISrsHttpConnOwner *MockHttpConn::handler()
{
return handler_;
}
MockHttpMessage::MockHttpMessage() : SrsHttpMessage()
{
mock_conn_ = new MockHttpConn();
set_connection(mock_conn_);
}
MockHttpMessage::~MockHttpMessage()
{
srs_freep(mock_conn_);
}
std::string MockHttpMessage::path()
{
return "/live/stream.flv";
}
MockHttpServeMux::MockHttpServeMux()
{
handle_count_ = 0;
serve_http_count_ = 0;
}
MockHttpServeMux::~MockHttpServeMux()
{
}
srs_error_t MockHttpServeMux::handle(std::string pattern, ISrsHttpHandler *handler)
{
handle_count_++;
patterns_.push_back(pattern);
// Free the handler since we're not actually using it
srs_freep(handler);
return srs_success;
}
srs_error_t MockHttpServeMux::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessage *r)
{
serve_http_count_++;
return srs_success;
}

View File

@ -42,6 +42,8 @@
#include <srs_app_srt_source.hpp>
#include <srs_protocol_rtmp_stack.hpp>
#include <srs_protocol_utility.hpp>
#include <srs_protocol_http_conn.hpp>
#include <srs_app_http_conn.hpp>
// Forward declarations
class SrsRtcTrackDescription;
@ -769,8 +771,35 @@ public:
void reset();
};
class MockRtmpTransport : public ISrsRtmpTransport, public ISrsProtocolReadWriter
// Mock ISrsProtocolReadWriter for testing SrsHttpConn::cycle()
class MockProtocolReadWriter : public ISrsProtocolReadWriter
{
public:
srs_utime_t recv_timeout_;
srs_utime_t send_timeout_;
public:
MockProtocolReadWriter();
virtual ~MockProtocolReadWriter();
public:
virtual srs_error_t read_fully(void *buf, size_t size, ssize_t *nread);
virtual srs_error_t read(void *buf, size_t size, ssize_t *nread);
virtual void set_recv_timeout(srs_utime_t tm);
virtual srs_utime_t get_recv_timeout();
virtual int64_t get_recv_bytes();
virtual srs_error_t write(void *buf, size_t size, ssize_t *nwrite);
virtual void set_send_timeout(srs_utime_t tm);
virtual srs_utime_t get_send_timeout();
virtual int64_t get_send_bytes();
virtual srs_error_t writev(const iovec *iov, int iov_size, ssize_t *nwrite);
};
class MockRtmpTransport : public ISrsRtmpTransport
{
public:
MockProtocolReadWriter mock_io_;
public:
MockRtmpTransport();
virtual ~MockRtmpTransport();
@ -797,6 +826,36 @@ public:
virtual srs_error_t writev(const iovec *iov, int iov_size, ssize_t *nwrite);
};
// Mock ISrsSslConnection for testing HTTPS connections
class MockSslConnection : public ISrsSslConnection
{
public:
bool handshake_called_;
srs_error_t handshake_error_;
srs_utime_t recv_timeout_;
srs_utime_t send_timeout_;
int64_t recv_bytes_;
int64_t send_bytes_;
public:
MockSslConnection();
virtual ~MockSslConnection();
public:
virtual srs_error_t handshake(std::string key_file, std::string crt_file);
virtual void set_recv_timeout(srs_utime_t tm);
virtual srs_utime_t get_recv_timeout();
virtual srs_error_t read_fully(void *buf, size_t size, ssize_t *nread);
virtual int64_t get_recv_bytes();
virtual int64_t get_send_bytes();
virtual srs_error_t read(void *buf, size_t size, ssize_t *nread);
virtual void set_send_timeout(srs_utime_t tm);
virtual srs_utime_t get_send_timeout();
virtual srs_error_t write(void *buf, size_t size, ssize_t *nwrite);
virtual srs_error_t writev(const iovec *iov, int iov_size, ssize_t *nwrite);
void reset();
};
// Mock ISrsProtocolReadWriter for testing SrsSrtRecvThread
class MockSrtConnection : public ISrsSrtConnection
{
@ -837,4 +896,107 @@ public:
virtual srs_error_t get_stats(SrsSrtStat &stat);
};
// Mock ISrsHttpParser for testing SrsHttpConn::cycle()
class MockHttpParser : public ISrsHttpParser
{
public:
bool initialize_called_;
bool parse_message_called_;
srs_error_t initialize_error_;
srs_error_t parse_message_error_;
public:
SrsCond *cond_;
std::vector<ISrsHttpMessage *> messages_;
public:
MockHttpParser();
virtual ~MockHttpParser();
public:
virtual srs_error_t initialize(enum llhttp_type type);
virtual void set_jsonp(bool allow_jsonp);
virtual srs_error_t parse_message(ISrsReader *reader, ISrsHttpMessage **ppmsg);
void reset();
};
// Mock SrsHttpxConn for testing SrsLiveStream (old version for backward compatibility)
class MockHttpxConn : public ISrsHttpConnOwner
{
public:
bool enable_stat_;
public:
bool on_start_called_;
bool on_http_message_called_;
bool on_message_done_called_;
bool on_conn_done_called_;
srs_error_t on_start_error_;
srs_error_t on_http_message_error_;
srs_error_t on_message_done_error_;
srs_error_t on_conn_done_error_;
public:
MockHttpxConn();
virtual ~MockHttpxConn();
public:
virtual void set_enable_stat(bool v);
virtual srs_error_t on_start();
virtual srs_error_t on_http_message(ISrsHttpMessage *r, ISrsHttpResponseWriter *w);
virtual srs_error_t on_message_done(ISrsHttpMessage *r, ISrsHttpResponseWriter *w);
virtual srs_error_t on_conn_done(srs_error_t r0);
void reset();
};
// Mock SrsHttpConn for testing SrsLiveStream (old version for backward compatibility)
class MockHttpConn : public ISrsConnection, public ISrsExpire
{
public:
MockHttpxConn *handler_;
std::string remote_ip_;
public:
MockHttpConn();
virtual ~MockHttpConn();
public:
virtual std::string remote_ip();
virtual const SrsContextId &get_id();
virtual std::string desc();
virtual void expire();
virtual ISrsHttpConnOwner *handler();
};
// Mock SrsHttpMessage for testing SrsLiveStream (old version for backward compatibility)
class MockHttpMessage : public SrsHttpMessage
{
public:
MockHttpConn *mock_conn_;
public:
MockHttpMessage();
virtual ~MockHttpMessage();
public:
virtual std::string path();
};
// Mock ISrsCommonHttpHandler for testing SrsServer::http_handle()
class MockHttpServeMux : public ISrsCommonHttpHandler
{
public:
int handle_count_;
int serve_http_count_;
std::vector<std::string> patterns_;
public:
MockHttpServeMux();
virtual ~MockHttpServeMux();
public:
virtual srs_error_t handle(std::string pattern, ISrsHttpHandler *handler);
virtual srs_error_t serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessage *r);
};
#endif