diff --git a/README.md b/README.md index 9a83d3bda..7ceea8d6f 100755 --- a/README.md +++ b/README.md @@ -145,6 +145,7 @@ For previous versions, please read: ## V3 changes +* v3.0, 2019-12-17, Fix HTTP CORS bug when sending response for OPTIONS. 3.0.72 * 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. diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index 9f8ed7726..c5fae9974 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -907,7 +907,7 @@ SrsJsonAny* SrsConfDirective::dumps_arg0_to_str() SrsJsonAny* SrsConfDirective::dumps_arg0_to_integer() { - return SrsJsonAny::integer(::atol(arg0().c_str())); + return SrsJsonAny::integer(::atoll(arg0().c_str())); } SrsJsonAny* SrsConfDirective::dumps_arg0_to_number() diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index f7cee590d..8a5ce5e16 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -27,7 +27,7 @@ // The version config. #define VERSION_MAJOR 3 #define VERSION_MINOR 0 -#define VERSION_REVISION 71 +#define VERSION_REVISION 72 // The macros generated by configure script. #include diff --git a/trunk/src/protocol/srs_http_stack.cpp b/trunk/src/protocol/srs_http_stack.cpp index 67d7e6e0a..63a70b8d7 100644 --- a/trunk/src/protocol/srs_http_stack.cpp +++ b/trunk/src/protocol/srs_http_stack.cpp @@ -823,8 +823,6 @@ srs_error_t SrsHttpCorsMux::initialize(ISrsHttpServeMux* worker, bool cros_enabl srs_error_t SrsHttpCorsMux::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { - srs_error_t err = srs_success; - // If CORS enabled, and there is a "Origin" header, it's CORS. if (enabled) { SrsHttpHeader* h = r->header(); @@ -848,9 +846,7 @@ srs_error_t SrsHttpCorsMux::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessag } else { w->write_header(SRS_CONSTS_HTTP_MethodNotAllowed); } - if ((err = w->final_request()) != srs_success) { - return srs_error_wrap(err, "final request"); - } + return w->final_request(); } srs_assert(next); diff --git a/trunk/src/protocol/srs_rtsp_stack.cpp b/trunk/src/protocol/srs_rtsp_stack.cpp index 656737039..d3f5d7366 100644 --- a/trunk/src/protocol/srs_rtsp_stack.cpp +++ b/trunk/src/protocol/srs_rtsp_stack.cpp @@ -887,7 +887,7 @@ srs_error_t SrsRtspStack::do_recv_message(SrsRtspRequest* req) if ((err = recv_token_eof(seq)) != srs_success) { return srs_error_wrap(err, "seq"); } - req->seq = ::atol(seq.c_str()); + req->seq = ::atoll(seq.c_str()); } else if (token == SRS_RTSP_TOKEN_CONTENT_TYPE) { std::string ct; if ((err = recv_token_eof(ct)) != srs_success) { @@ -899,7 +899,7 @@ srs_error_t SrsRtspStack::do_recv_message(SrsRtspRequest* req) if ((err = recv_token_eof(cl)) != srs_success) { return srs_error_wrap(err, "cl"); } - req->content_length = ::atol(cl.c_str()); + req->content_length = ::atoll(cl.c_str()); } else if (token == SRS_RTSP_TOKEN_TRANSPORT) { std::string transport; if ((err = recv_token_eof(transport)) != srs_success) { diff --git a/trunk/src/service/srs_service_http_conn.cpp b/trunk/src/service/srs_service_http_conn.cpp index a2fd307ec..2cc6196b4 100644 --- a/trunk/src/service/srs_service_http_conn.cpp +++ b/trunk/src/service/srs_service_http_conn.cpp @@ -264,7 +264,7 @@ SrsHttpMessage::SrsHttpMessage(ISrsReader* reader, SrsFastStream* buffer) : ISrs _method = 0; _status = 0; - _content_length = 0; + _content_length = -1; _keep_alive = true; } @@ -278,7 +278,9 @@ void SrsHttpMessage::set_basic(uint8_t method, uint16_t status, int64_t content_ { _method = method; _status = status; - _content_length = content_length; + if (_content_length == -1) { + _content_length = content_length; + } } void SrsHttpMessage::set_header(SrsHttpHeader* header, bool keep_alive) @@ -288,6 +290,12 @@ void SrsHttpMessage::set_header(SrsHttpHeader* header, bool keep_alive) // whether chunked. chunked = (header->get("Transfer-Encoding") == "chunked"); + + // Update the content-length in header. + string clv = header->get("Content-Length"); + if (!clv.empty()) { + _content_length = ::atoll(clv.c_str()); + } } srs_error_t SrsHttpMessage::set_url(string url, bool allow_jsonp) diff --git a/trunk/src/service/srs_service_http_conn.hpp b/trunk/src/service/srs_service_http_conn.hpp index 2313b0207..cdd9524cf 100644 --- a/trunk/src/service/srs_service_http_conn.hpp +++ b/trunk/src/service/srs_service_http_conn.hpp @@ -126,12 +126,14 @@ private: // The method in QueryString will override the HTTP method. std::string jsonp_method; public: - SrsHttpMessage(ISrsReader* reader, SrsFastStream* buffer); + SrsHttpMessage(ISrsReader* reader = NULL, SrsFastStream* buffer = NULL); virtual ~SrsHttpMessage(); public: // Set the basic information for HTTP request. + // @remark User must call set_basic before set_header, because the content_length will be overwrite by header. virtual void set_basic(uint8_t method, uint16_t status, int64_t content_length); // Set HTTP header and whether the request require keep alive. + // @remark User must call set_header before set_url, because the Host in header is used for url. virtual void set_header(SrsHttpHeader* header, bool keep_alive); // set the original messages, then update the message. virtual srs_error_t set_url(std::string url, bool allow_jsonp); diff --git a/trunk/src/utest/srs_utest_http.cpp b/trunk/src/utest/srs_utest_http.cpp index d70750ed2..fa7b8abaa 100644 --- a/trunk/src/utest/srs_utest_http.cpp +++ b/trunk/src/utest/srs_utest_http.cpp @@ -320,7 +320,112 @@ VOID TEST(ProtocolHTTPTest, HTTPHeader) srs_freep(o); } -VOID TEST(ProtocolHTTPTest, HTTPServerMuxer) +VOID TEST(ProtocolHTTPTest, HTTPServerMuxerVhost) +{ + srs_error_t err; + + // For vhost. + if (true) { + SrsHttpServeMux s; + HELPER_ASSERT_SUCCESS(s.initialize()); + + MockHttpHandler* h0 = new MockHttpHandler("Hello, world!"); + HELPER_ASSERT_SUCCESS(s.handle("/", h0)); + + MockHttpHandler* h1 = new MockHttpHandler("Done"); + HELPER_ASSERT_SUCCESS(s.handle("ossrs.net/api/", h1)); + + MockResponseWriter w; + SrsHttpMessage r(NULL, NULL); + + SrsHttpHeader h; + h.set("Host", "ossrs.net"); + r.set_header(&h, false); + + HELPER_ASSERT_SUCCESS(r.set_url("/api/", false)); + + HELPER_ASSERT_SUCCESS(s.serve_http(&w, &r)); + __MOCK_HTTP_EXPECT_STREQ(200, "Done", w); + } + + // For vhost. + if (true) { + SrsHttpServeMux s; + HELPER_ASSERT_SUCCESS(s.initialize()); + + MockHttpHandler* h0 = new MockHttpHandler("Hello, world!"); + HELPER_ASSERT_SUCCESS(s.handle("/api/", h0)); + + MockHttpHandler* h1 = new MockHttpHandler("Done"); + HELPER_ASSERT_SUCCESS(s.handle("ossrs.net/api/", h1)); + + MockResponseWriter w; + SrsHttpMessage r(NULL, NULL); + + SrsHttpHeader h; + h.set("Host", "ossrs.net"); + r.set_header(&h, false); + + HELPER_ASSERT_SUCCESS(r.set_url("/api/", false)); + + HELPER_ASSERT_SUCCESS(s.serve_http(&w, &r)); + __MOCK_HTTP_EXPECT_STREQ(200, "Done", w); + } +} + +VOID TEST(ProtocolHTTPTest, HTTPServerMuxerImplicitHandler) +{ + srs_error_t err; + + // Fail if explicit handler exists. + if (true) { + SrsHttpServeMux s; + HELPER_ASSERT_SUCCESS(s.initialize()); + + MockHttpHandler* h0 = new MockHttpHandler("Hello, world!"); + HELPER_ASSERT_SUCCESS(s.handle("/api/", h0)); + + MockHttpHandler* h1 = new MockHttpHandler("Done"); + HELPER_EXPECT_FAILED(s.handle("/api/", h1)); + } + + // Explicit handler will overwrite the implicit handler. + if (true) { + SrsHttpServeMux s; + HELPER_ASSERT_SUCCESS(s.initialize()); + + MockHttpHandler* h0 = new MockHttpHandler("Hello, world!"); + HELPER_ASSERT_SUCCESS(s.handle("/api/", h0)); + + MockHttpHandler* h1 = new MockHttpHandler("Done"); + HELPER_ASSERT_SUCCESS(s.handle("/api", h1)); + + MockResponseWriter w; + SrsHttpMessage r(NULL, NULL); + HELPER_ASSERT_SUCCESS(r.set_url("/api", false)); + + HELPER_ASSERT_SUCCESS(s.serve_http(&w, &r)); + __MOCK_HTTP_EXPECT_STREQ(200, "Done", w); + } + + // Implicit handler. + if (true) { + SrsHttpServeMux s; + HELPER_ASSERT_SUCCESS(s.initialize()); + + MockHttpHandler* h0 = new MockHttpHandler("Hello, world!"); + HELPER_ASSERT_SUCCESS(s.handle("/api/", h0)); + + MockResponseWriter w; + SrsHttpMessage r(NULL, NULL); + HELPER_ASSERT_SUCCESS(r.set_url("/api", false)); + + HELPER_ASSERT_SUCCESS(s.serve_http(&w, &r)); + __MOCK_HTTP_EXPECT_STREQ(302, "Redirect to /api/", w); + } +} + +VOID TEST(ProtocolHTTPTest, HTTPServerMuxerHijack) { srs_error_t err; @@ -362,6 +467,51 @@ VOID TEST(ProtocolHTTPTest, HTTPServerMuxer) __MOCK_HTTP_EXPECT_STREQ(200, "Done", w); } + if (true) { + SrsHttpServeMux s; + HELPER_ASSERT_SUCCESS(s.initialize()); + + MockHttpHandler hroot("Hello, world!"); + s.hijack(&hroot); + s.unhijack(&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(404, "Not Found", w); + } + + if (true) { + SrsHttpServeMux s; + HELPER_ASSERT_SUCCESS(s.initialize()); + + MockHttpHandler hroot("Hello, world!"); + s.hijack(&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, HTTPServerMuxerBasic) +{ + srs_error_t err; + + // Empty pattern, failed. + if (true) { + SrsHttpServeMux s; + HELPER_ASSERT_SUCCESS(s.initialize()); + + MockHttpHandler* h0 = new MockHttpHandler("Hello, world!"); + HELPER_EXPECT_FAILED(s.handle("", h0)); + } + // If not endswith '/', exactly match. if (true) { SrsHttpServeMux s; @@ -497,30 +647,39 @@ VOID TEST(ProtocolHTTPTest, HTTPServerMuxer) SrsHttpServeMux s; HELPER_ASSERT_SUCCESS(s.initialize()); - MockHttpHandler hroot("Hello, world!"); - s.hijack(&hroot); - s.unhijack(&hroot); + 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(404, "Not Found", w); + __MOCK_HTTP_EXPECT_STREQ(200, "Hello, world!", w); } +} +VOID TEST(ProtocolHTTPTest, HTTPServerMuxerCORS) +{ + srs_error_t err; + + // If CORS not enabled, directly response any request. if (true) { SrsHttpServeMux s; HELPER_ASSERT_SUCCESS(s.initialize()); - MockHttpHandler hroot("Hello, world!"); - s.hijack(&hroot); + MockHttpHandler* hroot = new MockHttpHandler("Hello, world!"); + HELPER_ASSERT_SUCCESS(s.handle("/", hroot)); MockResponseWriter w; SrsHttpMessage r(NULL, NULL); + r.set_basic(HTTP_OPTIONS, 200, -1); HELPER_ASSERT_SUCCESS(r.set_url("/index.html", false)); - HELPER_ASSERT_SUCCESS(s.serve_http(&w, &r)); + SrsHttpCorsMux cs; + HELPER_ASSERT_SUCCESS(cs.initialize(&s, false)); + + HELPER_ASSERT_SUCCESS(cs.serve_http(&w, &r)); __MOCK_HTTP_EXPECT_STREQ(200, "Hello, world!", w); } @@ -535,7 +694,10 @@ VOID TEST(ProtocolHTTPTest, HTTPServerMuxer) SrsHttpMessage r(NULL, NULL); HELPER_ASSERT_SUCCESS(r.set_url("/index.html", false)); - HELPER_ASSERT_SUCCESS(s.serve_http(&w, &r)); + SrsHttpCorsMux cs; + HELPER_ASSERT_SUCCESS(cs.initialize(&s, true)); + + HELPER_ASSERT_SUCCESS(cs.serve_http(&w, &r)); __MOCK_HTTP_EXPECT_STREQ(200, "Hello, world!", w); } } @@ -623,12 +785,82 @@ VOID TEST(ProtocolHTTPTest, VodStreamHandlers) HELPER_ASSERT_SUCCESS(h.serve_http(&w, &r)); __MOCK_HTTP_EXPECT_STREQ(206, "llo,", w); } + + // Invalid format of range, use static file handler. + if (true) { + SrsHttpMuxEntry e; + e.pattern = "/"; + + SrsVodStream 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", false)); + + HELPER_ASSERT_SUCCESS(h.serve_http(&w, &r)); + __MOCK_HTTP_EXPECT_STREQ(200, "Hello, world!", w); + } + + // No range, use static file handler. + if (true) { + SrsHttpMuxEntry e; + e.pattern = "/"; + + SrsVodStream 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", false)); + + HELPER_ASSERT_SUCCESS(h.serve_http(&w, &r)); + __MOCK_HTTP_EXPECT_STREQ(200, "Hello, world!", w); + } } VOID TEST(ProtocolHTTPTest, BasicHandlers) { srs_error_t err; + 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=0", 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", false)); + + HELPER_ASSERT_SUCCESS(h.serve_http(&w, &r)); + __MOCK_HTTP_EXPECT_STREQ(200, "Hello, world!", w); + } + if (true) { SrsHttpMuxEntry e; e.pattern = "/"; @@ -966,3 +1198,88 @@ VOID TEST(ProtocolHTTPTest, HTTPMessageParser) srs_freep(msg); } } + +VOID TEST(ProtocolHTTPTest, HTTPMessageUpdate) +{ + // The host is overwrite by header. + if (true) { + SrsHttpHeader h; + h.set("Host", "ossrs.net"); + + SrsHttpMessage m; + m.set_url("/api/v1", false); + m.set_header(&h, false); + EXPECT_STRNE("ossrs.net", m.host().c_str()); + } + + // The host is overwrite by header. + if (true) { + SrsHttpHeader h; + h.set("Host", "ossrs.net"); + + SrsHttpMessage m; + m.set_header(&h, false); + m.set_url("/api/v1", false); + EXPECT_STREQ("ossrs.net", m.host().c_str()); + } + + // The content-length is overwrite by header. + if (true) { + SrsHttpHeader h; + h.set("Content-Length", "10"); + + SrsHttpMessage m; + m.set_header(&h, false); + m.set_basic(HTTP_POST, 200, 2); + EXPECT_EQ(10, m.content_length()); + } + + // The content-length is overwrite by header. + if (true) { + SrsHttpHeader h; + h.set("Content-Length", "10"); + + SrsHttpMessage m; + m.set_basic(HTTP_POST, 200, 2); + m.set_header(&h, false); + EXPECT_EQ(10, m.content_length()); + } + + if (true) { + SrsHttpHeader h; + h.set("Content-Length", "abc"); + + SrsHttpMessage m; + m.set_header(&h, false); + EXPECT_EQ(0, m.content_length()); + } + + if (true) { + SrsHttpHeader h; + h.set("Content-Length", "10"); + + SrsHttpMessage m; + m.set_header(&h, false); + EXPECT_EQ(10, m.content_length()); + } + + if (true) { + SrsHttpHeader h; + h.set("Content-Length", "0"); + + SrsHttpMessage m; + m.set_header(&h, false); + EXPECT_EQ(0, m.content_length()); + } + + if (true) { + SrsHttpMessage m; + m.set_basic(HTTP_POST, 200, 0); + EXPECT_EQ(0, m.content_length()); + } + + if (true) { + SrsHttpMessage m; + EXPECT_EQ(-1, m.content_length()); + } +}