Update MOQT to draft-03 wire image. Does not add ANNOUNCE_CANCEL because it does not have a code point.

Not in production

PiperOrigin-RevId: 615417995
diff --git a/quiche/quic/moqt/moqt_framer.cc b/quiche/quic/moqt/moqt_framer.cc
index 9137418..05a0440 100644
--- a/quiche/quic/moqt/moqt_framer.cc
+++ b/quiche/quic/moqt/moqt_framer.cc
@@ -34,6 +34,7 @@
 using ::quiche::WireOptional;
 using ::quiche::WireSpan;
 using ::quiche::WireStringWithVarInt62Length;
+using ::quiche::WireUint8;
 using ::quiche::WireVarInt62;
 
 // Encoding for MOQT Locations:
@@ -288,9 +289,17 @@
 
 quiche::QuicheBuffer MoqtFramer::SerializeSubscribeOk(
     const MoqtSubscribeOk& message) {
+  if (message.largest_id.has_value()) {
+    return Serialize(WireVarInt62(MoqtMessageType::kSubscribeOk),
+                     WireVarInt62(message.subscribe_id),
+                     WireVarInt62(message.expires.ToMilliseconds()),
+                     WireUint8(1), WireVarInt62(message.largest_id->group),
+                     WireVarInt62(message.largest_id->object));
+  }
   return Serialize(WireVarInt62(MoqtMessageType::kSubscribeOk),
                    WireVarInt62(message.subscribe_id),
-                   WireVarInt62(message.expires.ToMilliseconds()));
+                   WireVarInt62(message.expires.ToMilliseconds()),
+                   WireUint8(0));
 }
 
 quiche::QuicheBuffer MoqtFramer::SerializeSubscribeError(
@@ -308,21 +317,20 @@
                    WireVarInt62(message.subscribe_id));
 }
 
-quiche::QuicheBuffer MoqtFramer::SerializeSubscribeFin(
-    const MoqtSubscribeFin& message) {
-  return Serialize(WireVarInt62(MoqtMessageType::kSubscribeFin),
-                   WireVarInt62(message.subscribe_id),
-                   WireVarInt62(message.final_group),
-                   WireVarInt62(message.final_object));
-}
-
-quiche::QuicheBuffer MoqtFramer::SerializeSubscribeRst(
-    const MoqtSubscribeRst& message) {
+quiche::QuicheBuffer MoqtFramer::SerializeSubscribeDone(
+    const MoqtSubscribeDone& message) {
+  if (message.final_id.has_value()) {
+    return Serialize(WireVarInt62(MoqtMessageType::kSubscribeDone),
+                     WireVarInt62(message.subscribe_id),
+                     WireVarInt62(message.status_code),
+                     WireStringWithVarInt62Length(message.reason_phrase),
+                     WireUint8(1), WireVarInt62(message.final_id->group),
+                     WireVarInt62(message.final_id->object));
+  }
   return Serialize(
-      WireVarInt62(MoqtMessageType::kSubscribeRst),
-      WireVarInt62(message.subscribe_id), WireVarInt62(message.error_code),
-      WireStringWithVarInt62Length(message.reason_phrase),
-      WireVarInt62(message.final_group), WireVarInt62(message.final_object));
+      WireVarInt62(MoqtMessageType::kSubscribeDone),
+      WireVarInt62(message.subscribe_id), WireVarInt62(message.status_code),
+      WireStringWithVarInt62Length(message.reason_phrase), WireUint8(0));
 }
 
 quiche::QuicheBuffer MoqtFramer::SerializeAnnounce(
diff --git a/quiche/quic/moqt/moqt_framer.h b/quiche/quic/moqt/moqt_framer.h
index bf17364..7f19fa8 100644
--- a/quiche/quic/moqt/moqt_framer.h
+++ b/quiche/quic/moqt/moqt_framer.h
@@ -46,8 +46,7 @@
   quiche::QuicheBuffer SerializeSubscribeError(
       const MoqtSubscribeError& message);
   quiche::QuicheBuffer SerializeUnsubscribe(const MoqtUnsubscribe& message);
-  quiche::QuicheBuffer SerializeSubscribeFin(const MoqtSubscribeFin& message);
-  quiche::QuicheBuffer SerializeSubscribeRst(const MoqtSubscribeRst& message);
+  quiche::QuicheBuffer SerializeSubscribeDone(const MoqtSubscribeDone& message);
   quiche::QuicheBuffer SerializeAnnounce(const MoqtAnnounce& message);
   quiche::QuicheBuffer SerializeAnnounceOk(const MoqtAnnounceOk& message);
   quiche::QuicheBuffer SerializeAnnounceError(const MoqtAnnounceError& message);
diff --git a/quiche/quic/moqt/moqt_framer_test.cc b/quiche/quic/moqt/moqt_framer_test.cc
index 36ab8ea..607e1db 100644
--- a/quiche/quic/moqt/moqt_framer_test.cc
+++ b/quiche/quic/moqt/moqt_framer_test.cc
@@ -35,8 +35,7 @@
       MoqtMessageType::kSubscribeOk,
       MoqtMessageType::kSubscribeError,
       MoqtMessageType::kUnsubscribe,
-      MoqtMessageType::kSubscribeFin,
-      MoqtMessageType::kSubscribeRst,
+      MoqtMessageType::kSubscribeDone,
       MoqtMessageType::kAnnounce,
       MoqtMessageType::kAnnounceOk,
       MoqtMessageType::kAnnounceError,
@@ -126,13 +125,9 @@
         auto data = std::get<MoqtUnsubscribe>(structured_data);
         return framer_.SerializeUnsubscribe(data);
       }
