From 81947df819a2943283eefd30e188566297ed2dfd Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 17 Dec 2019 19:09:39 +0800 Subject: [PATCH] Enhance HTTP response write for final_request. --- README.md | 1 + trunk/src/service/srs_service_http_conn.cpp | 11 ++- trunk/src/utest/srs_utest_http.cpp | 98 ++++++++++++++++++++- 3 files changed, 107 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3d6d8e5c1..9a83d3bda 100755 --- a/README.md +++ b/README.md @@ -145,6 +145,7 @@ For previous versions, please read: ## V3 changes +* v3.0, 2019-12-17, Enhance HTTP response write for final_request. * v3.0, 2019-12-17, Refactor HTTP stream to disconnect client when unpublish. * v3.0, 2019-12-17, Fix HTTP-FLV and VOD-FLV conflicting bug. * v3.0, 2019-12-17, Refactor HttpResponseWriter.write, default to single text mode. diff --git a/trunk/src/service/srs_service_http_conn.cpp b/trunk/src/service/srs_service_http_conn.cpp index 748e3f795..a2fd307ec 100644 --- a/trunk/src/service/srs_service_http_conn.cpp +++ b/trunk/src/service/srs_service_http_conn.cpp @@ -619,10 +619,17 @@ SrsHttpResponseWriter::~SrsHttpResponseWriter() srs_error_t SrsHttpResponseWriter::final_request() { + srs_error_t err = srs_success; + // write the header data in memory. if (!header_wrote) { write_header(SRS_CONSTS_HTTP_OK); } + + // whatever header is wrote, we should try to send header. + if ((err = send_header(NULL, 0)) != srs_success) { + return srs_error_wrap(err, "send header"); + } // complete the chunked encoding. if (content_length == -1) { @@ -666,7 +673,7 @@ srs_error_t SrsHttpResponseWriter::write(char* data, int size) } // ignore NULL content. - if (!data) { + if (!data || size <= 0) { return err; } @@ -811,7 +818,7 @@ srs_error_t SrsHttpResponseWriter::send_header(char* data, int size) // detect content type if (srs_go_http_body_allowd(status)) { - if (hdr->content_type().empty()) { + if (data && hdr->content_type().empty()) { hdr->set_content_type(srs_go_http_detect(data, size)); } } diff --git a/trunk/src/utest/srs_utest_http.cpp b/trunk/src/utest/srs_utest_http.cpp index a53cf2bb4..9417ff4ad 100644 --- a/trunk/src/utest/srs_utest_http.cpp +++ b/trunk/src/utest/srs_utest_http.cpp @@ -108,6 +108,16 @@ string mock_http_response(int status, string content) return ss.str(); } +string mock_http_response2(int status, string content) +{ + stringstream ss; + ss << "HTTP/1.1 " << status << " " << srs_generate_http_status_text(status) << "\r\n" + << "Transfer-Encoding: chunked" << "\r\n" + << "\r\n" + << content; + return ss.str(); +} + class MockFileReaderFactory : public ISrsFileReaderFactory { public: @@ -149,6 +159,9 @@ bool _mock_srs_path_not_exists(std::string /*path*/) #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()) +#define __MOCK_HTTP_EXPECT_STREQ2(status, text, w) \ + EXPECT_STREQ(mock_http_response2(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()); @@ -169,6 +182,7 @@ VOID TEST(ProtocolHTTPTest, ResponseDetect) VOID TEST(ProtocolHTTPTest, ResponseWriter) { + // If directly write string, response with content-length. if (true) { MockResponseWriter w; @@ -177,6 +191,70 @@ VOID TEST(ProtocolHTTPTest, ResponseWriter) __MOCK_HTTP_EXPECT_STREQ(200, "Hello, world!", w); } + + // Response with specified length string, response with content-length. + if (true) { + MockResponseWriter w; + + char msg[] = "Hello, world!"; + + w.header()->set_content_type("text/plain; charset=utf-8"); + w.header()->set_content_length(sizeof(msg) - 1); + w.write_header(SRS_CONSTS_HTTP_OK); + w.write((char*)msg, sizeof(msg) - 1); + w.final_request(); + + __MOCK_HTTP_EXPECT_STREQ(200, "Hello, world!", w); + } + + // If set content-length to 0 then final_request, send an empty resonse with content-length 0. + if (true) { + MockResponseWriter w; + + w.header()->set_content_length(0); + w.write_header(SRS_CONSTS_HTTP_OK); + w.final_request(); + + __MOCK_HTTP_EXPECT_STREQ(200, "", w); + } + + // If set content-length to 0 then write, send an empty resonse with content-length 0. + if (true) { + MockResponseWriter w; + + w.header()->set_content_length(0); + w.write_header(SRS_CONSTS_HTTP_OK); + w.write(NULL, 0); + + __MOCK_HTTP_EXPECT_STREQ(200, "", w); + } + + // If write_header without content-length, enter chunked encoding mode. + if (true) { + MockResponseWriter w; + + w.header()->set_content_type("application/octet-stream"); + w.write_header(SRS_CONSTS_HTTP_OK); + w.write((char*)"Hello", 5); + w.write((char*)", world!", 8); + w.final_request(); + + __MOCK_HTTP_EXPECT_STREQ2(200, "5\r\nHello\r\n8\r\n, world!\r\n0\r\n\r\n", w); + } + + // If directly write empty string, sent an empty response with content-length 0 + if (true) { + MockResponseWriter w; + w.write(NULL, 0); + __MOCK_HTTP_EXPECT_STREQ(200, "", w); + } + + // If directly final request, response with EOF of chunked. + if (true) { + MockResponseWriter w; + w.final_request(); + __MOCK_HTTP_EXPECT_STREQ2(200, "0\r\n\r\n", w); + } } VOID TEST(ProtocolHTTPTest, ResponseHTTPError) @@ -356,7 +434,24 @@ VOID TEST(ProtocolHTTPTest, BasicHandlers) MockResponseWriter w; SrsHttpMessage r(NULL, NULL); - HELPER_ASSERT_SUCCESS(r.set_url("/index.mp4?start=2", false)); + HELPER_ASSERT_SUCCESS(r.set_url("/index.mp4?range=12-3", false)); + + HELPER_ASSERT_SUCCESS(h.serve_http(&w, &r)); + __MOCK_HTTP_EXPECT_STREQ(200, "Hello, world!", w); + } + + if (true) { + SrsHttpMuxEntry e; + e.pattern = "/"; + + SrsHttpFileServer h("/tmp"); + h.set_fs_factory(new MockFileReaderFactory("Hello, world!")); + h.set_path_check(_mock_srs_path_always_exists); + h.entry = &e; + + MockResponseWriter w; + SrsHttpMessage r(NULL, NULL); + HELPER_ASSERT_SUCCESS(r.set_url("/index.mp4?range=2-3", false)); HELPER_ASSERT_SUCCESS(h.serve_http(&w, &r)); __MOCK_HTTP_EXPECT_STREQ(200, "Hello, world!", w); @@ -444,6 +539,7 @@ VOID TEST(ProtocolHTTPTest, BasicHandlers) if (true) { SrsHttpRedirectHandler h("/api", 500); + EXPECT_FALSE(h.is_not_found()); MockResponseWriter w; SrsHttpMessage r(NULL, NULL);