1
0
Fork 0
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:
winlin 2019-12-24 20:23:31 +08:00
commit b8a81d8889
16 changed files with 374 additions and 121 deletions

View file

@ -36,3 +36,17 @@ CONTRIBUTORS ordered by first contribution.
* xubin<xubin@chnvideo.com> * xubin<xubin@chnvideo.com>
* intliang<yintiliang@gmail.com> * intliang<yintiliang@gmail.com>
* flowerwrong<sysuyangkang@gmail.com> * flowerwrong<sysuyangkang@gmail.com>
* YLX<568414379@qq.com>
* J<guotaojiang@qq.com>
* Harlan<hailiang@gvrcraft.com>
* hankun<hankun@bravovcloud.com>
* JonathanBarratt<jonathan.barratt@gmail.com>
* KeeganH<keeganwharris@gmail.com>
* StevenLiu<lingjiujianke@gmail.com>
* liuxc0116<liuxc0116@gmail.com>
* ChengdongZhang<lmajzcd@sina.com>
* lovacat<lovecat@china.sina.com>
* qiang.li<qiang.li@verycdn.com.cn>
* HungMingWu<u9089000@gmail.com>
* Himer<xishizhaohua@qq.com>
* xialixin<xlx0625@163.com>

View file

@ -148,6 +148,7 @@ For previous versions, please read:
## V3 changes ## V3 changes
* v3.0, 2019-12-24, For [#1508][bug #1508], support chunk length and content in multiple parts.
* v3.0, 2019-12-23, Merge SRS2 for running srs-librtmp on Windows. 3.0.80 * v3.0, 2019-12-23, Merge SRS2 for running srs-librtmp on Windows. 3.0.80
* v3.0, 2019-12-23, For [#1535][bug #1535], deprecate Adobe FMS/AMS edge token traversing([CN][v3_CN_DRM2], [EN][v3_EN_DRM2]) authentication. 3.0.79 * v3.0, 2019-12-23, For [#1535][bug #1535], deprecate Adobe FMS/AMS edge token traversing([CN][v3_CN_DRM2], [EN][v3_EN_DRM2]) authentication. 3.0.79
* v3.0, 2019-12-23, For [#1535][bug #1535], deprecate BWT(bandwidth testing)([CN][v1_CN_BandwidthTestTool], [EN][v1_EN_BandwidthTestTool]). 3.0.78 * v3.0, 2019-12-23, For [#1535][bug #1535], deprecate BWT(bandwidth testing)([CN][v1_CN_BandwidthTestTool], [EN][v1_EN_BandwidthTestTool]). 3.0.78

18
trunk/scripts/new_authors.sh Executable file
View file

@ -0,0 +1,18 @@
#!/bin/bash
AFILE=`dirname $0`/../../AUTHORS.txt
if [[ ! -f $AFILE ]]; then echo "No file at $AFILE"; exit -1; fi
authors=`git log --format='%ae'|grep -v localhost|grep -v demo|grep -v none|sort|uniq`
if [[ $? -ne 0 ]]; then echo "no authors"; exit -1; fi
for author in $authors; do
echo $author| grep 'winlin' >/dev/null 2>&1 && continue;
echo $author| grep 'winterserver' >/dev/null 2>&1 && continue;
echo $author| grep 'wenjie.zhao' >/dev/null 2>&1 && continue;
echo $author| grep 'zhaowenjie' >/dev/null 2>&1 && continue;
grep $author $AFILE 1>/dev/null 2>/dev/null && continue;
git log -1 --author="$author" --format='%an<%ae>'| sed 's/ //g'
done

View file

@ -295,7 +295,7 @@ srs_error_t SrsHttpFileReader::read(void* buf, size_t count, ssize_t* pnread)
int total_read = 0; int total_read = 0;
while (total_read < (int)count) { while (total_read < (int)count) {
int nread = 0; ssize_t nread = 0;
if ((err = http->read((char*)buf + total_read, (int)(count - total_read), &nread)) != srs_success) { if ((err = http->read((char*)buf + total_read, (int)(count - total_read), &nread)) != srs_success) {
return srs_error_wrap(err, "read"); return srs_error_wrap(err, "read");
} }
@ -306,7 +306,7 @@ srs_error_t SrsHttpFileReader::read(void* buf, size_t count, ssize_t* pnread)
} }
srs_assert(nread); srs_assert(nread);
total_read += nread; total_read += (int)nread;
} }
if (pnread) { if (pnread) {

View file

@ -3855,17 +3855,6 @@ srs_error_t SrsConfig::check_normal_config()
SRS_CONSTS_RTMP_MIN_CHUNK_SIZE, SRS_CONSTS_RTMP_MAX_CHUNK_SIZE); SRS_CONSTS_RTMP_MIN_CHUNK_SIZE, SRS_CONSTS_RTMP_MAX_CHUNK_SIZE);
} }
} }
for (int i = 0; i < (int)vhosts.size(); i++) {
SrsConfDirective* vhost = vhosts[i];
srs_assert(vhost != NULL);
if (get_dvr_enabled(vhost->arg0())) {
srs_warn("can't enable vhost.dvr of %s", vhost->arg0().c_str());
}
if (get_hls_enabled(vhost->arg0())) {
srs_warn("can't enable vhost.hls of %s", vhost->arg0().c_str());
}
// TODO: FIXME: required http server when hls storage is ram or both.
}
// asprocess conflict with daemon // asprocess conflict with daemon
if (get_asprocess() && get_daemon()) { if (get_asprocess() && get_daemon()) {

View file

@ -398,11 +398,11 @@ srs_error_t SrsHttpHooks::on_hls_notify(int cid, std::string url, SrsRequest* re
int nb_read = 0; int nb_read = 0;
ISrsHttpResponseReader* br = msg->body_reader(); ISrsHttpResponseReader* br = msg->body_reader();
while (nb_read < nb_notify && !br->eof()) { while (nb_read < nb_notify && !br->eof()) {
int nb_bytes = 0; ssize_t nb_bytes = 0;
if ((err = br->read(buf, nb_buf, &nb_bytes)) != srs_success) { if ((err = br->read(buf, nb_buf, &nb_bytes)) != srs_success) {
break; break;
} }
nb_read += nb_bytes; nb_read += (int)nb_bytes;
} }
int spenttime = (int)(srsu2ms(srs_update_system_time()) - starttime); int spenttime = (int)(srsu2ms(srs_update_system_time()) - starttime);

View file

@ -694,6 +694,7 @@ srs_error_t SrsFormat::video_avc_demux(SrsBuffer* stream, int64_t timestamp)
nb_raw = stream->size() - stream->pos(); nb_raw = stream->size() - stream->pos();
if (avc_packet_type == SrsVideoAvcFrameTraitSequenceHeader) { if (avc_packet_type == SrsVideoAvcFrameTraitSequenceHeader) {
// TODO: FIXME: Maybe we should ignore any error for parsing sps/pps.
if ((err = avc_demux_sps_pps(stream)) != srs_success) { if ((err = avc_demux_sps_pps(stream)) != srs_success) {
return srs_error_wrap(err, "demux SPS/PPS"); return srs_error_wrap(err, "demux SPS/PPS");
} }
@ -708,6 +709,9 @@ srs_error_t SrsFormat::video_avc_demux(SrsBuffer* stream, int64_t timestamp)
return err; return err;
} }
// For media server, we don't care the codec, so we just try to parse sps-pps, and we could ignore any error if fail.
// LCOV_EXCL_START
srs_error_t SrsFormat::avc_demux_sps_pps(SrsBuffer* stream) srs_error_t SrsFormat::avc_demux_sps_pps(SrsBuffer* stream)
{ {
// AVCDecoderConfigurationRecord // AVCDecoderConfigurationRecord
@ -1016,6 +1020,8 @@ srs_error_t SrsFormat::avc_demux_sps_rbsp(char* rbsp, int nb_rbsp)
return err; return err;
} }
// LCOV_EXCL_STOP
srs_error_t SrsFormat::video_nalu_demux(SrsBuffer* stream) srs_error_t SrsFormat::video_nalu_demux(SrsBuffer* stream)
{ {
srs_error_t err = srs_success; srs_error_t err = srs_success;

View file

@ -866,6 +866,7 @@ public:
// 8.6.6 Edit List Box // 8.6.6 Edit List Box
// ISO_IEC_14496-12-base-format-2012.pdf, page 55 // ISO_IEC_14496-12-base-format-2012.pdf, page 55
// LCOV_EXCL_START
class SrsMp4ElstEntry class SrsMp4ElstEntry
{ {
public: public:
@ -890,6 +891,7 @@ public:
virtual std::stringstream& dumps(std::stringstream& ss, SrsMp4DumpContext dc); virtual std::stringstream& dumps(std::stringstream& ss, SrsMp4DumpContext dc);
virtual std::stringstream& dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc); virtual std::stringstream& dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc);
}; };
// LCOV_EXCL_STOP
// 8.6.6 Edit List Box (elst) // 8.6.6 Edit List Box (elst)
// ISO_IEC_14496-12-base-format-2012.pdf, page 54 // ISO_IEC_14496-12-base-format-2012.pdf, page 54
@ -1564,6 +1566,7 @@ public:
// 8.6.1.3 Composition Time to Sample Box (ctts), for Video. // 8.6.1.3 Composition Time to Sample Box (ctts), for Video.
// ISO_IEC_14496-12-base-format-2012.pdf, page 49 // ISO_IEC_14496-12-base-format-2012.pdf, page 49
// LCOV_EXCL_START
class SrsMp4CttsEntry class SrsMp4CttsEntry
{ {
public: public:
@ -1580,6 +1583,7 @@ public:
public: public:
virtual std::stringstream& dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc); virtual std::stringstream& dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc);
}; };
// LCOV_EXCL_STOP
// 8.6.1.3 Composition Time to Sample Box (ctts), for Video. // 8.6.1.3 Composition Time to Sample Box (ctts), for Video.
// ISO_IEC_14496-12-base-format-2012.pdf, page 49 // ISO_IEC_14496-12-base-format-2012.pdf, page 49

View file

@ -810,6 +810,8 @@ SrsTsPacket* SrsTsPacket::create_pes_first(SrsTsContext* context,
pkt->payload = pes; pkt->payload = pes;
if (pcr >= 0) { if (pcr >= 0) {
// Ignore coverage for PCR, we don't use it in HLS.
// LCOV_EXCL_START
SrsTsAdaptationField* af = new SrsTsAdaptationField(pkt); SrsTsAdaptationField* af = new SrsTsAdaptationField(pkt);
pkt->adaptation_field = af; pkt->adaptation_field = af;
pkt->adaption_field_control = SrsTsAdaptationFieldTypeBoth; pkt->adaption_field_control = SrsTsAdaptationFieldTypeBoth;
@ -825,6 +827,7 @@ SrsTsPacket* SrsTsPacket::create_pes_first(SrsTsContext* context,
af->adaptation_field_extension_flag = 0; af->adaptation_field_extension_flag = 0;
af->program_clock_reference_base = pcr; af->program_clock_reference_base = pcr;
af->program_clock_reference_extension = 0; af->program_clock_reference_extension = 0;
// LCOV_EXCL_STOP
} }
pes->packet_start_code_prefix = 0x01; pes->packet_start_code_prefix = 0x01;
@ -972,7 +975,9 @@ srs_error_t SrsTsAdaptationField::decode(SrsBuffer* stream)
const1_value0 = (pcrv >> 9) & 0x3F; const1_value0 = (pcrv >> 9) & 0x3F;
program_clock_reference_base = (pcrv >> 15) & 0x1ffffffffLL; program_clock_reference_base = (pcrv >> 15) & 0x1ffffffffLL;
} }
// Ignore coverage for bellow, we don't use it in HLS.
// LCOV_EXCL_START
if (OPCR_flag) { if (OPCR_flag) {
if (!stream->require(6)) { if (!stream->require(6)) {
return srs_error_new(ERROR_STREAM_CASTER_TS_AF, "ts: demux af OPCR_flag"); return srs_error_new(ERROR_STREAM_CASTER_TS_AF, "ts: demux af OPCR_flag");
@ -1080,6 +1085,7 @@ srs_error_t SrsTsAdaptationField::decode(SrsBuffer* stream)
nb_af_ext_reserved = adaptation_field_extension_length - (stream->pos() - pos_af_ext); nb_af_ext_reserved = adaptation_field_extension_length - (stream->pos() - pos_af_ext);
stream->skip(nb_af_ext_reserved); stream->skip(nb_af_ext_reserved);
} }
// LCOV_EXCL_STOP
nb_af_reserved = adaption_field_length - (stream->pos() - pos_af); nb_af_reserved = adaption_field_length - (stream->pos() - pos_af);
stream->skip(nb_af_reserved); stream->skip(nb_af_reserved);
@ -1143,7 +1149,9 @@ srs_error_t SrsTsAdaptationField::encode(SrsBuffer* stream)
tmpv |= (splicing_point_flag << 2) & 0x04; tmpv |= (splicing_point_flag << 2) & 0x04;
tmpv |= (transport_private_data_flag << 1) & 0x02; tmpv |= (transport_private_data_flag << 1) & 0x02;
stream->write_1bytes(tmpv); stream->write_1bytes(tmpv);
// Ignore the coverage bellow, for we don't use them in HLS.
// LCOV_EXCL_START
if (PCR_flag) { if (PCR_flag) {
if (!stream->require(6)) { if (!stream->require(6)) {
return srs_error_new(ERROR_STREAM_CASTER_TS_AF, "ts: mux af PCR_flag"); return srs_error_new(ERROR_STREAM_CASTER_TS_AF, "ts: mux af PCR_flag");
@ -1236,6 +1244,7 @@ srs_error_t SrsTsAdaptationField::encode(SrsBuffer* stream)
stream->skip(nb_af_ext_reserved); stream->skip(nb_af_ext_reserved);
} }
} }
// LCOV_EXCL_STOP
if (nb_af_reserved) { if (nb_af_reserved) {
stream->skip(nb_af_reserved); stream->skip(nb_af_reserved);
@ -1460,7 +1469,10 @@ srs_error_t SrsTsPayloadPES::decode(SrsBuffer* stream, SrsTsMessage** ppmsg)
msg->dts = dts; msg->dts = dts;
msg->pts = pts; msg->pts = pts;
} }
// Ignore coverage bellow, for we don't use them in HLS.
// LCOV_EXCL_START
// 6B // 6B
if (ESCR_flag) { if (ESCR_flag) {
ESCR_extension = 0; ESCR_extension = 0;
@ -1588,6 +1600,8 @@ srs_error_t SrsTsPayloadPES::decode(SrsBuffer* stream, SrsTsMessage** ppmsg)
} }
stream->skip(nb_stuffings); stream->skip(nb_stuffings);
} }
// LCOV_EXCL_STOP
// PES_packet_data_byte, page58. // PES_packet_data_byte, page58.
// the packet size contains the header size. // the packet size contains the header size.
@ -1610,6 +1624,9 @@ srs_error_t SrsTsPayloadPES::decode(SrsBuffer* stream, SrsTsMessage** ppmsg)
if ((err = msg->dump(stream, &nb_bytes)) != srs_success) { if ((err = msg->dump(stream, &nb_bytes)) != srs_success) {
return srs_error_wrap(err, "dump pes"); return srs_error_wrap(err, "dump pes");
} }
// Ignore coverage bellow, for we don't use them in HLS.
// LCOV_EXCL_START
} else if (sid == SrsTsPESStreamIdProgramStreamMap } else if (sid == SrsTsPESStreamIdProgramStreamMap
|| sid == SrsTsPESStreamIdPrivateStream2 || sid == SrsTsPESStreamIdPrivateStream2
|| sid == SrsTsPESStreamIdEcmStream || sid == SrsTsPESStreamIdEcmStream
@ -1633,6 +1650,8 @@ srs_error_t SrsTsPayloadPES::decode(SrsBuffer* stream, SrsTsMessage** ppmsg)
nb_paddings = stream->size() - stream->pos(); nb_paddings = stream->size() - stream->pos();
stream->skip(nb_paddings); stream->skip(nb_paddings);
srs_info("ts: drop %dB padding bytes", nb_paddings); srs_info("ts: drop %dB padding bytes", nb_paddings);
// LCOV_EXCL_STOP
} else { } else {
int nb_drop = stream->size() - stream->pos(); int nb_drop = stream->size() - stream->pos();
stream->skip(nb_drop); stream->skip(nb_drop);
@ -1685,13 +1704,16 @@ int SrsTsPayloadPES::size()
sz += additional_copy_info_flag? 1:0; sz += additional_copy_info_flag? 1:0;
sz += PES_CRC_flag? 2:0; sz += PES_CRC_flag? 2:0;
sz += PES_extension_flag? 1:0; sz += PES_extension_flag? 1:0;
if (PES_extension_flag) { if (PES_extension_flag) {
// Ignore coverage bellow, for we don't use them in HLS.
// LCOV_EXCL_START
sz += PES_private_data_flag? 16:0; sz += PES_private_data_flag? 16:0;
sz += pack_header_field_flag ? 1 + pack_field.size() : 0; // 1+x bytes. sz += pack_header_field_flag ? 1 + pack_field.size() : 0; // 1+x bytes.
sz += program_packet_sequence_counter_flag? 2:0; sz += program_packet_sequence_counter_flag? 2:0;
sz += P_STD_buffer_flag? 2:0; sz += P_STD_buffer_flag? 2:0;
sz += PES_extension_flag_2 ? 1 + PES_extension_field.size() : 0; // 1+x bytes. sz += PES_extension_flag_2 ? 1 + PES_extension_field.size() : 0; // 1+x bytes.
// LCOV_EXCL_STOP
} }
PES_header_data_length = sz - PES_header_data_length; PES_header_data_length = sz - PES_header_data_length;
@ -1802,6 +1824,9 @@ srs_error_t SrsTsPayloadPES::encode(SrsBuffer* stream)
srs_warn("ts: sync dts=%" PRId64 ", pts=%" PRId64, dts, pts); srs_warn("ts: sync dts=%" PRId64 ", pts=%" PRId64, dts, pts);
} }
} }
// Ignore coverage bellow, for we don't use them in HLS.
// LCOV_EXCL_START
// 6B // 6B
if (ESCR_flag) { if (ESCR_flag) {
@ -1861,6 +1886,8 @@ srs_error_t SrsTsPayloadPES::encode(SrsBuffer* stream)
stream->skip(nb_stuffings); stream->skip(nb_stuffings);
srs_warn("ts: demux PES, ignore the stuffings."); srs_warn("ts: demux PES, ignore the stuffings.");
} }
// LCOV_EXCL_STOP
return err; return err;
} }

