@@ -225,8 +225,13 @@ std::optional<PendingTicketAppData> Session::Application::ParseTicketData(
225225 auto app_type =
226226 static_cast <Type>(reinterpret_cast <const uint8_t *>(data.base )[0 ]);
227227 switch (app_type) {
228- case Type::DEFAULT :
229- return DefaultTicketData{};
228+ case Type::DEFAULT : {
229+ // Everything after the leading type byte is opaque application data.
230+ DefaultTicketData dtd;
231+ const auto * p = reinterpret_cast <const uint8_t *>(data.base );
232+ if (data.len > 1 ) dtd.data .assign (p + 1 , p + data.len );
233+ return dtd;
234+ }
230235 case Type::HTTP3 :
231236 return ParseHttp3TicketData (data);
232237 default :
@@ -235,10 +240,11 @@ std::optional<PendingTicketAppData> Session::Application::ParseTicketData(
235240}
236241
237242bool Session::Application::ValidateTicketData (
238- const PendingTicketAppData& data, const Application_Options& options ) {
243+ const PendingTicketAppData& data, const Session::Options& session_options ) {
239244 if (std::holds_alternative<Http3TicketData>(data)) {
240245 // TODO(@jasnell): This validation probably belongs in http3.cc but keeping
241246 // it here for now.
247+ const auto & options = session_options.application_options ;
242248 const auto & ticket = std::get<Http3TicketData>(data);
243249 return options.max_field_section_size >= ticket.max_field_section_size &&
244250 options.qpack_max_dtable_capacity >=
@@ -250,8 +256,19 @@ bool Session::Application::ValidateTicketData(
250256 options.enable_connect_protocol ) &&
251257 (!ticket.enable_datagrams || options.enable_datagrams );
252258 }
253- // DefaultTicketData always validates.
254- return true ;
259+ if (std::holds_alternative<DefaultTicketData>(data)) {
260+ // Opaque app-data (raw QUIC / non-h3): the embedded bytes must exactly
261+ // match the server's currently-configured app_ticket_data.
262+ const auto & dtd = std::get<DefaultTicketData>(data);
263+ uv_buf_t cur = session_options.app_ticket_data .has_value ()
264+ ? static_cast <uv_buf_t >(*session_options.app_ticket_data )
265+ : uv_buf_init (nullptr , 0 );
266+ return dtd.data .size () == cur.len &&
267+ (cur.len == 0 || memcmp (dtd.data .data (), cur.base , cur.len ) == 0 );
268+ }
269+ // Unknown/unparsed ticket data -> fail closed so no 0-RTT (falls back to
270+ // 1-RTT, so limited impact but avoids invalid resumptions).
271+ return false ;
255272}
256273
257274Packet::Ptr Session::Application::CreateStreamDataPacket () {
@@ -734,6 +751,23 @@ class DefaultApplication final : public Session::Application {
734751 }
735752 }
736753
754+ void CollectSessionTicketAppData (
755+ SessionTicket::AppData* app_data) const override {
756+ // Layout: [type byte][optional opaque app data]. With no app data this
757+ // degenerates to the single type byte written by the base class.
758+ const auto & atd = session ().config ().options .app_ticket_data ;
759+ uv_buf_t bytes =
760+ atd.has_value () ? static_cast <uv_buf_t >(*atd) : uv_buf_init (nullptr , 0 );
761+ std::vector<uint8_t > buf;
762+ buf.reserve (1 + bytes.len );
763+ buf.push_back (static_cast <uint8_t >(type ())); // Type::DEFAULT
764+ if (bytes.len > 0 ) {
765+ const auto * p = reinterpret_cast <const uint8_t *>(bytes.base );
766+ buf.insert (buf.end (), p, p + bytes.len );
767+ }
768+ app_data->Set (uv_buf_init (reinterpret_cast <char *>(buf.data ()), buf.size ()));
769+ }
770+
737771 bool ApplySessionTicketData (const PendingTicketAppData& data) override {
738772 return std::holds_alternative<DefaultTicketData>(data);
739773 }
0 commit comments