Skip to content

Commit 0892e34

Browse files
committed
src, quic: refine more of the quic implementation
Signed-off-by: James M Snell <[email protected]>
1 parent db3b0ad commit 0892e34

File tree

12 files changed

+593
-324
lines changed

12 files changed

+593
-324
lines changed

lib/internal/quic/quic.js

Lines changed: 131 additions & 19 deletions
Large diffs are not rendered by default.

lib/internal/quic/state.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const {
2323
} = require('util/types');
2424

2525
const { inspect } = require('internal/util/inspect');
26+
const assert = require('internal/assert');
2627

2728
const {
2829
kFinishClose,
@@ -77,6 +78,41 @@ const {
7778
IDX_STATE_STREAM_WANTS_TRAILERS,
7879
} = internalBinding('quic');
7980

81+
assert(IDX_STATE_SESSION_PATH_VALIDATION !== undefined);
82+
assert(IDX_STATE_SESSION_VERSION_NEGOTIATION !== undefined);
83+
assert(IDX_STATE_SESSION_DATAGRAM !== undefined);
84+
assert(IDX_STATE_SESSION_SESSION_TICKET !== undefined);
85+
assert(IDX_STATE_SESSION_CLOSING !== undefined);
86+
assert(IDX_STATE_SESSION_GRACEFUL_CLOSE !== undefined);
87+
assert(IDX_STATE_SESSION_SILENT_CLOSE !== undefined);
88+
assert(IDX_STATE_SESSION_STATELESS_RESET !== undefined);
89+
assert(IDX_STATE_SESSION_DESTROYED !== undefined);
90+
assert(IDX_STATE_SESSION_HANDSHAKE_COMPLETED !== undefined);
91+
assert(IDX_STATE_SESSION_HANDSHAKE_CONFIRMED !== undefined);
92+
assert(IDX_STATE_SESSION_STREAM_OPEN_ALLOWED !== undefined);
93+
assert(IDX_STATE_SESSION_PRIORITY_SUPPORTED !== undefined);
94+
assert(IDX_STATE_SESSION_WRAPPED !== undefined);
95+
assert(IDX_STATE_SESSION_LAST_DATAGRAM_ID !== undefined);
96+
assert(IDX_STATE_ENDPOINT_BOUND !== undefined);
97+
assert(IDX_STATE_ENDPOINT_RECEIVING !== undefined);
98+
assert(IDX_STATE_ENDPOINT_LISTENING !== undefined);
99+
assert(IDX_STATE_ENDPOINT_CLOSING !== undefined);
100+
assert(IDX_STATE_ENDPOINT_BUSY !== undefined);
101+
assert(IDX_STATE_ENDPOINT_PENDING_CALLBACKS !== undefined);
102+
assert(IDX_STATE_STREAM_ID !== undefined);
103+
assert(IDX_STATE_STREAM_FIN_SENT !== undefined);
104+
assert(IDX_STATE_STREAM_FIN_RECEIVED !== undefined);
105+
assert(IDX_STATE_STREAM_READ_ENDED !== undefined);
106+
assert(IDX_STATE_STREAM_WRITE_ENDED !== undefined);
107+
assert(IDX_STATE_STREAM_DESTROYED !== undefined);
108+
assert(IDX_STATE_STREAM_PAUSED !== undefined);
109+
assert(IDX_STATE_STREAM_RESET !== undefined);
110+
assert(IDX_STATE_STREAM_HAS_READER !== undefined);
111+
assert(IDX_STATE_STREAM_WANTS_BLOCK !== undefined);
112+
assert(IDX_STATE_STREAM_WANTS_HEADERS !== undefined);
113+
assert(IDX_STATE_STREAM_WANTS_RESET !== undefined);
114+
assert(IDX_STATE_STREAM_WANTS_TRAILERS !== undefined);
115+
80116
class QuicEndpointState {
81117
/** @type {DataView} */
82118
#handle;

src/node_http_common-inl.h

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -93,17 +93,13 @@ bool NgHeader<T>::IsZeroLength(
9393
}
9494

9595
template <typename T>
96-
bool NgHeader<T>::IsZeroLength(
97-
int32_t token,
98-
NgHeader<T>::rcbuf_t* name,
99-
NgHeader<T>::rcbuf_t* value) {
100-
96+
bool NgHeader<T>::IsZeroLength(int32_t token,
97+
NgHeader<T>::rcbuf_t* name,
98+
NgHeader<T>::rcbuf_t* value) {
10199
if (NgHeader<T>::rcbufferpointer_t::IsZeroLength(value))
102100
return true;
103101

104-
const char* header_name = T::ToHttpHeaderName(token);
105-
return header_name != nullptr ||
106-
NgHeader<T>::rcbufferpointer_t::IsZeroLength(name);
102+
return NgHeader<T>::rcbufferpointer_t::IsZeroLength(name);
107103
}
108104

109105
template <typename T>

src/quic/application.cc

Lines changed: 72 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -27,22 +27,25 @@ namespace quic {
2727

2828
// ============================================================================
2929
// Session::Application_Options
30-
const Session::Application_Options Session::Application_Options::kDefault = {};
30+
const Session::Application::Options Session::Application::Options::kDefault =
31+
{};
3132

32-
Session::Application_Options::operator const nghttp3_settings() const {
33-
// In theory, Application_Options might contain options for more than just
33+
Session::Application::Options::operator const nghttp3_settings() const {
34+
// In theory, Application::Options might contain options for more than just
3435
// HTTP/3. Here we extract only the properties that are relevant to HTTP/3.
3536
return nghttp3_settings{
36-
max_field_section_size,
37-
static_cast<size_t>(qpack_max_dtable_capacity),
38-
static_cast<size_t>(qpack_encoder_max_dtable_capacity),
39-
static_cast<size_t>(qpack_blocked_streams),
40-
enable_connect_protocol,
41-
enable_datagrams,
37+
.max_field_section_size = max_field_section_size,
38+
.qpack_max_dtable_capacity =
39+
static_cast<size_t>(qpack_max_dtable_capacity),
40+
.qpack_encoder_max_dtable_capacity =
41+
static_cast<size_t>(qpack_encoder_max_dtable_capacity),
42+
.qpack_blocked_streams = static_cast<size_t>(qpack_blocked_streams),
43+
.enable_connect_protocol = enable_connect_protocol,
44+
.h3_datagram = enable_datagrams,
4245
};
4346
}
4447

45-
std::string Session::Application_Options::ToString() const {
48+
std::string Session::Application::Options::ToString() const {
4649
DebugIndentScope indent;
4750
auto prefix = indent.Prefix();
4851
std::string res("{");
@@ -64,48 +67,58 @@ std::string Session::Application_Options::ToString() const {
6467
return res;
6568
}
6669

67-
Maybe<Session::Application_Options> Session::Application_Options::From(
70+
Maybe<Session::Application::Options> Session::Application::Options::From(
6871
Environment* env, Local<Value> value) {
69-
if (value.IsEmpty() || (!value->IsUndefined() && !value->IsObject())) {
72+
if (value.IsEmpty()) [[unlikely]] {
7073
THROW_ERR_INVALID_ARG_TYPE(env, "options must be an object");
71-
return Nothing<Application_Options>();
74+
return Nothing<Application::Options>();
7275
}
7376

74-
Application_Options options;
77+
Application::Options options;
7578
auto& state = BindingData::Get(env);
76-
if (value->IsUndefined()) {
77-
return Just<Application_Options>(options);
78-
}
79-
80-
auto params = value.As<Object>();
8179

8280
#define SET(name) \
83-
SetOption<Session::Application_Options, \
84-
&Session::Application_Options::name>( \
81+
SetOption<Session::Application::Options, \
82+
&Session::Application::Options::name>( \
8583
env, &options, params, state.name##_string())
8684

87-
if (!SET(max_header_pairs) || !SET(max_header_length) ||
88-
!SET(max_field_section_size) || !SET(qpack_max_dtable_capacity) ||
89-
!SET(qpack_encoder_max_dtable_capacity) || !SET(qpack_blocked_streams) ||
90-
!SET(enable_connect_protocol) || !SET(enable_datagrams)) {
91-
return Nothing<Application_Options>();
85+
if (!value->IsUndefined()) {
86+
if (!value->IsObject()) {
87+
THROW_ERR_INVALID_ARG_TYPE(env, "options must be an object");
88+
return Nothing<Application::Options>();
89+
}
90+
auto params = value.As<Object>();
91+
if (!SET(max_header_pairs) || !SET(max_header_length) ||
92+
!SET(max_field_section_size) || !SET(qpack_max_dtable_capacity) ||
93+
!SET(qpack_encoder_max_dtable_capacity) ||
94+
!SET(qpack_blocked_streams) || !SET(enable_connect_protocol) ||
95+
!SET(enable_datagrams)) {
96+
// The call to SetOption should have scheduled an exception to be thrown.
97+
return Nothing<Application::Options>();
98+
}
9299
}
93100

94101
#undef SET
95102

96-
return Just<Application_Options>(options);
103+
return Just<Application::Options>(options);
97104
}
98105

99106
// ============================================================================
100107

101108
std::string Session::Application::StreamData::ToString() const {
102109
DebugIndentScope indent;
110+
111+
size_t total_bytes = 0;
112+
for (size_t n = 0; n < count; n++) {
113+
total_bytes += data[n].len;
114+
}
115+
103116
auto prefix = indent.Prefix();
104117
std::string res("{");
105118
res += prefix + "count: " + std::to_string(count);
106-
res += prefix + "remaining: " + std::to_string(remaining);
107119
res += prefix + "id: " + std::to_string(id);
108120
res += prefix + "fin: " + std::to_string(fin);
121+
res += prefix + "total: " + std::to_string(total_bytes);
109122
res += indent.Close();
110123
return res;
111124
}
@@ -120,14 +133,16 @@ bool Session::Application::Start() {
120133
return true;
121134
}
122135

123-
void Session::Application::AcknowledgeStreamData(Stream* stream,
136+
bool Session::Application::AcknowledgeStreamData(int64_t stream_id,
124137
size_t datalen) {
125138
Debug(session_,
126139
"Application acknowledging stream %" PRIi64 " data: %zu",
127-
stream->id(),
140+
stream_id,
128141
datalen);
129-
DCHECK_NOT_NULL(stream);
142+
auto stream = session().FindStream(stream_id);
143+
if (!stream) return false;
130144
stream->Acknowledge(datalen);
145+
return true;
131146
}
132147

133148
void Session::Application::BlockStream(int64_t id) {
@@ -241,6 +256,14 @@ void Session::Application::SendPendingData() {
241256
PathStorage path;
242257
StreamData stream_data;
243258

259+
auto update_stats = OnScopeLeave([&] {
260+
auto& s = session();
261+
s.UpdateDataStats();
262+
if (!s.is_destroyed()) {
263+
s.UpdateTimer();
264+
}
265+
});
266+
244267
// The maximum size of packet to create.
245268
const size_t max_packet_size = session_->max_packet_size();
246269

@@ -296,7 +319,15 @@ void Session::Application::SendPendingData() {
296319
// Awesome, let's write our packet!
297320
ssize_t nwrite =
298321
WriteVStream(&path, pos, &ndatalen, max_packet_size, stream_data);
299-
Debug(session_, "Application accepted %zu bytes into packet", ndatalen);
322+
323+
if (ndatalen > 0) {
324+
Debug(session_,
325+
"Application accepted %zu bytes from stream into packet",
326+
ndatalen);
327+
} else {
328+
Debug(session_,
329+
"Application did not accept any bytes from stream into packet");
330+
}
300331

301332
// A negative nwrite value indicates either an error or that there is more
302333
// data to write into the packet.
@@ -309,7 +340,6 @@ void Session::Application::SendPendingData() {
309340
// ndatalen = -1 means that no stream data was accepted into the
310341
// packet, which is what we want here.
311342
DCHECK_EQ(ndatalen, -1);
312-
DCHECK(stream_data.stream);
313343
session_->StreamDataBlocked(stream_data.id);
314344
continue;
315345
}
@@ -323,8 +353,7 @@ void Session::Application::SendPendingData() {
323353
// ndatalen = -1 means that no stream data was accepted into the
324354
// packet, which is what we want here.
325355
DCHECK_EQ(ndatalen, -1);
326-
DCHECK(stream_data.stream);
327-
stream_data.stream->EndWritable();
356+
if (stream_data.stream) stream_data.stream->EndWritable();
328357
continue;
329358
}
330359
case NGTCP2_ERR_WRITE_MORE: {
@@ -406,16 +435,16 @@ ssize_t Session::Application::WriteVStream(PathStorage* path,
406435
DCHECK_LE(stream_data.count, kMaxVectorCount);
407436
uint32_t flags = NGTCP2_WRITE_STREAM_FLAG_MORE;
408437
if (stream_data.fin) flags |= NGTCP2_WRITE_STREAM_FLAG_FIN;
409-
ngtcp2_pkt_info pi;
438+
410439
return ngtcp2_conn_writev_stream(*session_,
411440
&path->path,
412-
&pi,
441+
nullptr,
413442
dest,
414443
max_packet_size,
415444
ndatalen,
416445
flags,
417446
stream_data.id,
418-
stream_data.buf,
447+
stream_data,
419448
stream_data.count,
420449
uv_hrtime());
421450
}
@@ -536,39 +565,20 @@ class DefaultApplication final : public Session::Application {
536565
}
537566

538567
bool ShouldSetFin(const StreamData& stream_data) override {
539-
auto const is_empty = [](auto vec, size_t cnt) {
540-
size_t i;
541-
for (i = 0; i < cnt && vec[i].len == 0; ++i) {
542-
}
543-
return i == cnt;
568+
auto const is_empty = [](const ngtcp2_vec* vec, size_t cnt) {
569+
size_t i = 0;
570+
for (size_t n = 0; n < cnt; n++) i += vec[n].len;
571+
return i > 0;
544572
};
545573

546-
return stream_data.stream && is_empty(stream_data.buf, stream_data.count);
574+
return stream_data.stream && is_empty(stream_data, stream_data.count);
547575
}
548576

549577
bool StreamCommit(StreamData* stream_data, size_t datalen) override {
550578
Debug(&session(), "Default application committing stream data");
551579
DCHECK_NOT_NULL(stream_data);
552-
const auto consume = [](ngtcp2_vec** pvec, size_t* pcnt, size_t len) {
553-
ngtcp2_vec* v = *pvec;
554-
size_t cnt = *pcnt;
555-
556-
for (; cnt > 0; --cnt, ++v) {
557-
if (v->len > len) {
558-
v->len -= len;
559-
v->base += len;
560-
break;
561-
}
562-
len -= v->len;
563-
}
564-
565-
*pvec = v;
566-
*pcnt = cnt;
567-
};
568580

569581
CHECK(stream_data->stream);
570-
stream_data->remaining -= datalen;
571-
consume(&stream_data->buf, &stream_data->count, datalen);
572582
stream_data->stream->Commit(datalen);
573583
return true;
574584
}

src/quic/application.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class Session::Application : public MemoryRetainer {
3535

3636
// Session will forward all data acknowledgements for a stream to the
3737
// Application.
38-
virtual void AcknowledgeStreamData(Stream* stream, size_t datalen);
38+
virtual bool AcknowledgeStreamData(int64_t stream_id, size_t datalen);
3939

4040
// Called to determine if a Header can be added to this application.
4141
// Applications that do not support headers will always return false.
@@ -146,10 +146,14 @@ struct Session::Application::StreamData final {
146146
int64_t id = -1;
147147
int fin = 0;
148148
ngtcp2_vec data[kMaxVectorCount]{};
149-
ngtcp2_vec* buf = data;
150149
BaseObjectPtr<Stream> stream;
151150

152-
inline operator nghttp3_vec() const { return {data[0].base, data[0].len}; }
151+
inline operator nghttp3_vec*() {
152+
return reinterpret_cast<nghttp3_vec*>(data);
153+
}
154+
155+
inline operator const ngtcp2_vec*() const { return data; }
156+
inline operator ngtcp2_vec*() { return data; }
153157

154158
std::string ToString() const;
155159
};

src/quic/data.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,12 @@ std::optional<int> QuicError::crypto_error() const {
257257
}
258258

259259
MaybeLocal<Value> QuicError::ToV8Value(Environment* env) const {
260+
if ((type() == QuicError::Type::TRANSPORT && code() == NGTCP2_NO_ERROR) ||
261+
(type() == QuicError::Type::APPLICATION && code() == NGTCP2_APP_NOERROR) ||
262+
(type() == QuicError::Type::APPLICATION && code() == NGHTTP3_H3_NO_ERROR)) {
263+
return Undefined(env->isolate());
264+
}
265+
260266
Local<Value> argv[] = {
261267
Integer::New(env->isolate(), static_cast<int>(type())),
262268
BigInt::NewFromUnsigned(env->isolate(), code()),

0 commit comments

Comments
 (0)