View file

@ -568,11 +568,11 @@ int srs_do_create_dir_recursively(string dir)
// create curren dir. // create curren dir.
// for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 // for srs-librtmp, @see https://github.com/ossrs/srs/issues/213
#ifndef _WIN32 #ifdef _WIN32
if (::_mkdir(dir.c_str()) < 0) {
#else
mode_t mode = S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IXOTH; mode_t mode = S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IXOTH;
if (::mkdir(dir.c_str(), mode) < 0) { if (::mkdir(dir.c_str(), mode) < 0) {
#else
if (::_mkdir(dir.c_str()) < 0) {
#endif #endif
if (errno == EEXIST) { if (errno == EEXIST) {
return ERROR_SYSTEM_DIR_EXISTS; return ERROR_SYSTEM_DIR_EXISTS;

View file

@ -187,7 +187,7 @@ int srs_hijack_io_set_recv_timeout(srs_hijack_io_t ctx, int64_t tm)
SrsBlockSyncSocket* skt = (SrsBlockSyncSocket*)ctx; SrsBlockSyncSocket* skt = (SrsBlockSyncSocket*)ctx;
#ifdef _WIN32 #ifdef _WIN32
DWORD tv = (DWORD)(timeout_us/1000); DWORD tv = (DWORD)(tm);
// To convert tv to const char* to make VS2015 happy. // To convert tv to const char* to make VS2015 happy.
if (setsockopt(skt->fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) { if (setsockopt(skt->fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
@ -229,7 +229,7 @@ int srs_hijack_io_set_send_timeout(srs_hijack_io_t ctx, int64_t tm)
SrsBlockSyncSocket* skt = (SrsBlockSyncSocket*)ctx; SrsBlockSyncSocket* skt = (SrsBlockSyncSocket*)ctx;
#ifdef _WIN32 #ifdef _WIN32
DWORD tv = (DWORD)(timeout_us/1000); DWORD tv = (DWORD)(tm);
// To convert tv to const char* to make VS2015 happy. // To convert tv to const char* to make VS2015 happy.
if (setsockopt(skt->fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1) { if (setsockopt(skt->fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1) {

View file

@ -26,6 +26,8 @@
#include <srs_core.hpp> #include <srs_core.hpp>
#include <srs_kernel_io.hpp>
// Default http listen port. // Default http listen port.
#define SRS_DEFAULT_HTTP_PORT 80 #define SRS_DEFAULT_HTTP_PORT 80
@ -160,7 +162,7 @@ public:
// w->header()->set_content_type("text/plain; charset=utf-8"); // w->header()->set_content_type("text/plain; charset=utf-8");
// w->header()->set_content_length(msg.length()); // w->header()->set_content_length(msg.length());
// w->write_header(SRS_CONSTS_HTTP_OK); // w->write_header(SRS_CONSTS_HTTP_OK);
// w->write((char*)msg.data(), (int)msg.length()); // w->write((char*)msg.data(), (int)msg.length()); // write N times, N>0
// w->final_request(); // optional flush. // w->final_request(); // optional flush.
// Usage 2, response with HTTP code only, zero content length. // Usage 2, response with HTTP code only, zero content length.
// ISrsHttpResponseWriter* w; // create or get response. // ISrsHttpResponseWriter* w; // create or get response.
@ -215,7 +217,7 @@ public:
}; };
// The reader interface for http response. // The reader interface for http response.
class ISrsHttpResponseReader class ISrsHttpResponseReader : public ISrsReader
{ {
public: public:
ISrsHttpResponseReader(); ISrsHttpResponseReader();
@ -223,18 +225,6 @@ public:
public: public:
// Whether response read EOF. // Whether response read EOF.
virtual bool eof() = 0; virtual bool eof() = 0;
// Read from the response body.
// @param data, the buffer to read data buffer to.
// @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 srs_error_t read(char* data, int nb_data, int* nb_read) = 0;
}; };
// Objects implementing the Handler interface can be // Objects implementing the Handler interface can be

View file

@ -185,6 +185,10 @@ int SrsHttpParser::on_headers_complete(http_parser* parser)
// save the parser when header parse completed. // save the parser when header parse completed.
obj->state = SrsHttpParseStateHeaderComplete; obj->state = SrsHttpParseStateHeaderComplete;
// We must update the body start when header complete, because sometimes we only got header.
// When we got the body start event, we will update it to much precious position.
obj->p_body_start = obj->buffer->bytes() + obj->buffer->size();
srs_info("***HEADERS COMPLETE***"); srs_info("***HEADERS COMPLETE***");
// see http_parser.c:1570, return 1 to skip body. // see http_parser.c:1570, return 1 to skip body.
@ -551,7 +555,7 @@ srs_error_t SrsHttpMessage::body_read_all(string& body)
// whatever, read util EOF. // whatever, read util EOF.
while (!_body->eof()) { while (!_body->eof()) {
int nb_read = 0; ssize_t nb_read = 0;
if ((err = _body->read(buf, SRS_HTTP_READ_CACHE_BYTES, &nb_read)) != srs_success) { if ((err = _body->read(buf, SRS_HTTP_READ_CACHE_BYTES, &nb_read)) != srs_success) {
return srs_error_wrap(err, "read body"); return srs_error_wrap(err, "read body");
} }
@ -699,7 +703,9 @@ srs_error_t SrsHttpResponseWriter::write(char* data, int size)
if (hdr->content_type().empty()) { if (hdr->content_type().empty()) {
hdr->set_content_type("text/plain; charset=utf-8"); hdr->set_content_type("text/plain; charset=utf-8");
} }
hdr->set_content_length(size); if (hdr->content_length() == -1) {
hdr->set_content_length(size);
}
write_header(SRS_CONSTS_HTTP_OK); write_header(SRS_CONSTS_HTTP_OK);
} }
@ -912,7 +918,7 @@ bool SrsHttpResponseReader::eof()
return is_eof; return is_eof;
} }
srs_error_t SrsHttpResponseReader::read(char* data, int nb_data, int* nb_read) srs_error_t SrsHttpResponseReader::read(void* data, size_t nb_data, ssize_t* nb_read)
{ {
srs_error_t err = srs_success; srs_error_t err = srs_success;
@ -927,7 +933,7 @@ srs_error_t SrsHttpResponseReader::read(char* data, int nb_data, int* nb_read)
// read by specified content-length // read by specified content-length
if (owner->content_length() != -1) { if (owner->content_length() != -1) {
int max = (int)owner->content_length() - (int)nb_total_read; size_t max = (size_t)owner->content_length() - (size_t)nb_total_read;
if (max <= 0) { if (max <= 0) {
is_eof = true; is_eof = true;
return err; return err;
@ -951,7 +957,7 @@ srs_error_t SrsHttpResponseReader::read(char* data, int nb_data, int* nb_read)
return err; return err;
} }
srs_error_t SrsHttpResponseReader::read_chunked(char* data, int nb_data, int* nb_read) srs_error_t SrsHttpResponseReader::read_chunked(void* data, size_t nb_data, ssize_t* nb_read)
{ {
srs_error_t err = srs_success; srs_error_t err = srs_success;
@ -1003,32 +1009,34 @@ srs_error_t SrsHttpResponseReader::read_chunked(char* data, int nb_data, int* nb
} }
// all bytes in chunk is left now. // all bytes in chunk is left now.
nb_chunk = nb_left_chunk = ilength; nb_chunk = nb_left_chunk = (size_t)ilength;
} }
if (nb_chunk <= 0) { if (nb_chunk <= 0) {
// for the last chunk, eof. // for the last chunk, eof.
is_eof = true; is_eof = true;
*nb_read = 0;
} else { } else {
// for not the last chunk, there must always exists bytes. // for not the last chunk, there must always exists bytes.
// left bytes in chunk, read some. // left bytes in chunk, read some.
srs_assert(nb_left_chunk); srs_assert(nb_left_chunk);
int nb_bytes = srs_min(nb_left_chunk, nb_data); size_t nb_bytes = srs_min(nb_left_chunk, nb_data);
err = read_specified(data, nb_bytes, &nb_bytes); err = read_specified(data, nb_bytes, (ssize_t*)&nb_bytes);
// the nb_bytes used for output already read size of bytes. // the nb_bytes used for output already read size of bytes.
if (nb_read) { if (nb_read) {
*nb_read = nb_bytes; *nb_read = nb_bytes;
} }
nb_left_chunk -= nb_bytes; nb_left_chunk -= nb_bytes;
// error or still left bytes in chunk, ignore and read in future.
if (err != srs_success) { if (err != srs_success) {
return srs_error_wrap(err, "read specified"); return srs_error_wrap(err, "read specified");
} }
// If still left bytes in chunk, ignore and read in future.
if (nb_left_chunk > 0) { if (nb_left_chunk > 0) {
return srs_error_new(ERROR_HTTP_INVALID_CHUNK_HEADER, "read specified left=%d", nb_left_chunk); return err;
} }
} }
@ -1041,7 +1049,7 @@ srs_error_t SrsHttpResponseReader::read_chunked(char* data, int nb_data, int* nb
return err; return err;
} }
srs_error_t SrsHttpResponseReader::read_specified(char* data, int nb_data, int* nb_read) srs_error_t SrsHttpResponseReader::read_specified(void* data, size_t nb_data, ssize_t* nb_read)
{ {
srs_error_t err = srs_success; srs_error_t err = srs_success;
@ -1052,7 +1060,7 @@ srs_error_t SrsHttpResponseReader::read_specified(char* data, int nb_data, int*
} }
} }
int nb_bytes = srs_min(nb_data, buffer->size()); size_t nb_bytes = srs_min(nb_data, (size_t)buffer->size());
// read data to buffer. // read data to buffer.
srs_assert(nb_bytes); srs_assert(nb_bytes);

View file

@ -261,9 +261,9 @@ private:
SrsFastStream* buffer; SrsFastStream* buffer;
bool is_eof; bool is_eof;
// The left bytes in chunk. // The left bytes in chunk.
int nb_left_chunk; size_t nb_left_chunk;
// The number of bytes of current chunk. // The number of bytes of current chunk.
int nb_chunk; size_t nb_chunk;
// Already read total bytes. // Already read total bytes.
int64_t nb_total_read; int64_t nb_total_read;
public: public:
@ -274,10 +274,10 @@ public:
// Interface ISrsHttpResponseReader // Interface ISrsHttpResponseReader
public: public:
virtual bool eof(); virtual bool eof();
virtual srs_error_t read(char* data, int nb_data, int* nb_read); virtual srs_error_t read(void* buf, size_t size, ssize_t* nread);
private: private:
virtual srs_error_t read_chunked(char* data, int nb_data, int* nb_read); virtual srs_error_t read_chunked(void* buf, size_t size, ssize_t* nread);
virtual srs_error_t read_specified(char* data, int nb_data, int* nb_read); virtual srs_error_t read_specified(void* buf, size_t size, ssize_t* nread);
}; };
#endif #endif

View file

@ -34,6 +34,60 @@ using namespace std;
#include <srs_utest_kernel.hpp> #include <srs_utest_kernel.hpp>
#include <srs_app_http_static.hpp> #include <srs_app_http_static.hpp>
class MockMSegmentsReader : public ISrsReader
{
public:
vector<string> in_bytes;
public:
MockMSegmentsReader();
virtual ~MockMSegmentsReader();
public:
virtual void append(string b) {
in_bytes.push_back(b);
}
virtual srs_error_t read(void* buf, size_t size, ssize_t* nread);
};
MockMSegmentsReader::MockMSegmentsReader()
{
}
MockMSegmentsReader::~MockMSegmentsReader()
{
}
srs_error_t MockMSegmentsReader::read(void* buf, size_t size, ssize_t* nread)
{
srs_error_t err = srs_success;
for (;;) {
if (in_bytes.empty() || size <= 0) {
return srs_error_new(-1, "EOF");
}
string v = in_bytes[0];
if (v.empty()) {
in_bytes.erase(in_bytes.begin());
continue;
}
int nn = srs_min(size, v.length());
memcpy(buf, v.data(), nn);
if (nread) {
*nread = nn;
}
if (nn < (int)v.length()) {
in_bytes[0] = string(v.data() + nn, v.length() - nn);
} else {
in_bytes.erase(in_bytes.begin());
}
break;
}
return err;
}
class MockResponseWriter : virtual public ISrsHttpResponseWriter, virtual public ISrsHttpHeaderFilter class MockResponseWriter : virtual public ISrsHttpResponseWriter, virtual public ISrsHttpHeaderFilter
{ {
public: public:
@ -192,12 +246,41 @@ VOID TEST(ProtocolHTTPTest, ResponseDetect)
VOID TEST(ProtocolHTTPTest, ResponseWriter) VOID TEST(ProtocolHTTPTest, ResponseWriter)
{ {
srs_error_t err;
// Directly final_request, should work.
if (true) {
MockResponseWriter w;
w.header()->set_content_length(0);
HELPER_ASSERT_SUCCESS(w.final_request());
}
// Directly final_request, should work.
if (true) {
MockResponseWriter w;
HELPER_ASSERT_SUCCESS(w.final_request());
}
// When content-length is set, we could write multiple parts.
if (true) {
MockResponseWriter w;
char msg[] = "Hello, world!";
w.header()->set_content_length(sizeof(msg) - 1);
HELPER_EXPECT_SUCCESS(w.write((char*)msg, 5));
HELPER_EXPECT_SUCCESS(w.write((char*)(msg+5), 2));
HELPER_EXPECT_SUCCESS(w.write((char*)(msg+7), 5));
HELPER_EXPECT_SUCCESS(w.write((char*)(msg+12), 1));
__MOCK_HTTP_EXPECT_STREQ(200, "Hello, world!", w);
}
// If directly write string, response with content-length. // If directly write string, response with content-length.
if (true) { if (true) {
MockResponseWriter w; MockResponseWriter w;
char msg[] = "Hello, world!"; char msg[] = "Hello, world!";
w.write((char*)msg, sizeof(msg) - 1); HELPER_EXPECT_SUCCESS(w.write((char*)msg, sizeof(msg) - 1));
__MOCK_HTTP_EXPECT_STREQ(200, "Hello, world!", w); __MOCK_HTTP_EXPECT_STREQ(200, "Hello, world!", w);
} }
@ -211,8 +294,8 @@ VOID TEST(ProtocolHTTPTest, ResponseWriter)
w.header()->set_content_type("text/plain; charset=utf-8"); w.header()->set_content_type("text/plain; charset=utf-8");
w.header()->set_content_length(sizeof(msg) - 1); w.header()->set_content_length(sizeof(msg) - 1);
w.write_header(SRS_CONSTS_HTTP_OK); w.write_header(SRS_CONSTS_HTTP_OK);
w.write((char*)msg, sizeof(msg) - 1); HELPER_EXPECT_SUCCESS(w.write((char*)msg, sizeof(msg) - 1));
w.final_request(); HELPER_ASSERT_SUCCESS(w.final_request());
__MOCK_HTTP_EXPECT_STREQ(200, "Hello, world!", w); __MOCK_HTTP_EXPECT_STREQ(200, "Hello, world!", w);
} }
@ -223,7 +306,7 @@ VOID TEST(ProtocolHTTPTest, ResponseWriter)
w.header()->set_content_length(0); w.header()->set_content_length(0);
w.write_header(SRS_CONSTS_HTTP_OK); w.write_header(SRS_CONSTS_HTTP_OK);
w.final_request(); HELPER_ASSERT_SUCCESS(w.final_request());
__MOCK_HTTP_EXPECT_STREQ(200, "", w); __MOCK_HTTP_EXPECT_STREQ(200, "", w);
} }
@ -234,7 +317,7 @@ VOID TEST(ProtocolHTTPTest, ResponseWriter)
w.header()->set_content_length(0); w.header()->set_content_length(0);
w.write_header(SRS_CONSTS_HTTP_OK); w.write_header(SRS_CONSTS_HTTP_OK);
w.write(NULL, 0); HELPER_EXPECT_SUCCESS(w.write(NULL, 0));
__MOCK_HTTP_EXPECT_STREQ(200, "", w); __MOCK_HTTP_EXPECT_STREQ(200, "", w);
} }
@ -245,9 +328,9 @@ VOID TEST(ProtocolHTTPTest, ResponseWriter)
w.header()->set_content_type("application/octet-stream"); w.header()->set_content_type("application/octet-stream");
w.write_header(SRS_CONSTS_HTTP_OK); w.write_header(SRS_CONSTS_HTTP_OK);
w.write((char*)"Hello", 5); HELPER_EXPECT_SUCCESS(w.write((char*)"Hello", 5));
w.write((char*)", world!", 8); HELPER_EXPECT_SUCCESS(w.write((char*)", world!", 8));
w.final_request(); HELPER_ASSERT_SUCCESS(w.final_request());
__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);
} }
@ -256,8 +339,8 @@ VOID TEST(ProtocolHTTPTest, ResponseWriter)
w.header()->set_content_type("application/octet-stream"); w.header()->set_content_type("application/octet-stream");
w.write_header(SRS_CONSTS_HTTP_OK); w.write_header(SRS_CONSTS_HTTP_OK);
w.write((char*)"Hello, world!", 13); HELPER_EXPECT_SUCCESS(w.write((char*)"Hello, world!", 13));
w.final_request(); HELPER_ASSERT_SUCCESS(w.final_request());
__MOCK_HTTP_EXPECT_STREQ2(200, "d\r\nHello, world!\r\n0\r\n\r\n", w); __MOCK_HTTP_EXPECT_STREQ2(200, "d\r\nHello, world!\r\n0\r\n\r\n", w);
} }
@ -266,8 +349,8 @@ VOID TEST(ProtocolHTTPTest, ResponseWriter)
w.header()->set_content_type("application/octet-stream"); w.header()->set_content_type("application/octet-stream");
w.write_header(SRS_CONSTS_HTTP_OK); w.write_header(SRS_CONSTS_HTTP_OK);
w.write((char*)"Hello, world!", 13); HELPER_EXPECT_SUCCESS(w.write((char*)"Hello, world!", 13));
w.final_request(); HELPER_ASSERT_SUCCESS(w.final_request());
__MOCK_HTTP_EXPECT_STREQ2(200, "d\r\nHello, world!\r\n0\r\n\r\n", w); __MOCK_HTTP_EXPECT_STREQ2(200, "d\r\nHello, world!\r\n0\r\n\r\n", w);
} }
@ -275,18 +358,176 @@ VOID TEST(ProtocolHTTPTest, ResponseWriter)
// 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) {
MockResponseWriter w; MockResponseWriter w;
w.write(NULL, 0); HELPER_EXPECT_SUCCESS(w.write(NULL, 0));
__MOCK_HTTP_EXPECT_STREQ(200, "", w); __MOCK_HTTP_EXPECT_STREQ(200, "", w);
} }
// If directly final request, response with EOF of chunked. // If directly final request, response with EOF of chunked.
if (true) { if (true) {
MockResponseWriter w; MockResponseWriter w;
w.final_request(); HELPER_ASSERT_SUCCESS(w.final_request());
__MOCK_HTTP_EXPECT_STREQ2(200, "0\r\n\r\n", w); __MOCK_HTTP_EXPECT_STREQ2(200, "0\r\n\r\n", w);
} }
} }
VOID TEST(ProtocolHTTPTest, ChunkSmallBuffer)
{
srs_error_t err;
// No chunk end flag, error.
if (true) {
MockMSegmentsReader io;
io.append(mock_http_response2(200, "0d\r\n"));
io.append("Hello, world!\r\n");
SrsHttpParser hp; HELPER_ASSERT_SUCCESS(hp.initialize(HTTP_RESPONSE, false));
ISrsHttpMessage* msg = NULL; HELPER_ASSERT_SUCCESS(hp.parse_message(&io, &msg));
char buf[32]; ssize_t nread = 0;
ISrsHttpResponseReader* r = msg->body_reader();
HELPER_ARRAY_INIT(buf, sizeof(buf), 0);
HELPER_ASSERT_SUCCESS(r->read(buf, 32, &nread));
EXPECT_EQ(13, nread);
EXPECT_STREQ("Hello, world!", buf);
err = r->read(buf, 32, &nread);
EXPECT_EQ(-1, srs_error_code(err));
srs_freep(err);
srs_freep(msg);
}
// Read util EOF(nread=0) or err(ERROR_HTTP_RESPONSE_EOF).
if (true) {
MockMSegmentsReader io;
io.append(mock_http_response2(200, "0d\r\n"));
io.append("Hello, 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));
char buf[32]; ssize_t nread = 0;
ISrsHttpResponseReader* r = msg->body_reader();
HELPER_ARRAY_INIT(buf, sizeof(buf), 0);
HELPER_ASSERT_SUCCESS(r->read(buf, 32, &nread));
EXPECT_EQ(13, nread);
EXPECT_STREQ("Hello, world!", buf);
HELPER_ASSERT_SUCCESS(r->read(buf, 32, &nread));
EXPECT_EQ(0, nread);
err = r->read(buf, 32, &nread);
EXPECT_EQ(ERROR_HTTP_RESPONSE_EOF, srs_error_code(err));
srs_freep(err);
srs_freep(msg);
}
// In this case, we only got header complete, no body start event.
if (true) {
MockMSegmentsReader io;
io.append(mock_http_response2(200, "0d\r\n"));
io.append("Hello, 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));
char buf[32]; ssize_t nread = 0;
ISrsHttpResponseReader* r = msg->body_reader();
HELPER_ARRAY_INIT(buf, sizeof(buf), 0);
HELPER_ASSERT_SUCCESS(r->read(buf, 32, &nread));
EXPECT_EQ(13, nread);
EXPECT_STREQ("Hello, world!", buf);
srs_freep(msg);
}
}
VOID TEST(ProtocolHTTPTest, ClientSmallBuffer)
{
srs_error_t err;
// The chunk content is sent in multiple parts.
if (true) {
MockMSegmentsReader io;
io.append(mock_http_response2(200, "0d\r\n"));
io.append("Hello,");
io.append(" world!");
io.append("\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));
char buf[32]; ssize_t nread = 0;
ISrsHttpResponseReader* r = msg->body_reader();
HELPER_ARRAY_INIT(buf, sizeof(buf), 0);
HELPER_ASSERT_SUCCESS(r->read(buf, 32, &nread));
EXPECT_EQ(6, nread);
EXPECT_STREQ("Hello,", buf);
HELPER_ARRAY_INIT(buf, sizeof(buf), 0);
HELPER_ASSERT_SUCCESS(r->read(buf, 32, &nread));
EXPECT_EQ(7, nread);
EXPECT_STREQ(" world!", buf);
srs_freep(msg);
}
// The chunk size is sent separately before chunk content.
if (true) {
MockMSegmentsReader io;
io.append(mock_http_response2(200, "0d\r\n"));
io.append("Hello, 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));
char buf[32]; ssize_t nread = 0;
ISrsHttpResponseReader* r = msg->body_reader();
HELPER_ARRAY_INIT(buf, sizeof(buf), 0);
HELPER_ASSERT_SUCCESS(r->read(buf, 32, &nread));
EXPECT_EQ(13, nread);
EXPECT_STREQ("Hello, world!", buf);
srs_freep(msg);
}
// If buffer is smaller than chunk, we could read N times to get the whole chunk.
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));
char buf[32]; ssize_t nread = 0;
ISrsHttpResponseReader* r = msg->body_reader();
HELPER_ARRAY_INIT(buf, sizeof(buf), 0);
HELPER_ASSERT_SUCCESS(r->read(buf, 5, &nread));
EXPECT_EQ(5, nread);
EXPECT_STREQ("Hello", buf);
HELPER_ARRAY_INIT(buf, sizeof(buf), 0);
HELPER_ASSERT_SUCCESS(r->read(buf, 7, &nread));
EXPECT_EQ(7, nread);
EXPECT_STREQ(", world", buf);
HELPER_ARRAY_INIT(buf, sizeof(buf), 0);
HELPER_ASSERT_SUCCESS(r->read(buf, 7, &nread));
EXPECT_EQ(1, nread);
EXPECT_STREQ("!", buf);
HELPER_ASSERT_SUCCESS(r->read(buf, 7, &nread));
EXPECT_EQ(0, nread);
srs_freep(msg);
}
}
VOID TEST(ProtocolHTTPTest, ClientRequest) VOID TEST(ProtocolHTTPTest, ClientRequest)
{ {
srs_error_t err; srs_error_t err;
@ -1157,57 +1398,6 @@ VOID TEST(ProtocolHTTPTest, BasicHandlers)
} }
} }
class MockMSegmentsReader : public ISrsReader
{
public:
vector<string> in_bytes;
public:
MockMSegmentsReader();
virtual ~MockMSegmentsReader();
public:
virtual srs_error_t read(void* buf, size_t size, ssize_t* nread);
};
MockMSegmentsReader::MockMSegmentsReader()
{
}
MockMSegmentsReader::~MockMSegmentsReader()
{
}
srs_error_t MockMSegmentsReader::read(void* buf, size_t size, ssize_t* nread)
{
srs_error_t err = srs_success;
for (;;) {
if (in_bytes.empty() || size <= 0) {
return srs_error_new(-1, "EOF");
}
string v = in_bytes[0];
if (v.empty()) {
in_bytes.erase(in_bytes.begin());
continue;
}
int nn = srs_min(size, v.length());
memcpy(buf, v.data(), nn);
if (nread) {
*nread = nn;
}
if (nn < (int)v.length()) {
in_bytes[0] = string(v.data() + nn, v.length() - nn);
} else {
in_bytes.erase(in_bytes.begin());
}
break;
}
return err;
}
VOID TEST(ProtocolHTTPTest, MSegmentsReader) VOID TEST(ProtocolHTTPTest, MSegmentsReader)
{ {
srs_error_t err; srs_error_t err;

View file

@ -2721,6 +2721,12 @@ VOID TEST(KernelErrorTest, CoverAll)
EXPECT_TRUE(srs_is_client_gracefully_close(err)); EXPECT_TRUE(srs_is_client_gracefully_close(err));
srs_freep(err); srs_freep(err);
} }
if (true) {
srs_error_t err = srs_error_new(ERROR_HTTP_STREAM_EOF, "graceful close error");
EXPECT_TRUE(srs_is_server_gracefully_close(err));
srs_freep(err);
}
if (true) { if (true) {
srs_error_t err = srs_error_wrap(srs_error_new(ERROR_CONTROL_RTMP_CLOSE, "control error"), "wrapped"); srs_error_t err = srs_error_wrap(srs_error_new(ERROR_CONTROL_RTMP_CLOSE, "control error"), "wrapped");