Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions moxygen/MoQSession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1968,6 +1968,10 @@ class MoQSession::SubscribeTrackReceiveState
<< "deliverPublishDoneAndRemove: Delivering PUBLISH_DONE to app; statusCode="
<< folly::to_underlying(pendingPublishDone_->statusCode)
<< " alias=" << alias_ << " requestID=" << requestID_;
MOQ_SUBSCRIBER_STATS(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@akash-a-n Publish_Done has meaning wrt to moqt spec so moving it here tracks publish_done received on the wire incorrectly. Can we have another callback for "subscription ended"?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it.
I think #159 addresses it with a new callbacks

session_->subscriberStatsCallback_,
onPublishDone,
pendingPublishDone_->statusCode);
auto token = cancelSource_.getToken();
auto cb = std::exchange(callback_, nullptr);
cb->publishDone(std::move(*pendingPublishDone_));
Expand Down Expand Up @@ -4078,8 +4082,6 @@ void MoQSession::onPublishDone(PublishDone publishDone) {
if (logger_) {
logger_->logPublishDone(publishDone, ControlMessageType::PARSED);
}
MOQ_SUBSCRIBER_STATS(
subscriberStatsCallback_, onPublishDone, publishDone.statusCode);

// Handle regular subscription PUBLISH_DONE
auto trackAliasIt = reqIdToTrackAlias_.find(publishDone.requestID);
Expand Down
34 changes: 34 additions & 0 deletions moxygen/test/MoQSessionSubscribeTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -806,6 +806,9 @@ CO_TEST_P_X(MoQSessionTest, UnsubscribeWithinPublishDone) {
auto subscribeHandler = res.value();

EXPECT_CALL(*clientSubscriberStatsCallback_, onUnsubscribe());
EXPECT_CALL(
*clientSubscriberStatsCallback_,
onPublishDone(PublishDoneStatusCode::TRACK_ENDED));

EXPECT_CALL(*subscribeCallback_, publishDone(_))
.WillOnce(testing::Invoke([&](const auto&) {
Expand Down Expand Up @@ -1020,6 +1023,9 @@ CO_TEST_P_X(MoQSessionTest, UnsubscribeFromWithinPublishDoneHandler) {
// When publishDone is delivered, immediately call unsubscribe() from
// handler
folly::coro::Baton publishDoneInvoked;
EXPECT_CALL(
*clientSubscriberStatsCallback_,
onPublishDone(PublishDoneStatusCode::TRACK_ENDED));
EXPECT_CALL(*subscribeCallback_, publishDone(_))
.WillOnce(testing::Invoke([&](const auto&) {
subscribeHandle->unsubscribe();
Expand Down Expand Up @@ -1107,3 +1113,31 @@ CO_TEST_P_X(MoQSessionTest, BeginSubgroupCallbackError) {
co_await publishDone_;
clientSession_->close(SessionCloseErrorCode::NO_ERROR);
}

// Regression test: the onPublishDone subscriber stat must fire when the
// subscription ends due to session closure, even if the publisher never
// sent a PUBLISH_DONE frame (abandoned publisher).
CO_TEST_P_X(MoQSessionTest, OnPublishDoneStatFiredOnAbandonedPublisher) {
co_await setupMoQSession();
expectSubscribe([](auto sub, auto) -> TaskSubscribeResult {
// Publisher accepts the subscription but intentionally never calls
// publishDone, simulating an abandoned / improperly-closed publisher.
co_return makeSubscribeOkResult(sub);
});

auto res = co_await clientSession_->subscribe(
getSubscribe(kTestTrackName), subscribeCallback_);
EXPECT_FALSE(res.hasError());

// When the session closes the client synthesizes a SESSION_CLOSED
// publishDone for every active subscription via the subscribeError path.
// The stat must fire here even though no PUBLISH_DONE frame was received.
EXPECT_CALL(
*clientSubscriberStatsCallback_,
onPublishDone(PublishDoneStatusCode::SESSION_CLOSED));
EXPECT_CALL(*subscribeCallback_, publishDone(_))
.WillOnce(testing::Return(folly::unit));

clientSession_->close(SessionCloseErrorCode::NO_ERROR);
serverSession_->close(SessionCloseErrorCode::NO_ERROR);
}
Loading