From d9842b037139d9158a932e22ca49e1139521bf9b Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 17 Dec 2019 15:14:59 +0800 Subject: [PATCH] Refactor HttpResponseWriter.write, default to single text mode. --- trunk/src/app/srs_app_http_static.cpp | 3 +- trunk/src/app/srs_app_http_stream.cpp | 3 + trunk/src/protocol/srs_http_stack.cpp | 3 + trunk/src/protocol/srs_http_stack.hpp | 6 +- trunk/src/service/srs_service_http_conn.cpp | 4 + trunk/src/utest/srs_utest_http.cpp | 101 ++++++++++++++++---- 6 files changed, 96 insertions(+), 24 deletions(-) diff --git a/trunk/src/app/srs_app_http_static.cpp b/trunk/src/app/srs_app_http_static.cpp index 1bfd8bfe6..5be561259 100644 --- a/trunk/src/app/srs_app_http_static.cpp +++ b/trunk/src/app/srs_app_http_static.cpp @@ -117,6 +117,7 @@ srs_error_t SrsVodStream::serve_flv_stream(ISrsHttpResponseWriter* w, ISrsHttpMe // write http header for ts. w->header()->set_content_length((int)(sizeof(flv_header) + sh_size + left)); w->header()->set_content_type("video/x-flv"); + w->write_header(SRS_CONSTS_HTTP_OK); // write flv header and sequence header. if ((err = w->write(flv_header, sizeof(flv_header))) != srs_success) { @@ -170,8 +171,6 @@ srs_error_t SrsVodStream::serve_mp4_stream(ISrsHttpResponseWriter* w, ISrsHttpMe // write http header for ts. w->header()->set_content_length(left); w->header()->set_content_type("video/mp4"); - - // status code 206 to make dash.as happy. w->write_header(SRS_CONSTS_HTTP_PartialContent); // response the content range header. diff --git a/trunk/src/app/srs_app_http_stream.cpp b/trunk/src/app/srs_app_http_stream.cpp index 3e5551131..494a50193 100755 --- a/trunk/src/app/srs_app_http_stream.cpp +++ b/trunk/src/app/srs_app_http_stream.cpp @@ -540,6 +540,9 @@ srs_error_t SrsLiveStream::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMess return srs_error_new(ERROR_HTTP_LIVE_STREAM_EXT, "invalid pattern=%s", entry->pattern.c_str()); } SrsAutoFree(ISrsBufferEncoder, enc); + + // Enter chunked mode, because we didn't set the content-length. + w->write_header(SRS_CONSTS_HTTP_OK); // create consumer of souce, ignore gop cache, use the audio gop cache. SrsConsumer* consumer = NULL; diff --git a/trunk/src/protocol/srs_http_stack.cpp b/trunk/src/protocol/srs_http_stack.cpp index 4b478603b..abbfe8081 100644 --- a/trunk/src/protocol/srs_http_stack.cpp +++ b/trunk/src/protocol/srs_http_stack.cpp @@ -446,6 +446,9 @@ srs_error_t SrsHttpFileServer::serve_file(ISrsHttpResponseWriter* w, ISrsHttpMes w->header()->set_content_type(_mime[ext]); } } + + // Enter chunked mode, because we didn't set the content-length. + w->write_header(SRS_CONSTS_HTTP_OK); // write body. int64_t left = length; diff --git a/trunk/src/protocol/srs_http_stack.hpp b/trunk/src/protocol/srs_http_stack.hpp index f456c26d5..9044f5549 100644 --- a/trunk/src/protocol/srs_http_stack.hpp +++ b/trunk/src/protocol/srs_http_stack.hpp @@ -150,7 +150,11 @@ public: // A ResponseWriter interface is used by an HTTP handler to // construct an HTTP response. -// Usage 1, response with specified length content: +// Usage 0, response with a message once: +// ISrsHttpResponseWriter* w; // create or get response. +// std::string msg = "Hello, HTTP!"; +// w->write((char*)msg.data(), (int)msg.length()); +// Usage 1, response with specified length content, same to #0: // ISrsHttpResponseWriter* w; // create or get response. // std::string msg = "Hello, HTTP!"; // w->header()->set_content_type("text/plain; charset=utf-8"); diff --git a/trunk/src/service/srs_service_http_conn.cpp b/trunk/src/service/srs_service_http_conn.cpp index 8919b6283..748e3f795 100644 --- a/trunk/src/service/srs_service_http_conn.cpp +++ b/trunk/src/service/srs_service_http_conn.cpp @@ -647,6 +647,10 @@ srs_error_t SrsHttpResponseWriter::write(char* data, int size) // write the header data in memory. if (!header_wrote) { + if (hdr->content_type().empty()) { + hdr->set_content_type("text/plain; charset=utf-8"); + } + hdr->set_content_length(size); write_header(SRS_CONSTS_HTTP_OK); } diff --git a/trunk/src/utest/srs_utest_http.cpp b/trunk/src/utest/srs_utest_http.cpp index 8dae41ca5..a53cf2bb4 100644 --- a/trunk/src/utest/srs_utest_http.cpp +++ b/trunk/src/utest/srs_utest_http.cpp @@ -108,6 +108,47 @@ string mock_http_response(int status, string content) return ss.str(); } +class MockFileReaderFactory : public ISrsFileReaderFactory +{ +public: + string bytes; + MockFileReaderFactory(string data) { + bytes = data; + } + virtual ~MockFileReaderFactory() { + } + virtual SrsFileReader* create_file_reader() { + return new MockSrsFileReader((const char*)bytes.data(), (int)bytes.length()); + } +}; + +class MockHttpHandler : public ISrsHttpHandler +{ +public: + string bytes; + MockHttpHandler(string data) { + bytes = data; + } + virtual ~MockHttpHandler() { + } + virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* /*r*/) { + return w->write((char*)bytes.data(), (int)bytes.length()); + } +}; + +bool _mock_srs_path_always_exists(std::string /*path*/) +{ + return true; +} + +bool _mock_srs_path_not_exists(std::string /*path*/) +{ + return false; +} + +#define __MOCK_HTTP_EXPECT_STREQ(status, text, w) \ + EXPECT_STREQ(mock_http_response(status, text).c_str(), HELPER_BUFFER2STR(&w.io.out_buffer).c_str()) + VOID TEST(ProtocolHTTPTest, StatusCode2Text) { EXPECT_STREQ(SRS_CONSTS_HTTP_OK_str, srs_generate_http_status_text(SRS_CONSTS_HTTP_OK).c_str()); @@ -123,10 +164,20 @@ VOID TEST(ProtocolHTTPTest, StatusCode2Text) VOID TEST(ProtocolHTTPTest, ResponseDetect) { EXPECT_STREQ("application/octet-stream", srs_go_http_detect(NULL, 0).c_str()); + EXPECT_STREQ("application/octet-stream", srs_go_http_detect((char*)"Hello, world!", 0).c_str()); } -#define __MOCK_HTTP_EXPECT_STREQ(status, text, w) \ - EXPECT_STREQ(mock_http_response(status, text).c_str(), HELPER_BUFFER2STR(&w.io.out_buffer).c_str()) +VOID TEST(ProtocolHTTPTest, ResponseWriter) +{ + if (true) { + MockResponseWriter w; + + char msg[] = "Hello, world!"; + w.write((char*)msg, sizeof(msg) - 1); + + __MOCK_HTTP_EXPECT_STREQ(200, "Hello, world!", w); + } +} VOID TEST(ProtocolHTTPTest, ResponseHTTPError) { @@ -137,6 +188,18 @@ VOID TEST(ProtocolHTTPTest, ResponseHTTPError) HELPER_EXPECT_SUCCESS(srs_go_http_error(&w, SRS_CONSTS_HTTP_Found)); __MOCK_HTTP_EXPECT_STREQ(302, "Found", w); } + + if (true) { + MockResponseWriter w; + HELPER_EXPECT_SUCCESS(srs_go_http_error(&w, SRS_CONSTS_HTTP_InternalServerError)); + __MOCK_HTTP_EXPECT_STREQ(500, "Internal Server Error", w); + } + + if (true) { + MockResponseWriter w; + HELPER_EXPECT_SUCCESS(srs_go_http_error(&w, SRS_CONSTS_HTTP_ServiceUnavailable)); + __MOCK_HTTP_EXPECT_STREQ(503, "Service Unavailable", w); + } } VOID TEST(ProtocolHTTPTest, HTTPHeader) @@ -173,28 +236,24 @@ VOID TEST(ProtocolHTTPTest, HTTPHeader) srs_freep(o); } -class MockFileReaderFactory : public ISrsFileReaderFactory +VOID TEST(ProtocolHTTPTest, HTTPServerMuxer) { -public: - string bytes; - MockFileReaderFactory(string data) { - bytes = data; - } - virtual ~MockFileReaderFactory() { - } - virtual SrsFileReader* create_file_reader() { - return new MockSrsFileReader((const char*)bytes.data(), (int)bytes.length()); - } -}; + srs_error_t err; -bool _mock_srs_path_always_exists(std::string /*path*/) -{ - return true; -} + if (true) { + SrsHttpServeMux s; + HELPER_ASSERT_SUCCESS(s.initialize()); -bool _mock_srs_path_not_exists(std::string /*path*/) -{ - return false; + MockHttpHandler* hroot = new MockHttpHandler("Hello, world!"); + HELPER_ASSERT_SUCCESS(s.handle("/", hroot)); + + MockResponseWriter w; + SrsHttpMessage r(NULL, NULL); + HELPER_ASSERT_SUCCESS(r.set_url("/index.html", false)); + + HELPER_ASSERT_SUCCESS(s.serve_http(&w, &r)); + __MOCK_HTTP_EXPECT_STREQ(200, "Hello, world!", w); + } } VOID TEST(ProtocolHTTPTest, VodStreamHandlers)