diff --git a/README.md b/README.md
index 3ddee5355..930a8dd1f 100755
--- a/README.md
+++ b/README.md
@@ -147,6 +147,8 @@ For previous versions, please read:
## V3 changes
+* v3.0, 2019-12-20, Fix [#1508][bug #1508], http-client support read chunked response. 3.0.76
+* v3.0, 2019-12-20, For [#1508][bug #1508], refactor srs_is_digital, support all zeros.
* v3.0, 2019-12-19, [3.0 alpha5(3.0.75)][r3.0a5] released. 115362 lines.
* v3.0, 2019-12-19, Refine the RTMP iovs cache increasing to much faster.
* v3.0, 2019-12-19, Fix [#1524][bug #1524], memory leak for amf0 strict array. 3.0.75
@@ -1543,6 +1545,7 @@ Winlin
[bug #1506]: https://github.com/ossrs/srs/issues/1506
[bug #1520]: https://github.com/ossrs/srs/issues/1520
[bug #1223]: https://github.com/ossrs/srs/issues/1223
+[bug #1508]: https://github.com/ossrs/srs/issues/1508
[bug #xxxxxxxxxxxxx]: https://github.com/ossrs/srs/issues/xxxxxxxxxxxxx
[bug #1111]: https://github.com/ossrs/srs/issues/1111
diff --git a/trunk/auto/apps.sh b/trunk/auto/apps.sh
index 59fc92672..aaccac70d 100755
--- a/trunk/auto/apps.sh
+++ b/trunk/auto/apps.sh
@@ -23,6 +23,8 @@ echo "# build ${APP_TARGET}" >> ${FILE}
# generate the binary depends, for example:
# srs: objs/srs
echo "${BUILD_KEY}: ${APP_TARGET}" >> ${FILE}
+echo "" >> ${FILE}
+
# the link commands, for example:
# objs/srs: objs/src/core/srs_core.o
echo -n "${APP_TARGET}: " >> ${FILE}
@@ -88,5 +90,6 @@ done
# link options.
echo -n "${LINK_OPTIONS}" >> ${FILE}
echo "" >> ${FILE}
+echo "" >> ${FILE}
echo -n "Generate app ${APP_NAME} ok"; echo '!';
diff --git a/trunk/auto/libs.sh b/trunk/auto/libs.sh
index 89f4e8ccd..af1103414 100755
--- a/trunk/auto/libs.sh
+++ b/trunk/auto/libs.sh
@@ -19,6 +19,7 @@ echo "Generating lib ${LIB_NAME} depends."
echo "" >> ${FILE}
echo "# archive library ${LIB_TAGET_STATIC}" >> ${FILE}
echo "${BUILD_KEY}: ${LIB_TAGET_STATIC}" >> ${FILE}
+echo "" >> ${FILE}
# build depends
echo -n "${LIB_TAGET_STATIC}: " >> ${FILE}
diff --git a/trunk/configure b/trunk/configure
index 23119b755..bcffc0dc3 100755
--- a/trunk/configure
+++ b/trunk/configure
@@ -429,13 +429,13 @@ for SRS_MODULE in ${SRS_MODULES[*]}; do
# if export librtmp, donot build the bravo-ingest.
if [ $SRS_EXPORT_LIBRTMP_PROJECT != NO ]; then
cat << END >> ${SRS_WORKDIR}/${SRS_MAKEFILE}
-$SRS_MODULE_NAME: _prepare_dir
+$SRS_MODULE_NAME: _prepare_dir server
@echo "Ingore the $SRS_MODULE_NAME for srs-librtmp"
END
else
cat << END >> ${SRS_WORKDIR}/${SRS_MAKEFILE}
-$SRS_MODULE_NAME: _prepare_dir
+$SRS_MODULE_NAME: _prepare_dir server
@echo "Build the $SRS_MODULE_NAME over SRS"
\$(MAKE) -f ${SRS_OBJS_DIR}/${SRS_MAKEFILE} $SRS_MODULE_NAME
diff --git a/trunk/src/app/srs_app_utility.cpp b/trunk/src/app/srs_app_utility.cpp
index 6ea2ba0f8..9c6343860 100644
--- a/trunk/src/app/srs_app_utility.cpp
+++ b/trunk/src/app/srs_app_utility.cpp
@@ -29,7 +29,6 @@
#include
#include
#include
-#include
#include
#ifdef SRS_OSX
@@ -1158,17 +1157,6 @@ string srs_get_peer_ip(int fd)
return std::string(saddr);
}
-bool srs_is_digit_number(const string& str)
-{
- if (str.empty()) {
- return false;
- }
-
- int v = ::atoi(str.c_str());
- int powv = (int)pow(10, str.length() - 1);
- return v / powv >= 1 && v / powv <= 9;
-}
-
bool srs_is_boolean(const string& str)
{
return str == "true" || str == "false";
diff --git a/trunk/src/app/srs_app_utility.hpp b/trunk/src/app/srs_app_utility.hpp
index e058e0a93..5e4d09afe 100644
--- a/trunk/src/app/srs_app_utility.hpp
+++ b/trunk/src/app/srs_app_utility.hpp
@@ -640,12 +640,6 @@ extern int srs_get_local_port(int fd);
// Where peer ip is the client public ip which connected to server.
extern std::string srs_get_peer_ip(int fd);
-// Whether string is digit number
-// is_digit("1234567890") === true
-// is_digit("0123456789") === false
-// is_digit("1234567890a") === false
-// is_digit("a1234567890") === false
-extern bool srs_is_digit_number(const std::string& str);
// Whether string is boolean
// is_bool("true") == true
// is_bool("false") == true
diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp
index 182235ff9..ad70e2fa2 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 75
+#define VERSION_REVISION 76
// The macros generated by configure script.
#include
diff --git a/trunk/src/protocol/srs_protocol_stream.cpp b/trunk/src/protocol/srs_protocol_stream.cpp
index 53bee1535..dfc24d7e2 100755
--- a/trunk/src/protocol/srs_protocol_stream.cpp
+++ b/trunk/src/protocol/srs_protocol_stream.cpp
@@ -52,14 +52,14 @@ IMergeReadHandler::~IMergeReadHandler()
}
#endif
-SrsFastStream::SrsFastStream()
+SrsFastStream::SrsFastStream(int size)
{
#ifdef SRS_PERF_MERGED_READ
merged_read = false;
_handler = NULL;
#endif
- nb_buffer = SRS_DEFAULT_RECV_BUFFER_SIZE;
+ nb_buffer = size? size:SRS_DEFAULT_RECV_BUFFER_SIZE;
buffer = (char*)malloc(nb_buffer);
p = end = buffer;
}
@@ -84,8 +84,7 @@ void SrsFastStream::set_buffer(int buffer_size)
{
// never exceed the max size.
if (buffer_size > SRS_MAX_SOCKET_BUFFER) {
- srs_warn("limit the user-space buffer from %d to %d",
- buffer_size, SRS_MAX_SOCKET_BUFFER);
+ srs_warn("limit buffer size %d to %d", buffer_size, SRS_MAX_SOCKET_BUFFER);
}
// the user-space buffer size limit to a max value.
@@ -152,7 +151,7 @@ srs_error_t SrsFastStream::grow(ISrsReader* reader, int required_size)
srs_assert(nb_exists_bytes >= 0);
// resize the space when no left space.
- if (nb_free_space < required_size - nb_exists_bytes) {
+ if (nb_exists_bytes + nb_free_space < required_size) {
// reset or move to get more space.
if (!nb_exists_bytes) {
// reset when buffer is empty.
@@ -168,7 +167,7 @@ srs_error_t SrsFastStream::grow(ISrsReader* reader, int required_size)
// check whether enough free space in buffer.
nb_free_space = (int)(buffer + nb_buffer - end);
- if (nb_free_space < required_size - nb_exists_bytes) {
+ if (nb_exists_bytes + nb_free_space < required_size) {
return srs_error_new(ERROR_READER_BUFFER_OVERFLOW, "overflow, required=%d, max=%d, left=%d", required_size, nb_buffer, nb_free_space);
}
}
diff --git a/trunk/src/protocol/srs_protocol_stream.hpp b/trunk/src/protocol/srs_protocol_stream.hpp
index 17d935fdb..247d75aef 100644
--- a/trunk/src/protocol/srs_protocol_stream.hpp
+++ b/trunk/src/protocol/srs_protocol_stream.hpp
@@ -84,7 +84,8 @@ private:
// the size of buffer.
int nb_buffer;
public:
- SrsFastStream();
+ // If buffer is 0, use default size.
+ SrsFastStream(int size=0);
virtual ~SrsFastStream();
public:
/**
@@ -128,7 +129,7 @@ public:
virtual void skip(int size);
public:
/**
- * grow buffer to the required size, loop to read from skt to fill.
+ * grow buffer to atleast required size, loop to read from skt to fill.
* @param reader, read more bytes from reader to fill the buffer to required size.
* @param required_size, loop to fill to ensure buffer size to required.
* @return an int error code, error if required_size negative.
diff --git a/trunk/src/service/srs_service_http_conn.cpp b/trunk/src/service/srs_service_http_conn.cpp
index 3d8d1df06..9503c19d0 100644
--- a/trunk/src/service/srs_service_http_conn.cpp
+++ b/trunk/src/service/srs_service_http_conn.cpp
@@ -40,6 +40,8 @@ SrsHttpParser::SrsHttpParser()
{
buffer = new SrsFastStream();
header = NULL;
+
+ p_body_start = p_header_tail = NULL;
}
SrsHttpParser::~SrsHttpParser()
@@ -80,7 +82,7 @@ srs_error_t SrsHttpParser::parse_message(ISrsReader* reader, ISrsHttpMessage** p
state = SrsHttpParseStateInit;
hp_header = http_parser();
// The body that we have read from cache.
- pbody = NULL;
+ p_body_start = p_header_tail = NULL;
// We must reset the field name and value, because we may get a partial value in on_header_value.
field_name = field_value = "";
// The header of the request.
@@ -115,15 +117,26 @@ srs_error_t SrsHttpParser::parse_message_imp(ISrsReader* reader)
while (true) {
if (buffer->size() > 0) {
- ssize_t nparsed = http_parser_execute(&parser, &settings, buffer->bytes(), buffer->size());
- if (buffer->size() != nparsed) {
- return srs_error_new(ERROR_HTTP_PARSE_HEADER, "parse failed, nparsed=%d, size=%d", nparsed, buffer->size());
+ ssize_t consumed = http_parser_execute(&parser, &settings, buffer->bytes(), buffer->size());
+
+ // The error is set in http_errno.
+ enum http_errno code;
+ if ((code = HTTP_PARSER_ERRNO(&parser)) != HPE_OK) {
+ return srs_error_new(ERROR_HTTP_PARSE_HEADER, "parse %dB, nparsed=%d, err=%d/%s %s",
+ buffer->size(), consumed, http_errno_name(code), http_errno_description(code));
}
- // The consumed size, does not include the body.
- ssize_t consumed = nparsed;
- if (pbody && buffer->bytes() < pbody) {
- consumed = pbody - buffer->bytes();
+ // When buffer consumed these bytes, it's dropped so the new ptr is actually the HTTP body. But http-parser
+ // doesn't indicate the specific sizeof header, so we must finger it out.
+ // @remark We shouldn't use on_body, because it only works for normal case, and losts the chunk header and length.
+ // @see https://github.com/ossrs/srs/issues/1508
+ if (p_header_tail && buffer->bytes() < p_body_start) {
+ for (const char* p = p_header_tail; p <= p_body_start - 4; p++) {
+ if (p[0] == SRS_CONSTS_CR && p[1] == SRS_CONSTS_LF && p[2] == SRS_CONSTS_CR && p[3] == SRS_CONSTS_LF) {
+ consumed = p + 4 - buffer->bytes();
+ break;
+ }
+ }
}
srs_info("size=%d, nparsed=%d, consumed=%d", buffer->size(), (int)nparsed, consumed);
@@ -199,6 +212,11 @@ int SrsHttpParser::on_url(http_parser* parser, const char* at, size_t length)
if (length > 0) {
obj->url = string(at, (int)length);
}
+
+ // When header parsed, we must save the position of start for body,
+ // because we have to consume the header in buffer.
+ // @see https://github.com/ossrs/srs/issues/1508
+ obj->p_header_tail = at;
srs_info("Method: %d, Url: %.*s", parser->method, (int)length, at);
@@ -218,6 +236,11 @@ int SrsHttpParser::on_header_field(http_parser* parser, const char* at, size_t l
if (length > 0) {
obj->field_name.append(at, (int)length);
}
+
+ // When header parsed, we must save the position of start for body,
+ // because we have to consume the header in buffer.
+ // @see https://github.com/ossrs/srs/issues/1508
+ obj->p_header_tail = at;
srs_info("Header field(%d bytes): %.*s", (int)length, (int)length, at);
return 0;
@@ -231,6 +254,11 @@ int SrsHttpParser::on_header_value(http_parser* parser, const char* at, size_t l
if (length > 0) {
obj->field_value.append(at, (int)length);
}
+
+ // When header parsed, we must save the position of start for body,
+ // because we have to consume the header in buffer.
+ // @see https://github.com/ossrs/srs/issues/1508
+ obj->p_header_tail = at;
srs_info("Header value(%d bytes): %.*s", (int)length, (int)length, at);
return 0;
@@ -244,9 +272,10 @@ int SrsHttpParser::on_body(http_parser* parser, const char* at, size_t length)
// save the parser when body parsed.
obj->state = SrsHttpParseStateBody;
- // Save the body position.
- obj->pbody = at;
-
+ // Used to discover the header length.
+ // @see https://github.com/ossrs/srs/issues/1508
+ obj->p_body_start = at;
+
srs_info("Body: %.*s", (int)length, at);
return 0;
@@ -962,11 +991,15 @@ srs_error_t SrsHttpResponseReader::read_chunked(char* data, int nb_data, int* nb
// it's ok to set the pos and pos+1 to NULL.
at[length - 1] = 0;
at[length - 2] = 0;
-
+
// size is the bytes size, excludes the chunk header and end CRLF.
- int ilength = (int)::strtol(at, NULL, 16);
- if (ilength < 0) {
- return srs_error_new(ERROR_HTTP_INVALID_CHUNK_HEADER, "invalid length=%d", ilength);
+ // @remark It must be hex format, please read https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding#Directives
+ // @remark For strtol, note that: If no conversion could be performed, 0 is returned and the global variable errno is set to EINVAL.
+ char* at_parsed = at; errno = 0;
+ int ilength = (int)::strtol(at, &at_parsed, 16);
+ if (ilength < 0 || errno != 0 || at_parsed - at != length - 2) {
+ return srs_error_new(ERROR_HTTP_INVALID_CHUNK_HEADER, "invalid length %s as %d, parsed=%.*s, errno=%d",
+ at, ilength, (int)(at_parsed-at), at, errno);
}
// all bytes in chunk is left now.
diff --git a/trunk/src/service/srs_service_http_conn.hpp b/trunk/src/service/srs_service_http_conn.hpp
index 997e26b66..59fb50fb7 100644
--- a/trunk/src/service/srs_service_http_conn.hpp
+++ b/trunk/src/service/srs_service_http_conn.hpp
@@ -55,7 +55,11 @@ private:
http_parser hp_header;
std::string url;
SrsHttpHeader* header;
- const char* pbody;
+private:
+ // Point to the start of body.
+ const char* p_body_start;
+ // To discover the length of header, point to the last few bytes in header.
+ const char* p_header_tail;
public:
SrsHttpParser();
virtual ~SrsHttpParser();
diff --git a/trunk/src/service/srs_service_utility.cpp b/trunk/src/service/srs_service_utility.cpp
index 434cd9d4d..e6312d272 100644
--- a/trunk/src/service/srs_service_utility.cpp
+++ b/trunk/src/service/srs_service_utility.cpp
@@ -28,6 +28,8 @@
#include
#include
#include
+#include
+#include
#include