-      case MoqtMessageType::kSubscribeFin: {
-        auto data = std::get<MoqtSubscribeFin>(structured_data);
-        return framer_.SerializeSubscribeFin(data);
-      }
-      case MoqtMessageType::kSubscribeRst: {
-        auto data = std::get<MoqtSubscribeRst>(structured_data);
-        return framer_.SerializeSubscribeRst(data);
+      case MoqtMessageType::kSubscribeDone: {
+        auto data = std::get<MoqtSubscribeDone>(structured_data);
+        return framer_.SerializeSubscribeDone(data);
       }
       case MoqtMessageType::kAnnounce: {
         auto data = std::get<MoqtAnnounce>(structured_data);
diff --git a/quiche/quic/moqt/moqt_integration_test.cc b/quiche/quic/moqt/moqt_integration_test.cc
index 2905bd2..8691be6 100644
--- a/quiche/quic/moqt/moqt_integration_test.cc
+++ b/quiche/quic/moqt/moqt_integration_test.cc
@@ -120,9 +120,9 @@
  public:
   void CreateDefaultEndpoints() {
     client_ = std::make_unique<ClientEndpoint>(
-        &test_harness_.simulator(), "Client", "Server", MoqtVersion::kDraft02);
+        &test_harness_.simulator(), "Client", "Server", MoqtVersion::kDraft03);
     server_ = std::make_unique<ServerEndpoint>(
-        &test_harness_.simulator(), "Server", "Client", MoqtVersion::kDraft02);
+        &test_harness_.simulator(), "Server", "Client", MoqtVersion::kDraft03);
     test_harness_.set_client(client_.get());
     test_harness_.set_server(server_.get());
   }
@@ -173,7 +173,7 @@
       &test_harness_.simulator(), "Client", "Server",
       MoqtVersion::kUnrecognizedVersionForTests);
   server_ = std::make_unique<ServerEndpoint>(
-      &test_harness_.simulator(), "Server", "Client", MoqtVersion::kDraft02);
+      &test_harness_.simulator(), "Server", "Client", MoqtVersion::kDraft03);
   test_harness_.set_client(client_.get());
   test_harness_.set_server(server_.get());
   WireUpEndpoints();
diff --git a/quiche/quic/moqt/moqt_messages.cc b/quiche/quic/moqt/moqt_messages.cc
index ab18885..fba312b 100644
--- a/quiche/quic/moqt/moqt_messages.cc
+++ b/quiche/quic/moqt/moqt_messages.cc
@@ -28,10 +28,8 @@
       return "SUBSCRIBE_ERROR";
     case MoqtMessageType::kUnsubscribe:
       return "UNSUBSCRIBE";
-    case MoqtMessageType::kSubscribeFin:
-      return "SUBSCRIBE_FIN";
-    case MoqtMessageType::kSubscribeRst:
-      return "SUBSCRIBE_RST";
+    case MoqtMessageType::kSubscribeDone:
+      return "SUBSCRIBE_DONE";
     case MoqtMessageType::kAnnounce:
       return "ANNOUNCE";
     case MoqtMessageType::kAnnounceOk:
diff --git a/quiche/quic/moqt/moqt_messages.h b/quiche/quic/moqt/moqt_messages.h
index 67c9f1c..eef04cc 100644
--- a/quiche/quic/moqt/moqt_messages.h
+++ b/quiche/quic/moqt/moqt_messages.h
@@ -27,7 +27,7 @@
 }
 
 enum class MoqtVersion : uint64_t {
-  kDraft02 = 0xff000002,
+  kDraft03 = 0xff000003,
   kUnrecognizedVersionForTests = 0xfe0000ff,
 };
 
