/* The MIT License (MIT) Copyright (c) 2013-2014 winlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include using namespace std; #include #include #include #include #include #include #include MockEmptyIO::MockEmptyIO() { } MockEmptyIO::~MockEmptyIO() { } bool MockEmptyIO::is_never_timeout(int64_t /*timeout_us*/) { return true; } int MockEmptyIO::read_fully(void* /*buf*/, size_t /*size*/, ssize_t* /*nread*/) { return ERROR_SUCCESS; } int MockEmptyIO::write(void* /*buf*/, size_t /*size*/, ssize_t* /*nwrite*/) { return ERROR_SUCCESS; } void MockEmptyIO::set_recv_timeout(int64_t /*timeout_us*/) { } int64_t MockEmptyIO::get_recv_timeout() { return -1; } int64_t MockEmptyIO::get_recv_bytes() { return -1; } void MockEmptyIO::set_send_timeout(int64_t /*timeout_us*/) { } int64_t MockEmptyIO::get_send_timeout() { return 0; } int64_t MockEmptyIO::get_send_bytes() { return 0; } int MockEmptyIO::writev(const iovec */*iov*/, int /*iov_size*/, ssize_t* /*nwrite*/) { return ERROR_SUCCESS; } int MockEmptyIO::read(void* /*buf*/, size_t /*size*/, ssize_t* /*nread*/) { return ERROR_SUCCESS; } MockBufferIO::MockBufferIO() { recv_timeout = send_timeout = ST_UTIME_NO_TIMEOUT; recv_bytes = send_bytes = 0; } MockBufferIO::~MockBufferIO() { } bool MockBufferIO::is_never_timeout(int64_t timeout_us) { return (int64_t)ST_UTIME_NO_TIMEOUT == timeout_us; } int MockBufferIO::read_fully(void* buf, size_t size, ssize_t* nread) { if (in_buffer.length() < (int)size) { return ERROR_SOCKET_READ; } memcpy(buf, in_buffer.bytes(), size); recv_bytes += size; if (nread) { *nread = size; } in_buffer.erase(size); return ERROR_SUCCESS; } int MockBufferIO::write(void* buf, size_t size, ssize_t* nwrite) { send_bytes += size; if (nwrite) { *nwrite = size; } out_buffer.append((char*)buf, size); return ERROR_SUCCESS; } void MockBufferIO::set_recv_timeout(int64_t timeout_us) { recv_timeout = timeout_us; } int64_t MockBufferIO::get_recv_timeout() { return recv_timeout; } int64_t MockBufferIO::get_recv_bytes() { return recv_bytes; } void MockBufferIO::set_send_timeout(int64_t timeout_us) { send_timeout = timeout_us; } int64_t MockBufferIO::get_send_timeout() { return send_timeout; } int64_t MockBufferIO::get_send_bytes() { return send_bytes; } int MockBufferIO::writev(const iovec *iov, int iov_size, ssize_t* nwrite) { int ret = ERROR_SUCCESS; ssize_t total = 0; for (int i = 0; i c0c1 + 1, srs_schema0)); ASSERT_EQ(ERROR_SUCCESS, c1.c1_validate_digest(is_valid)); ASSERT_TRUE(is_valid); c1s1 s1; ASSERT_EQ(ERROR_SUCCESS, s1.parse(hs_bytes->s0s1s2 + 1, c1.schema)); ASSERT_EQ(ERROR_SUCCESS, s1.s1_validate_digest(is_valid)); ASSERT_TRUE(is_valid); c2s2 c2; c2.parse(hs_bytes->c2); ASSERT_EQ(ERROR_SUCCESS, c2.c2_validate(&s1, is_valid)); ASSERT_TRUE(is_valid); c2s2 s2; s2.parse(hs_bytes->s0s1s2 + 1 + 1536); ASSERT_EQ(ERROR_SUCCESS, s2.s2_validate(&c1, is_valid)); ASSERT_TRUE(is_valid); } } VOID TEST(ProtocolHandshakeTest, BytesEqual) { char a1[] = { (char)0x01 }; char b1[] = { (char)0x02 }; char a2[] = { (char)0x01, (char)0x02 }; char b2[] = { (char)0x02, (char)0x03 }; EXPECT_TRUE(srs_bytes_equals(NULL, NULL, 0)); EXPECT_FALSE(srs_bytes_equals(a1, NULL, 1)); EXPECT_FALSE(srs_bytes_equals(NULL, a1, 1)); EXPECT_FALSE(srs_bytes_equals(a1, b1, 1)); EXPECT_TRUE(srs_bytes_equals(a1, a1, 1)); EXPECT_TRUE(srs_bytes_equals(a1, a2, 1)); EXPECT_FALSE(srs_bytes_equals(a1, b2, 1)); } VOID TEST(ProtocolUtilityTest, VhostResolve) { std::string vhost = "vhost"; std::string app = "app"; srs_vhost_resolve(vhost, app); EXPECT_STREQ("vhost", vhost.c_str()); EXPECT_STREQ("app", app.c_str()); app = "app?vhost=changed"; srs_vhost_resolve(vhost, app); EXPECT_STREQ("changed", vhost.c_str()); EXPECT_STREQ("app", app.c_str()); app = "app?vhost=changed1&&query=true"; srs_vhost_resolve(vhost, app); EXPECT_STREQ("changed1", vhost.c_str()); EXPECT_STREQ("app", app.c_str()); app = "app?other=true&&vhost=changed2&&query=true"; srs_vhost_resolve(vhost, app); EXPECT_STREQ("changed2", vhost.c_str()); EXPECT_STREQ("app", app.c_str()); app = "app...other...true...vhost...changed3...query...true"; srs_vhost_resolve(vhost, app); EXPECT_STREQ("changed3", vhost.c_str()); EXPECT_STREQ("app", app.c_str()); } VOID TEST(ProtocolUtilityTest, DiscoveryTcUrl) { std::string tcUrl; std::string schema; std::string host; std::string vhost; std::string app; std::string port; tcUrl = "rtmp://127.0.0.1:1935/live"; srs_discovery_tc_url(tcUrl, schema, host, vhost, app, port); EXPECT_STREQ("rtmp", schema.c_str()); EXPECT_STREQ("127.0.0.1", host.c_str()); EXPECT_STREQ("127.0.0.1", vhost.c_str()); EXPECT_STREQ("live", app.c_str()); EXPECT_STREQ("1935", port.c_str()); tcUrl = "rtmp://127.0.0.1:19351/live"; srs_discovery_tc_url(tcUrl, schema, host, vhost, app, port); EXPECT_STREQ("rtmp", schema.c_str()); EXPECT_STREQ("127.0.0.1", host.c_str()); EXPECT_STREQ("127.0.0.1", vhost.c_str()); EXPECT_STREQ("live", app.c_str()); EXPECT_STREQ("19351", port.c_str()); tcUrl = "rtmp://127.0.0.1:19351/live?vhost=demo"; srs_discovery_tc_url(tcUrl, schema, host, vhost, app, port); EXPECT_STREQ("rtmp", schema.c_str()); EXPECT_STREQ("127.0.0.1", host.c_str()); EXPECT_STREQ("demo", vhost.c_str()); EXPECT_STREQ("live", app.c_str()); EXPECT_STREQ("19351", port.c_str()); tcUrl = "rtmp://127.0.0.1:19351/live/show?vhost=demo"; srs_discovery_tc_url(tcUrl, schema, host, vhost, app, port); EXPECT_STREQ("rtmp", schema.c_str()); EXPECT_STREQ("127.0.0.1", host.c_str()); EXPECT_STREQ("demo", vhost.c_str()); EXPECT_STREQ("live/show", app.c_str()); EXPECT_STREQ("19351", port.c_str()); } VOID TEST(ProtocolUtilityTest, GenerateTcUrl) { string ip; string vhost; string app; string port; string tcUrl; ip = "127.0.0.1"; vhost = "__defaultVhost__"; app = "live"; port = "1935"; tcUrl = srs_generate_tc_url(ip, vhost, app, port); EXPECT_STREQ("rtmp://127.0.0.1/live", tcUrl.c_str()); ip = "127.0.0.1"; vhost = "demo"; app = "live"; port = "1935"; tcUrl = srs_generate_tc_url(ip, vhost, app, port); EXPECT_STREQ("rtmp://demo/live", tcUrl.c_str()); ip = "127.0.0.1"; vhost = "demo"; app = "live"; port = "19351"; tcUrl = srs_generate_tc_url(ip, vhost, app, port); EXPECT_STREQ("rtmp://demo:19351/live", tcUrl.c_str()); } VOID TEST(ProtocolMsgArrayTest, MessageArray) { SrsMessageHeader header; SrsSharedPtrMessage msg; char* payload = new char[1024]; EXPECT_TRUE(ERROR_SUCCESS == msg.create(&header, payload, 1024)); EXPECT_EQ(0, msg.count()); if (true) { SrsSharedPtrMessageArray arr(3); arr.msgs[0] = msg.copy(); EXPECT_EQ(1, msg.count()); arr.msgs[1] = msg.copy(); EXPECT_EQ(2, msg.count()); arr.msgs[2] = msg.copy(); EXPECT_EQ(3, msg.count()); } EXPECT_EQ(0, msg.count()); if (true) { SrsSharedPtrMessageArray arr(3); arr.msgs[0] = msg.copy(); EXPECT_EQ(1, msg.count()); arr.msgs[2] = msg.copy(); EXPECT_EQ(2, msg.count()); } EXPECT_EQ(0, msg.count()); } VOID TEST(ProtocolStackTest, ProtocolTimeout) { MockBufferIO bio; SrsProtocol proto(&bio); EXPECT_TRUE((int64_t)ST_UTIME_NO_TIMEOUT == proto.get_recv_timeout()); EXPECT_TRUE((int64_t)ST_UTIME_NO_TIMEOUT == proto.get_send_timeout()); proto.set_recv_timeout(10); EXPECT_TRUE(10 == proto.get_recv_timeout()); proto.set_send_timeout(10); EXPECT_TRUE(10 == proto.get_send_timeout()); } VOID TEST(ProtocolStackTest, ProtocolBytes) { MockBufferIO bio; SrsProtocol proto(&bio); EXPECT_TRUE(0 == proto.get_recv_bytes()); EXPECT_TRUE(0 == proto.get_send_bytes()); SrsConnectAppPacket* pkt = new SrsConnectAppPacket(); EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0)); EXPECT_TRUE(0 < proto.get_send_bytes()); } VOID TEST(ProtocolStackTest, ProtocolRecvMessage) { MockBufferIO bio; SrsProtocol proto(&bio); // packet is SrsConnectAppPacket char data[] = { // 12bytes header, 1byts chunk header, 11bytes msg heder (char)0x03, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x01, (char)0xa1, (char)0x14, (char)0x00, (char)0x00, (char)0x00, (char)0x00, // msg payload start (char)0x02, (char)0x00, (char)0x07, (char)0x63, (char)0x6f, (char)0x6e, (char)0x6e, (char)0x65, (char)0x63, (char)0x74, (char)0x00, (char)0x3f, (char)0xf0, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x03, (char)0x00, (char)0x03, (char)0x61, (char)0x70, (char)0x70, (char)0x02, (char)0x00, (char)0x04, (char)0x6c, (char)0x69, (char)0x76, (char)0x65, (char)0x00, (char)0x08, (char)0x66, (char)0x6c, (char)0x61, (char)0x73, (char)0x68, (char)0x56, (char)0x65, (char)0x72, (char)0x02, (char)0x00, (char)0x0d, (char)0x57, (char)0x49, (char)0x4e, (char)0x20, (char)0x31, (char)0x32, (char)0x2c, (char)0x30, (char)0x2c, (char)0x30, (char)0x2c, (char)0x34, (char)0x31, (char)0x00, (char)0x06, (char)0x73, (char)0x77, (char)0x66, (char)0x55, (char)0x72, (char)0x6c, (char)0x02, (char)0x00, (char)0x51, (char)0x68, (char)0x74, (char)0x74, (char)0x70, (char)0x3a, (char)0x2f, (char)0x2f, (char)0x77, (char)0x77, (char)0x77, (char)0x2e, (char)0x6f, (char)0x73, (char)0x73, (char)0x72, (char)0x73, (char)0x2e, (char)0x6e, (char)0x65, (char)0x74, (char)0x3a, (char)0x38, (char)0x30, (char)0x38, (char)0x35, (char)0x2f, (char)0x70, (char)0x6c, (char)0x61, (char)0x79, (char)0x65, (char)0x72, (char)0x73, (char)0x2f, (char)0x73, (char)0x72, (char)0x73, (char)0x5f, (char)0x70, (char)0x6c, (char)0x61, (char)0x79, (char)0x65, (char)0x72, (char)0x2f, (char)0x72, (char)0x65, (char)0x6c, (char)0x65, (char)0x61, (char)0x73, (char)0x65, (char)0x2f, (char)0x73, (char)0x72, (char)0x73, (char)0x5f, (char)0x70, (char)0x6c, (char)0xC3, /*next chunk.*/ (char)0x61, (char)0x79, (char)0x65, (char)0x72, (char)0x2e, (char)0x73, (char)0x77, (char)0x66, (char)0x3f, (char)0x5f, (char)0x76, (char)0x65, (char)0x72, (char)0x73, (char)0x69, (char)0x6f, (char)0x6e, (char)0x3d, (char)0x31, (char)0x2e, (char)0x32, (char)0x33, (char)0x00, (char)0x05, (char)0x74, (char)0x63, (char)0x55, (char)0x72, (char)0x6c, (char)0x02, (char)0x00, (char)0x14, (char)0x72, (char)0x74, (char)0x6d, (char)0x70, (char)0x3a, (char)0x2f, (char)0x2f, (char)0x64, (char)0x65, (char)0x76, (char)0x3a, (char)0x31, (char)0x39, (char)0x33, (char)0x35, (char)0x2f, (char)0x6c, (char)0x69, (char)0x76, (char)0x65, (char)0x00, (char)0x04, (char)0x66, (char)0x70, (char)0x61, (char)0x64, (char)0x01, (char)0x00, (char)0x00, (char)0x0c, (char)0x63, (char)0x61, (char)0x70, (char)0x61, (char)0x62, (char)0x69, (char)0x6c, (char)0x69, (char)0x74, (char)0x69, (char)0x65, (char)0x73, (char)0x00, (char)0x40, (char)0x6d, (char)0xe0, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x0b, (char)0x61, (char)0x75, (char)0x64, (char)0x69, (char)0x6f, (char)0x43, (char)0x6f, (char)0x64, (char)0x65, (char)0x63, (char)0x73, (char)0x00, (char)0x40, (char)0xab, (char)0xee, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x0b, (char)0x76, (char)0x69, (char)0x64, (char)0x65, (char)0x6f, (char)0x43, (char)0x6f, (char)0x64, (char)0x65, (char)0x63, (char)0x73, (char)0x00, (char)0x40, (char)0x6f, (char)0x80, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0xC3, /*next chunk.*/ (char)0x0d, (char)0x76, (char)0x69, (char)0x64, (char)0x65, (char)0x6f, (char)0x46, (char)0x75, (char)0x6e, (char)0x63, (char)0x74, (char)0x69, (char)0x6f, (char)0x6e, (char)0x00, (char)0x3f, (char)0xf0, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x07, (char)0x70, (char)0x61, (char)0x67, (char)0x65, (char)0x55, (char)0x72, (char)0x6c, (char)0x02, (char)0x00, (char)0x62, (char)0x68, (char)0x74, (char)0x74, (char)0x70, (char)0x3a, (char)0x2f, (char)0x2f, (char)0x77, (char)0x77, (char)0x77, (char)0x2e, (char)0x6f, (char)0x73, (char)0x73, (char)0x72, (char)0x73, (char)0x2e, (char)0x6e, (char)0x65, (char)0x74, (char)0x3a, (char)0x38, (char)0x30, (char)0x38, (char)0x35, (char)0x2f, (char)0x70, (char)0x6c, (char)0x61, (char)0x79, (char)0x65, (char)0x72, (char)0x73, (char)0x2f, (char)0x73, (char)0x72, (char)0x73, (char)0x5f, (char)0x70, (char)0x6c, (char)0x61, (char)0x79, (char)0x65, (char)0x72, (char)0x2e, (char)0x68, (char)0x74, (char)0x6d, (char)0x6c, (char)0x3f, (char)0x76, (char)0x68, (char)0x6f, (char)0x73, (char)0x74, (char)0x3d, (char)0x64, (char)0x65, (char)0x76, (char)0x26, (char)0x73, (char)0x74, (char)0x72, (char)0x65, (char)0x61, (char)0x6d, (char)0x3d, (char)0x6c, (char)0x69, (char)0x76, (char)0x65, (char)0x73, (char)0x74, (char)0x72, (char)0x65, (char)0x61, (char)0x6d, (char)0x26, (char)0x73, (char)0x65, (char)0x72, (char)0x76, (char)0x65, (char)0x72, (char)0x3d, (char)0x64, (char)0x65, (char)0x76, (char)0x26, (char)0x70, (char)0x6f, (char)0x72, (char)0x74, (char)0xC3, /*next chunk.*/ (char)0x3d, (char)0x31, (char)0x39, (char)0x33, (char)0x35, (char)0x00, (char)0x0e, (char)0x6f, (char)0x62, (char)0x6a, (char)0x65, (char)0x63, (char)0x74, (char)0x45, (char)0x6e, (char)0x63, (char)0x6f, (char)0x64, (char)0x69, (char)0x6e, (char)0x67, (char)0x00, (char)0x40, (char)0x08, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x09 }; bio.in_buffer.append(data, sizeof(data)); SrsMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); SrsAutoFree(SrsMessage, msg); SrsPacket* pkt = NULL; EXPECT_TRUE(ERROR_SUCCESS == proto.decode_message(msg, &pkt)); SrsAutoFree(SrsPacket, pkt); SrsConnectAppPacket* spkt = dynamic_cast(pkt); ASSERT_TRUE(NULL != spkt); } // for librtmp, if ping, it will send a fresh stream with fmt=1, // 0x42 where: fmt=1, cid=2, protocol contorl user-control message // 0x00 0x00 0x00 where: timestamp=0 // 0x00 0x00 0x06 where: payload_length=6 // 0x04 where: message_type=4(protocol control user-control message) // 0x00 0x06 where: event Ping(0x06) // 0x00 0x00 0x0d 0x0f where: event data 4bytes ping timestamp. // @see: https://github.com/winlinvip/simple-rtmp-server/issues/98 VOID TEST(ProtocolStackTest, ProtocolRecvMessageBug98) { MockBufferIO bio; SrsProtocol proto(&bio); // packet is SrsConnectAppPacket char data[] = { (char)0x42, // 1bytes chunk header (char)0x00, (char)0x00, (char)0x00, // timestamp=0 (char)0x00, (char)0x00, (char)0x06, // payload_length=6 (char)0x04, // message_type=4(protocol control user-control message) (char)0x00, (char)0x06, // event Ping(0x06) (char)0x00, (char)0x00, (char)0x0d, (char)0x0f // event data 4bytes ping timestamp. }; bio.in_buffer.append(data, sizeof(data)); SrsMessage* msg = NULL; ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg)); SrsAutoFree(SrsMessage, msg); SrsPacket* pkt = NULL; EXPECT_TRUE(ERROR_SUCCESS == proto.decode_message(msg, &pkt)); SrsAutoFree(SrsPacket, pkt); SrsUserControlPacket* spkt = dynamic_cast(pkt); ASSERT_TRUE(NULL != spkt); EXPECT_EQ(SrcPCUCPingRequest, spkt->event_type); EXPECT_EQ(0x0d0f, spkt->event_data); }