// // Copyright (c) 2013-2021 The SRS Authors // // SPDX-License-Identifier: MIT // #include using namespace std; #include #include #include #include #include #include #include class MockIDResource : public ISrsResource { public: int id; MockIDResource(int v) { id = v; } virtual ~MockIDResource() { } virtual const SrsContextId& get_id() { return _srs_context->get_id(); } virtual std::string desc() { return ""; } }; VOID TEST(AppResourceManagerTest, FindByFastID) { srs_error_t err = srs_success; if (true) { SrsResourceManager m("test"); HELPER_EXPECT_SUCCESS(m.start()); m.add_with_fast_id(101, new MockIDResource(1)); m.add_with_fast_id(102, new MockIDResource(2)); m.add_with_fast_id(103, new MockIDResource(3)); EXPECT_EQ(1, ((MockIDResource*)m.find_by_fast_id(101))->id); EXPECT_EQ(2, ((MockIDResource*)m.find_by_fast_id(102))->id); EXPECT_EQ(3, ((MockIDResource*)m.find_by_fast_id(103))->id); } if (true) { SrsResourceManager m("test"); HELPER_EXPECT_SUCCESS(m.start()); MockIDResource* r1 = new MockIDResource(1); MockIDResource* r2 = new MockIDResource(2); MockIDResource* r3 = new MockIDResource(3); m.add_with_fast_id(101, r1); m.add_with_fast_id(102, r2); m.add_with_fast_id(103, r3); EXPECT_EQ(1, ((MockIDResource*)m.find_by_fast_id(101))->id); EXPECT_EQ(2, ((MockIDResource*)m.find_by_fast_id(102))->id); EXPECT_EQ(3, ((MockIDResource*)m.find_by_fast_id(103))->id); m.remove(r2); srs_usleep(0); EXPECT_TRUE(m.find_by_fast_id(102) == NULL); } if (true) { SrsResourceManager m("test"); HELPER_EXPECT_SUCCESS(m.start()); MockIDResource* r1 = new MockIDResource(1); MockIDResource* r2 = new MockIDResource(2); MockIDResource* r3 = new MockIDResource(3); m.add_with_fast_id(1, r1); m.add_with_fast_id(100001, r2); m.add_with_fast_id(1000001, r3); EXPECT_EQ(1, ((MockIDResource*)m.find_by_fast_id(1))->id); EXPECT_EQ(2, ((MockIDResource*)m.find_by_fast_id(100001))->id); EXPECT_EQ(3, ((MockIDResource*)m.find_by_fast_id(1000001))->id); m.remove(r2); srs_usleep(0); EXPECT_TRUE(m.find_by_fast_id(100001) == NULL); m.remove(r3); srs_usleep(0); EXPECT_TRUE(m.find_by_fast_id(1000001) == NULL); m.remove(r1); srs_usleep(0); EXPECT_TRUE(m.find_by_fast_id(1) == NULL); } if (true) { SrsResourceManager m("test"); HELPER_EXPECT_SUCCESS(m.start()); m.add_with_fast_id(101, new MockIDResource(1)); m.add_with_fast_id(10101, new MockIDResource(2)); m.add_with_fast_id(1010101, new MockIDResource(3)); m.add_with_fast_id(101010101, new MockIDResource(4)); m.add_with_fast_id(10101010101LL, new MockIDResource(5)); m.add_with_fast_id(1010101010101LL, new MockIDResource(6)); m.add_with_fast_id(101010101010101LL, new MockIDResource(7)); m.add_with_fast_id(10101010101010101LL, new MockIDResource(8)); m.add_with_fast_id(1010101010101010101ULL, new MockIDResource(9)); m.add_with_fast_id(11010101010101010101ULL, new MockIDResource(10)); EXPECT_EQ(1, ((MockIDResource*)m.find_by_fast_id(101))->id); EXPECT_EQ(2, ((MockIDResource*)m.find_by_fast_id(10101))->id); EXPECT_EQ(3, ((MockIDResource*)m.find_by_fast_id(1010101))->id); EXPECT_EQ(4, ((MockIDResource*)m.find_by_fast_id(101010101))->id); EXPECT_EQ(5, ((MockIDResource*)m.find_by_fast_id(10101010101LL))->id); EXPECT_EQ(6, ((MockIDResource*)m.find_by_fast_id(1010101010101LL))->id); EXPECT_EQ(7, ((MockIDResource*)m.find_by_fast_id(101010101010101LL))->id); EXPECT_EQ(8, ((MockIDResource*)m.find_by_fast_id(10101010101010101LL))->id); EXPECT_EQ(9, ((MockIDResource*)m.find_by_fast_id(1010101010101010101ULL))->id); EXPECT_EQ(10, ((MockIDResource*)m.find_by_fast_id(11010101010101010101ULL))->id); } if (true) { SrsResourceManager m("test"); HELPER_EXPECT_SUCCESS(m.start()); m.add_with_fast_id(101, new MockIDResource(1)); m.add_with_fast_id(101, new MockIDResource(4)); EXPECT_EQ(1, ((MockIDResource*)m.find_by_fast_id(101))->id); } } VOID TEST(AppCoroutineTest, Dummy) { SrsDummyCoroutine dc; if (true) { SrsContextId v = dc.cid(); EXPECT_FALSE(v.empty()); srs_error_t err = dc.pull(); EXPECT_TRUE(err != srs_success); EXPECT_TRUE(ERROR_THREAD_DUMMY == srs_error_code(err)); srs_freep(err); err = dc.start(); EXPECT_TRUE(err != srs_success); EXPECT_TRUE(ERROR_THREAD_DUMMY == srs_error_code(err)); srs_freep(err); } if (true) { dc.stop(); SrsContextId v = dc.cid(); EXPECT_FALSE(v.empty()); srs_error_t err = dc.pull(); EXPECT_TRUE(err != srs_success); EXPECT_TRUE(ERROR_THREAD_DUMMY == srs_error_code(err)); srs_freep(err); err = dc.start(); EXPECT_TRUE(err != srs_success); EXPECT_TRUE(ERROR_THREAD_DUMMY == srs_error_code(err)); srs_freep(err); } if (true) { dc.interrupt(); SrsContextId v = dc.cid(); EXPECT_FALSE(v.empty()); srs_error_t err = dc.pull(); EXPECT_TRUE(err != srs_success); EXPECT_TRUE(ERROR_THREAD_DUMMY == srs_error_code(err)); srs_freep(err); err = dc.start(); EXPECT_TRUE(err != srs_success); EXPECT_TRUE(ERROR_THREAD_DUMMY == srs_error_code(err)); srs_freep(err); } } class MockCoroutineHandler : public ISrsCoroutineHandler { public: SrsSTCoroutine* trd; srs_error_t err; srs_cond_t running; srs_cond_t exited; SrsContextId cid; // Quit without error. bool quit; public: MockCoroutineHandler() : trd(NULL), err(srs_success), quit(false) { cid.set_value("0"); running = srs_cond_new(); exited = srs_cond_new(); } virtual ~MockCoroutineHandler() { srs_cond_destroy(running); srs_cond_destroy(exited); } public: virtual srs_error_t cycle() { srs_error_t r0 = srs_success; srs_cond_signal(running); cid = _srs_context->get_id(); while (!quit && (r0 = trd->pull()) == srs_success && err == srs_success) { srs_usleep(10 * SRS_UTIME_MILLISECONDS); } srs_cond_signal(exited); if (err != srs_success) { srs_freep(r0); return err; } return r0; } }; VOID TEST(AppCoroutineTest, StartStop) { if (true) { MockCoroutineHandler ch; SrsSTCoroutine sc("test", &ch); ch.trd = ≻ EXPECT_TRUE(sc.cid().empty()); // Thread stop after created. sc.stop(); EXPECT_TRUE(sc.cid().empty()); srs_error_t err = sc.pull(); EXPECT_TRUE(srs_success != err); EXPECT_TRUE(ERROR_THREAD_TERMINATED == srs_error_code(err)); srs_freep(err); // Should never reuse a disposed thread. err = sc.start(); EXPECT_TRUE(srs_success != err); EXPECT_TRUE(ERROR_THREAD_DISPOSED == srs_error_code(err)); srs_freep(err); } if (true) { MockCoroutineHandler ch; SrsSTCoroutine sc("test", &ch); ch.trd = ≻ EXPECT_TRUE(sc.cid().empty()); EXPECT_TRUE(srs_success == sc.start()); EXPECT_TRUE(srs_success == sc.pull()); srs_cond_timedwait(ch.running, 100 * SRS_UTIME_MILLISECONDS); EXPECT_TRUE(!sc.cid().empty()); // Thread stop after started. sc.stop(); srs_error_t err = sc.pull(); EXPECT_TRUE(srs_success != err); EXPECT_TRUE(ERROR_THREAD_INTERRUPED == srs_error_code(err)); srs_freep(err); // Should never reuse a disposed thread. err = sc.start(); EXPECT_TRUE(srs_success != err); EXPECT_TRUE(ERROR_THREAD_DISPOSED == srs_error_code(err)); srs_freep(err); } if (true) { MockCoroutineHandler ch; SrsSTCoroutine sc("test", &ch); ch.trd = ≻ EXPECT_TRUE(sc.cid().empty()); EXPECT_TRUE(srs_success == sc.start()); EXPECT_TRUE(srs_success == sc.pull()); // Error when start multiple times. srs_error_t err = sc.start(); EXPECT_TRUE(srs_success != err); EXPECT_TRUE(ERROR_THREAD_STARTED == srs_error_code(err)); srs_freep(err); err = sc.pull(); EXPECT_TRUE(srs_success != err); EXPECT_TRUE(ERROR_THREAD_STARTED == srs_error_code(err)); srs_freep(err); } } VOID TEST(AppCoroutineTest, Cycle) { if (true) { MockCoroutineHandler ch; SrsSTCoroutine sc("test", &ch); ch.trd = ≻ EXPECT_TRUE(srs_success == sc.start()); EXPECT_TRUE(srs_success == sc.pull()); // Set cycle to error. ch.err = srs_error_new(-1, "cycle"); srs_cond_timedwait(ch.running, 100 * SRS_UTIME_MILLISECONDS); // The cycle error should be pulled. srs_error_t err = sc.pull(); EXPECT_TRUE(srs_success != err); EXPECT_TRUE(-1 == srs_error_code(err)); srs_freep(err); } if (true) { MockCoroutineHandler ch; SrsContextId cid; SrsSTCoroutine sc("test", &ch, cid.set_value("250")); ch.trd = ≻ EXPECT_TRUE(!sc.cid().compare(cid)); EXPECT_TRUE(srs_success == sc.start()); EXPECT_TRUE(srs_success == sc.pull()); // After running, the cid in cycle should equal to the thread. srs_cond_timedwait(ch.running, 100 * SRS_UTIME_MILLISECONDS); EXPECT_TRUE(!ch.cid.compare(cid)); } if (true) { MockCoroutineHandler ch; SrsSTCoroutine sc("test", &ch); ch.trd = ≻ EXPECT_TRUE(srs_success == sc.start()); EXPECT_TRUE(srs_success == sc.pull()); srs_cond_timedwait(ch.running, 100 * SRS_UTIME_MILLISECONDS); // Interrupt thread, set err to interrupted. sc.interrupt(); // Set cycle to error. ch.err = srs_error_new(-1, "cycle"); // When thread terminated, thread will get its error. srs_cond_timedwait(ch.exited, 100 * SRS_UTIME_MILLISECONDS); // Override the error by cycle error. sc.stop(); // Should be cycle error. srs_error_t err = sc.pull(); EXPECT_TRUE(srs_success != err); EXPECT_TRUE(-1 == srs_error_code(err)); srs_freep(err); } if (true) { MockCoroutineHandler ch; SrsSTCoroutine sc("test", &ch); ch.trd = ≻ EXPECT_TRUE(srs_success == sc.start()); EXPECT_TRUE(srs_success == sc.pull()); // Quit without error. ch.quit = true; // Wait for thread to done. srs_cond_timedwait(ch.exited, 100 * SRS_UTIME_MILLISECONDS); // Override the error by cycle error. sc.stop(); // Should be cycle error. srs_error_t err = sc.pull(); EXPECT_TRUE(srs_success == err); srs_freep(err); } } void* mock_st_thread_create(void *(*/*start*/)(void *arg), void */*arg*/, int /*joinable*/, int /*stack_size*/) { return NULL; } VOID TEST(AppCoroutineTest, StartThread) { MockCoroutineHandler ch; SrsSTCoroutine sc("test", &ch); ch.trd = ≻ _ST_THREAD_CREATE_PFN ov = _pfn_st_thread_create; _pfn_st_thread_create = (_ST_THREAD_CREATE_PFN)mock_st_thread_create; srs_error_t err = sc.start(); _pfn_st_thread_create = ov; EXPECT_TRUE(srs_success != err); EXPECT_TRUE(ERROR_ST_CREATE_CYCLE_THREAD == srs_error_code(err)); srs_freep(err); } VOID TEST(AppFragmentTest, CheckDuration) { if (true) { SrsFragment frg; EXPECT_EQ(-1, frg.start_dts); EXPECT_EQ(0, frg.dur); EXPECT_FALSE(frg.sequence_header); } if (true) { SrsFragment frg; frg.append(0); EXPECT_EQ(0, frg.duration()); frg.append(10); EXPECT_EQ(10 * SRS_UTIME_MILLISECONDS, frg.duration()); frg.append(99); EXPECT_EQ(99 * SRS_UTIME_MILLISECONDS, frg.duration()); frg.append(0x7fffffffLL); EXPECT_EQ(0x7fffffffLL * SRS_UTIME_MILLISECONDS, frg.duration()); frg.append(0xffffffffLL); EXPECT_EQ(0xffffffffLL * SRS_UTIME_MILLISECONDS, frg.duration()); frg.append(0x20c49ba5e353f7LL); EXPECT_EQ(0x20c49ba5e353f7LL * SRS_UTIME_MILLISECONDS, frg.duration()); } if (true) { SrsFragment frg; frg.append(0); EXPECT_EQ(0, frg.duration()); frg.append(0x7fffffffffffffffLL); EXPECT_EQ(0, frg.duration()); } if (true) { SrsFragment frg; frg.append(100); EXPECT_EQ(0, frg.duration()); frg.append(10); EXPECT_EQ(0, frg.duration()); frg.append(100); EXPECT_EQ(90 * SRS_UTIME_MILLISECONDS, frg.duration()); } if (true) { SrsFragment frg; frg.append(-10); EXPECT_EQ(0, frg.duration()); frg.append(-5); EXPECT_EQ(0, frg.duration()); frg.append(10); EXPECT_EQ(10 * SRS_UTIME_MILLISECONDS, frg.duration()); } } VOID TEST(AppSecurity, CheckSecurity) { srs_error_t err; // Deny if no rules. if (true) { SrsSecurity sec; SrsRequest rr; HELPER_EXPECT_FAILED(sec.do_check(NULL, SrsRtmpConnUnknown, "", &rr)); } // Deny if not allowed. if (true) { SrsSecurity sec; SrsRequest rr; SrsConfDirective rules; HELPER_EXPECT_FAILED(sec.do_check(&rules, SrsRtmpConnUnknown, "", &rr)); } if (true) { SrsSecurity sec; SrsRequest rr; SrsConfDirective rules; rules.get_or_create("others"); rules.get_or_create("any"); HELPER_EXPECT_FAILED(sec.do_check(&rules, SrsRtmpConnUnknown, "", &rr)); } // Deny by rule. if (true) { SrsSecurity sec; SrsRequest rr; SrsConfDirective rules; rules.get_or_create("deny", "play", "all"); HELPER_EXPECT_FAILED(sec.do_check(&rules, SrsRtmpConnPlay, "", &rr)); } if (true) { SrsSecurity sec; SrsRequest rr; SrsConfDirective rules; rules.get_or_create("deny", "play", "12.13.14.15"); HELPER_EXPECT_FAILED(sec.do_check(&rules, SrsRtmpConnPlay, "12.13.14.15", &rr)); } if (true) { SrsSecurity sec; SrsRequest rr; SrsConfDirective rules; rules.get_or_create("deny", "play", "11.12.13.14"); if (true) { SrsConfDirective* d = new SrsConfDirective(); d->name = "deny"; d->args.push_back("play"); d->args.push_back("12.13.14.15"); rules.directives.push_back(d); } HELPER_EXPECT_FAILED(sec.do_check(&rules, SrsRtmpConnPlay, "12.13.14.15", &rr)); } if (true) { SrsSecurity sec; SrsRequest rr; SrsConfDirective rules; rules.get_or_create("deny", "publish", "12.13.14.15"); HELPER_EXPECT_FAILED(sec.do_check(&rules, SrsRtmpConnFMLEPublish, "12.13.14.15", &rr)); } if (true) { SrsSecurity sec; SrsRequest rr; SrsConfDirective rules; rules.get_or_create("deny", "publish", "12.13.14.15"); HELPER_EXPECT_FAILED(sec.do_check(&rules, SrsRtmpConnFlashPublish, "12.13.14.15", &rr)); } if (true) { SrsSecurity sec; SrsRequest rr; SrsConfDirective rules; rules.get_or_create("deny", "publish", "all"); HELPER_EXPECT_FAILED(sec.do_check(&rules, SrsRtmpConnFlashPublish, "11.12.13.14", &rr)); } if (true) { SrsSecurity sec; SrsRequest rr; SrsConfDirective rules; rules.get_or_create("deny", "publish", "12.13.14.15"); HELPER_EXPECT_FAILED(sec.do_check(&rules, SrsRtmpConnHaivisionPublish, "12.13.14.15", &rr)); } // Allowed if not denied. if (true) { SrsSecurity sec; SrsRequest rr; SrsConfDirective rules; rules.get_or_create("deny", "play", "all"); HELPER_EXPECT_SUCCESS(sec.do_check(&rules, SrsRtmpConnFMLEPublish, "12.13.14.15", &rr)); } if (true) { SrsSecurity sec; SrsRequest rr; SrsConfDirective rules; rules.get_or_create("deny", "play", "12.13.14.15"); HELPER_EXPECT_SUCCESS(sec.do_check(&rules, SrsRtmpConnFMLEPublish, "12.13.14.15", &rr)); } if (true) { SrsSecurity sec; SrsRequest rr; SrsConfDirective rules; rules.get_or_create("deny", "play", "12.13.14.15"); HELPER_EXPECT_SUCCESS(sec.do_check(&rules, SrsRtmpConnPlay, "11.12.13.14", &rr)); } if (true) { SrsSecurity sec; SrsRequest rr; SrsConfDirective rules; rules.get_or_create("deny", "publish", "12.13.14.15"); HELPER_EXPECT_SUCCESS(sec.do_check(&rules, SrsRtmpConnPlay, "12.13.14.15", &rr)); } if (true) { SrsSecurity sec; SrsRequest rr; SrsConfDirective rules; rules.get_or_create("deny", "publish", "12.13.14.15"); HELPER_EXPECT_SUCCESS(sec.do_check(&rules, SrsRtmpConnUnknown, "12.13.14.15", &rr)); } if (true) { SrsSecurity sec; SrsRequest rr; SrsConfDirective rules; rules.get_or_create("deny", "publish", "12.13.14.15"); HELPER_EXPECT_SUCCESS(sec.do_check(&rules, SrsRtmpConnFlashPublish, "11.12.13.14", &rr)); } // Allowed by rule. if (true) { SrsSecurity sec; SrsRequest rr; SrsConfDirective rules; rules.get_or_create("allow", "play", "12.13.14.15"); HELPER_EXPECT_SUCCESS(sec.do_check(&rules, SrsRtmpConnPlay, "12.13.14.15", &rr)); } if (true) { SrsSecurity sec; SrsRequest rr; SrsConfDirective rules; rules.get_or_create("allow", "play", "all"); HELPER_EXPECT_SUCCESS(sec.do_check(&rules, SrsRtmpConnPlay, "12.13.14.15", &rr)); } if (true) { SrsSecurity sec; SrsRequest rr; SrsConfDirective rules; rules.get_or_create("allow", "publish", "all"); HELPER_EXPECT_SUCCESS(sec.do_check(&rules, SrsRtmpConnFMLEPublish, "12.13.14.15", &rr)); } if (true) { SrsSecurity sec; SrsRequest rr; SrsConfDirective rules; rules.get_or_create("allow", "publish", "all"); HELPER_EXPECT_SUCCESS(sec.do_check(&rules, SrsRtmpConnFlashPublish, "12.13.14.15", &rr)); } if (true) { SrsSecurity sec; SrsRequest rr; SrsConfDirective rules; rules.get_or_create("allow", "publish", "all"); HELPER_EXPECT_SUCCESS(sec.do_check(&rules, SrsRtmpConnHaivisionPublish, "12.13.14.15", &rr)); } if (true) { SrsSecurity sec; SrsRequest rr; SrsConfDirective rules; rules.get_or_create("allow", "publish", "12.13.14.15"); HELPER_EXPECT_SUCCESS(sec.do_check(&rules, SrsRtmpConnHaivisionPublish, "12.13.14.15", &rr)); } // Allowed if not denied. if (true) { SrsSecurity sec; SrsRequest rr; SrsConfDirective rules; rules.get_or_create("deny", "play", "12.13.14.15"); HELPER_EXPECT_SUCCESS(sec.do_check(&rules, SrsRtmpConnFMLEPublish, "12.13.14.15", &rr)); } if (true) { SrsSecurity sec; SrsRequest rr; SrsConfDirective rules; rules.get_or_create("deny", "play", "all"); HELPER_EXPECT_SUCCESS(sec.do_check(&rules, SrsRtmpConnFMLEPublish, "12.13.14.15", &rr)); } // Denied if not allowd. if (true) { SrsSecurity sec; SrsRequest rr; SrsConfDirective rules; rules.get_or_create("allow", "play", "11.12.13.14"); HELPER_EXPECT_FAILED(sec.do_check(&rules, SrsRtmpConnFMLEPublish, "12.13.14.15", &rr)); } if (true) { SrsSecurity sec; SrsRequest rr; SrsConfDirective rules; rules.get_or_create("allow", "play", "11.12.13.14"); HELPER_EXPECT_FAILED(sec.do_check(&rules, SrsRtmpConnPlay, "12.13.14.15", &rr)); } if (true) { SrsSecurity sec; SrsRequest rr; SrsConfDirective rules; rules.get_or_create("allow", "publish", "12.13.14.15"); HELPER_EXPECT_FAILED(sec.do_check(&rules, SrsRtmpConnPlay, "12.13.14.15", &rr)); } if (true) { SrsSecurity sec; SrsRequest rr; SrsConfDirective rules; rules.get_or_create("allow", "publish", "11.12.13.14"); HELPER_EXPECT_FAILED(sec.do_check(&rules, SrsRtmpConnHaivisionPublish, "12.13.14.15", &rr)); } if (true) { SrsSecurity sec; SrsRequest rr; SrsConfDirective rules; rules.get_or_create("allow", "publish", "11.12.13.14"); HELPER_EXPECT_FAILED(sec.do_check(&rules, SrsRtmpConnUnknown, "11.12.13.14", &rr)); } // Denied if dup. if (true) { SrsSecurity sec; SrsRequest rr; SrsConfDirective rules; rules.get_or_create("allow", "play", "11.12.13.14"); rules.get_or_create("deny", "play", "11.12.13.14"); HELPER_EXPECT_FAILED(sec.do_check(&rules, SrsRtmpConnPlay, "11.12.13.14", &rr)); } // SRS apply the following simple strategies one by one: // 1. allow all if security disabled. // 2. default to deny all when security enabled. // 3. allow if matches allow strategy. // 4. deny if matches deny strategy. }