From 28080efec86eeafa91caddae7977ceabe049d9b9 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 13 Jan 2016 12:44:51 +0800 Subject: [PATCH] fix http reader bug, support infinite chunkted. 2.0.209 --- README.md | 1 + trunk/src/app/srs_app_http_api.cpp | 8 +++- trunk/src/app/srs_app_http_conn.cpp | 65 +++++++++++++++++++++------ trunk/src/app/srs_app_http_conn.hpp | 14 ++++-- trunk/src/app/srs_app_utility.cpp | 62 ++++++++++++++++++++++++- trunk/src/app/srs_app_utility.hpp | 3 ++ trunk/src/core/srs_core.hpp | 2 +- trunk/src/protocol/srs_http_stack.hpp | 28 ++++++++++-- 8 files changed, 160 insertions(+), 23 deletions(-) mode change 100755 => 100644 trunk/src/app/srs_app_http_api.cpp diff --git a/README.md b/README.md index f69c6c0f7..71b4c1838 100755 --- a/README.md +++ b/README.md @@ -338,6 +338,7 @@ Remark: ## History +* v2.0, 2016-01-13, fix http reader bug, support infinite chunkted. 2.0.209 * v2.0, 2016-01-09, merge [#559][pr #559] fix memory leak bug. 2.0.208 * v2.0, 2016-01-09, merge [#558][pr #558] add tcUrl for on_publish. * v2.0, 2016-01-05, add keyword XCORE for coredump to identify the version. 2.0.207 diff --git a/trunk/src/app/srs_app_http_api.cpp b/trunk/src/app/srs_app_http_api.cpp old mode 100755 new mode 100644 index 65321a0ce..af1a5e259 --- a/trunk/src/app/srs_app_http_api.cpp +++ b/trunk/src/app/srs_app_http_api.cpp @@ -950,8 +950,12 @@ int SrsHttpApi::process_request(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { int ret = ERROR_SUCCESS; - srs_trace("HTTP %s %s, content-length=%"PRId64"", - r->method_str().c_str(), r->url().c_str(), r->content_length()); + SrsHttpMessage* hm = dynamic_cast(r); + srs_assert(hm); + + srs_trace("HTTP API %s %s, content-length=%"PRId64", chunked=%d/%d", + r->method_str().c_str(), r->url().c_str(), r->content_length(), + hm->is_chunked(), hm->is_infinite_chunked()); // method is OPTIONS and enable crossdomain, required crossdomain header. if (r->is_http_options() && _srs_config->get_http_api_crossdomain()) { diff --git a/trunk/src/app/srs_app_http_conn.cpp b/trunk/src/app/srs_app_http_conn.cpp index ac9747ce2..836bed892 100644 --- a/trunk/src/app/srs_app_http_conn.cpp +++ b/trunk/src/app/srs_app_http_conn.cpp @@ -55,6 +55,7 @@ using namespace std; #include #include #include +#include #endif @@ -347,15 +348,29 @@ int SrsHttpResponseReader::read(char* data, int nb_data, int* nb_read) } // read by specified content-length - int max = (int)owner->content_length() - (int)nb_total_read; - if (max <= 0) { - is_eof = true; - return ret; + if (owner->content_length() != -1) { + int max = (int)owner->content_length() - (int)nb_total_read; + if (max <= 0) { + is_eof = true; + return ret; + } + + // change the max to read. + nb_data = srs_min(nb_data, max); + return read_specified(data, nb_data, nb_read); } - // change the max to read. - nb_data = srs_min(nb_data, max); - return read_specified(data, nb_data, nb_read); + // infinite chunked mode, directly read. + if (owner->is_infinite_chunked()) { + srs_assert(!owner->is_chunked() && owner->content_length() == -1); + return read_specified(data, nb_data, nb_read); + } + + // infinite chunked mode, but user not set it, + // we think there is no data left. + is_eof = true; + + return ret; } int SrsHttpResponseReader::read_chunked(char* data, int nb_data, int* nb_read) @@ -480,8 +495,8 @@ int SrsHttpResponseReader::read_specified(char* data, int nb_data, int* nb_read) // increase the total read to determine whether EOF. nb_total_read += nb_bytes; - // for not chunked - if (!owner->is_chunked()) { + // for not chunked and specified content length. + if (!owner->is_chunked() && owner->content_length() != -1) { // when read completed, eof. if (nb_total_read >= (int)owner->content_length()) { is_eof = true; @@ -495,6 +510,7 @@ SrsHttpMessage::SrsHttpMessage(SrsStSocket* io, SrsConnection* c) : ISrsHttpMess { conn = c; chunked = false; + infinite_chunked = false; keep_alive = true; _uri = new SrsHttpUri(); _body = new SrsHttpResponseReader(this, io); @@ -532,11 +548,10 @@ int SrsHttpMessage::update(string url, http_parser* header, SrsFastBuffer* body, // parse uri from url. std::string host = get_request_header("Host"); - // donot parse the empty host for uri, - // for example, the response contains no host, - // ignore it is ok. + // use server public ip when no host specified. + // to make telnet happy. if (host.empty()) { - return ret; + host= srs_get_public_internet_address(); } // parse uri to schema/server:port/path?query @@ -674,6 +689,11 @@ bool SrsHttpMessage::is_keep_alive() return keep_alive; } +bool SrsHttpMessage::is_infinite_chunked() +{ + return infinite_chunked; +} + string SrsHttpMessage::uri() { std::string uri = _uri->get_schema(); @@ -728,6 +748,25 @@ int SrsHttpMessage::parse_rest_id(string pattern) return -1; } +int SrsHttpMessage::enter_infinite_chunked() +{ + int ret = ERROR_SUCCESS; + + if (infinite_chunked) { + return ret; + } + + if (is_chunked() || content_length() != -1) { + ret = ERROR_HTTP_DATA_INVALID; + srs_error("infinite chunkted not supported in specified codec. ret=%d", ret); + return ret; + } + + infinite_chunked = true; + + return ret; +} + int SrsHttpMessage::body_read_all(string& body) { 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 e43e2f14f..c490a2924 100644 --- a/trunk/src/app/srs_app_http_conn.hpp +++ b/trunk/src/app/srs_app_http_conn.hpp @@ -156,9 +156,6 @@ typedef std::pair SrsHttpHeaderField; // 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. -/** - * the http message, request or response. - */ class SrsHttpMessage : public ISrsHttpMessage { private: @@ -183,6 +180,10 @@ private: * whether the body is chunked. */ bool chunked; + /** + * whether the body is infinite chunked. + */ + bool infinite_chunked; /** * whether the request indicates should keep alive * for the http connection. @@ -235,6 +236,11 @@ public: * whether body is chunked encoding, for reader only. */ virtual bool is_chunked(); + /** + * whether body is infinite chunked encoding. + * @remark set by enter_infinite_chunked. + */ + virtual bool is_infinite_chunked(); /** * whether should keep the connection alive. */ @@ -255,6 +261,8 @@ public: * get the RESTful matched id. */ virtual int parse_rest_id(std::string pattern); +public: + virtual int enter_infinite_chunked(); public: /** * read body to string. diff --git a/trunk/src/app/srs_app_utility.cpp b/trunk/src/app/srs_app_utility.cpp index 23bd2333f..66107cc6c 100644 --- a/trunk/src/app/srs_app_utility.cpp +++ b/trunk/src/app/srs_app_utility.cpp @@ -485,7 +485,7 @@ void srs_update_proc_stat() static int user_hz = 0; if (user_hz <= 0) { user_hz = (int)sysconf(_SC_CLK_TCK); - srs_trace("USER_HZ=%d", user_hz); + srs_info("USER_HZ=%d", user_hz); srs_assert(user_hz > 0); } @@ -1279,6 +1279,66 @@ vector& srs_get_local_ipv4_ips() return _srs_system_ipv4_ips; } +std::string _public_internet_address; + +string srs_get_public_internet_address() +{ + if (!_public_internet_address.empty()) { + return _public_internet_address; + } + + std::vector& ips = srs_get_local_ipv4_ips(); + + // find the best match public address. + for (int i = 0; i < (int)ips.size(); i++) { + std::string ip = ips[i]; + in_addr_t addr = inet_addr(ip.c_str()); + u_int32_t addr_h = ntohl(addr); + // lo, 127.0.0.0-127.0.0.1 + if (addr_h >= 0x7f000000 && addr_h <= 0x7f000001) { + srs_trace("ignore private address: %s", ip.c_str()); + continue; + } + // Class A 10.0.0.0-10.255.255.255 + if (addr_h >= 0x0a000000 && addr_h <= 0x0affffff) { + srs_trace("ignore private address: %s", ip.c_str()); + continue; + } + // Class B 172.16.0.0-172.31.255.255 + if (addr_h >= 0xac100000 && addr_h <= 0xac1fffff) { + srs_trace("ignore private address: %s", ip.c_str()); + continue; + } + // Class C 192.168.0.0-192.168.255.255 + if (addr_h >= 0xc0a80000 && addr_h <= 0xc0a8ffff) { + srs_trace("ignore private address: %s", ip.c_str()); + continue; + } + srs_warn("use public address as ip: %s", ip.c_str()); + + _public_internet_address = ip; + return ip; + } + + // no public address, use private address. + for (int i = 0; i < (int)ips.size(); i++) { + std::string ip = ips[i]; + in_addr_t addr = inet_addr(ip.c_str()); + u_int32_t addr_h = ntohl(addr); + // lo, 127.0.0.0-127.0.0.1 + if (addr_h >= 0x7f000000 && addr_h <= 0x7f000001) { + srs_trace("ignore private address: %s", ip.c_str()); + continue; + } + srs_warn("use private address as ip: %s", ip.c_str()); + + _public_internet_address = ip; + return ip; + } + + return ""; +} + string srs_get_local_ip(int fd) { std::string ip; diff --git a/trunk/src/app/srs_app_utility.hpp b/trunk/src/app/srs_app_utility.hpp index e7548cdbd..87340a24b 100644 --- a/trunk/src/app/srs_app_utility.hpp +++ b/trunk/src/app/srs_app_utility.hpp @@ -660,6 +660,9 @@ extern void srs_update_rtmp_server(int nb_conn, SrsKbps* kbps); // get local ip, fill to @param ips extern std::vector& srs_get_local_ipv4_ips(); +// get local public ip, empty string if no public internet address found. +extern std::string srs_get_public_internet_address(); + // get local or peer ip. // where local ip is the server ip which client connected. extern std::string srs_get_local_ip(int fd); diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 7093c53a2..ab1c7890d 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 208 +#define VERSION_REVISION 209 // generated by configure, only macros. #include diff --git a/trunk/src/protocol/srs_http_stack.hpp b/trunk/src/protocol/srs_http_stack.hpp index a5bd37bca..5193924d9 100644 --- a/trunk/src/protocol/srs_http_stack.hpp +++ b/trunk/src/protocol/srs_http_stack.hpp @@ -231,6 +231,12 @@ public: * @param nb_data, the max size of data buffer. * @param nb_read, the actual read size of bytes. NULL to ignore. * @remark when eof(), return error. + * @remark for some server, the content-length not specified and not chunked, + * which is actually the infinite chunked encoding, which after http header + * is http response data, it's ok for browser. that is, + * when user call this read, please ensure there is data to read(by content-length + * or by chunked), because the sdk never know whether there is no data or + * infinite chunked. */ virtual int read(char* data, int nb_data, int* nb_read) = 0; }; @@ -445,9 +451,19 @@ typedef std::pair SrsHttpHeaderField; // 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. -/** - * the http message, request or response. - */ +// +// There are some modes to determine the length of body: +// 1. content-length and chunked. +// 2. user confirmed infinite chunked. +// 3. no body or user not confirmed infinite chunked. +// For example: +// ISrsHttpMessage* r = ...; +// while (!r->eof()) r->read(); // read in mode 1 or 3. +// For some server, we can confirm the body is infinite chunked: +// ISrsHttpMessage* r = ...; +// r->enter_infinite_chunked(); +// while (!r->eof()) r->read(); // read in mode 2 +// @rmark for mode 2, the infinite chunked, all left data is body. class ISrsHttpMessage { private: @@ -502,6 +518,12 @@ public: */ virtual int parse_rest_id(std::string pattern) = 0; public: + /** + * the left all data is chunked body, the infinite chunked mode, + * which is chunked encoding without chunked header. + * @remark error when message is in chunked or content-length specified. + */ + virtual int enter_infinite_chunked() = 0; /** * read body to string. * @remark for small http body.