@@ -57,8 +57,7 @@
   kAnnounceError = 0x08,
   kUnannounce = 0x09,
   kUnsubscribe = 0x0a,
-  kSubscribeFin = 0x0b,
-  kSubscribeRst = 0x0c,
+  kSubscribeDone = 0x0b,
   kGoAway = 0x10,
   kClientSetup = 0x40,
   kServerSetup = 0x41,
@@ -251,6 +250,8 @@
   uint64_t subscribe_id;
   // The message uses ms, but expires is in us.
   quic::QuicTimeDelta expires = quic::QuicTimeDelta::FromMilliseconds(0);
+  // If ContextExists on the wire is zero, largest_id has no value.
+  std::optional<FullSequence> largest_id;
 };
 
 enum class QUICHE_EXPORT SubscribeErrorCode : uint64_t {
@@ -270,18 +271,21 @@
   uint64_t subscribe_id;
 };
 
-struct QUICHE_EXPORT MoqtSubscribeFin {
-  uint64_t subscribe_id;
-  uint64_t final_group;
-  uint64_t final_object;
+enum class QUICHE_EXPORT SubscribeDoneCode : uint64_t {
+  kUnsubscribed = 0x0,
+  kInternalError = 0x1,
+  kUnauthorized = 0x2,
+  kTrackEnded = 0x3,
+  kSubscriptionEnded = 0x4,
+  kGoingAway = 0x5,
+  kExpired = 0x6,
 };
 
-struct QUICHE_EXPORT MoqtSubscribeRst {
+struct QUICHE_EXPORT MoqtSubscribeDone {
   uint64_t subscribe_id;
-  uint64_t error_code;
+  uint64_t status_code;
   std::string reason_phrase;
-  uint64_t final_group;
-  uint64_t final_object;
+  std::optional<FullSequence> final_id;
 };
 
 struct QUICHE_EXPORT MoqtAnnounce {
diff --git a/quiche/quic/moqt/moqt_parser.cc b/quiche/quic/moqt/moqt_parser.cc
index 616a9ed..1e8cf8a 100644
--- a/quiche/quic/moqt/moqt_parser.cc
+++ b/quiche/quic/moqt/moqt_parser.cc
@@ -166,10 +166,8 @@
       return ProcessSubscribeError(reader);
     case MoqtMessageType::kUnsubscribe:
       return ProcessUnsubscribe(reader);
-    case MoqtMessageType::kSubscribeFin:
-      return ProcessSubscribeFin(reader);
-    case MoqtMessageType::kSubscribeRst:
-      return ProcessSubscribeRst(reader);
+    case MoqtMessageType::kSubscribeDone:
+      return ProcessSubscribeDone(reader);
     case MoqtMessageType::kAnnounce:
       return ProcessAnnounce(reader);
     case MoqtMessageType::kAnnounceOk:
@@ -434,11 +432,24 @@
 size_t MoqtParser::ProcessSubscribeOk(quic::QuicDataReader& reader) {
   MoqtSubscribeOk subscribe_ok;
   uint64_t milliseconds;
+  uint8_t content_exists;
   if (!reader.ReadVarInt62(&subscribe_ok.subscribe_id) ||
-      !reader.ReadVarInt62(&milliseconds)) {
+      !reader.ReadVarInt62(&milliseconds) ||
+      !reader.ReadUInt8(&content_exists)) {
+    return 0;
+  }
+  if (content_exists > 1) {
+    ParseError("SUBSCRIBE_OK ContentExists has invalid value");
     return 0;
   }
   subscribe_ok.expires = quic::QuicTimeDelta::FromMilliseconds(milliseconds);
+  if (content_exists) {
+    subscribe_ok.largest_id = FullSequence();
+    if (!reader.ReadVarInt62(&subscribe_ok.largest_id->group) ||
+        !reader.ReadVarInt62(&subscribe_ok.largest_id->object)) {
+      return 0;
+    }
+  }
   visitor_.OnSubscribeOkMessage(subscribe_ok);
   return reader.PreviouslyReadPayload().length();
 }
@@ -466,27 +477,27 @@
   return reader.PreviouslyReadPayload().length();
 }
 
-size_t MoqtParser::ProcessSubscribeFin(quic::QuicDataReader& reader) {
-  MoqtSubscribeFin subscribe_fin;
-  if (!reader.ReadVarInt62(&subscribe_fin.subscribe_id) ||
-      !reader.ReadVarInt62(&subscribe_fin.final_group) ||
-      !reader.ReadVarInt62(&subscribe_fin.final_object)) {
+size_t MoqtParser::ProcessSubscribeDone(quic::QuicDataReader& reader) {
+  MoqtSubscribeDone subscribe_done;
+  uint8_t content_exists;
+  if (!reader.ReadVarInt62(&subscribe_done.subscribe_id) ||
+      !reader.ReadVarInt62(&subscribe_done.status_code) ||
+      !reader.ReadStringVarInt62(subscribe_done.reason_phrase) ||
+      !reader.ReadUInt8(&content_exists)) {
     return 0;
   }
-  visitor_.OnSubscribeFinMessage(subscribe_fin);
-  return reader.PreviouslyReadPayload().length();
-}
-
-size_t MoqtParser::ProcessSubscribeRst(quic::QuicDataReader& reader) {
-  MoqtSubscribeRst subscribe_rst;
-  if (!reader.ReadVarInt62(&subscribe_rst.subscribe_id) ||
-      !reader.ReadVarInt62(&subscribe_rst.error_code) ||
-      !reader.ReadStringVarInt62(subscribe_rst.reason_phrase) ||
-      !reader.ReadVarInt62(&subscribe_rst.final_group) ||
-      !reader.ReadVarInt62(&subscribe_rst.final_object)) {
+  if (content_exists > 1) {
+    ParseError("SUBSCRIBE_DONE ContentExists has invalid value");
     return 0;
   }
-  visitor_.OnSubscribeRstMessage(subscribe_rst);
+  if (content_exists == 1) {
+    subscribe_done.final_id = FullSequence();
+    if (!reader.ReadVarInt62(&subscribe_done.final_id->group) ||
+        !reader.ReadVarInt62(&subscribe_done.final_id->object)) {
+      return 0;
+    }
+  }
+  visitor_.OnSubscribeDoneMessage(subscribe_done);
   return reader.PreviouslyReadPayload().length();
 }
 
diff --git a/quiche/quic/moqt/moqt_parser.h b/quiche/quic/moqt/moqt_parser.h
index 4cd19ca..084e6fe 100644
--- a/quiche/quic/moqt/moqt_parser.h
+++ b/quiche/quic/moqt/moqt_parser.h
@@ -38,8 +38,7 @@
   virtual void OnSubscribeOkMessage(const MoqtSubscribeOk& message) = 0;
   virtual void OnSubscribeErrorMessage(const MoqtSubscribeError& message) = 0;
   virtual void OnUnsubscribeMessage(const MoqtUnsubscribe& message) = 0;
-  virtual void OnSubscribeFinMessage(const MoqtSubscribeFin& message) = 0;
-  virtual void OnSubscribeRstMessage(const MoqtSubscribeRst& message) = 0;
+  virtual void OnSubscribeDoneMessage(const MoqtSubscribeDone& message) = 0;
   virtual void OnAnnounceMessage(const MoqtAnnounce& message) = 0;
   virtual void OnAnnounceOkMessage(const MoqtAnnounceOk& message) = 0;
   virtual void OnAnnounceErrorMessage(const MoqtAnnounceError& message) = 0;
@@ -90,8 +89,7 @@
   size_t ProcessSubscribeOk(quic::QuicDataReader& reader);
   size_t ProcessSubscribeError(quic::QuicDataReader& reader);
   size_t ProcessUnsubscribe(quic::QuicDataReader& reader);
-  size_t ProcessSubscribeFin(quic::QuicDataReader& reader);
-  size_t ProcessSubscribeRst(quic::QuicDataReader& reader);
+  size_t ProcessSubscribeDone(quic::QuicDataReader& reader);
   size_t ProcessAnnounce(quic::QuicDataReader& reader);
   size_t ProcessAnnounceOk(quic::QuicDataReader& reader);
   size_t ProcessAnnounceError(quic::QuicDataReader& reader);
diff --git a/quiche/quic/moqt/moqt_parser_test.cc b/quiche/quic/moqt/moqt_parser_test.cc
index 3572ae8..9d0f992 100644
--- a/quiche/quic/moqt/moqt_parser_test.cc
+++ b/quiche/quic/moqt/moqt_parser_test.cc
@@ -42,8 +42,7 @@
     MoqtMessageType::kSubscribeOk,
     MoqtMessageType::kSubscribeError,
     MoqtMessageType::kUnsubscribe,
-    MoqtMessageType::kSubscribeFin,
-    MoqtMessageType::kSubscribeRst,
+    MoqtMessageType::kSubscribeDone,
     MoqtMessageType::kAnnounce,
     MoqtMessageType::kAnnounceOk,
     MoqtMessageType::kAnnounceError,
@@ -129,10 +128,7 @@
   void OnUnsubscribeMessage(const MoqtUnsubscribe& message) override {
     OnControlMessage(message);
   }
-  void OnSubscribeFinMessage(const MoqtSubscribeFin& message) override {
-    OnControlMessage(message);
-  }
-  void OnSubscribeRstMessage(const MoqtSubscribeRst& message) override {
+  void OnSubscribeDoneMessage(const MoqtSubscribeDone& message) override {
     OnControlMessage(message);
   }
   void OnAnnounceMessage(const MoqtAnnounce& message) override {
@@ -868,4 +864,26 @@
   EXPECT_TRUE(payload.empty());
 }
 
+TEST_F(MoqtMessageSpecificTest, SubscribeOkInvalidContentExists) {
+  MoqtParser parser(kRawQuic, visitor_);
+  SubscribeOkMessage subscribe_ok;
+  subscribe_ok.SetInvalidContentExists();
+  parser.ProcessData(subscribe_ok.PacketSample(), false);
+  EXPECT_EQ(visitor_.messages_received_, 0);
+  EXPECT_TRUE(visitor_.parsing_error_.has_value());
+  EXPECT_EQ(*visitor_.parsing_error_,
+            "SUBSCRIBE_OK ContentExists has invalid value");
+}
+
+TEST_F(MoqtMessageSpecificTest, SubscribeDoneInvalidContentExists) {
+  MoqtParser parser(kRawQuic, visitor_);
+  SubscribeDoneMessage subscribe_done;
+  subscribe_done.SetInvalidContentExists();
+  parser.ProcessData(subscribe_done.PacketSample(), false);
+  EXPECT_EQ(visitor_.messages_received_, 0);
+  EXPECT_TRUE(visitor_.parsing_error_.has_value());
+  EXPECT_EQ(*visitor_.parsing_error_,
+            "SUBSCRIBE_DONE ContentExists has invalid value");
+}
+
 }  // namespace moqt::test
diff --git a/quiche/quic/moqt/moqt_session.cc b/quiche/quic/moqt/moqt_session.cc
index 3442e04..dcbbf61 100644
--- a/quiche/quic/moqt/moqt_session.cc
+++ b/quiche/quic/moqt/moqt_session.cc
@@ -734,6 +734,7 @@
   for (auto& [name, track] : session_->local_tracks_) {
     track.DeleteWindow(message.subscribe_id);
   }
+  // TODO(martinduke): Send SUBSCRIBE_DONE in response.
 }
 
 void MoqtSession::Stream::OnAnnounceMessage(const MoqtAnnounce& message) {
diff --git a/quiche/quic/moqt/moqt_session.h b/quiche/quic/moqt/moqt_session.h
index 48504b7..08eb658 100644
--- a/quiche/quic/moqt/moqt_session.h
+++ b/quiche/quic/moqt/moqt_session.h
@@ -168,8 +168,8 @@
     void OnSubscribeOkMessage(const MoqtSubscribeOk& message) override;
     void OnSubscribeErrorMessage(const MoqtSubscribeError& message) override;
     void OnUnsubscribeMessage(const MoqtUnsubscribe& message) override;
-    void OnSubscribeFinMessage(const MoqtSubscribeFin& /*message*/) override {}
-    void OnSubscribeRstMessage(const MoqtSubscribeRst& /*message*/) override {}
+    void OnSubscribeDoneMessage(const MoqtSubscribeDone& /*message*/) override {
+    }
     void OnAnnounceMessage(const MoqtAnnounce& message) override;
     void OnAnnounceOkMessage(const MoqtAnnounceOk& message) override;
     void OnAnnounceErrorMessage(const MoqtAnnounceError& message) override;
diff --git a/quiche/quic/moqt/moqt_session_test.cc b/quiche/quic/moqt/moqt_session_test.cc
index dfc0336..5cac2fe 100644
--- a/quiche/quic/moqt/moqt_session_test.cc
+++ b/quiche/quic/moqt/moqt_session_test.cc
@@ -42,7 +42,7 @@
 constexpr webtransport::StreamId kOutgoingUniStreamId = 14;
 
 constexpr MoqtSessionParameters default_parameters = {
-    /*version=*/MoqtVersion::kDraft02,
+    /*version=*/MoqtVersion::kDraft03,
     /*perspective=*/quic::Perspective::IS_CLIENT,
     /*using_webtrans=*/true,
     /*path=*/std::string(),
@@ -185,7 +185,7 @@
           &session_, visitor.get());
   // Handle the server setup
   MoqtServerSetup setup = {
-      MoqtVersion::kDraft02,
+      MoqtVersion::kDraft03,
       MoqtRole::kPubSub,
   };
   EXPECT_CALL(session_callbacks_.session_established_callback, Call()).Times(1);
@@ -194,7 +194,7 @@
 
 TEST_F(MoqtSessionTest, OnClientSetup) {
   MoqtSessionParameters server_parameters = {
-      /*version=*/MoqtVersion::kDraft02,
+      /*version=*/MoqtVersion::kDraft03,
       /*perspective=*/quic::Perspective::IS_SERVER,
       /*using_webtrans=*/true,
       /*path=*/"",
@@ -206,7 +206,7 @@
   std::unique_ptr<MoqtParserVisitor> stream_input =
       MoqtSessionPeer::CreateControlStream(&server_session, &mock_stream);
   MoqtClientSetup setup = {
-      /*supported_versions=*/{MoqtVersion::kDraft02},
+      /*supported_versions=*/{MoqtVersion::kDraft03},
       /*role=*/MoqtRole::kPubSub,
       /*path=*/std::nullopt,
   };
@@ -607,7 +607,7 @@
 
 TEST_F(MoqtSessionTest, IncomingPartialObjectNoBuffer) {
   MoqtSessionParameters parameters = {
-      /*version=*/MoqtVersion::kDraft02,
+      /*version=*/MoqtVersion::kDraft03,
       /*perspective=*/quic::Perspective::IS_CLIENT,
       /*using_webtrans=*/true,
       /*path=*/"",
@@ -685,6 +685,7 @@
   MoqtSubscribeOk ok = {
       /*subscribe_id=*/1,
       /*expires=*/quic::QuicTimeDelta::FromMilliseconds(0),
+      /*largest_id=*/std::nullopt,
   };
   StrictMock<webtransport::test::MockStream> mock_control_stream;
   std::unique_ptr<MoqtParserVisitor> control_stream =
@@ -853,6 +854,7 @@
   MoqtSubscribeOk ok = {
       /*subscribe_id=*/1,
       /*expires=*/quic::QuicTimeDelta::FromMilliseconds(0),
+      /*largest_id=*/std::nullopt,
   };
   StrictMock<webtransport::test::MockStream> mock_control_stream;
   std::unique_ptr<MoqtParserVisitor> control_stream =
@@ -1016,7 +1018,7 @@
 
 TEST_F(MoqtSessionTest, OneBidirectionalStreamServer) {
   MoqtSessionParameters server_parameters = {
-      /*version=*/MoqtVersion::kDraft02,
+      /*version=*/MoqtVersion::kDraft03,
       /*perspective=*/quic::Perspective::IS_SERVER,
       /*using_webtrans=*/true,
       /*path=*/"",
@@ -1028,7 +1030,7 @@
   std::unique_ptr<MoqtParserVisitor> stream_input =
       MoqtSessionPeer::CreateControlStream(&server_session, &mock_stream);
   MoqtClientSetup setup = {
-      /*supported_versions*/ {MoqtVersion::kDraft02},
+      /*supported_versions*/ {MoqtVersion::kDraft03},
       /*role=*/MoqtRole::kPubSub,
       /*path=*/std::nullopt,
   };
diff --git a/quiche/quic/moqt/test_tools/moqt_test_message.h b/quiche/quic/moqt/test_tools/moqt_test_message.h
index 156b874..98dee0f 100644
--- a/quiche/quic/moqt/test_tools/moqt_test_message.h
+++ b/quiche/quic/moqt/test_tools/moqt_test_message.h
@@ -36,9 +36,9 @@
 
   typedef absl::variant<MoqtClientSetup, MoqtServerSetup, MoqtObject,
                         MoqtSubscribe, MoqtSubscribeOk, MoqtSubscribeError,
-                        MoqtUnsubscribe, MoqtSubscribeFin, MoqtSubscribeRst,
-                        MoqtAnnounce, MoqtAnnounceOk, MoqtAnnounceError,
-                        MoqtUnannounce, MoqtGoAway>
+                        MoqtUnsubscribe, MoqtSubscribeDone, MoqtAnnounce,
+                        MoqtAnnounceOk, MoqtAnnounceError, MoqtUnannounce,
+                        MoqtGoAway>
       MessageStructuredData;
 
   // The total actual size of the message.
@@ -513,23 +513,34 @@
       QUIC_LOG(INFO) << "SUBSCRIBE OK expiration mismatch";
       return false;
     }
+    if (cast.largest_id != subscribe_ok_.largest_id) {
+      QUIC_LOG(INFO) << "SUBSCRIBE OK largest ID mismatch";
+      return false;
+    }
     return true;
   }
 
-  void ExpandVarints() override { ExpandVarintsImpl("vvv"); }
+  void ExpandVarints() override { ExpandVarintsImpl("vvv-vv"); }
 
   MessageStructuredData structured_data() const override {
     return TestMessageBase::MessageStructuredData(subscribe_ok_);
   }
 
+  void SetInvalidContentExists() {
+    raw_packet_[3] = 0x02;
+    SetWireImage(raw_packet_, sizeof(raw_packet_));
+  }
+
  private:
-  uint8_t raw_packet_[3] = {
+  uint8_t raw_packet_[6] = {
       0x04, 0x01, 0x03,  // subscribe_id = 1, expires = 3
+      0x01, 0x0c, 0x14,  // largest_group_id = 12, largest_object_id = 20,
   };
 
   MoqtSubscribeOk subscribe_ok_ = {
       /*subscribe_id=*/1,
       /*expires=*/quic::QuicTimeDelta::FromMilliseconds(3),
+      /*largest_id=*/FullSequence(12, 20),
   };
 };
 
@@ -613,101 +624,56 @@
   };
 };
 
-class QUICHE_NO_EXPORT SubscribeFinMessage : public TestMessageBase {
+class QUICHE_NO_EXPORT SubscribeDoneMessage : public TestMessageBase {
  public:
-  SubscribeFinMessage() : TestMessageBase(MoqtMessageType::kSubscribeFin) {
+  SubscribeDoneMessage() : TestMessageBase(MoqtMessageType::kSubscribeDone) {
     SetWireImage(raw_packet_, sizeof(raw_packet_));
   }
 
   bool EqualFieldValues(MessageStructuredData& values) const override {
-    auto cast = std::get<MoqtSubscribeFin>(values);
-    if (cast.subscribe_id != subscribe_fin_.subscribe_id) {
-      QUIC_LOG(INFO) << "SUBSCRIBE_FIN subscribe ID mismatch";
+    auto cast = std::get<MoqtSubscribeDone>(values);
+    if (cast.subscribe_id != subscribe_done_.subscribe_id) {
+      QUIC_LOG(INFO) << "SUBSCRIBE_DONE subscribe ID mismatch";
       return false;
     }
-    if (cast.final_group != subscribe_fin_.final_group) {
-      QUIC_LOG(INFO) << "SUBSCRIBE_FIN final group mismatch";
+    if (cast.status_code != subscribe_done_.status_code) {
+      QUIC_LOG(INFO) << "SUBSCRIBE_DONE status code mismatch";
       return false;
     }
-    if (cast.final_object != subscribe_fin_.final_object) {
-      QUIC_LOG(INFO) << "SUBSCRIBE_FIN final object mismatch";
+    if (cast.reason_phrase != subscribe_done_.reason_phrase) {
+      QUIC_LOG(INFO) << "SUBSCRIBE_DONE reason phrase mismatch";
+      return false;
+    }
+    if (cast.final_id != subscribe_done_.final_id) {
+      QUIC_LOG(INFO) << "SUBSCRIBE_DONE final ID mismatch";
       return false;
     }
     return true;
   }
 
-  void ExpandVarints() override { ExpandVarintsImpl("vvvv"); }
+  void ExpandVarints() override { ExpandVarintsImpl("vvvv---vv"); }
 
   MessageStructuredData structured_data() const override {
-    return TestMessageBase::MessageStructuredData(subscribe_fin_);
+    return TestMessageBase::MessageStructuredData(subscribe_done_);
   }
 
- private:
-  uint8_t raw_packet_[4] = {
-      0x0b, 0x03,  // subscribe_id = 3
-      0x08,        // final_group = 8
-      0x0c,        // final_object = 12
-  };
-
-  MoqtSubscribeFin subscribe_fin_ = {
-      /*subscribe_id=*/3,
-      /*final_group=*/8,
-      /*final_object=*/12,
-  };
-};
-
-class QUICHE_NO_EXPORT SubscribeRstMessage : public TestMessageBase {
- public:
-  SubscribeRstMessage() : TestMessageBase(MoqtMessageType::kSubscribeRst) {
+  void SetInvalidContentExists() {
+    raw_packet_[6] = 0x02;
     SetWireImage(raw_packet_, sizeof(raw_packet_));
   }
 
-  bool EqualFieldValues(MessageStructuredData& values) const override {
-    auto cast = std::get<MoqtSubscribeRst>(values);
-    if (cast.subscribe_id != subscribe_rst_.subscribe_id) {
-      QUIC_LOG(INFO) << "SUBSCRIBE_RST subscribe ID mismatch";
-      return false;
-    }
-    if (cast.error_code != subscribe_rst_.error_code) {
-      QUIC_LOG(INFO) << "SUBSCRIBE_RST error code mismatch";
-      return false;
-    }
-    if (cast.reason_phrase != subscribe_rst_.reason_phrase) {
-      QUIC_LOG(INFO) << "SUBSCRIBE_RST reason phrase mismatch";
-      return false;
-    }
-    if (cast.final_group != subscribe_rst_.final_group) {
-      QUIC_LOG(INFO) << "SUBSCRIBE_RST final group mismatch";
-      return false;
-    }
-    if (cast.final_object != subscribe_rst_.final_object) {
-      QUIC_LOG(INFO) << "SUBSCRIBE_RST final object mismatch";
-      return false;
-    }
-    return true;
-  }
-
-  void ExpandVarints() override { ExpandVarintsImpl("vvvv--vv"); }
-
-  MessageStructuredData structured_data() const override {
-    return TestMessageBase::MessageStructuredData(subscribe_rst_);
-  }
-
  private:
-  uint8_t raw_packet_[8] = {
-      0x0c, 0x02,        // subscribe_id = 2
-      0x03,              // error_code = 3
+  uint8_t raw_packet_[9] = {
+      0x0b, 0x02, 0x03,  // subscribe_id = 2, error_code = 3,
       0x02, 0x68, 0x69,  // reason_phrase = "hi"
-      0x08,              // final_group = 8
-      0x0c,              // final_object = 12
+      0x01, 0x08, 0x0c,  // final_id = (8,12)
   };
 
-  MoqtSubscribeRst subscribe_rst_ = {
+  MoqtSubscribeDone subscribe_done_ = {
       /*subscribe_id=*/2,
       /*error_code=*/3,
       /*reason_phrase=*/"hi",
-      /*final_group=*/8,
-      /*final_object=*/12,
+      /*final_id=*/FullSequence(8, 12),
   };
 };
 
@@ -901,10 +867,8 @@
       return std::make_unique<SubscribeErrorMessage>();
     case MoqtMessageType::kUnsubscribe:
       return std::make_unique<UnsubscribeMessage>();
-    case MoqtMessageType::kSubscribeFin:
-      return std::make_unique<SubscribeFinMessage>();
-    case MoqtMessageType::kSubscribeRst:
-      return std::make_unique<SubscribeRstMessage>();
+    case MoqtMessageType::kSubscribeDone:
+      return std::make_unique<SubscribeDoneMessage>();
     case MoqtMessageType::kAnnounce:
       return std::make_unique<AnnounceMessage>();
     case MoqtMessageType::kAnnounceOk:
diff --git a/quiche/quic/moqt/tools/moqt_client.cc b/quiche/quic/moqt/tools/moqt_client.cc
index 754b68d..4920c16 100644
--- a/quiche/quic/moqt/tools/moqt_client.cc
+++ b/quiche/quic/moqt/tools/moqt_client.cc
@@ -89,7 +89,7 @@
   }
 
   MoqtSessionParameters parameters;
-  parameters.version = MoqtVersion::kDraft02;
+  parameters.version = MoqtVersion::kDraft03;
   parameters.perspective = quic::Perspective::IS_CLIENT,
   parameters.using_webtrans = true;
   parameters.path = "";
diff --git a/quiche/quic/moqt/tools/moqt_server.cc b/quiche/quic/moqt/tools/moqt_server.cc
index d12847c..9a60977 100644
--- a/quiche/quic/moqt/tools/moqt_server.cc
+++ b/quiche/quic/moqt/tools/moqt_server.cc
@@ -33,7 +33,7 @@
     parameters.perspective = quic::Perspective::IS_SERVER;
     parameters.path = path;
     parameters.using_webtrans = true;
-    parameters.version = MoqtVersion::kDraft02;
+    parameters.version = MoqtVersion::kDraft03;
     parameters.deliver_partial_objects = false;
     auto moqt_session = std::make_unique<MoqtSession>(session, parameters);
     std::move (*configurator)(moqt_session.get());