mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
Merge branch '3.0release' into develop
This commit is contained in:
commit
fee58a8acb
16 changed files with 381 additions and 57 deletions
|
@ -147,6 +147,8 @@ For previous versions, please read:
|
||||||
|
|
||||||
## V3 changes
|
## 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.
|
||||||
* <strong>v3.0, 2019-12-19, [3.0 alpha5(3.0.75)][r3.0a5] released. 115362 lines.</strong>
|
* <strong>v3.0, 2019-12-19, [3.0 alpha5(3.0.75)][r3.0a5] released. 115362 lines.</strong>
|
||||||
* v3.0, 2019-12-19, Refine the RTMP iovs cache increasing to much faster.
|
* 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
|
* 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 #1506]: https://github.com/ossrs/srs/issues/1506
|
||||||
[bug #1520]: https://github.com/ossrs/srs/issues/1520
|
[bug #1520]: https://github.com/ossrs/srs/issues/1520
|
||||||
[bug #1223]: https://github.com/ossrs/srs/issues/1223
|
[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 #xxxxxxxxxxxxx]: https://github.com/ossrs/srs/issues/xxxxxxxxxxxxx
|
||||||
|
|
||||||
[bug #1111]: https://github.com/ossrs/srs/issues/1111
|
[bug #1111]: https://github.com/ossrs/srs/issues/1111
|
||||||
|
|
|
@ -23,6 +23,8 @@ echo "# build ${APP_TARGET}" >> ${FILE}
|
||||||
# generate the binary depends, for example:
|
# generate the binary depends, for example:
|
||||||
# srs: objs/srs
|
# srs: objs/srs
|
||||||
echo "${BUILD_KEY}: ${APP_TARGET}" >> ${FILE}
|
echo "${BUILD_KEY}: ${APP_TARGET}" >> ${FILE}
|
||||||
|
echo "" >> ${FILE}
|
||||||
|
|
||||||
# the link commands, for example:
|
# the link commands, for example:
|
||||||
# objs/srs: objs/src/core/srs_core.o
|
# objs/srs: objs/src/core/srs_core.o
|
||||||
echo -n "${APP_TARGET}: " >> ${FILE}
|
echo -n "${APP_TARGET}: " >> ${FILE}
|
||||||
|
@ -88,5 +90,6 @@ done
|
||||||
# link options.
|
# link options.
|
||||||
echo -n "${LINK_OPTIONS}" >> ${FILE}
|
echo -n "${LINK_OPTIONS}" >> ${FILE}
|
||||||
echo "" >> ${FILE}
|
echo "" >> ${FILE}
|
||||||
|
echo "" >> ${FILE}
|
||||||
|
|
||||||
echo -n "Generate app ${APP_NAME} ok"; echo '!';
|
echo -n "Generate app ${APP_NAME} ok"; echo '!';
|
||||||
|
|
|
@ -19,6 +19,7 @@ echo "Generating lib ${LIB_NAME} depends."
|
||||||
echo "" >> ${FILE}
|
echo "" >> ${FILE}
|
||||||
echo "# archive library ${LIB_TAGET_STATIC}" >> ${FILE}
|
echo "# archive library ${LIB_TAGET_STATIC}" >> ${FILE}
|
||||||
echo "${BUILD_KEY}: ${LIB_TAGET_STATIC}" >> ${FILE}
|
echo "${BUILD_KEY}: ${LIB_TAGET_STATIC}" >> ${FILE}
|
||||||
|
echo "" >> ${FILE}
|
||||||
|
|
||||||
# build depends
|
# build depends
|
||||||
echo -n "${LIB_TAGET_STATIC}: " >> ${FILE}
|
echo -n "${LIB_TAGET_STATIC}: " >> ${FILE}
|
||||||
|
|
4
trunk/configure
vendored
4
trunk/configure
vendored
|
@ -429,13 +429,13 @@ for SRS_MODULE in ${SRS_MODULES[*]}; do
|
||||||
# if export librtmp, donot build the bravo-ingest.
|
# if export librtmp, donot build the bravo-ingest.
|
||||||
if [ $SRS_EXPORT_LIBRTMP_PROJECT != NO ]; then
|
if [ $SRS_EXPORT_LIBRTMP_PROJECT != NO ]; then
|
||||||
cat << END >> ${SRS_WORKDIR}/${SRS_MAKEFILE}
|
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"
|
@echo "Ingore the $SRS_MODULE_NAME for srs-librtmp"
|
||||||
|
|
||||||
END
|
END
|
||||||
else
|
else
|
||||||
cat << END >> ${SRS_WORKDIR}/${SRS_MAKEFILE}
|
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"
|
@echo "Build the $SRS_MODULE_NAME over SRS"
|
||||||
\$(MAKE) -f ${SRS_OBJS_DIR}/${SRS_MAKEFILE} $SRS_MODULE_NAME
|
\$(MAKE) -f ${SRS_OBJS_DIR}/${SRS_MAKEFILE} $SRS_MODULE_NAME
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,6 @@
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <math.h>
|
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
|
|
||||||
#ifdef SRS_OSX
|
#ifdef SRS_OSX
|
||||||
|
@ -1158,17 +1157,6 @@ string srs_get_peer_ip(int fd)
|
||||||
return std::string(saddr);
|
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)
|
bool srs_is_boolean(const string& str)
|
||||||
{
|
{
|
||||||
return str == "true" || str == "false";
|
return str == "true" || str == "false";
|
||||||
|
|
|
@ -640,12 +640,6 @@ extern int srs_get_local_port(int fd);
|
||||||
// Where peer ip is the client public ip which connected to server.
|
// Where peer ip is the client public ip which connected to server.
|
||||||
extern std::string srs_get_peer_ip(int fd);
|
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
|
// Whether string is boolean
|
||||||
// is_bool("true") == true
|
// is_bool("true") == true
|
||||||
// is_bool("false") == true
|
// is_bool("false") == true
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
// The version config.
|
// The version config.
|
||||||
#define VERSION_MAJOR 3
|
#define VERSION_MAJOR 3
|
||||||
#define VERSION_MINOR 0
|
#define VERSION_MINOR 0
|
||||||
#define VERSION_REVISION 75
|
#define VERSION_REVISION 76
|
||||||
|
|
||||||
// The macros generated by configure script.
|
// The macros generated by configure script.
|
||||||
#include <srs_auto_headers.hpp>
|
#include <srs_auto_headers.hpp>
|
||||||
|
|
|
@ -52,14 +52,14 @@ IMergeReadHandler::~IMergeReadHandler()
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SrsFastStream::SrsFastStream()
|
SrsFastStream::SrsFastStream(int size)
|
||||||
{
|
{
|
||||||
#ifdef SRS_PERF_MERGED_READ
|
#ifdef SRS_PERF_MERGED_READ
|
||||||
merged_read = false;
|
merged_read = false;
|
||||||
_handler = NULL;
|
_handler = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
nb_buffer = SRS_DEFAULT_RECV_BUFFER_SIZE;
|
nb_buffer = size? size:SRS_DEFAULT_RECV_BUFFER_SIZE;
|
||||||
buffer = (char*)malloc(nb_buffer);
|
buffer = (char*)malloc(nb_buffer);
|
||||||
p = end = buffer;
|
p = end = buffer;
|
||||||
}
|
}
|
||||||
|
@ -84,8 +84,7 @@ void SrsFastStream::set_buffer(int buffer_size)
|
||||||
{
|
{
|
||||||
// never exceed the max size.
|
// never exceed the max size.
|
||||||
if (buffer_size > SRS_MAX_SOCKET_BUFFER) {
|
if (buffer_size > SRS_MAX_SOCKET_BUFFER) {
|
||||||
srs_warn("limit the user-space buffer from %d to %d",
|
srs_warn("limit buffer size %d to %d", buffer_size, SRS_MAX_SOCKET_BUFFER);
|
||||||
buffer_size, SRS_MAX_SOCKET_BUFFER);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// the user-space buffer size limit to a max value.
|
// 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);
|
srs_assert(nb_exists_bytes >= 0);
|
||||||
|
|
||||||
// resize the space when no left space.
|
// 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.
|
// reset or move to get more space.
|
||||||
if (!nb_exists_bytes) {
|
if (!nb_exists_bytes) {
|
||||||
// reset when buffer is empty.
|
// 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.
|
// check whether enough free space in buffer.
|
||||||
nb_free_space = (int)(buffer + nb_buffer - end);
|
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);
|
return srs_error_new(ERROR_READER_BUFFER_OVERFLOW, "overflow, required=%d, max=%d, left=%d", required_size, nb_buffer, nb_free_space);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,8 @@ private:
|
||||||
// the size of buffer.
|
// the size of buffer.
|
||||||
int nb_buffer;
|
int nb_buffer;
|
||||||
public:
|
public:
|
||||||
SrsFastStream();
|
// If buffer is 0, use default size.
|
||||||
|
SrsFastStream(int size=0);
|
||||||
virtual ~SrsFastStream();
|
virtual ~SrsFastStream();
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
|
@ -128,7 +129,7 @@ public:
|
||||||
virtual void skip(int size);
|
virtual void skip(int size);
|
||||||
public:
|
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 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.
|
* @param required_size, loop to fill to ensure buffer size to required.
|
||||||
* @return an int error code, error if required_size negative.
|
* @return an int error code, error if required_size negative.
|
||||||
|
|
|
@ -40,6 +40,8 @@ SrsHttpParser::SrsHttpParser()
|
||||||
{
|
{
|
||||||
buffer = new SrsFastStream();
|
buffer = new SrsFastStream();
|
||||||
header = NULL;
|
header = NULL;
|
||||||
|
|
||||||
|
p_body_start = p_header_tail = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsHttpParser::~SrsHttpParser()
|
SrsHttpParser::~SrsHttpParser()
|
||||||
|
@ -80,7 +82,7 @@ srs_error_t SrsHttpParser::parse_message(ISrsReader* reader, ISrsHttpMessage** p
|
||||||
state = SrsHttpParseStateInit;
|
state = SrsHttpParseStateInit;
|
||||||
hp_header = http_parser();
|
hp_header = http_parser();
|
||||||
// The body that we have read from cache.
|
// 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.
|
// We must reset the field name and value, because we may get a partial value in on_header_value.
|
||||||
field_name = field_value = "";
|
field_name = field_value = "";
|
||||||
// The header of the request.
|
// The header of the request.
|
||||||
|
@ -115,15 +117,26 @@ srs_error_t SrsHttpParser::parse_message_imp(ISrsReader* reader)
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (buffer->size() > 0) {
|
if (buffer->size() > 0) {
|
||||||
ssize_t nparsed = http_parser_execute(&parser, &settings, buffer->bytes(), buffer->size());
|
ssize_t consumed = 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());
|
// 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.
|
// When buffer consumed these bytes, it's dropped so the new ptr is actually the HTTP body. But http-parser
|
||||||
ssize_t consumed = nparsed;
|
// doesn't indicate the specific sizeof header, so we must finger it out.
|
||||||
if (pbody && buffer->bytes() < pbody) {
|
// @remark We shouldn't use on_body, because it only works for normal case, and losts the chunk header and length.
|
||||||
consumed = pbody - buffer->bytes();
|
// @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);
|
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) {
|
if (length > 0) {
|
||||||
obj->url = string(at, (int)length);
|
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);
|
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) {
|
if (length > 0) {
|
||||||
obj->field_name.append(at, (int)length);
|
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);
|
srs_info("Header field(%d bytes): %.*s", (int)length, (int)length, at);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -231,6 +254,11 @@ int SrsHttpParser::on_header_value(http_parser* parser, const char* at, size_t l
|
||||||
if (length > 0) {
|
if (length > 0) {
|
||||||
obj->field_value.append(at, (int)length);
|
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);
|
srs_info("Header value(%d bytes): %.*s", (int)length, (int)length, at);
|
||||||
return 0;
|
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.
|
// save the parser when body parsed.
|
||||||
obj->state = SrsHttpParseStateBody;
|
obj->state = SrsHttpParseStateBody;
|
||||||
|
|
||||||
// Save the body position.
|
// Used to discover the header length.
|
||||||
obj->pbody = at;
|
// @see https://github.com/ossrs/srs/issues/1508
|
||||||
|
obj->p_body_start = at;
|
||||||
|
|
||||||
srs_info("Body: %.*s", (int)length, at);
|
srs_info("Body: %.*s", (int)length, at);
|
||||||
|
|
||||||
return 0;
|
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.
|
// it's ok to set the pos and pos+1 to NULL.
|
||||||
at[length - 1] = 0;
|
at[length - 1] = 0;
|
||||||
at[length - 2] = 0;
|
at[length - 2] = 0;
|
||||||
|
|
||||||
// size is the bytes size, excludes the chunk header and end CRLF.
|
// size is the bytes size, excludes the chunk header and end CRLF.
|
||||||
int ilength = (int)::strtol(at, NULL, 16);
|
// @remark It must be hex format, please read https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding#Directives
|
||||||
if (ilength < 0) {
|
// @remark For strtol, note that: If no conversion could be performed, 0 is returned and the global variable errno is set to EINVAL.
|
||||||
return srs_error_new(ERROR_HTTP_INVALID_CHUNK_HEADER, "invalid length=%d", ilength);
|
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.
|
// all bytes in chunk is left now.
|
||||||
|
|
|
@ -55,7 +55,11 @@ private:
|
||||||
http_parser hp_header;
|
http_parser hp_header;
|
||||||
std::string url;
|
std::string url;
|
||||||
SrsHttpHeader* header;
|
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:
|
public:
|
||||||
SrsHttpParser();
|
SrsHttpParser();
|
||||||
virtual ~SrsHttpParser();
|
virtual ~SrsHttpParser();
|
||||||
|
|
|
@ -28,6 +28,8 @@
|
||||||
#include <net/if.h>
|
#include <net/if.h>
|
||||||
#include <ifaddrs.h>
|
#include <ifaddrs.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
@ -48,6 +50,28 @@ bool srs_string_is_rtmp(string url)
|
||||||
return srs_string_starts_with(url, "rtmp://");
|
return srs_string_starts_with(url, "rtmp://");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool srs_is_digit_number(const string& str)
|
||||||
|
{
|
||||||
|
if (str.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* p = str.c_str();
|
||||||
|
const char* p_end = str.data() + str.length();
|
||||||
|
for (; p < p_end; p++) {
|
||||||
|
if (*p != '0') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (p == p_end) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t v = ::atoll(p);
|
||||||
|
int64_t powv = (int64_t)pow(10, p_end - p - 1);
|
||||||
|
return v / powv >= 1 && v / powv <= 9;
|
||||||
|
}
|
||||||
|
|
||||||
// we detect all network device as internet or intranet device, by its ip address.
|
// we detect all network device as internet or intranet device, by its ip address.
|
||||||
// key is device name, for instance, eth0
|
// key is device name, for instance, eth0
|
||||||
// value is whether internet, for instance, true.
|
// value is whether internet, for instance, true.
|
||||||
|
|
|
@ -36,6 +36,18 @@
|
||||||
extern bool srs_string_is_http(std::string url);
|
extern bool srs_string_is_http(std::string url);
|
||||||
extern bool srs_string_is_rtmp(std::string url);
|
extern bool srs_string_is_rtmp(std::string url);
|
||||||
|
|
||||||
|
// Whether string is digit number
|
||||||
|
// is_digit("0") === true
|
||||||
|
// is_digit("0000000000") === true
|
||||||
|
// is_digit("1234567890") === true
|
||||||
|
// is_digit("0123456789") === true
|
||||||
|
// is_digit("1234567890a") === false
|
||||||
|
// is_digit("a1234567890") === false
|
||||||
|
// is_digit("10e3") === false
|
||||||
|
// is_digit("!1234567890") === false
|
||||||
|
// is_digit("") === false
|
||||||
|
extern bool srs_is_digit_number(const std::string& str);
|
||||||
|
|
||||||
// Get local ip, fill to @param ips
|
// Get local ip, fill to @param ips
|
||||||
extern std::vector<std::string>& srs_get_local_ips();
|
extern std::vector<std::string>& srs_get_local_ips();
|
||||||
|
|
||||||
|
|
|
@ -251,6 +251,26 @@ VOID TEST(ProtocolHTTPTest, ResponseWriter)
|
||||||
|
|
||||||
__MOCK_HTTP_EXPECT_STREQ2(200, "5\r\nHello\r\n8\r\n, world!\r\n0\r\n\r\n", w);
|
__MOCK_HTTP_EXPECT_STREQ2(200, "5\r\nHello\r\n8\r\n, world!\r\n0\r\n\r\n", w);
|
||||||
}
|
}
|
||||||
|
if (true) {
|
||||||
|
MockResponseWriter w;
|
||||||
|
|
||||||
|
w.header()->set_content_type("application/octet-stream");
|
||||||
|
w.write_header(SRS_CONSTS_HTTP_OK);
|
||||||
|
w.write((char*)"Hello, world!", 13);
|
||||||
|
w.final_request();
|
||||||
|
|
||||||
|
__MOCK_HTTP_EXPECT_STREQ2(200, "d\r\nHello, world!\r\n0\r\n\r\n", w);
|
||||||
|
}
|
||||||
|
if (true) {
|
||||||
|
MockResponseWriter w;
|
||||||
|
|
||||||
|
w.header()->set_content_type("application/octet-stream");
|
||||||
|
w.write_header(SRS_CONSTS_HTTP_OK);
|
||||||
|
w.write((char*)"Hello, world!", 13);
|
||||||
|
w.final_request();
|
||||||
|
|
||||||
|
__MOCK_HTTP_EXPECT_STREQ2(200, "d\r\nHello, world!\r\n0\r\n\r\n", w);
|
||||||
|
}
|
||||||
|
|
||||||
// If directly write empty string, sent an empty response with content-length 0
|
// If directly write empty string, sent an empty response with content-length 0
|
||||||
if (true) {
|
if (true) {
|
||||||
|
@ -267,6 +287,42 @@ VOID TEST(ProtocolHTTPTest, ResponseWriter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VOID TEST(ProtocolHTTPTest, ClientRequest)
|
||||||
|
{
|
||||||
|
srs_error_t err;
|
||||||
|
|
||||||
|
// Normal case, with chunked encoding.
|
||||||
|
if (true) {
|
||||||
|
MockBufferIO io; io.append(mock_http_response2(200, "0d\r\nHello, world!\r\n0\r\n\r\n"));
|
||||||
|
SrsHttpParser hp; HELPER_ASSERT_SUCCESS(hp.initialize(HTTP_RESPONSE, false));
|
||||||
|
ISrsHttpMessage* msg = NULL; HELPER_ASSERT_SUCCESS(hp.parse_message(&io, &msg));
|
||||||
|
string res; HELPER_ASSERT_SUCCESS(msg->body_read_all(res));
|
||||||
|
EXPECT_EQ(200, msg->status_code());
|
||||||
|
EXPECT_STREQ("Hello, world!", res.c_str());
|
||||||
|
srs_freep(msg);
|
||||||
|
}
|
||||||
|
if (true) {
|
||||||
|
MockBufferIO io; io.append(mock_http_response2(200, "6\r\nHello!\r\n0\r\n\r\n"));
|
||||||
|
SrsHttpParser hp; HELPER_ASSERT_SUCCESS(hp.initialize(HTTP_RESPONSE, false));
|
||||||
|
ISrsHttpMessage* msg = NULL; HELPER_ASSERT_SUCCESS(hp.parse_message(&io, &msg));
|
||||||
|
string res; HELPER_ASSERT_SUCCESS(msg->body_read_all(res));
|
||||||
|
EXPECT_EQ(200, msg->status_code());
|
||||||
|
EXPECT_STREQ("Hello!", res.c_str());
|
||||||
|
srs_freep(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normal case, with specified content-length.
|
||||||
|
if (true) {
|
||||||
|
MockBufferIO io; io.append(mock_http_response(200, "Hello, world!"));
|
||||||
|
SrsHttpParser hp; HELPER_ASSERT_SUCCESS(hp.initialize(HTTP_RESPONSE, false));
|
||||||
|
ISrsHttpMessage* msg = NULL; HELPER_ASSERT_SUCCESS(hp.parse_message(&io, &msg));
|
||||||
|
string res; HELPER_ASSERT_SUCCESS(msg->body_read_all(res));
|
||||||
|
EXPECT_EQ(200, msg->status_code());
|
||||||
|
EXPECT_STREQ("Hello, world!", res.c_str());
|
||||||
|
srs_freep(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
VOID TEST(ProtocolHTTPTest, ResponseHTTPError)
|
VOID TEST(ProtocolHTTPTest, ResponseHTTPError)
|
||||||
{
|
{
|
||||||
srs_error_t err;
|
srs_error_t err;
|
||||||
|
|
|
@ -271,8 +271,12 @@ MockBufferReader::~MockBufferReader()
|
||||||
srs_error_t MockBufferReader::read(void* buf, size_t size, ssize_t* nread)
|
srs_error_t MockBufferReader::read(void* buf, size_t size, ssize_t* nread)
|
||||||
{
|
{
|
||||||
int len = srs_min(str.length(), size);
|
int len = srs_min(str.length(), size);
|
||||||
|
if (len == 0) {
|
||||||
|
return srs_error_new(-1, "no data");
|
||||||
|
}
|
||||||
|
|
||||||
memcpy(buf, str.data(), len);
|
memcpy(buf, str.data(), len);
|
||||||
|
str = str.substr(len);
|
||||||
|
|
||||||
if (nread) {
|
if (nread) {
|
||||||
*nread = len;
|
*nread = len;
|
||||||
|
@ -388,19 +392,73 @@ VOID TEST(KernelBufferTest, EraseBytes)
|
||||||
|
|
||||||
VOID TEST(KernelFastBufferTest, Grow)
|
VOID TEST(KernelFastBufferTest, Grow)
|
||||||
{
|
{
|
||||||
SrsFastStream b;
|
srs_error_t err;
|
||||||
MockBufferReader r("winlin");
|
|
||||||
|
|
||||||
b.grow(&r, 1);
|
|
||||||
EXPECT_EQ('w', b.read_1byte());
|
|
||||||
|
|
||||||
b.grow(&r, 3);
|
if(true) {
|
||||||
b.skip(1);
|
SrsFastStream b(5);
|
||||||
EXPECT_EQ('n', b.read_1byte());
|
MockBufferReader r("Hello, world!");
|
||||||
|
|
||||||
b.grow(&r, 100);
|
HELPER_ASSERT_SUCCESS(b.grow(&r, 5));
|
||||||
b.skip(99);
|
b.skip(2);
|
||||||
EXPECT_EQ('w', b.read_1byte());
|
|
||||||
|
HELPER_ASSERT_FAILED(b.grow(&r, 6));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(true) {
|
||||||
|
SrsFastStream b(5);
|
||||||
|
MockBufferReader r("Hello, world!");
|
||||||
|
|
||||||
|
HELPER_ASSERT_SUCCESS(b.grow(&r, 5));
|
||||||
|
b.skip(5);
|
||||||
|
|
||||||
|
HELPER_ASSERT_FAILED(b.grow(&r, 6));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(true) {
|
||||||
|
SrsFastStream b(6);
|
||||||
|
MockBufferReader r("Hello, world!");
|
||||||
|
|
||||||
|
HELPER_ASSERT_SUCCESS(b.grow(&r, 5));
|
||||||
|
EXPECT_EQ('H', b.read_1byte()); EXPECT_EQ('e', b.read_1byte()); EXPECT_EQ('l', b.read_1byte());
|
||||||
|
b.skip(2);
|
||||||
|
|
||||||
|
HELPER_ASSERT_SUCCESS(b.grow(&r, 2));
|
||||||
|
b.skip(2);
|
||||||
|
|
||||||
|
HELPER_ASSERT_SUCCESS(b.grow(&r, 5));
|
||||||
|
EXPECT_EQ('w', b.read_1byte()); EXPECT_EQ('o', b.read_1byte()); EXPECT_EQ('r', b.read_1byte());
|
||||||
|
b.skip(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(true) {
|
||||||
|
SrsFastStream b(5);
|
||||||
|
MockBufferReader r("Hello, world!");
|
||||||
|
|
||||||
|
HELPER_ASSERT_SUCCESS(b.grow(&r, 5));
|
||||||
|
EXPECT_EQ('H', b.read_1byte()); EXPECT_EQ('e', b.read_1byte()); EXPECT_EQ('l', b.read_1byte());
|
||||||
|
b.skip(2);
|
||||||
|
|
||||||
|
HELPER_ASSERT_SUCCESS(b.grow(&r, 2));
|
||||||
|
b.skip(2);
|
||||||
|
|
||||||
|
HELPER_ASSERT_SUCCESS(b.grow(&r, 5));
|
||||||
|
EXPECT_EQ('w', b.read_1byte()); EXPECT_EQ('o', b.read_1byte()); EXPECT_EQ('r', b.read_1byte());
|
||||||
|
b.skip(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
SrsFastStream b;
|
||||||
|
MockBufferReader r("winlin");
|
||||||
|
|
||||||
|
HELPER_ASSERT_SUCCESS(b.grow(&r, 1));
|
||||||
|
EXPECT_EQ('w', b.read_1byte());
|
||||||
|
|
||||||
|
HELPER_ASSERT_SUCCESS(b.grow(&r, 3));
|
||||||
|
b.skip(1);
|
||||||
|
EXPECT_EQ('n', b.read_1byte());
|
||||||
|
|
||||||
|
HELPER_ASSERT_FAILED(b.grow(&r, 100));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -27,6 +27,7 @@ using namespace std;
|
||||||
#include <srs_kernel_error.hpp>
|
#include <srs_kernel_error.hpp>
|
||||||
#include <srs_app_listener.hpp>
|
#include <srs_app_listener.hpp>
|
||||||
#include <srs_service_st.hpp>
|
#include <srs_service_st.hpp>
|
||||||
|
#include <srs_service_utility.hpp>
|
||||||
|
|
||||||
// Disable coroutine test for OSX.
|
// Disable coroutine test for OSX.
|
||||||
#if !defined(SRS_OSX)
|
#if !defined(SRS_OSX)
|
||||||
|
@ -225,6 +226,153 @@ VOID TEST(TCPServerTest, PingPongWithTimeout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VOID TEST(TCPServerTest, StringIsDigital)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(0, ::atoi("0"));
|
||||||
|
EXPECT_EQ(0, ::atoi("0000000000"));
|
||||||
|
EXPECT_EQ(1, ::atoi("01"));
|
||||||
|
EXPECT_EQ(12, ::atoi("012"));
|
||||||
|
EXPECT_EQ(1234567890L, ::atol("1234567890"));
|
||||||
|
EXPECT_EQ(123456789L, ::atol("0123456789"));
|
||||||
|
EXPECT_EQ(1234567890, ::atoi("1234567890a"));
|
||||||
|
EXPECT_EQ(10, ::atoi("10e3"));
|
||||||
|
EXPECT_EQ(0, ::atoi("!1234567890"));
|
||||||
|
EXPECT_EQ(0, ::atoi(""));
|
||||||
|
|
||||||
|
EXPECT_TRUE(srs_is_digit_number("0"));
|
||||||
|
EXPECT_TRUE(srs_is_digit_number("0000000000"));
|
||||||
|
EXPECT_TRUE(srs_is_digit_number("1234567890"));
|
||||||
|
EXPECT_TRUE(srs_is_digit_number("0123456789"));
|
||||||
|
EXPECT_FALSE(srs_is_digit_number("1234567890a"));
|
||||||
|
EXPECT_FALSE(srs_is_digit_number("a1234567890"));
|
||||||
|
EXPECT_FALSE(srs_is_digit_number("10e3"));
|
||||||
|
EXPECT_FALSE(srs_is_digit_number("!1234567890"));
|
||||||
|
EXPECT_FALSE(srs_is_digit_number(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
VOID TEST(TCPServerTest, StringIsHex)
|
||||||
|
{
|
||||||
|
if (true) {
|
||||||
|
char* str = (char*)"0";
|
||||||
|
char* parsed = str; errno = 0;
|
||||||
|
EXPECT_EQ(0x0, ::strtol(str, &parsed, 16));
|
||||||
|
EXPECT_EQ(0, errno);
|
||||||
|
EXPECT_EQ(str + 1, parsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
char* str = (char*)"0";
|
||||||
|
char* parsed = str; errno = 0;
|
||||||
|
EXPECT_EQ(0x0, ::strtol(str, &parsed, 16));
|
||||||
|
EXPECT_EQ(0, errno);
|
||||||
|
EXPECT_EQ(str + 1, parsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
char* str = (char*)"0000000000";
|
||||||
|
char* parsed = str; errno = 0;
|
||||||
|
EXPECT_EQ(0x0, ::strtol(str, &parsed, 16));
|
||||||
|
EXPECT_EQ(0, errno);
|
||||||
|
EXPECT_EQ(str + 10, parsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
char* str = (char*)"01";
|
||||||
|
char* parsed = str; errno = 0;
|
||||||
|
EXPECT_EQ(0x1, ::strtol(str, &parsed, 16));
|
||||||
|
EXPECT_EQ(0, errno);
|
||||||
|
EXPECT_EQ(str + 2, parsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
char* str = (char*)"012";
|
||||||
|
char* parsed = str; errno = 0;
|
||||||
|
EXPECT_EQ(0x12, ::strtol(str, &parsed, 16));
|
||||||
|
EXPECT_EQ(0, errno);
|
||||||
|
EXPECT_EQ(str + 3, parsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
char* str = (char*)"1234567890";
|
||||||
|
char* parsed = str; errno = 0;
|
||||||
|
EXPECT_EQ(0x1234567890L, ::strtol(str, &parsed, 16));
|
||||||
|
EXPECT_EQ(0, errno);
|
||||||
|
EXPECT_EQ(str + 10, parsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
char* str = (char*)"0123456789";
|
||||||
|
char* parsed = str; errno = 0;
|
||||||
|
EXPECT_EQ(0x123456789L, ::strtol(str, &parsed, 16));
|
||||||
|
EXPECT_EQ(0, errno);
|
||||||
|
EXPECT_EQ(str + 10, parsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
char* str = (char*)"1234567890a";
|
||||||
|
char* parsed = str; errno = 0;
|
||||||
|
EXPECT_EQ(0x1234567890a, ::strtol(str, &parsed, 16));
|
||||||
|
EXPECT_EQ(0, errno);
|
||||||
|
EXPECT_EQ(str + 11, parsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
char* str = (char*)"0x1234567890a";
|
||||||
|
char* parsed = str; errno = 0;
|
||||||
|
EXPECT_EQ(0x1234567890a, ::strtol(str, &parsed, 16));
|
||||||
|
EXPECT_EQ(0, errno);
|
||||||
|
EXPECT_EQ(str + 13, parsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
char* str = (char*)"1234567890f";
|
||||||
|
char* parsed = str; errno = 0;
|
||||||
|
EXPECT_EQ(0x1234567890f, ::strtol(str, &parsed, 16));
|
||||||
|
EXPECT_EQ(0, errno);
|
||||||
|
EXPECT_EQ(str + 11, parsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
char* str = (char*)"10e3";
|
||||||
|
char* parsed = str; errno = 0;
|
||||||
|
EXPECT_EQ(0x10e3, ::strtol(str, &parsed, 16));
|
||||||
|
EXPECT_EQ(0, errno);
|
||||||
|
EXPECT_EQ(str + 4, parsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
char* str = (char*)"!1234567890";
|
||||||
|
char* parsed = str; errno = 0;
|
||||||
|
EXPECT_EQ(0x0, ::strtol(str, &parsed, 16));
|
||||||
|
EXPECT_EQ(0, errno);
|
||||||
|
EXPECT_EQ(str, parsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
char* str = (char*)"1234567890g";
|
||||||
|
char* parsed = str; errno = 0;
|
||||||
|
EXPECT_EQ(0x1234567890, ::strtol(str, &parsed, 16));
|
||||||
|
EXPECT_EQ(0, errno);
|
||||||
|
EXPECT_EQ(str + 10, parsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
char* str = (char*)"";
|
||||||
|
char* parsed = str; errno = 0;
|
||||||
|
EXPECT_EQ(0x0, ::strtol(str, &parsed, 16));
|
||||||
|
EXPECT_EQ(0, errno);
|
||||||
|
EXPECT_EQ(str, parsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
char* str = (char*)"1fffffffffffffffffffffffffffff";
|
||||||
|
char* parsed = str; errno = 0;
|
||||||
|
EXPECT_EQ(0x7fffffffffffffff, ::strtol(str, &parsed, 16));
|
||||||
|
EXPECT_NE(0, errno);
|
||||||
|
EXPECT_EQ(str+30, parsed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
VOID TEST(TCPServerTest, WritevIOVC)
|
VOID TEST(TCPServerTest, WritevIOVC)
|
||||||
{
|
{
|
||||||
srs_error_t err;
|
srs_error_t err;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue