diff --git a/README.md b/README.md index 96c7cb181..af84aa06f 100755 --- a/README.md +++ b/README.md @@ -215,6 +215,8 @@ Please select your language: ### V2 changes +* v2.0, 2017-05-01, Fix [#865][bug #865], shouldn't remove ts/m3u8 when hls_dispose disabled. 2.0.242 +* v2.0, 2017-04-30, Fix [#636][bug #636], FD leak for requesting empty HTTP stream. 2.0.241 * v2.0, 2017-04-23, Fix [#851][bug #851], HTTP API support number of video frames for FPS. 2.0.240 * v2.0, 2017-04-18, [2.0 release1(2.0.239)][r2.0r1] released. 86515 lines. * v2.0, 2017-04-18, Fix [#848][bug #848], crash at HTTP fast buffer grow. 2.0.239 @@ -1399,6 +1401,8 @@ Winlin [bug #844]: https://github.com/ossrs/srs/issues/844 [bug #848]: https://github.com/ossrs/srs/issues/848 [bug #851]: https://github.com/ossrs/srs/issues/851 +[bug #636]: https://github.com/ossrs/srs/issues/636 +[bug #865]: https://github.com/ossrs/srs/issues/865 [bug #xxxxxxxxxx]: https://github.com/ossrs/srs/issues/xxxxxxxxxx [bug #735]: https://github.com/ossrs/srs/issues/735 diff --git a/trunk/src/app/srs_app_hls.cpp b/trunk/src/app/srs_app_hls.cpp index 5875991cc..d5dadae3f 100644 --- a/trunk/src/app/srs_app_hls.cpp +++ b/trunk/src/app/srs_app_hls.cpp @@ -992,6 +992,13 @@ void SrsHls::dispose() on_unpublish(); } + // Ignore when hls_dispose disabled. + // @see https://github.com/ossrs/srs/issues/865 + int hls_dispose = _srs_config->get_hls_dispose(req->vhost); + if (!hls_dispose) { + return; + } + controller->dispose(); } diff --git a/trunk/src/app/srs_app_http_conn.cpp b/trunk/src/app/srs_app_http_conn.cpp index a49b4c6c7..15a833b7c 100644 --- a/trunk/src/app/srs_app_http_conn.cpp +++ b/trunk/src/app/srs_app_http_conn.cpp @@ -212,6 +212,19 @@ SrsResponseOnlyHttpConn::~SrsResponseOnlyHttpConn() { } +int SrsResponseOnlyHttpConn::pop_message(ISrsHttpMessage** preq) +{ + int ret = ERROR_SUCCESS; + + SrsStSocket skt(stfd); + + if ((ret = parser->parse_message(&skt, this, preq)) != ERROR_SUCCESS) { + return ret; + } + + return ret; +} + int SrsResponseOnlyHttpConn::on_got_http_message(ISrsHttpMessage* msg) { int ret = ERROR_SUCCESS; diff --git a/trunk/src/app/srs_app_http_conn.hpp b/trunk/src/app/srs_app_http_conn.hpp index 525265ddc..bd4d4d4aa 100644 --- a/trunk/src/app/srs_app_http_conn.hpp +++ b/trunk/src/app/srs_app_http_conn.hpp @@ -60,7 +60,7 @@ class SrsHttpStaticServer; */ class SrsHttpConn : public SrsConnection { -private: +protected: SrsHttpParser* parser; ISrsHttpServeMux* http_mux; SrsHttpCorsMux* cors; @@ -101,6 +101,13 @@ class SrsResponseOnlyHttpConn : public SrsHttpConn public: SrsResponseOnlyHttpConn(IConnectionManager* cm, st_netfd_t fd, ISrsHttpServeMux* m, std::string cip); virtual ~SrsResponseOnlyHttpConn(); +public: + // Directly read a HTTP request message. + // It's exported for HTTP stream, such as HTTP FLV, only need to write to client when + // serving it, but we need to start a thread to read message to detect whether FD is closed. + // @see https://github.com/ossrs/srs/issues/636#issuecomment-298208427 + // @remark Should only used in HTTP-FLV streaming connection. + virtual int pop_message(ISrsHttpMessage** preq); public: virtual int on_got_http_message(ISrsHttpMessage* msg); }; diff --git a/trunk/src/app/srs_app_http_stream.cpp b/trunk/src/app/srs_app_http_stream.cpp index 252202f94..053ee4129 100755 --- a/trunk/src/app/srs_app_http_stream.cpp +++ b/trunk/src/app/srs_app_http_stream.cpp @@ -53,6 +53,7 @@ using namespace std; #include #include #include +#include SrsBufferCache::SrsBufferCache(SrsSource* s, SrsRequest* r) { @@ -538,10 +539,28 @@ int SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) SrsFastFlvStreamEncoder* ffe = dynamic_cast(enc); #endif + // Use receive thread to accept the close event to avoid FD leak. + // @see https://github.com/ossrs/srs/issues/636#issuecomment-298208427 + SrsHttpMessage* hr = dynamic_cast(r); + SrsResponseOnlyHttpConn* hc = dynamic_cast(hr->connection()); + + SrsHttpRecvThread* trd = new SrsHttpRecvThread(hc); + SrsAutoFree(SrsHttpRecvThread, trd); + + if ((ret = trd->start()) != ERROR_SUCCESS) { + srs_error("http: start notify thread failed, ret=%d", ret); + return ret; + } + // TODO: free and erase the disabled entry after all related connections is closed. while (entry->enabled) { pprint->elapse(); + // Whether client closed the FD. + if ((ret = trd->error_code()) != ERROR_SUCCESS) { + return ret; + } + // get messages from consumer. // each msg in msgs.msgs must be free, for the SrsMessageArray never free them. int count = 0; diff --git a/trunk/src/app/srs_app_recv_thread.cpp b/trunk/src/app/srs_app_recv_thread.cpp index 1b5c08ea4..bdfd580c1 100644 --- a/trunk/src/app/srs_app_recv_thread.cpp +++ b/trunk/src/app/srs_app_recv_thread.cpp @@ -31,6 +31,8 @@ #include #include #include +#include +#include using namespace std; @@ -527,3 +529,42 @@ void SrsPublishRecvThread::set_socket_buffer(int sleep_ms) rtmp->set_recv_buffer(nb_rbuf); } +SrsHttpRecvThread::SrsHttpRecvThread(SrsResponseOnlyHttpConn* c) +{ + conn = c; + error = ERROR_SUCCESS; + trd = new SrsOneCycleThread("http-receive", this); +} + +SrsHttpRecvThread::~SrsHttpRecvThread() +{ + srs_freep(trd); +} + +int SrsHttpRecvThread::start() +{ + return trd->start(); +} + +int SrsHttpRecvThread::error_code() +{ + return error; +} + +int SrsHttpRecvThread::cycle() +{ + int ret = ERROR_SUCCESS; + + while (true) { + ISrsHttpMessage* req = NULL; + SrsAutoFree(ISrsHttpMessage, req); + + if ((ret = conn->pop_message(&req)) != ERROR_SUCCESS) { + error = ret; + break; + } + } + + return ret; +} + diff --git a/trunk/src/app/srs_app_recv_thread.hpp b/trunk/src/app/srs_app_recv_thread.hpp index b425e717c..cb18b0916 100644 --- a/trunk/src/app/srs_app_recv_thread.hpp +++ b/trunk/src/app/srs_app_recv_thread.hpp @@ -39,6 +39,8 @@ class SrsRtmpConn; class SrsSource; class SrsRequest; class SrsConsumer; +class SrsHttpConn; +class SrsResponseOnlyHttpConn; /** * The message consumer which consume a message. @@ -221,5 +223,29 @@ private: virtual void set_socket_buffer(int sleep_ms); }; +/** + * The HTTP receive thread, try to read messages util EOF. + * For example, the HTTP FLV serving thread will use the receive thread to break + * when client closed the request, to avoid FD leak. + * @see https://github.com/ossrs/srs/issues/636#issuecomment-298208427 + */ +class SrsHttpRecvThread : public ISrsOneCycleThreadHandler +{ +private: + SrsResponseOnlyHttpConn* conn; + SrsOneCycleThread* trd; + int error; +public: + SrsHttpRecvThread(SrsResponseOnlyHttpConn* c); + virtual ~SrsHttpRecvThread(); +public: + virtual int start(); +public: + virtual int error_code(); +// interface ISrsOneCycleThreadHandler +public: + virtual int cycle(); +}; + #endif diff --git a/trunk/src/app/srs_app_server.cpp b/trunk/src/app/srs_app_server.cpp index 7aa1da359..7c8d7490e 100644 --- a/trunk/src/app/srs_app_server.cpp +++ b/trunk/src/app/srs_app_server.cpp @@ -271,7 +271,7 @@ int SrsHttpFlvListener::listen(string i, int p) return ret; } - srs_info("listen thread current_cid=%d, listen at port=%d, type=%d, fd=%d started success, ep=%s:%d", port, type, listener->fd(), ip.c_str(), port); + srs_info("listen thread listen at port=%d, type=%d, fd=%d started success, ep=%s:%d", port, type, listener->fd(), ip.c_str(), port); srs_trace("%s listen at tcp://%s:%d, fd=%d", srs_listener_type2string(type).c_str(), ip.c_str(), port, listener->fd());