Skip to content

Commit 50880eb

Browse files
Aman Sharmameta-codesync[bot]
authored andcommitted
DUPLICATE_SUBSCRIPTION for PUBLISH received during pending SUBSCRIBE_OK
Summary: This addresses a part of the PR moq-wg/moq-transport#1341 Reviewed By: afrind Differential Revision: D95634475 fbshipit-source-id: de071715d07ee72f7bb078a6bcf05d60a184b070
1 parent 2afb132 commit 50880eb

5 files changed

Lines changed: 102 additions & 3 deletions

File tree

moxygen/MoQSession.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2184,6 +2184,7 @@ void MoQSession::cleanup() {
21842184
}
21852185
pendingRequests_.clear();
21862186
pendingPublishTracks_.clear();
2187+
pendingSubscribeTracks_.clear();
21872188
if (!cancellationSource_.isCancellationRequested()) {
21882189
XLOG(DBG1) << "requestCancellation from cleanup sess=" << this;
21892190
cancellationSource_.requestCancellation();
@@ -3652,6 +3653,11 @@ void MoQSession::onRequestError(RequestError error, FrameType frameType) {
36523653
if (it != pendingRequests_.end()) {
36533654
auto pendingState = std::move(it->second);
36543655
pendingRequests_.erase(it);
3656+
// Remove from pending subscribe tracks if this was a subscribe
3657+
auto* trackPtr = pendingState->tryGetSubscribeTrack();
3658+
if (trackPtr) {
3659+
pendingSubscribeTracks_.erase((*trackPtr)->fullTrackName());
3660+
}
36553661
if (getDraftMajorVersion(*getNegotiatedVersion()) > 14) {
36563662
// determine real frame type from pendingRequest
36573663
frameType = pendingState->getErrorFrameType();
@@ -3730,6 +3736,7 @@ void MoQSession::onSubscribeOk(SubscribeOk subOk) {
37303736
}
37313737
auto trackReceiveState = std::move(*trackPtr);
37323738
pendingRequests_.erase(it);
3739+
pendingSubscribeTracks_.erase(trackReceiveState->fullTrackName());
37333740

37343741
auto res = reqIdToTrackAlias_.try_emplace(subOk.requestID, subOk.trackAlias);
37353742
if (!res.second) {
@@ -3886,6 +3893,19 @@ void MoQSession::onPublish(PublishRequest publish) {
38863893
return;
38873894
}
38883895

3896+
// Check for duplicate subscription: if there's already a pending outgoing
3897+
// SUBSCRIBE for the same track, reject the PUBLISH
3898+
if (pendingSubscribeTracks_.count(publish.fullTrackName)) {
3899+
XLOG(DBG1) << "Duplicate subscription for track with pending subscribe"
3900+
<< " ftn=" << publish.fullTrackName << " sess=" << this;
3901+
publishError(
3902+
PublishError{
3903+
publish.requestID,
3904+
PublishErrorCode::DUPLICATE_SUBSCRIPTION,
3905+
"duplicate subscription"});
3906+
return;
3907+
}
3908+
38893909
auto publishHandle = std::make_shared<ReceiverSubscriptionHandle>(
38903910
SubscribeOk{publish.requestID}, publish.trackAlias, shared_from_this());
38913911

@@ -4719,6 +4739,7 @@ folly::coro::Task<Publisher::SubscribeResult> MoQSession::subscribe(
47194739
fullTrackName, reqID, callback, this, trackAlias, logger_);
47204740
pendingRequests_.emplace(
47214741
reqID, PendingRequestState::makeSubscribeTrack(trackReceiveState));
4742+
pendingSubscribeTracks_.insert(fullTrackName);
47224743
auto subscribeResultTry =
47234744
co_await co_awaitTry(trackReceiveState->subscribeFuture());
47244745
if (subscribeResultTry.hasException()) {

moxygen/MoQSession.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -896,6 +896,7 @@ class MoQSession : public Subscriber,
896896
folly::F14FastMap<RequestID, std::shared_ptr<PublisherImpl>, RequestID::hash>
897897
pubTracks_;
898898
folly::F14FastSet<FullTrackName, FullTrackName::hash> pendingPublishTracks_;
899+
folly::F14FastSet<FullTrackName, FullTrackName::hash> pendingSubscribeTracks_;
899900
folly::F14FastMap<TrackAlias, std::list<Payload>, TrackAlias::hash>
900901
bufferedDatagrams_;
901902
folly::F14FastMap<TrackAlias, std::list<TimedBaton*>, TrackAlias::hash>

moxygen/test/MoQSessionPublishTests.cpp

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -938,10 +938,13 @@ CO_TEST_P_X(MoQSessionTest, SubscribeDuplicatesPendingPublish) {
938938

939939
// Use a baton to delay the PUBLISH_OK so the publish stays pending
940940
folly::coro::Baton publishOkBaton;
941+
bool publishMockCalled = false;
941942
EXPECT_CALL(*serverSubscriber, publish(_, _))
942943
.WillOnce(
943-
[&publishOkBaton](
944-
PublishRequest actualPub, auto) -> Subscriber::PublishResult {
944+
[&publishOkBaton, &publishMockCalled](
945+
const PublishRequest& actualPub,
946+
auto) -> Subscriber::PublishResult {
947+
publishMockCalled = true;
945948
auto consumer =
946949
std::make_shared<testing::NiceMock<MockTrackConsumer>>();
947950
ON_CALL(*consumer, setTrackAlias(_))
@@ -972,7 +975,12 @@ CO_TEST_P_X(MoQSessionTest, SubscribeDuplicatesPendingPublish) {
972975
auto publishResult =
973976
clientSession_->publish(std::move(pub), makePublishHandle());
974977
EXPECT_TRUE(publishResult.hasValue());
975-
co_await folly::coro::co_reschedule_on_current_executor;
978+
979+
// Wait for the server to process the PUBLISH (handlePublish is dispatched
980+
// asynchronously via .start(), so multiple event loop iterations may be
981+
// needed)
982+
co_await rescheduleN(5);
983+
EXPECT_TRUE(publishMockCalled);
976984

977985
// Server subscribes to client for the same track while publish is pending
978986
auto subRes = co_await serverSession_->subscribe(
@@ -988,3 +996,64 @@ CO_TEST_P_X(MoQSessionTest, SubscribeDuplicatesPendingPublish) {
988996

989997
clientSession_->close(SessionCloseErrorCode::NO_ERROR);
990998
}
999+
CO_TEST_P_X(MoQSessionTest, PublishDuplicatesPendingSubscribe) {
1000+
co_await setupMoQSessionForPublish(initialMaxRequestID_);
1001+
1002+
FullTrackName ftn{TrackNamespace{{"test"}}, "test-track"};
1003+
1004+
// Use a baton to delay the SUBSCRIBE_OK so the subscribe stays pending
1005+
folly::coro::Baton subscribeOkBaton;
1006+
bool subscribeMockCalled = false;
1007+
expectSubscribe(
1008+
[&subscribeOkBaton, &subscribeMockCalled](
1009+
auto sub, auto) -> TaskSubscribeResult {
1010+
subscribeMockCalled = true;
1011+
co_await subscribeOkBaton;
1012+
co_return makeSubscribeOkResult(sub);
1013+
});
1014+
1015+
// Client subscribes — start eagerly so it populates pendingSubscribeTracks_
1016+
auto subscribeConsumer =
1017+
std::make_shared<testing::NiceMock<MockTrackConsumer>>();
1018+
ON_CALL(*subscribeConsumer, setTrackAlias(_))
1019+
.WillByDefault(
1020+
testing::Return(
1021+
folly::Expected<folly::Unit, MoQPublishError>(folly::unit)));
1022+
ON_CALL(*subscribeConsumer, publishDone(_))
1023+
.WillByDefault(testing::Return(folly::unit));
1024+
auto subscribeSf =
1025+
clientSession_->subscribe(getSubscribe(ftn), subscribeConsumer)
1026+
.scheduleOn(&eventBase_)
1027+
.start();
1028+
1029+
// Wait for the SUBSCRIBE to be delivered and processed by the server
1030+
co_await rescheduleN(5);
1031+
EXPECT_TRUE(subscribeMockCalled);
1032+
1033+
// Server sends PUBLISH for the same track while subscribe is pending.
1034+
// Client should reject with DUPLICATE_SUBSCRIPTION.
1035+
auto handle = makePublishHandle();
1036+
auto publishResult = serverSession_->publish(
1037+
PublishRequest{
1038+
RequestID(0),
1039+
ftn,
1040+
TrackAlias(100),
1041+
GroupOrder::Default,
1042+
AbsoluteLocation{0, 100},
1043+
true,
1044+
},
1045+
handle);
1046+
EXPECT_TRUE(publishResult.hasValue());
1047+
1048+
auto replyRes = co_await std::move(publishResult.value().reply);
1049+
EXPECT_TRUE(replyRes.hasError());
1050+
EXPECT_EQ(
1051+
replyRes.error().errorCode, PublishErrorCode::DUPLICATE_SUBSCRIPTION);
1052+
1053+
// Let the SUBSCRIBE_OK through and complete the subscribe
1054+
subscribeOkBaton.post();
1055+
auto subRes = co_await std::move(subscribeSf).via(&eventBase_);
1056+
EXPECT_TRUE(subRes.hasValue());
1057+
1058+
clientSession_->close(SessionCloseErrorCode::NO_ERROR);
1059+
}

moxygen/test/MoQSessionTestCommon.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,4 +480,10 @@ uint8_t MoQSessionTest::getRequestIDMultiplier() const {
480480
return 2;
481481
}
482482

483+
folly::coro::Task<void> MoQSessionTest::rescheduleN(int n) {
484+
for (int i = 0; i < n; ++i) {
485+
co_await folly::coro::co_reschedule_on_current_executor;
486+
}
487+
}
488+
483489
}} // namespace moxygen::test

moxygen/test/MoQSessionTestCommon.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,8 @@ class MoQSessionTest : public testing::TestWithParam<VersionParams>,
227227
std::shared_ptr<MockSubgroupConsumer> sgc)>;
228228
folly::coro::Task<void> publishValidationTest(TestLogicFn testLogic);
229229

230+
folly::coro::Task<void> rescheduleN(int n);
231+
230232
folly::EventBase eventBase_;
231233
std::shared_ptr<MoQFollyExecutorImpl> MoQExecutor_;
232234
std::unique_ptr<proxygen::test::FakeSharedWebTransport> clientWt_;

0 commit comments

Comments
 (0)