Skip to content

Commit 32ff1bc

Browse files
committed
quic: make Applications optional, with raw stream support in sessions
node:quic no longer requires an Application. When none is set, the session schedules streams on its own send queue and pulls/commits their data directly. This lets us drop DefaultApplication, to support the imminent HTTP 3 app changes coming. The Session also gains the data-plane dispatchers (GetStreamData/StreamCommit/ReceiveStream*/ScheduleStream) that route to an installed application of present or a native path otherwise, with the ngtcp2 callbacks guarding on whether an application is installed. HTTP/3 is unchanged (for now).
1 parent 5b2ead9 commit 32ff1bc

10 files changed

Lines changed: 381 additions & 796 deletions

src/quic/application.cc

Lines changed: 0 additions & 215 deletions
Original file line numberDiff line numberDiff line change
@@ -295,221 +295,6 @@ void Session::Application::ReceiveStreamReset(Stream* stream,
295295
stream->ReceiveStreamReset(final_size, std::move(error));
296296
}
297297

298-
// ============================================================================
299-
// The DefaultApplication is the default implementation of Session::Application
300-
// that is used for all unrecognized ALPN identifiers.
301-
class DefaultApplication final : public Session::Application {
302-
public:
303-
// Marked NOLINT because the cpp linter gets confused about this using
304-
// statement not being sorted with the using v8 statements at the top
305-
// of the namespace.
306-
DefaultApplication(Session* session, const Options& options)
307-
: Session::Application(session, options), options_(options) {}
308-
309-
const Options& options() const override { return options_; }
310-
311-
Session::Application::Type type() const override {
312-
return Session::Application::Type::DEFAULT;
313-
}
314-
315-
error_code GetNoErrorCode() const override { return 0; }
316-
317-
// Raw QUIC has no application-defined "general failure" code, so
318-
// fall back to the QUIC transport-level INTERNAL_ERROR (0x1) used
319-
// by ngtcp2 for unspecified failures.
320-
error_code GetInternalErrorCode() const override {
321-
return NGTCP2_INTERNAL_ERROR;
322-
}
323-
324-
void EarlyDataRejected() override {
325-
// Destroy all open streams — ngtcp2 has already discarded their
326-
// internal state when it rejected the early data. Use the
327-
// application's internal error code since this is an error
328-
// condition (code 0 would be treated as a clean close).
329-
session().DestroyAllStreams(
330-
QuicError::ForApplication(GetInternalErrorCode()));
331-
if (!session().is_destroyed()) {
332-
session().EmitEarlyDataRejected();
333-
}
334-
}
335-
336-
void CollectSessionTicketAppData(
337-
SessionTicket::AppData* app_data) const override {
338-
const auto& atd = session().config().options.app_ticket_data;
339-
if (!atd.has_value() || atd->length() == 0) {
340-
// No app data configured — write just the type byte (base behaviour).
341-
Session::Application::CollectSessionTicketAppData(app_data);
342-
return;
343-
}
344-
// Layout: [type byte][opaque app data].
345-
uv_buf_t bytes = *atd;
346-
std::vector<uint8_t> buf;
347-
buf.reserve(1 + bytes.len);
348-
buf.push_back(static_cast<uint8_t>(type())); // Type::DEFAULT
349-
const auto* p = reinterpret_cast<const uint8_t*>(bytes.base);
350-
buf.insert(buf.end(), p, p + bytes.len);
351-
app_data->Set(
352-
uv_buf_init(reinterpret_cast<char*>(buf.data()), buf.size()));
353-
}
354-
355-
bool ApplySessionTicketData(const PendingTicketAppData& data) override {
356-
return std::holds_alternative<DefaultTicketData>(data);
357-
}
358-
359-
bool ReceiveStreamOpen(stream_id id) override {
360-
auto stream = session().CreateStream(id);
361-
if (!stream || session().is_destroyed()) [[unlikely]] {
362-
return !session().is_destroyed();
363-
}
364-
return true;
365-
}
366-
367-
bool ReceiveStreamData(stream_id id,
368-
const uint8_t* data,
369-
size_t datalen,
370-
const Stream::ReceiveDataFlags& flags,
371-
void* stream_user_data) override {
372-
BaseObjectPtr<Stream> stream;
373-
if (stream_user_data == nullptr) {
374-
// This is the first time we're seeing this stream. Implicitly create it.
375-
stream = session().CreateStream(id);
376-
if (!stream || session().is_destroyed()) [[unlikely]] {
377-
// We couldn't create the stream, or the session was destroyed
378-
// during the onstream callback (via MakeCallback re-entrancy).
379-
return false;
380-
}
381-
} else {
382-
stream = BaseObjectPtr<Stream>(Stream::From(stream_user_data));
383-
if (!stream) {
384-
Debug(&session(),
385-
"Default application failed to get existing stream "
386-
"from user data");
387-
return false;
388-
}
389-
}
390-
391-
CHECK(stream);
392-
393-
// Now we can actually receive the data! Woo!
394-
stream->ReceiveData(data, datalen, flags);
395-
return true;
396-
}
397-
398-
int GetStreamData(StreamData* stream_data) override {
399-
// Reset the state of stream_data before proceeding...
400-
stream_data->id = -1;
401-
stream_data->count = 0;
402-
stream_data->fin = 0;
403-
stream_data->stream.reset();
404-
Debug(&session(), "Default application getting stream data");
405-
DCHECK_NOT_NULL(stream_data);
406-
// If the queue is empty, there aren't any streams with data yet
407-
408-
// If the connection-level flow control window is exhausted,
409-
// there is no point in pulling stream data.
410-
if (!session().max_data_left()) return 0;
411-
if (stream_queue_.IsEmpty()) return 0;
412-
413-
Stream* stream = stream_queue_.PopFront();
414-
CHECK_NOT_NULL(stream);
415-
stream_data->stream.reset(stream);
416-
stream_data->id = stream->id();
417-
auto next =
418-
[&](int status, const ngtcp2_vec* data, size_t count, bob::Done done) {
419-
switch (status) {
420-
case bob::Status::STATUS_BLOCK:
421-
// Fall through
422-
case bob::Status::STATUS_WAIT:
423-
return;
424-
case bob::Status::STATUS_EOS:
425-
stream_data->fin = 1;
426-
}
427-
428-
// It is possible that the data pointers returned are not actually
429-
// the data pointers in the stream_data. If that's the case, we need
430-
// to copy over the pointers.
431-
count = std::min(count, kMaxVectorCount);
432-
ngtcp2_vec* dest = *stream_data;
433-
if (dest != data) {
434-
for (size_t n = 0; n < count; n++) {
435-
dest[n] = data[n];
436-
}
437-
}
438-
439-
stream_data->count = count;
440-
441-
if (count > 0) {
442-
stream->Schedule(&stream_queue_);
443-
}
444-
445-
// Not calling done here because we defer committing
446-
// the data until after we're sure it's written.
447-
};
448-
449-
if (!stream->is_eos()) [[likely]] {
450-
int ret = stream->Pull(std::move(next),
451-
bob::Options::OPTIONS_SYNC,
452-
stream_data->data,
453-
arraysize(stream_data->data),
454-
kMaxVectorCount);
455-
if (ret == bob::Status::STATUS_EOS) {
456-
stream_data->fin = 1;
457-
}
458-
} else {
459-
stream_data->fin = 1;
460-
}
461-
462-
return 0;
463-
}
464-
465-
void ResumeStream(stream_id id) override { ScheduleStream(id); }
466-
467-
void BlockStream(stream_id id) override {
468-
if (auto stream = session().FindStream(id)) [[likely]] {
469-
// Remove the stream from the send queue. It will be re-scheduled
470-
// via ExtendMaxStreamData when the peer grants more flow control.
471-
// Without this, SendPendingData would repeatedly pop and retry
472-
// the same blocked stream in an infinite loop.
473-
stream->Unschedule();
474-
stream->EmitBlocked();
475-
}
476-
}
477-
478-
void ExtendMaxStreamData(Stream* stream, uint64_t max_data) override {
479-
// The peer granted more flow control for this stream. Re-schedule
480-
// it so SendPendingData will resume writing.
481-
DCHECK_NOT_NULL(stream);
482-
stream->Schedule(&stream_queue_);
483-
}
484-
485-
bool StreamCommit(StreamData* stream_data, size_t datalen) override {
486-
DCHECK_NOT_NULL(stream_data);
487-
CHECK(stream_data->stream);
488-
stream_data->stream->Commit(datalen, stream_data->fin);
489-
return true;
490-
}
491-
492-
SET_SELF_SIZE(DefaultApplication)
493-
SET_MEMORY_INFO_NAME(DefaultApplication)
494-
SET_NO_MEMORY_INFO()
495-
496-
private:
497-
void ScheduleStream(stream_id id) {
498-
if (auto stream = session().FindStream(id)) [[likely]] {
499-
stream->Schedule(&stream_queue_);
500-
}
501-
}
502-
503-
Options options_;
504-
505-
Stream::Queue stream_queue_;
506-
};
507-
508-
std::unique_ptr<Session::Application> CreateDefaultApplication(
509-
Session* session, const Session::Application_Options& options) {
510-
return std::make_unique<DefaultApplication>(session, options);
511-
}
512-
513298
} // namespace quic
514299
} // namespace node
515300

0 commit comments

Comments
 (0)