Skip to content

Commit 96c0a38

Browse files
committed
quic: complete the internal implementation of QUIC
Signed-off-by: James M Snell <[email protected]> Assisted-by: Opencode:Opus 4.6
1 parent b2248fd commit 96c0a38

41 files changed

Lines changed: 4203 additions & 1621 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

deps/ngtcp2/ngtcp2.gyp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@
206206
'defines': [
207207
'BUILDING_NGHTTP3',
208208
'NGHTTP3_STATICLIB',
209+
'DEBUGBUILD',
209210
],
210211
'dependencies': [
211212
'ngtcp2'
@@ -247,7 +248,10 @@
247248
},
248249
{
249250
'target_name': 'ngtcp2_test_server',
250-
'type': 'executable',
251+
# Disabled: ngtcp2 examples now require C++23 (<print>, <expected>,
252+
# std::println, std::expected) which is not yet supported on all
253+
# Node.js platforms. Re-enable when C++23 is available.
254+
'type': 'none',
251255
'cflags': [ '-Wno-everything' ],
252256
'include_dirs': [
253257
'',
@@ -305,7 +309,10 @@
305309
},
306310
{
307311
'target_name': 'ngtcp2_test_client',
308-
'type': 'executable',
312+
# Disabled: ngtcp2 examples now require C++23 (<print>, <expected>,
313+
# std::println, std::expected) which is not yet supported on all
314+
# Node.js platforms. Re-enable when C++23 is available.
315+
'type': 'none',
309316
'cflags': [ '-Wno-everything' ],
310317
'include_dirs': [
311318
'',

node.gyp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,6 @@
346346
'src/quic/bindingdata.cc',
347347
'src/quic/cid.cc',
348348
'src/quic/data.cc',
349-
'src/quic/logstream.cc',
350349
'src/quic/packet.cc',
351350
'src/quic/preferredaddress.cc',
352351
'src/quic/sessionticket.cc',
@@ -355,6 +354,7 @@
355354
'src/quic/endpoint.cc',
356355
'src/quic/http3.cc',
357356
'src/quic/session.cc',
357+
'src/quic/session_manager.cc',
358358
'src/quic/streams.cc',
359359
'src/quic/tlscontext.cc',
360360
'src/quic/transportparams.cc',
@@ -364,7 +364,6 @@
364364
'src/quic/cid.h',
365365
'src/quic/data.h',
366366
'src/quic/defs.h',
367-
'src/quic/logstream.h',
368367
'src/quic/packet.h',
369368
'src/quic/preferredaddress.h',
370369
'src/quic/sessionticket.h',
@@ -374,6 +373,7 @@
374373
'src/quic/endpoint.h',
375374
'src/quic/http3.h',
376375
'src/quic/session.h',
376+
'src/quic/session_manager.h',
377377
'src/quic/streams.h',
378378
'src/quic/tlscontext.h',
379379
'src/quic/guard.h',

src/dataqueue/queue.cc

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -391,10 +391,11 @@ class NonIdempotentDataQueueReader final
391391
// If the collection of entries is empty, there's nothing currently left to
392392
// read. How we respond depends on whether the data queue has been capped
393393
// or not.
394+
394395
if (data_queue_->entries_.empty()) {
395396
// If the data_queue_ is empty, and not capped, then we can reasonably
396397
// expect more data to be provided later, but we don't know exactly when
397-
// that'll happe, so the proper response here is to return a blocked
398+
// that'll happen, so the proper response here is to return a blocked
398399
// status.
399400
if (!data_queue_->is_capped()) {
400401
std::move(next)(bob::Status::STATUS_BLOCK, nullptr, 0, [](uint64_t) {});
@@ -437,22 +438,39 @@ class NonIdempotentDataQueueReader final
437438
CHECK(!pull_pending_);
438439
pull_pending_ = true;
439440
int status = current_reader->Pull(
440-
[this, next = std::move(next)](
441-
int status, const DataQueue::Vec* vecs, uint64_t count, Done done) {
441+
[this, next = std::move(next), options, data, count, max_count_hint](
442+
int status,
443+
const DataQueue::Vec* vecs,
444+
uint64_t vcount,
445+
Done done) mutable {
442446
pull_pending_ = false;
443447

444448
// In each of these cases, we do not expect that the source will
445449
// actually have provided any actual data.
446450
CHECK_IMPLIES(status == bob::Status::STATUS_BLOCK ||
447451
status == bob::Status::STATUS_WAIT ||
448452
status == bob::Status::STATUS_EOS,
449-
vecs == nullptr && count == 0);
453+
vecs == nullptr && vcount == 0);
450454
if (status == bob::Status::STATUS_EOS) {
451455
data_queue_->entries_.erase(data_queue_->entries_.begin());
452-
ended_ = data_queue_->entries_.empty();
453456
current_reader_ = nullptr;
454-
if (!ended_) status = bob::Status::STATUS_CONTINUE;
455-
std::move(next)(status, nullptr, 0, [](uint64_t) {});
457+
if (!data_queue_->entries_.empty()) {
458+
// More entries remain. Pull from the next entry immediately
459+
// rather than returning empty CONTINUE, which would leave
460+
// callers with no data and no way to know they should retry.
461+
Pull(std::move(next), options, data, count, max_count_hint);
462+
} else if (!data_queue_->is_capped()) {
463+
// The queue is empty but not capped — more data may arrive
464+
// later. Return BLOCK so the consumer waits rather than
465+
// falsely treating this as end-of-stream.
466+
std::move(next)(
467+
bob::Status::STATUS_BLOCK, nullptr, 0, [](uint64_t) {});
468+
} else {
469+
// Empty and capped — truly done.
470+
ended_ = true;
471+
std::move(next)(
472+
bob::Status::STATUS_EOS, nullptr, 0, [](uint64_t) {});
473+
}
456474
return;
457475
}
458476

@@ -461,15 +479,15 @@ class NonIdempotentDataQueueReader final
461479
if (data_queue_->HasBackpressureListeners()) {
462480
// How much did we actually read?
463481
size_t read = 0;
464-
for (uint64_t n = 0; n < count; n++) {
482+
for (uint64_t n = 0; n < vcount; n++) {
465483
read += vecs[n].len;
466484
}
467485
data_queue_->NotifyBackpressure(read);
468486
}
469487

470488
// Now that we have updated this readers state, we can forward
471489
// everything on to the outer next.
472-
std::move(next)(status, vecs, count, std::move(done));
490+
std::move(next)(status, vecs, vcount, std::move(done));
473491
},
474492
options,
475493
data,

src/debug_utils.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ void NODE_EXTERN_PRIVATE FWrite(FILE* file, const std::string& str);
5454
V(INSPECTOR_CLIENT) \
5555
V(INSPECTOR_PROFILER) \
5656
V(CODE_CACHE) \
57-
V(NGTCP2_DEBUG) \
57+
V(NGTCP2) \
58+
V(NGHTTP3) \
5859
V(SEA) \
5960
V(WASI) \
6061
V(MODULE) \

src/node_blob.cc

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,7 @@ Local<FunctionTemplate> Blob::GetConstructorTemplate(Environment* env) {
156156
Isolate* isolate = env->isolate();
157157
tmpl = NewFunctionTemplate(isolate, nullptr);
158158
tmpl->InstanceTemplate()->SetInternalFieldCount(Blob::kInternalFieldCount);
159-
tmpl->SetClassName(
160-
FIXED_ONE_BYTE_STRING(env->isolate(), "Blob"));
159+
tmpl->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "Blob"));
161160
SetProtoMethod(isolate, tmpl, "getReader", GetReader);
162161
SetProtoMethod(isolate, tmpl, "slice", ToSlice);
163162
env->set_blob_constructor_template(tmpl);
@@ -255,8 +254,7 @@ void Blob::New(const FunctionCallbackInfo<Value>& args) {
255254
}
256255

257256
auto blob = Create(env, DataQueue::CreateIdempotent(std::move(entries)));
258-
if (blob)
259-
args.GetReturnValue().Set(blob->object());
257+
if (blob) args.GetReturnValue().Set(blob->object());
260258
}
261259

262260
void Blob::GetReader(const FunctionCallbackInfo<Value>& args) {
@@ -278,8 +276,7 @@ void Blob::ToSlice(const FunctionCallbackInfo<Value>& args) {
278276
size_t start = args[0].As<Uint32>()->Value();
279277
size_t end = args[1].As<Uint32>()->Value();
280278
BaseObjectPtr<Blob> slice = blob->Slice(env, start, end);
281-
if (slice)
282-
args.GetReturnValue().Set(slice->object());
279+
if (slice) args.GetReturnValue().Set(slice->object());
283280
}
284281

285282
void Blob::MemoryInfo(MemoryTracker* tracker) const {
@@ -343,6 +340,7 @@ void Blob::Reader::Pull(const FunctionCallbackInfo<Value>& args) {
343340
Environment* env = Environment::GetCurrent(args);
344341
Blob::Reader* reader;
345342
ASSIGN_OR_RETURN_UNWRAP(&reader, args.This());
343+
reader->pull_pending_ = false;
346344

347345
CHECK(args[0]->IsFunction());
348346
Local<Function> fn = args[0].As<Function>();
@@ -414,19 +412,31 @@ void Blob::Reader::Pull(const FunctionCallbackInfo<Value>& args) {
414412
void Blob::Reader::SetWakeup(const FunctionCallbackInfo<Value>& args) {
415413
Blob::Reader* reader;
416414
ASSIGN_OR_RETURN_UNWRAP(&reader, args.This());
415+
if (args[0]->IsUndefined()) {
416+
reader->wakeup_.Reset();
417+
return;
418+
}
417419
CHECK(args[0]->IsFunction());
418420
reader->wakeup_.Reset(args.GetIsolate(), args[0].As<Function>());
419421
}
420422

421-
void Blob::Reader::NotifyPull() {
423+
void Blob::Reader::NotifyPull(bool fin) {
422424
if (wakeup_.IsEmpty() || !env()->can_call_into_js()) return;
425+
// FIN notifications always fire — they must not be suppressed by
426+
// pull_pending_ because there will be no further notifications to
427+
// wake the iterator. Regular data notifications respect pull_pending_
428+
// to coalesce multiple deliveries within a single packet.
429+
if (!fin && pull_pending_) return;
430+
pull_pending_ = true;
423431
HandleScope handle_scope(env()->isolate());
424432
Local<Function> fn = wakeup_.Get(env()->isolate());
425-
MakeCallback(fn, 0, nullptr);
433+
// Pass fin as the first argument so the JS iterator knows EOS is
434+
// imminent and should pull again without waiting for another wakeup.
435+
Local<Value> argv[] = {v8::Boolean::New(env()->isolate(), fin)};
436+
MakeCallback(fn, 1, argv);
426437
}
427438

428-
BaseObjectPtr<BaseObject>
429-
Blob::BlobTransferData::Deserialize(
439+
BaseObjectPtr<BaseObject> Blob::BlobTransferData::Deserialize(
430440
Environment* env,
431441
Local<Context> context,
432442
std::unique_ptr<worker::TransferData> self) {
@@ -448,10 +458,10 @@ std::unique_ptr<worker::TransferData> Blob::CloneForMessaging() const {
448458
void Blob::StoreDataObject(const FunctionCallbackInfo<Value>& args) {
449459
Realm* realm = Realm::GetCurrent(args);
450460

451-
CHECK(args[0]->IsString()); // ID key
461+
CHECK(args[0]->IsString()); // ID key
452462
CHECK(Blob::HasInstance(realm->env(), args[1])); // Blob
453-
CHECK(args[2]->IsUint32()); // Length
454-
CHECK(args[3]->IsString()); // Type
463+
CHECK(args[2]->IsUint32()); // Length
464+
CHECK(args[3]->IsString()); // Type
455465

456466
BlobBindingData* binding_data = realm->GetBindingData<BlobBindingData>();
457467
Isolate* isolate = realm->isolate();
@@ -531,12 +541,8 @@ void BlobBindingData::StoredDataObject::MemoryInfo(
531541
}
532542

533543
BlobBindingData::StoredDataObject::StoredDataObject(
534-
const BaseObjectPtr<Blob>& blob_,
535-
size_t length_,
536-
const std::string& type_)
537-
: blob(blob_),
538-
length(length_),
539-
type(type_) {}
544+
const BaseObjectPtr<Blob>& blob_, size_t length_, const std::string& type_)
545+
: blob(blob_), length(length_), type(type_) {}
540546

541547
BlobBindingData::BlobBindingData(Realm* realm, Local<Object> wrap)
542548
: SnapshotableObject(realm, wrap, type_int) {
@@ -550,8 +556,7 @@ void BlobBindingData::MemoryInfo(MemoryTracker* tracker) const {
550556
}
551557

552558
void BlobBindingData::store_data_object(
553-
const std::string& uuid,
554-
const BlobBindingData::StoredDataObject& object) {
559+
const std::string& uuid, const BlobBindingData::StoredDataObject& object) {
555560
data_objects_[uuid] = object;
556561
}
557562

@@ -566,8 +571,7 @@ void BlobBindingData::revoke_data_object(const std::string& uuid) {
566571
BlobBindingData::StoredDataObject BlobBindingData::get_data_object(
567572
const std::string& uuid) {
568573
auto entry = data_objects_.find(uuid);
569-
if (entry == data_objects_.end())
570-
return BlobBindingData::StoredDataObject {};
574+
if (entry == data_objects_.end()) return BlobBindingData::StoredDataObject{};
571575
return entry->second;
572576
}
573577

src/node_blob.h

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@ namespace node {
2323

2424
class Blob : public BaseObject {
2525
public:
26-
static void RegisterExternalReferences(
27-
ExternalReferenceRegistry* registry);
26+
static void RegisterExternalReferences(ExternalReferenceRegistry* registry);
2827

2928
static void CreatePerIsolateProperties(IsolateData* isolate_data,
3029
v8::Local<v8::ObjectTemplate> target);
@@ -83,7 +82,7 @@ class Blob : public BaseObject {
8382
BaseObjectPtr<Blob> blob);
8483
static void Pull(const v8::FunctionCallbackInfo<v8::Value>& args);
8584
static void SetWakeup(const v8::FunctionCallbackInfo<v8::Value>& args);
86-
void NotifyPull();
85+
void NotifyPull(bool fin = false);
8786

8887
explicit Reader(Environment* env,
8988
v8::Local<v8::Object> obj,
@@ -97,6 +96,7 @@ class Blob : public BaseObject {
9796
std::shared_ptr<DataQueue::Reader> inner_;
9897
BaseObjectPtr<Blob> strong_ptr_;
9998
bool eos_ = false;
99+
bool pull_pending_ = false;
100100
v8::Global<v8::Function> wakeup_;
101101
};
102102

@@ -134,19 +134,17 @@ class BlobBindingData : public SnapshotableObject {
134134

135135
StoredDataObject() = default;
136136

137-
StoredDataObject(
138-
const BaseObjectPtr<Blob>& blob_,
139-
size_t length_,
140-
const std::string& type_);
137+
StoredDataObject(const BaseObjectPtr<Blob>& blob_,
138+
size_t length_,
139+
const std::string& type_);
141140

142141
void MemoryInfo(MemoryTracker* tracker) const override;
143142
SET_SELF_SIZE(StoredDataObject)
144143
SET_MEMORY_INFO_NAME(StoredDataObject)
145144
};
146145

147-
void store_data_object(
148-
const std::string& uuid,
149-
const StoredDataObject& object);
146+
void store_data_object(const std::string& uuid,
147+
const StoredDataObject& object);
150148

151149
void revoke_data_object(const std::string& uuid);
152150

src/node_file.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,7 @@ class FileHandle final : public AsyncWrap, public StreamBase {
338338
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
339339

340340
int GetFD() override { return fd_; }
341+
const std::string& original_name() const { return original_name_; }
341342

342343
int Release();
343344

src/node_perf_common.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,13 @@ extern uint64_t performance_v8_start;
3434
V(LOOP_EXIT, "loopExit") \
3535
V(BOOTSTRAP_COMPLETE, "bootstrapComplete")
3636

37-
#define NODE_PERFORMANCE_ENTRY_TYPES(V) \
38-
V(GC, "gc") \
39-
V(HTTP, "http") \
40-
V(HTTP2, "http2") \
41-
V(NET, "net") \
42-
V(DNS, "dns")
37+
#define NODE_PERFORMANCE_ENTRY_TYPES(V) \
38+
V(GC, "gc") \
39+
V(HTTP, "http") \
40+
V(HTTP2, "http2") \
41+
V(NET, "net") \
42+
V(DNS, "dns") \
43+
V(QUIC, "quic")
4344

4445
enum PerformanceMilestone {
4546
#define V(name, _) NODE_PERFORMANCE_MILESTONE_##name,

0 commit comments

Comments
 (0)