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
b8a81d8889
16 changed files with 374 additions and 121 deletions
14
AUTHORS.txt
14
AUTHORS.txt
|
@ -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>
|
|
@ -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
18
trunk/scripts/new_authors.sh
Executable 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
|
|
@ -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) {
|
||||||
|
|
|
@ -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()) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue