GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage-daily/nodes/benchmark/out/../src/node_http2.cc Lines: 1548 1604 96.5 %
Date: 2019-07-28 22:34:34 Branches: 540 741 72.9 %

Line Branch Exec Source
1
#include "aliased_buffer.h"
2
#include "debug_utils.h"
3
#include "memory_tracker-inl.h"
4
#include "node.h"
5
#include "node_buffer.h"
6
#include "node_http2.h"
7
#include "node_http2_state.h"
8
#include "node_perf.h"
9
#include "util-inl.h"
10
11
#include <algorithm>
12
13
namespace node {
14
15
using v8::ArrayBuffer;
16
using v8::ArrayBufferView;
17
using v8::Boolean;
18
using v8::Context;
19
using v8::Float64Array;
20
using v8::Function;
21
using v8::Integer;
22
using v8::NewStringType;
23
using v8::Number;
24
using v8::ObjectTemplate;
25
using v8::String;
26
using v8::Uint32;
27
using v8::Uint32Array;
28
using v8::Undefined;
29
30
using node::performance::PerformanceEntry;
31
namespace http2 {
32
33
namespace {
34
35
const char zero_bytes_256[256] = {};
36
37
38520
inline Http2Stream* GetStream(Http2Session* session,
38
                              int32_t id,
39
                              nghttp2_data_source* source) {
40
38520
  Http2Stream* stream = static_cast<Http2Stream*>(source->ptr);
41
38520
  if (stream == nullptr)
42
31995
    stream = session->FindStream(id);
43
38520
  CHECK_NOT_NULL(stream);
44
38520
  CHECK_EQ(id, stream->id());
45
38520
  return stream;
46
}
47
48
}  // anonymous namespace
49
50
// These configure the callbacks required by nghttp2 itself. There are
51
// two sets of callback functions, one that is used if a padding callback
52
// is set, and other that does not include the padding callback.
53
9656
const Http2Session::Callbacks Http2Session::callback_struct_saved[2] = {
54
    Callbacks(false),
55
4828
    Callbacks(true)};
56
57
// The Http2Scope object is used to queue a write to the i/o stream. It is
58
// used whenever any action is take on the underlying nghttp2 API that may
59
// push data into nghttp2 outbound data queue.
60
//
61
// For example:
62
//
63
// Http2Scope h2scope(session);
64
// nghttp2_submit_ping(**session, ... );
65
//
66
// When the Http2Scope passes out of scope and is deconstructed, it will
67
// call Http2Session::MaybeScheduleWrite().
68
60058
Http2Scope::Http2Scope(Http2Stream* stream) : Http2Scope(stream->session()) {}
69
70
102194
Http2Scope::Http2Scope(Http2Session* session) {
71
102194
  if (session == nullptr)
72
    return;
73
74
102194
  if (session->flags_ & (SESSION_STATE_HAS_SCOPE |
75
                         SESSION_STATE_WRITE_SCHEDULED)) {
76
    // There is another scope further below on the stack, or it is already
77
    // known that a write is scheduled. In either case, there is nothing to do.
78
71898
    return;
79
  }
80
30296
  session->flags_ |= SESSION_STATE_HAS_SCOPE;
81
30296
  session_ = session;
82
83
  // Always keep the session object alive for at least as long as
84
  // this scope is active.
85
30296
  session_handle_ = session->object();
86
60592
  CHECK(!session_handle_.IsEmpty());
87
}
88
89
102194
Http2Scope::~Http2Scope() {
90
102194
  if (session_ == nullptr)
91
71898
    return;
92
93
30296
  session_->flags_ &= ~SESSION_STATE_HAS_SCOPE;
94
30296
  session_->MaybeScheduleWrite();
95
30296
}
96
97
// The Http2Options object is used during the construction of Http2Session
98
// instances to configure an appropriate nghttp2_options struct. The class
99
// uses a single TypedArray instance that is shared with the JavaScript side
100
// to more efficiently pass values back and forth.
101
577
Http2Options::Http2Options(Environment* env, nghttp2_session_type type) {
102
577
  nghttp2_option_new(&options_);
103
104
  // Make sure closed connections aren't kept around, taking up memory.
105
  // Note that this breaks the priority tree, which we don't use.
106
577
  nghttp2_option_set_no_closed_streams(options_, 1);
107
108
  // We manually handle flow control within a session in order to
109
  // implement backpressure -- that is, we only send WINDOW_UPDATE
110
  // frames to the remote peer as data is actually consumed by user
111
  // code. This ensures that the flow of data over the connection
112
  // does not move too quickly and limits the amount of data we
113
  // are required to buffer.
114
577
  nghttp2_option_set_no_auto_window_update(options_, 1);
115
116
  // Enable built in support for receiving ALTSVC and ORIGIN frames (but
117
  // only on client side sessions
118
577
  if (type == NGHTTP2_SESSION_CLIENT) {
119
283
    nghttp2_option_set_builtin_recv_extension_type(options_, NGHTTP2_ALTSVC);
120
283
    nghttp2_option_set_builtin_recv_extension_type(options_, NGHTTP2_ORIGIN);
121
  }
122
123
577
  AliasedUint32Array& buffer = env->http2_state()->options_buffer;
124
577
  uint32_t flags = buffer[IDX_OPTIONS_FLAGS];
125
126
577
  if (flags & (1 << IDX_OPTIONS_MAX_DEFLATE_DYNAMIC_TABLE_SIZE)) {
127
    nghttp2_option_set_max_deflate_dynamic_table_size(
128
        options_,
129
        buffer[IDX_OPTIONS_MAX_DEFLATE_DYNAMIC_TABLE_SIZE]);
130
  }
131
132
577
  if (flags & (1 << IDX_OPTIONS_MAX_RESERVED_REMOTE_STREAMS)) {
133
    nghttp2_option_set_max_reserved_remote_streams(
134
        options_,
135
1
        buffer[IDX_OPTIONS_MAX_RESERVED_REMOTE_STREAMS]);
136
  }
137
138
577
  if (flags & (1 << IDX_OPTIONS_MAX_SEND_HEADER_BLOCK_LENGTH)) {
139
    nghttp2_option_set_max_send_header_block_length(
140
        options_,
141
1
        buffer[IDX_OPTIONS_MAX_SEND_HEADER_BLOCK_LENGTH]);
142
  }
143
144
  // Recommended default
145
577
  nghttp2_option_set_peer_max_concurrent_streams(options_, 100);
146
577
  if (flags & (1 << IDX_OPTIONS_PEER_MAX_CONCURRENT_STREAMS)) {
147
    nghttp2_option_set_peer_max_concurrent_streams(
148
        options_,
149
        buffer[IDX_OPTIONS_PEER_MAX_CONCURRENT_STREAMS]);
150
  }
151
152
  // The padding strategy sets the mechanism by which we determine how much
153
  // additional frame padding to apply to DATA and HEADERS frames. Currently
154
  // this is set on a per-session basis, but eventually we may switch to
155
  // a per-stream setting, giving users greater control
156
577
  if (flags & (1 << IDX_OPTIONS_PADDING_STRATEGY)) {
157
    padding_strategy_type strategy =
158
        static_cast<padding_strategy_type>(
159
4
            buffer.GetValue(IDX_OPTIONS_PADDING_STRATEGY));
160
4
    SetPaddingStrategy(strategy);
161
  }
162
163
  // The max header list pairs option controls the maximum number of
164
  // header pairs the session may accept. This is a hard limit.. that is,
165
  // if the remote peer sends more than this amount, the stream will be
166
  // automatically closed with an RST_STREAM.
167
577
  if (flags & (1 << IDX_OPTIONS_MAX_HEADER_LIST_PAIRS)) {
168
3
    SetMaxHeaderPairs(buffer[IDX_OPTIONS_MAX_HEADER_LIST_PAIRS]);
169
  }
170
171
  // The HTTP2 specification places no limits on the number of HTTP2
172
  // PING frames that can be sent. In order to prevent PINGS from being
173
  // abused as an attack vector, however, we place a strict upper limit
174
  // on the number of unacknowledged PINGS that can be sent at any given
175
  // time.
176
577
  if (flags & (1 << IDX_OPTIONS_MAX_OUTSTANDING_PINGS)) {
177
2
    SetMaxOutstandingPings(buffer[IDX_OPTIONS_MAX_OUTSTANDING_PINGS]);
178
  }
179
180
  // The HTTP2 specification places no limits on the number of HTTP2
181
  // SETTINGS frames that can be sent. In order to prevent PINGS from being
182
  // abused as an attack vector, however, we place a strict upper limit
183
  // on the number of unacknowledged SETTINGS that can be sent at any given
184
  // time.
185
577
  if (flags & (1 << IDX_OPTIONS_MAX_OUTSTANDING_SETTINGS)) {
186
2
    SetMaxOutstandingSettings(buffer[IDX_OPTIONS_MAX_OUTSTANDING_SETTINGS]);
187
  }
188
189
  // The HTTP2 specification places no limits on the amount of memory
190
  // that a session can consume. In order to prevent abuse, we place a
191
  // cap on the amount of memory a session can consume at any given time.
192
  // this is a credit based system. Existing streams may cause the limit
193
  // to be temporarily exceeded but once over the limit, new streams cannot
194
  // created.
195
  // Important: The maxSessionMemory option in javascript is expressed in
196
  //            terms of MB increments (i.e. the value 1 == 1 MB)
197
577
  if (flags & (1 << IDX_OPTIONS_MAX_SESSION_MEMORY)) {
198
4
    SetMaxSessionMemory(buffer[IDX_OPTIONS_MAX_SESSION_MEMORY] * 1e6);
199
  }
200
577
}
201
202
601
void Http2Session::Http2Settings::Init() {
203
601
  AliasedUint32Array& buffer = env()->http2_state()->settings_buffer;
204
601
  uint32_t flags = buffer[IDX_SETTINGS_COUNT];
205
206
601
  size_t n = 0;
207
208
#define GRABSETTING(N, trace)                                                 \
209
  if (flags & (1 << IDX_SETTINGS_##N)) {                                      \
210
    uint32_t val = buffer[IDX_SETTINGS_##N];                                  \
211
    if (session_ != nullptr)                                                  \
212
      Debug(session_, "setting " trace ": %d\n", val);                        \
213
    entries_[n++] =                                                           \
214
        nghttp2_settings_entry {NGHTTP2_SETTINGS_##N, val};                   \
215
  }
216
217

606
  GRABSETTING(HEADER_TABLE_SIZE, "header table size");
218

606
  GRABSETTING(MAX_CONCURRENT_STREAMS, "max concurrent streams");
219

607
  GRABSETTING(MAX_FRAME_SIZE, "max frame size");
220

619
  GRABSETTING(INITIAL_WINDOW_SIZE, "initial window size");
221

608
  GRABSETTING(MAX_HEADER_LIST_SIZE, "max header list size");
222

612
  GRABSETTING(ENABLE_PUSH, "enable push");
223

604
  GRABSETTING(ENABLE_CONNECT_PROTOCOL, "enable connect protocol");
224
225
#undef GRABSETTING
226
227
601
  count_ = n;
228
601
}
229
230
// The Http2Settings class is used to configure a SETTINGS frame that is
231
// to be sent to the connected peer. The settings are set using a TypedArray
232
// that is shared with the JavaScript side.
233
601
Http2Session::Http2Settings::Http2Settings(Environment* env,
234
                                           Http2Session* session,
235
                                           Local<Object> obj,
236
                                           uint64_t start_time)
237
    : AsyncWrap(env, obj, PROVIDER_HTTP2SETTINGS),
238
      session_(session),
239
601
      startTime_(start_time) {
240
601
  RemoveCleanupHook();  // This object is owned by the Http2Session.
241
601
  Init();
242
601
}
243
244
// Generates a Buffer that contains the serialized payload of a SETTINGS
245
// frame. This can be used, for instance, to create the Base64-encoded
246
// content of an Http2-Settings header field.
247
15
Local<Value> Http2Session::Http2Settings::Pack() {
248
15
  const size_t len = count_ * 6;
249
30
  Local<Value> buf = Buffer::New(env(), len).ToLocalChecked();
250
  ssize_t ret =
251
      nghttp2_pack_settings_payload(
252
15
        reinterpret_cast<uint8_t*>(Buffer::Data(buf)), len,
253
30
        &entries_[0], count_);
254
15
  if (ret >= 0)
255
14
    return buf;
256
  else
257
2
    return Undefined(env()->isolate());
258
}
259
260
// Updates the shared TypedArray with the current remote or local settings for
261
// the session.
262
18280
void Http2Session::Http2Settings::Update(Environment* env,
263
                                         Http2Session* session,
264
                                         get_setting fn) {
265
18280
  AliasedUint32Array& buffer = env->http2_state()->settings_buffer;
266
36560
  buffer[IDX_SETTINGS_HEADER_TABLE_SIZE] =
267
36560
      fn(**session, NGHTTP2_SETTINGS_HEADER_TABLE_SIZE);
268
36560
  buffer[IDX_SETTINGS_MAX_CONCURRENT_STREAMS] =
269
36560
      fn(**session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
270
36560
  buffer[IDX_SETTINGS_INITIAL_WINDOW_SIZE] =
271
36560
      fn(**session, NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE);
272
36560
  buffer[IDX_SETTINGS_MAX_FRAME_SIZE] =
273
36560
      fn(**session, NGHTTP2_SETTINGS_MAX_FRAME_SIZE);
274
36560
  buffer[IDX_SETTINGS_MAX_HEADER_LIST_SIZE] =
275
36560
      fn(**session, NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE);
276
36560
  buffer[IDX_SETTINGS_ENABLE_PUSH] =
277
36560
      fn(**session, NGHTTP2_SETTINGS_ENABLE_PUSH);
278
36560
  buffer[IDX_SETTINGS_ENABLE_CONNECT_PROTOCOL] =
279
36560
      fn(**session, NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL);
280
18280
}
281
282
// Initializes the shared TypedArray with the default settings values.
283
3
void Http2Session::Http2Settings::RefreshDefaults(Environment* env) {
284
3
  AliasedUint32Array& buffer = env->http2_state()->settings_buffer;
285
286
6
  buffer[IDX_SETTINGS_HEADER_TABLE_SIZE] =
287
3
      DEFAULT_SETTINGS_HEADER_TABLE_SIZE;
288
6
  buffer[IDX_SETTINGS_ENABLE_PUSH] =
289
3
      DEFAULT_SETTINGS_ENABLE_PUSH;
290
6
  buffer[IDX_SETTINGS_INITIAL_WINDOW_SIZE] =
291
3
      DEFAULT_SETTINGS_INITIAL_WINDOW_SIZE;
292
6
  buffer[IDX_SETTINGS_MAX_FRAME_SIZE] =
293
3
      DEFAULT_SETTINGS_MAX_FRAME_SIZE;
294
6
  buffer[IDX_SETTINGS_MAX_HEADER_LIST_SIZE] =
295
3
      DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE;
296
6
  buffer[IDX_SETTINGS_COUNT] =
297
    (1 << IDX_SETTINGS_HEADER_TABLE_SIZE) |
298
    (1 << IDX_SETTINGS_ENABLE_PUSH) |
299
    (1 << IDX_SETTINGS_INITIAL_WINDOW_SIZE) |
300
    (1 << IDX_SETTINGS_MAX_FRAME_SIZE) |
301
3
    (1 << IDX_SETTINGS_MAX_HEADER_LIST_SIZE);
302
3
}
303
304
305
584
void Http2Session::Http2Settings::Send() {
306
584
  Http2Scope h2scope(session_);
307
584
  CHECK_EQ(nghttp2_submit_settings(**session_, NGHTTP2_FLAG_NONE,
308
584
                                   &entries_[0], count_), 0);
309
584
}
310
311
478
void Http2Session::Http2Settings::Done(bool ack) {
312
478
  uint64_t end = uv_hrtime();
313
478
  double duration = (end - startTime_) / 1e6;
314
315
  Local<Value> argv[] = {
316
    Boolean::New(env()->isolate(), ack),
317
    Number::New(env()->isolate(), duration)
318
1434
  };
319
478
  MakeCallback(env()->ondone_string(), arraysize(argv), argv);
320
478
}
321
322
// The Http2Priority class initializes an appropriate nghttp2_priority_spec
323
// struct used when either creating a stream or updating its priority
324
// settings.
325
11629
Http2Priority::Http2Priority(Environment* env,
326
                             Local<Value> parent,
327
                             Local<Value> weight,
328
                             Local<Value> exclusive) {
329
11629
  Local<Context> context = env->context();
330
23258
  int32_t parent_ = parent->Int32Value(context).ToChecked();
331
23258
  int32_t weight_ = weight->Int32Value(context).ToChecked();
332
23258
  bool exclusive_ = exclusive->BooleanValue(env->isolate());
333
  Debug(env, DebugCategory::HTTP2STREAM,
334
        "Http2Priority: parent: %d, weight: %d, exclusive: %d\n",
335
        parent_, weight_, exclusive_);
336
11629
  nghttp2_priority_spec_init(&spec, parent_, weight_, exclusive_ ? 1 : 0);
337
11629
}
338
339
340
130
const char* Http2Session::TypeName() const {
341
130
  switch (session_type_) {
342
68
    case NGHTTP2_SESSION_SERVER: return "server";
343
62
    case NGHTTP2_SESSION_CLIENT: return "client";
344
    default:
345
      // This should never happen
346
      ABORT();
347
  }
348
}
349
350
// The Headers class initializes a proper array of nghttp2_nv structs
351
// containing the header name value pairs.
352
23217
Headers::Headers(Isolate* isolate,
353
                 Local<Context> context,
354
23217
                 Local<Array> headers) {
355
46434
  Local<Value> header_string = headers->Get(context, 0).ToLocalChecked();
356
46434
  Local<Value> header_count = headers->Get(context, 1).ToLocalChecked();
357
46434
  count_ = header_count.As<Uint32>()->Value();
358
46434
  int header_string_len = header_string.As<String>()->Length();
359
360
23217
  if (count_ == 0) {
361
24
    CHECK_EQ(header_string_len, 0);
362
49
    return;
363
  }
364
365
  // Allocate a single buffer with count_ nghttp2_nv structs, followed
366
  // by the raw header data as passed from JS. This looks like:
367
  // | possible padding | nghttp2_nv | nghttp2_nv | ... | header contents |
368
  buf_.AllocateSufficientStorage((alignof(nghttp2_nv) - 1) +
369
23193
                                 count_ * sizeof(nghttp2_nv) +
370
46386
                                 header_string_len);
371
  // Make sure the start address is aligned appropriately for an nghttp2_nv*.
372
  char* start = reinterpret_cast<char*>(
373
23193
      RoundUp(reinterpret_cast<uintptr_t>(*buf_), alignof(nghttp2_nv)));
374
23193
  char* header_contents = start + (count_ * sizeof(nghttp2_nv));
375
23193
  nghttp2_nv* const nva = reinterpret_cast<nghttp2_nv*>(start);
376
377
23193
  CHECK_LE(header_contents + header_string_len, *buf_ + buf_.length());
378
69579
  CHECK_EQ(header_string.As<String>()->WriteOneByte(
379
               isolate,
380
               reinterpret_cast<uint8_t*>(header_contents),
381
               0,
382
               header_string_len,
383
               String::NO_NULL_TERMINATION),
384
           header_string_len);
385
386
23193
  size_t n = 0;
387
  char* p;
388
94099
  for (p = header_contents; p < header_contents + header_string_len; n++) {
389
70907
    if (n >= count_) {
390
      // This can happen if a passed header contained a null byte. In that
391
      // case, just provide nghttp2 with an invalid header to make it reject
392
      // the headers list.
393
      static uint8_t zero = '\0';
394
1
      nva[0].name = nva[0].value = &zero;
395
1
      nva[0].namelen = nva[0].valuelen = 1;
396
1
      count_ = 1;
397
1
      return;
398
    }
399
400
70906
    nva[n].flags = NGHTTP2_NV_FLAG_NONE;
401
70906
    nva[n].name = reinterpret_cast<uint8_t*>(p);
402
70906
    nva[n].namelen = strlen(p);
403
70906
    p += nva[n].namelen + 1;
404
70906
    nva[n].value = reinterpret_cast<uint8_t*>(p);
405
70906
    nva[n].valuelen = strlen(p);
406
70906
    p += nva[n].valuelen + 1;
407
  }
408
}
409
410
5
Origins::Origins(Isolate* isolate,
411
                 Local<Context> context,
412
                 Local<String> origin_string,
413
5
                 size_t origin_count) : count_(origin_count) {
414
5
  int origin_string_len = origin_string->Length();
415
5
  if (count_ == 0) {
416
    CHECK_EQ(origin_string_len, 0);
417
    return;
418
  }
419
420
  // Allocate a single buffer with count_ nghttp2_nv structs, followed
421
  // by the raw header data as passed from JS. This looks like:
422
  // | possible padding | nghttp2_nv | nghttp2_nv | ... | header contents |
423
  buf_.AllocateSufficientStorage((alignof(nghttp2_origin_entry) - 1) +
424
5
                                 count_ * sizeof(nghttp2_origin_entry) +
425
10
                                 origin_string_len);
426
427
  // Make sure the start address is aligned appropriately for an nghttp2_nv*.
428
  char* start = reinterpret_cast<char*>(
429
5
      RoundUp(reinterpret_cast<uintptr_t>(*buf_),
430
5
              alignof(nghttp2_origin_entry)));
431
5
  char* origin_contents = start + (count_ * sizeof(nghttp2_origin_entry));
432
  nghttp2_origin_entry* const nva =
433
5
      reinterpret_cast<nghttp2_origin_entry*>(start);
434
435
5
  CHECK_LE(origin_contents + origin_string_len, *buf_ + buf_.length());
436
10
  CHECK_EQ(origin_string->WriteOneByte(
437
               isolate,
438
               reinterpret_cast<uint8_t*>(origin_contents),
439
               0,
440
               origin_string_len,
441
               String::NO_NULL_TERMINATION),
442
           origin_string_len);
443
444
5
  size_t n = 0;
445
  char* p;
446
14
  for (p = origin_contents; p < origin_contents + origin_string_len; n++) {
447
9
    if (n >= count_) {
448
      static uint8_t zero = '\0';
449
      nva[0].origin = &zero;
450
      nva[0].origin_len = 1;
451
      count_ = 1;
452
      return;
453
    }
454
455
9
    nva[n].origin = reinterpret_cast<uint8_t*>(p);
456
9
    nva[n].origin_len = strlen(p);
457
9
    p += nva[n].origin_len + 1;
458
  }
459
}
460
461
// Sets the various callback functions that nghttp2 will use to notify us
462
// about significant events while processing http2 stuff.
463
9656
Http2Session::Callbacks::Callbacks(bool kHasGetPaddingCallback) {
464
9656
  CHECK_EQ(nghttp2_session_callbacks_new(&callbacks), 0);
465
466
  nghttp2_session_callbacks_set_on_begin_headers_callback(
467
9656
    callbacks, OnBeginHeadersCallback);
468
  nghttp2_session_callbacks_set_on_header_callback2(
469
9656
    callbacks, OnHeaderCallback);
470
  nghttp2_session_callbacks_set_on_frame_recv_callback(
471
9656
    callbacks, OnFrameReceive);
472
  nghttp2_session_callbacks_set_on_stream_close_callback(
473
9656
    callbacks, OnStreamClose);
474
  nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
475
9656
    callbacks, OnDataChunkReceived);
476
  nghttp2_session_callbacks_set_on_frame_not_send_callback(
477
9656
    callbacks, OnFrameNotSent);
478
  nghttp2_session_callbacks_set_on_invalid_header_callback2(
479
9656
    callbacks, OnInvalidHeader);
480
  nghttp2_session_callbacks_set_error_callback(
481
9656
    callbacks, OnNghttpError);
482
  nghttp2_session_callbacks_set_send_data_callback(
483
9656
    callbacks, OnSendData);
484
  nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(
485
9656
    callbacks, OnInvalidFrame);
486
  nghttp2_session_callbacks_set_on_frame_send_callback(
487
9656
    callbacks, OnFrameSent);
488
489
9656
  if (kHasGetPaddingCallback) {
490
    nghttp2_session_callbacks_set_select_padding_callback(
491
4828
      callbacks, OnSelectPadding);
492
  }
493
9656
}
494
495
496
9656
Http2Session::Callbacks::~Callbacks() {
497
9656
  nghttp2_session_callbacks_del(callbacks);
498
9656
}
499
500
// Track memory allocated by nghttp2 using a custom allocator.
501
class Http2Session::MemoryAllocatorInfo {
502
 public:
503
577
  explicit MemoryAllocatorInfo(Http2Session* session)
504
577
      : info({ session, H2Malloc, H2Free, H2Calloc, H2Realloc }) {}
505
506
126354
  static void* H2Malloc(size_t size, void* user_data) {
507
126354
    return H2Realloc(nullptr, size, user_data);
508
  }
509
510
1154
  static void* H2Calloc(size_t nmemb, size_t size, void* user_data) {
511
1154
    size_t real_size = MultiplyWithOverflowCheck(nmemb, size);
512
1154
    void* mem = H2Malloc(real_size, user_data);
513
1154
    if (mem != nullptr)
514
1154
      memset(mem, 0, real_size);
515
1154
    return mem;
516
  }
517
518
330955
  static void H2Free(void* ptr, void* user_data) {
519
661910
    if (ptr == nullptr) return;  // free(null); happens quite often.
520
127162
    void* result = H2Realloc(ptr, 0, user_data);
521
127162
    CHECK_NULL(result);
522
  }
523
524
254343
  static void* H2Realloc(void* ptr, size_t size, void* user_data) {
525
254343
    Http2Session* session = static_cast<Http2Session*>(user_data);
526
254343
    size_t previous_size = 0;
527
254343
    char* original_ptr = nullptr;
528
529
    // We prepend each allocated buffer with a size_t containing the full
530
    // size of the allocation.
531
254343
    if (size > 0) size += sizeof(size_t);
532
533
254343
    if (ptr != nullptr) {
534
      // We are free()ing or re-allocating.
535
127181
      original_ptr = static_cast<char*>(ptr) - sizeof(size_t);
536
127181
      previous_size = *reinterpret_cast<size_t*>(original_ptr);
537
      // This means we called StopTracking() on this pointer before.
538
127181
      if (previous_size == 0) {
539
        // Fall back to the standard Realloc() function.
540
1790
        char* ret = UncheckedRealloc(original_ptr, size);
541
1790
        if (ret != nullptr)
542
          ret += sizeof(size_t);
543
1790
        return ret;
544
      }
545
    }
546
252553
    CHECK_GE(session->current_nghttp2_memory_, previous_size);
547
548
    // TODO(addaleax): Add the following, and handle NGHTTP2_ERR_NOMEM properly
549
    // everywhere:
550
    //
551
    // if (size > previous_size &&
552
    //     !session->IsAvailableSessionMemory(size - previous_size)) {
553
    //  return nullptr;
554
    //}
555
556
252553
    char* mem = UncheckedRealloc(original_ptr, size);
557
558
252553
    if (mem != nullptr) {
559
      // Adjust the memory info counter.
560
      // TODO(addaleax): Avoid the double bookkeeping we do with
561
      // current_nghttp2_memory_ + AdjustAmountOfExternalAllocatedMemory
562
      // and provide versions of our memory allocation utilities that take an
563
      // Environment*/Isolate* parameter and call the V8 method transparently.
564
127181
      const int64_t new_size = size - previous_size;
565
127181
      session->current_nghttp2_memory_ += new_size;
566
      session->env()->isolate()->AdjustAmountOfExternalAllocatedMemory(
567
127181
          new_size);
568
127181
      *reinterpret_cast<size_t*>(mem) = size;
569
127181
      mem += sizeof(size_t);
570
125372
    } else if (size == 0) {
571
125372
      session->current_nghttp2_memory_ -= previous_size;
572
      session->env()->isolate()->AdjustAmountOfExternalAllocatedMemory(
573
125372
          -static_cast<int64_t>(previous_size));
574
    }
575
576
252553
    return mem;
577
  }
578
579
24687
  static void StopTracking(Http2Session* session, void* ptr) {
580
    size_t* original_ptr = reinterpret_cast<size_t*>(
581
24687
        static_cast<char*>(ptr) - sizeof(size_t));
582
24687
    session->current_nghttp2_memory_ -= *original_ptr;
583
    session->env()->isolate()->AdjustAmountOfExternalAllocatedMemory(
584
24687
        -static_cast<int64_t>(*original_ptr));
585
24687
    *original_ptr = 0;
586
24687
  }
587
588
577
  inline nghttp2_mem* operator*() { return &info; }
589
590
  nghttp2_mem info;
591
};
592
593
24687
void Http2Session::StopTrackingRcbuf(nghttp2_rcbuf* buf) {
594
24687
  MemoryAllocatorInfo::StopTracking(this, buf);
595
24687
}
596
597
577
Http2Session::Http2Session(Environment* env,
598
                           Local<Object> wrap,
599
                           nghttp2_session_type type)
600
    : AsyncWrap(env, wrap, AsyncWrap::PROVIDER_HTTP2SESSION),
601
1154
      session_type_(type) {
602
577
  MakeWeak();
603
577
  statistics_.start_time = uv_hrtime();
604
605
  // Capture the configuration options for this session
606
577
  Http2Options opts(env, type);
607
608
577
  max_session_memory_ = opts.GetMaxSessionMemory();
609
610
577
  uint32_t maxHeaderPairs = opts.GetMaxHeaderPairs();
611
  max_header_pairs_ =
612
      type == NGHTTP2_SESSION_SERVER
613
1459
          ? std::max(maxHeaderPairs, 4U)     // minimum # of request headers
614

1448
          : std::max(maxHeaderPairs, 1U);    // minimum # of response headers
615
616
577
  max_outstanding_pings_ = opts.GetMaxOutstandingPings();
617
577
  max_outstanding_settings_ = opts.GetMaxOutstandingSettings();
618
619
577
  padding_strategy_ = opts.GetPaddingStrategy();
620
621
  bool hasGetPaddingCallback =
622
577
      padding_strategy_ != PADDING_STRATEGY_NONE;
623
624
  nghttp2_session_callbacks* callbacks
625
577
      = callback_struct_saved[hasGetPaddingCallback ? 1 : 0].callbacks;
626
627
  auto fn = type == NGHTTP2_SESSION_SERVER ?
628
      nghttp2_session_server_new3 :
629
577
      nghttp2_session_client_new3;
630
631
577
  MemoryAllocatorInfo allocator_info(this);
632
633
  // This should fail only if the system is out of memory, which
634
  // is going to cause lots of other problems anyway, or if any
635
  // of the options are out of acceptable range, which we should
636
  // be catching before it gets this far. Either way, crash if this
637
  // fails.
638
577
  CHECK_EQ(fn(&session_, callbacks, this, *opts, *allocator_info), 0);
639
640
577
  outgoing_storage_.reserve(4096);
641
577
  outgoing_buffers_.reserve(32);
642
577
}
643
644
1731
Http2Session::~Http2Session() {
645
577
  CHECK_EQ(flags_ & SESSION_STATE_HAS_SCOPE, 0);
646
577
  Debug(this, "freeing nghttp2 session");
647
579
  for (const auto& iter : streams_)
648
2
    iter.second->session_ = nullptr;
649
577
  nghttp2_session_del(session_);
650
577
  CHECK_EQ(current_nghttp2_memory_, 0);
651
1154
}
652
653
130
std::string Http2Session::diagnostic_name() const {
654
260
  return std::string("Http2Session ") + TypeName() + " (" +
655
390
      std::to_string(static_cast<int64_t>(get_async_id())) + ")";
656
}
657
658
23805
inline bool HasHttp2Observer(Environment* env) {
659
23805
  AliasedUint32Array& observers = env->performance_state()->observers;
660
23805
  return observers[performance::NODE_PERFORMANCE_ENTRY_TYPE_HTTP2] != 0;
661
}
662
663
23250
void Http2Stream::EmitStatistics() {
664
23250
  if (!HasHttp2Observer(env()))
665
46498
    return;
666
  Http2StreamPerformanceEntry* entry =
667
2
    new Http2StreamPerformanceEntry(env(), id_, statistics_);
668
6
  env()->SetImmediate([](Environment* env, void* data) {
669
    // This takes ownership, the entry is destroyed at the end of this scope.
670
    std::unique_ptr<Http2StreamPerformanceEntry> entry {
671
2
        static_cast<Http2StreamPerformanceEntry*>(data) };
672
2
    if (!HasHttp2Observer(env))
673
2
      return;
674
4
    HandleScope handle_scope(env->isolate());
675
2
    AliasedFloat64Array& buffer = env->http2_state()->stream_stats_buffer;
676
2
    buffer[IDX_STREAM_STATS_ID] = entry->id();
677
2
    if (entry->first_byte() != 0) {
678
      buffer[IDX_STREAM_STATS_TIMETOFIRSTBYTE] =
679
          (entry->first_byte() - entry->startTimeNano()) / 1e6;
680
    } else {
681
2
      buffer[IDX_STREAM_STATS_TIMETOFIRSTBYTE] = 0;
682
    }
683
2
    if (entry->first_header() != 0) {
684
4
      buffer[IDX_STREAM_STATS_TIMETOFIRSTHEADER] =
685
4
          (entry->first_header() - entry->startTimeNano()) / 1e6;
686
    } else {
687
      buffer[IDX_STREAM_STATS_TIMETOFIRSTHEADER] = 0;
688
    }
689
2
    if (entry->first_byte_sent() != 0) {
690
2
      buffer[IDX_STREAM_STATS_TIMETOFIRSTBYTESENT] =
691
2
          (entry->first_byte_sent() - entry->startTimeNano()) / 1e6;
692
    } else {
693
1
      buffer[IDX_STREAM_STATS_TIMETOFIRSTBYTESENT] = 0;
694
    }
695
2
    buffer[IDX_STREAM_STATS_SENTBYTES] = entry->sent_bytes();
696
2
    buffer[IDX_STREAM_STATS_RECEIVEDBYTES] = entry->received_bytes();
697
    Local<Object> obj;
698
6
    if (entry->ToObject().ToLocal(&obj)) entry->Notify(obj);
699
8
  }, static_cast<void*>(entry));
700
}
701
702
551
void Http2Session::EmitStatistics() {
703
551
  if (!HasHttp2Observer(env()))
704
1100
    return;
705
  Http2SessionPerformanceEntry* entry =
706
2
    new Http2SessionPerformanceEntry(env(), statistics_, session_type_);
707
6
  env()->SetImmediate([](Environment* env, void* data) {
708
    // This takes ownership, the entr is destroyed at the end of this scope.
709
    std::unique_ptr<Http2SessionPerformanceEntry> entry {
710
2
        static_cast<Http2SessionPerformanceEntry*>(data) };
711
2
    if (!HasHttp2Observer(env))
712
2
      return;
713
4
    HandleScope handle_scope(env->isolate());
714
2
    AliasedFloat64Array& buffer = env->http2_state()->session_stats_buffer;
715
2
    buffer[IDX_SESSION_STATS_TYPE] = entry->type();
716
2
    buffer[IDX_SESSION_STATS_PINGRTT] = entry->ping_rtt() / 1e6;
717
2
    buffer[IDX_SESSION_STATS_FRAMESRECEIVED] = entry->frame_count();
718
2
    buffer[IDX_SESSION_STATS_FRAMESSENT] = entry->frame_sent();
719
2
    buffer[IDX_SESSION_STATS_STREAMCOUNT] = entry->stream_count();
720
4
    buffer[IDX_SESSION_STATS_STREAMAVERAGEDURATION] =
721
4
        entry->stream_average_duration();
722
2
    buffer[IDX_SESSION_STATS_DATA_SENT] = entry->data_sent();
723
2
    buffer[IDX_SESSION_STATS_DATA_RECEIVED] = entry->data_received();
724
4
    buffer[IDX_SESSION_STATS_MAX_CONCURRENT_STREAMS] =
725
4
        entry->max_concurrent_streams();
726
    Local<Object> obj;
727
6
    if (entry->ToObject().ToLocal(&obj)) entry->Notify(obj);
728
8
  }, static_cast<void*>(entry));
729
}
730
731
// Closes the session and frees the associated resources
732
551
void Http2Session::Close(uint32_t code, bool socket_closed) {
733
551
  Debug(this, "closing session");
734
735
551
  if (flags_ & SESSION_STATE_CLOSING)
736
551
    return;
737
551
  flags_ |= SESSION_STATE_CLOSING;
738
739
  // Stop reading on the i/o stream
740
551
  if (stream_ != nullptr)
741
540
    stream_->ReadStop();
742
743
  // If the socket is not closed, then attempt to send a closing GOAWAY
744
  // frame. There is no guarantee that this GOAWAY will be received by
745
  // the peer but the HTTP/2 spec recommends sending it anyway. We'll
746
  // make a best effort.
747
551
  if (!socket_closed) {
748
520
    Debug(this, "terminating session with code %d", code);
749
520
    CHECK_EQ(nghttp2_session_terminate_session(session_, code), 0);
750
520
    SendPendingData();
751
31
  } else if (stream_ != nullptr) {
752
20
    stream_->RemoveStreamListener(this);
753
  }
754
755
551
  flags_ |= SESSION_STATE_CLOSED;
756
757
  // If there are outstanding pings, those will need to be canceled, do
758
  // so on the next iteration of the event loop to avoid calling out into
759
  // javascript since this may be called during garbage collection.
760
552
  while (std::unique_ptr<Http2Ping> ping = PopPing()) {
761
1
    ping->DetachFromSession();
762
    env()->SetImmediate(
763
3
        [](Environment* env, void* data) {
764
1
          std::unique_ptr<Http2Ping> ping{static_cast<Http2Ping*>(data)};
765
1
          ping->Done(false);
766
3
        },
767
1
        static_cast<void*>(ping.release()));
768
1
  }
769
770
551
  statistics_.end_time = uv_hrtime();
771
552
  EmitStatistics();
772
}
773
774
// Locates an existing known stream by ID. nghttp2 has a similar method
775
// but this is faster and does not fail if the stream is not found.
776
207668
inline Http2Stream* Http2Session::FindStream(int32_t id) {
777
207668
  auto s = streams_.find(id);
778
207668
  return s != streams_.end() ? s->second : nullptr;
779
}
780
781
11629
inline bool Http2Session::CanAddStream() {
782
  uint32_t maxConcurrentStreams =
783
      nghttp2_session_get_local_settings(
784
11629
          session_, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
785
  size_t maxSize =
786
11629
      std::min(streams_.max_size(), static_cast<size_t>(maxConcurrentStreams));
787
  // We can add a new stream so long as we are less than the current
788
  // maximum on concurrent streams and there's enough available memory
789

23258
  return streams_.size() < maxSize &&
790
23258
         IsAvailableSessionMemory(sizeof(Http2Stream));
791
}
792
793
23258
inline void Http2Session::AddStream(Http2Stream* stream) {
794
23258
  CHECK_GE(++statistics_.stream_count, 0);
795
23258
  streams_[stream->id()] = stream;
796
23258
  size_t size = streams_.size();
797
23258
  if (size > statistics_.max_concurrent_streams)
798
1039
    statistics_.max_concurrent_streams = size;
799
23258
  IncrementCurrentSessionMemory(sizeof(*stream));
800
23258
}
801
802
803
23256
inline void Http2Session::RemoveStream(Http2Stream* stream) {
804

23256
  if (streams_.empty() || stream == nullptr)
805
23256
    return;  // Nothing to remove, item was never added?
806
23256
  streams_.erase(stream->id());
807
23256
  DecrementCurrentSessionMemory(sizeof(*stream));
808
}
809
810
// Used as one of the Padding Strategy functions. Will attempt to ensure
811
// that the total frame size, including header bytes, are 8-byte aligned.
812
// If maxPayloadLen is smaller than the number of bytes necessary to align,
813
// will return maxPayloadLen instead.
814
4
ssize_t Http2Session::OnDWordAlignedPadding(size_t frameLen,
815
                                            size_t maxPayloadLen) {
816
4
  size_t r = (frameLen + 9) % 8;
817
4
  if (r == 0) return frameLen;  // If already a multiple of 8, return.
818
819
4
  size_t pad = frameLen + (8 - r);
820
821
  // If maxPayloadLen happens to be less than the calculated pad length,
822
  // use the max instead, even tho this means the frame will not be
823
  // aligned.
824
4
  pad = std::min(maxPayloadLen, pad);
825
4
  Debug(this, "using frame size padding: %d", pad);
826
4
  return pad;
827
}
828
829
// Used as one of the Padding Strategy functions. Uses the maximum amount
830
// of padding allowed for the current frame.
831
ssize_t Http2Session::OnMaxFrameSizePadding(size_t frameLen,
832
                                            size_t maxPayloadLen) {
833
  Debug(this, "using max frame size padding: %d", maxPayloadLen);
834
  return maxPayloadLen;
835
}
836
837
// Used as one of the Padding Strategy functions. Uses a callback to JS land
838
// to determine the amount of padding for the current frame. This option is
839
// rather more expensive because of the JS boundary cross. It generally should
840
// not be the preferred option.
841
4
ssize_t Http2Session::OnCallbackPadding(size_t frameLen,
842
                                        size_t maxPayloadLen) {
843
4
  if (frameLen == 0) return 0;
844
3
  Debug(this, "using callback to determine padding");
845
3
  Isolate* isolate = env()->isolate();
846
3
  HandleScope handle_scope(isolate);
847
3
  Local<Context> context = env()->context();
848
  Context::Scope context_scope(context);
849
850
3
  AliasedUint32Array& buffer = env()->http2_state()->padding_buffer;
851
3
  buffer[PADDING_BUF_FRAME_LENGTH] = frameLen;
852
3
  buffer[PADDING_BUF_MAX_PAYLOAD_LENGTH] = maxPayloadLen;
853
3
  buffer[PADDING_BUF_RETURN_VALUE] = frameLen;
854
3
  MakeCallback(env()->http2session_on_select_padding_function(), 0, nullptr);
855
3
  uint32_t retval = buffer[PADDING_BUF_RETURN_VALUE];
856
3
  retval = std::min(retval, static_cast<uint32_t>(maxPayloadLen));
857
3
  retval = std::max(retval, static_cast<uint32_t>(frameLen));
858
3
  Debug(this, "using padding size %d", retval);
859
6
  return retval;
860
}
861
862
863
// Write data received from the i/o stream to the underlying nghttp2_session.
864
// On each call to nghttp2_session_mem_recv, nghttp2 will begin calling the
865
// various callback functions. Each of these will typically result in a call
866
// out to JavaScript so this particular function is rather hot and can be
867
// quite expensive. This is a potential performance optimization target later.
868
29382
ssize_t Http2Session::Write(const uv_buf_t* bufs, size_t nbufs) {
869
29382
  size_t total = 0;
870
  // Note that nghttp2_session_mem_recv is a synchronous operation that
871
  // will trigger a number of other callbacks. Those will, in turn have
872
  // multiple side effects.
873
58760
  for (size_t n = 0; n < nbufs; n++) {
874
    Debug(this, "receiving %d bytes [wants data? %d]",
875
29382
          bufs[n].len,
876
88146
          nghttp2_session_want_read(session_));
877
    ssize_t ret =
878
      nghttp2_session_mem_recv(session_,
879
29382
                               reinterpret_cast<uint8_t*>(bufs[n].base),
880
58764
                               bufs[n].len);
881
29382
    CHECK_NE(ret, NGHTTP2_ERR_NOMEM);
882
883
29382
    if (ret < 0)
884
4
      return ret;
885
886
29378
    total += ret;
887
  }
888
  // Send any data that was queued up while processing the received data.
889
29378
  if (!IsDestroyed()) {
890
28916
    SendPendingData();
891
  }
892
29378
  return total;
893
}
894
895
896
140865
inline int32_t GetFrameID(const nghttp2_frame* frame) {
897
  // If this is a push promise, we want to grab the id of the promised stream
898
140865
  return (frame->hd.type == NGHTTP2_PUSH_PROMISE) ?
899
      frame->push_promise.promised_stream_id :
900
140865
      frame->hd.stream_id;
901
}
902
903
904
// Called by nghttp2 at the start of receiving a HEADERS frame. We use this
905
// callback to determine if a new stream is being created or if we are simply
906
// adding a new block of headers to an existing stream. The header pairs
907
// themselves are set in the OnHeaderCallback
908
23183
int Http2Session::OnBeginHeadersCallback(nghttp2_session* handle,
909
                                         const nghttp2_frame* frame,
910
                                         void* user_data) {
911
23183
  Http2Session* session = static_cast<Http2Session*>(user_data);
912
23183
  int32_t id = GetFrameID(frame);
913
  Debug(session, "beginning headers for stream %d", id);
914
915
23183
  Http2Stream* stream = session->FindStream(id);
916
  // The common case is that we're creating a new stream. The less likely
917
  // case is that we're receiving a set of trailers
918
23183
  if (LIKELY(stream == nullptr)) {
919

11629
    if (UNLIKELY(!session->CanAddStream() ||
920
                 Http2Stream::New(session, id, frame->headers.cat) ==
921
                     nullptr)) {
922
      // Too many concurrent streams being opened
923
      nghttp2_submit_rst_stream(**session, NGHTTP2_FLAG_NONE, id,
924
1
                                NGHTTP2_ENHANCE_YOUR_CALM);
925
1
      return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
926
    }
927
11554
  } else if (!stream->IsDestroyed()) {
928
11554
    stream->StartHeaders(frame->headers.cat);
929
  }
930
23182
  return 0;
931
}
932
933
// Called by nghttp2 for each header name/value pair in a HEADERS block.
934
// This had to have been preceded by a call to OnBeginHeadersCallback so
935
// the Http2Stream is guaranteed to already exist.
936
70846
int Http2Session::OnHeaderCallback(nghttp2_session* handle,
937
                                   const nghttp2_frame* frame,
938
                                   nghttp2_rcbuf* name,
939
                                   nghttp2_rcbuf* value,
940
                                   uint8_t flags,
941
                                   void* user_data) {
942
70846
  Http2Session* session = static_cast<Http2Session*>(user_data);
943
70846
  int32_t id = GetFrameID(frame);
944
70846
  Http2Stream* stream = session->FindStream(id);
945
  // If stream is null at this point, either something odd has happened
946
  // or the stream was closed locally while header processing was occurring.
947
  // either way, do not proceed and close the stream.
948
70846
  if (UNLIKELY(stream == nullptr))
949
    return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
950
951
  // If the stream has already been destroyed, ignore.
952

70846
  if (!stream->IsDestroyed() && !stream->AddHeader(name, value, flags)) {
953
    // This will only happen if the connected peer sends us more
954
    // than the allowed number of header items at any given time
955
2
    stream->SubmitRstStream(NGHTTP2_ENHANCE_YOUR_CALM);
956
2
    return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
957
  }
958
70844
  return 0;
959
}
960
961
962
// Called by nghttp2 when a complete HTTP2 frame has been received. There are
963
// only a handful of frame types that we care about handling here.
964
82601
int Http2Session::OnFrameReceive(nghttp2_session* handle,
965
                                 const nghttp2_frame* frame,
966
                                 void* user_data) {
967
82601
  Http2Session* session = static_cast<Http2Session*>(user_data);
968
82601
  session->statistics_.frame_count++;
969
  Debug(session, "complete frame received: type: %d",
970
82601
        frame->hd.type);
971


82601
  switch (frame->hd.type) {
972
    case NGHTTP2_DATA:
973
23670
      session->HandleDataFrame(frame);
974
23670
      break;
975
    case NGHTTP2_PUSH_PROMISE:
976
      // Intentional fall-through, handled just like headers frames
977
    case NGHTTP2_HEADERS:
978
23146
      session->HandleHeadersFrame(frame);
979
23146
      break;
980
    case NGHTTP2_SETTINGS:
981
18283
      session->HandleSettingsFrame(frame);
982
18283
      break;
983
    case NGHTTP2_PRIORITY:
984
16
      session->HandlePriorityFrame(frame);
985
16
      break;
986
    case NGHTTP2_GOAWAY:
987
267
      session->HandleGoawayFrame(frame);
988
267
      break;
989
    case NGHTTP2_PING:
990
13876
      session->HandlePingFrame(frame);
991
13876
      break;
992
    case NGHTTP2_ALTSVC:
993
4
      session->HandleAltSvcFrame(frame);
994
4
      break;
995
    case NGHTTP2_ORIGIN:
996
5
      session->HandleOriginFrame(frame);
997
5
      break;
998
    default:
999
3334
      break;
1000
  }
1001
82601
  return 0;
1002
}
1003
1004
38
int Http2Session::OnInvalidFrame(nghttp2_session* handle,
1005
                                 const nghttp2_frame* frame,
1006
                                 int lib_error_code,
1007
                                 void* user_data) {
1008
38
  Http2Session* session = static_cast<Http2Session*>(user_data);
1009
1010
  Debug(session, "invalid frame received, code: %d", lib_error_code);
1011
1012
  // If the error is fatal or if error code is ERR_STREAM_CLOSED... emit error
1013

76
  if (nghttp2_is_fatal(lib_error_code) ||
1014
38
      lib_error_code == NGHTTP2_ERR_STREAM_CLOSED) {
1015
1
    Environment* env = session->env();
1016
1
    Isolate* isolate = env->isolate();
1017
1
    HandleScope scope(isolate);
1018
1
    Local<Context> context = env->context();
1019
    Context::Scope context_scope(context);
1020
1
    Local<Value> arg = Integer::New(isolate, lib_error_code);
1021
2
    session->MakeCallback(env->http2session_on_error_function(), 1, &arg);
1022
  }
1023
38
  return 0;
1024
}
1025
1026
// If nghttp2 is unable to send a queued up frame, it will call this callback
1027
// to let us know. If the failure occurred because we are in the process of
1028
// closing down the session or stream, we go ahead and ignore it. We don't
1029
// really care about those and there's nothing we can reasonably do about it
1030
// anyway. Other types of failures are reported up to JavaScript. This should
1031
// be exceedingly rare.
1032
74
int Http2Session::OnFrameNotSent(nghttp2_session* handle,
1033
                                 const nghttp2_frame* frame,
1034
                                 int error_code,
1035
                                 void* user_data) {
1036
74
  Http2Session* session = static_cast<Http2Session*>(user_data);
1037
74
  Environment* env = session->env();
1038
  Debug(session, "frame type %d was not sent, code: %d",
1039
74
        frame->hd.type, error_code);
1040
1041
  // Do not report if the frame was not sent due to the session closing
1042

78
  if (error_code == NGHTTP2_ERR_SESSION_CLOSING ||
1043
7
      error_code == NGHTTP2_ERR_STREAM_CLOSED ||
1044
3
      error_code == NGHTTP2_ERR_STREAM_CLOSING) {
1045
72
    return 0;
1046
  }
1047
1048
2
  Isolate* isolate = env->isolate();
1049
2
  HandleScope scope(isolate);
1050
2
  Local<Context> context = env->context();
1051
  Context::Scope context_scope(context);
1052
1053
  Local<Value> argv[3] = {
1054
    Integer::New(isolate, frame->hd.stream_id),
1055
    Integer::New(isolate, frame->hd.type),
1056
    Integer::New(isolate, error_code)
1057
8
  };
1058
  session->MakeCallback(
1059
      env->http2session_on_frame_error_function(),
1060
2
      arraysize(argv), argv);
1061
4
  return 0;
1062
}
1063
1064
63491
int Http2Session::OnFrameSent(nghttp2_session* handle,
1065
                              const nghttp2_frame* frame,
1066
                              void* user_data) {
1067
63491
  Http2Session* session = static_cast<Http2Session*>(user_data);
1068
63491
  session->statistics_.frame_sent += 1;
1069
63491
  return 0;
1070
}
1071
1072
// Called by nghttp2 when a stream closes.
1073
23231
int Http2Session::OnStreamClose(nghttp2_session* handle,
1074
                                int32_t id,
1075
                                uint32_t code,
1076
                                void* user_data) {
1077
23231
  Http2Session* session = static_cast<Http2Session*>(user_data);
1078
23231
  Environment* env = session->env();
1079
23231
  Isolate* isolate = env->isolate();
1080
23231
  HandleScope scope(isolate);
1081
23231
  Local<Context> context = env->context();
1082
  Context::Scope context_scope(context);
1083
  Debug(session, "stream %d closed with code: %d", id, code);
1084
23231
  Http2Stream* stream = session->FindStream(id);
1085
  // Intentionally ignore the callback if the stream does not exist or has
1086
  // already been destroyed
1087

23231
  if (stream == nullptr || stream->IsDestroyed())
1088
48
    return 0;
1089
1090
23183
  stream->Close(code);
1091
1092
  // It is possible for the stream close to occur before the stream is
1093
  // ever passed on to the javascript side. If that happens, the callback
1094
  // will return false.
1095
23183
  Local<Value> arg = Integer::NewFromUnsigned(isolate, code);
1096
  MaybeLocal<Value> answer =
1097
    stream->MakeCallback(env->http2session_on_stream_close_function(),
1098
23183
                          1, &arg);
1099

115915
  if (answer.IsEmpty() ||
1100
115915
      !(answer.ToLocalChecked()->BooleanValue(env->isolate()))) {
1101
    // Skip to destroy
1102
36
    stream->Destroy();
1103
  }
1104
46414
  return 0;
1105
}
1106
1107
// Called by nghttp2 when an invalid header has been received. For now, we
1108
// ignore these. If this callback was not provided, nghttp2 would handle
1109
// invalid headers strictly and would shut down the stream. We are intentionally
1110
// being more lenient here although we may want to revisit this choice later.
1111
4
int Http2Session::OnInvalidHeader(nghttp2_session* session,
1112
                                  const nghttp2_frame* frame,
1113
                                  nghttp2_rcbuf* name,
1114
                                  nghttp2_rcbuf* value,
1115
                                  uint8_t flags,
1116
                                  void* user_data) {
1117
  // Ignore invalid header fields by default.
1118
4
  return 0;
1119
}
1120
1121
// When nghttp2 receives a DATA frame, it will deliver the data payload to
1122
// us in discrete chunks. We push these into a linked list stored in the
1123
// Http2Sttream which is flushed out to JavaScript as quickly as possible.
1124
// This can be a particularly hot path.
1125
11594
int Http2Session::OnDataChunkReceived(nghttp2_session* handle,
1126
                                      uint8_t flags,
1127
                                      int32_t id,
1128
                                      const uint8_t* data,
1129
                                      size_t len,
1130
                                      void* user_data) {
1131
11594
  Http2Session* session = static_cast<Http2Session*>(user_data);
1132
  Debug(session, "buffering data chunk for stream %d, size: "
1133
        "%d, flags: %d", id, len, flags);
1134
11594
  Environment* env = session->env();
1135
11594
  HandleScope scope(env->isolate());
1136
1137
  // We should never actually get a 0-length chunk so this check is
1138
  // only a precaution at this point.
1139
11594
  if (len == 0)
1140
    return 0;
1141
1142
  // Notify nghttp2 that we've consumed a chunk of data on the connection
1143
  // so that it can send a WINDOW_UPDATE frame. This is a critical part of
1144
  // the flow control process in http2
1145
11594
  CHECK_EQ(nghttp2_session_consume_connection(handle, len), 0);
1146
11594
  Http2Stream* stream = session->FindStream(id);
1147
  // If the stream has been destroyed, ignore this chunk
1148
11594
  if (stream->IsDestroyed())
1149
    return 0;
1150
1151
11594
  stream->statistics_.received_bytes += len;
1152
1153
  // Repeatedly ask the stream's owner for memory, and copy the read data
1154
  // into those buffers.
1155
  // The typical case is actually the exception here; Http2StreamListeners
1156
  // know about the HTTP2 session associated with this stream, so they know
1157
  // about the larger from-socket read buffer, so they do not require copying.
1158
11594
  do {
1159
11594
    uv_buf_t buf = stream->EmitAlloc(len);
1160
11594
    ssize_t avail = len;
1161
11594
    if (static_cast<ssize_t>(buf.len) < avail)
1162
      avail = buf.len;
1163
1164
    // `buf.base == nullptr` is the default Http2StreamListener's way
1165
    // of saying that it wants a pointer to the raw original.
1166
    // Since it has access to the original socket buffer from which the data
1167
    // was read in the first place, it can use that to minimize ArrayBuffer
1168
    // allocations.
1169
11594
    if (LIKELY(buf.base == nullptr))
1170
11594
      buf.base = reinterpret_cast<char*>(const_cast<uint8_t*>(data));
1171
    else
1172
      memcpy(buf.base, data, avail);
1173
11594
    data += avail;
1174
11594
    len -= avail;
1175
11594
    stream->EmitRead(avail, buf);
1176
1177
    // If the stream owner (e.g. the JS Http2Stream) wants more data, just
1178
    // tell nghttp2 that all data has been consumed. Otherwise, defer until
1179
    // more data is being requested.
1180
11594
    if (stream->IsReading())
1181
10484
      nghttp2_session_consume_stream(handle, id, avail);
1182
    else
1183
1110
      stream->inbound_consumed_data_while_paused_ += avail;
1184
11594
  } while (len != 0);
1185
1186
11594
  return 0;
1187
}
1188
1189
// Called by nghttp2 when it needs to determine how much padding to use in
1190
// a DATA or HEADERS frame.
1191
8
ssize_t Http2Session::OnSelectPadding(nghttp2_session* handle,
1192
                                      const nghttp2_frame* frame,
1193
                                      size_t maxPayloadLen,
1194
                                      void* user_data) {
1195
8
  Http2Session* session = static_cast<Http2Session*>(user_data);
1196
8
  ssize_t padding = frame->hd.length;
1197
1198

8
  switch (session->padding_strategy_) {
1199
    case PADDING_STRATEGY_NONE:
1200
      // Fall-through
1201
      break;
1202
    case PADDING_STRATEGY_MAX:
1203
      padding = session->OnMaxFrameSizePadding(padding, maxPayloadLen);
1204
      break;
1205
    case PADDING_STRATEGY_ALIGNED:
1206
4
      padding = session->OnDWordAlignedPadding(padding, maxPayloadLen);
1207
4
      break;
1208
    case PADDING_STRATEGY_CALLBACK:
1209
4
      padding = session->OnCallbackPadding(padding, maxPayloadLen);
1210
4
      break;
1211
  }
1212
8
  return padding;
1213
}
1214
1215
#define BAD_PEER_MESSAGE "Remote peer returned unexpected data while we "     \
1216
                         "expected SETTINGS frame.  Perhaps, peer does not "  \
1217
                         "support HTTP/2 properly."
1218
1219
// We use this currently to determine when an attempt is made to use the http2
1220
// protocol with a non-http2 peer.
1221
35
int Http2Session::OnNghttpError(nghttp2_session* handle,
1222
                                const char* message,
1223
                                size_t len,
1224
                                void* user_data) {
1225
  // Unfortunately, this is currently the only way for us to know if
1226
  // the session errored because the peer is not an http2 peer.
1227
35
  Http2Session* session = static_cast<Http2Session*>(user_data);
1228
  Debug(session, "Error '%.*s'", len, message);
1229
35
  if (strncmp(message, BAD_PEER_MESSAGE, len) == 0) {
1230
1
    Environment* env = session->env();
1231
1
    Isolate* isolate = env->isolate();
1232
1
    HandleScope scope(isolate);
1233
1
    Local<Context> context = env->context();
1234
    Context::Scope context_scope(context);
1235
1
    Local<Value> arg = Integer::New(isolate, NGHTTP2_ERR_PROTO);
1236
2
    session->MakeCallback(env->http2session_on_error_function(), 1, &arg);
1237
  }
1238
35
  return 0;
1239
}
1240
1241
11594
uv_buf_t Http2StreamListener::OnStreamAlloc(size_t size) {
1242
  // See the comments in Http2Session::OnDataChunkReceived
1243
  // (which is the only possible call site for this method).
1244
11594
  return uv_buf_init(nullptr, size);
1245
}
1246
1247
24097
void Http2StreamListener::OnStreamRead(ssize_t nread, const uv_buf_t& buf) {
1248
24097
  Http2Stream* stream = static_cast<Http2Stream*>(stream_);
1249
24097
  Http2Session* session = stream->session();
1250
24097
  Environment* env = stream->env();
1251
24097
  HandleScope handle_scope(env->isolate());
1252
35691
  Context::Scope context_scope(env->context());
1253
1254
24097
  if (nread < 0) {
1255
12503
    PassReadErrorToPreviousListener(nread);
1256
36600
    return;
1257
  }
1258
1259
23188
  CHECK(!session->stream_buf_ab_.IsEmpty());
1260
1261
  // There is a single large array buffer for the entire data read from the
1262
  // network; create a slice of that array buffer and emit it as the
1263
  // received data buffer.
1264
11594
  size_t offset = buf.base - session->stream_buf_.base;
1265
1266
  // Verify that the data offset is inside the current read buffer.
1267
11594
  CHECK_LE(offset, session->stream_buf_.len);
1268
11594
  CHECK_LE(offset + buf.len, session->stream_buf_.len);
1269
1270
23188
  stream->CallJSOnreadMethod(nread, session->stream_buf_ab_, offset);
1271
}
1272
1273
1274
// Called by OnFrameReceived to notify JavaScript land that a complete
1275
// HEADERS frame has been received and processed. This method converts the
1276
// received headers into a JavaScript array and pushes those out to JS.
1277
23146
void Http2Session::HandleHeadersFrame(const nghttp2_frame* frame) {
1278
23146
  Isolate* isolate = env()->isolate();
1279
23146
  HandleScope scope(isolate);
1280
23146
  Local<Context> context = env()->context();
1281
23146
  Context::Scope context_scope(context);
1282
1283
23146
  int32_t id = GetFrameID(frame);
1284
23146
  Debug(this, "handle headers frame for stream %d", id);
1285
23146
  Http2Stream* stream = FindStream(id);
1286
1287
  // If the stream has already been destroyed, ignore.
1288
23146
  if (stream->IsDestroyed())
1289
23146
    return;
1290
1291
46292
  std::vector<nghttp2_header> headers(stream->move_headers());
1292
1293
  // The headers are passed in above as a queue of nghttp2_header structs.
1294
  // The following converts that into a JS array with the structure:
1295
  // [name1, value1, name2, value2, name3, value3, name3, value4] and so on.
1296
  // That array is passed up to the JS layer and converted into an Object form
1297
  // like {name1: value1, name2: value2, name3: [value3, value4]}. We do it
1298
  // this way for performance reasons (it's faster to generate and pass an
1299
  // array than it is to generate and pass the object).
1300
23146
  size_t headers_size = headers.size();
1301
46292
  std::vector<Local<Value>> headers_v(headers_size * 2);
1302
93881
  for (size_t i = 0; i < headers_size; ++i) {
1303
70735
    const nghttp2_header& item = headers[i];
1304
    // The header name and value are passed as external one-byte strings
1305
70735
    headers_v[i * 2] =
1306
141470
        ExternalHeader::New<true>(this, item.name).ToLocalChecked();
1307
70735
    headers_v[i * 2 + 1] =
1308
141470
        ExternalHeader::New<false>(this, item.value).ToLocalChecked();
1309
  }
1310
1311
  Local<Value> args[5] = {
1312
      stream->object(),
1313
      Integer::New(isolate, id),
1314
23146
      Integer::New(isolate, stream->headers_category()),
1315
      Integer::New(isolate, frame->hd.flags),
1316
162022
      Array::New(isolate, headers_v.data(), headers_size * 2)};
1317
  MakeCallback(env()->http2session_on_headers_function(),
1318
46292
               arraysize(args), args);
1319
}
1320
1321
1322
// Called by OnFrameReceived when a complete PRIORITY frame has been
1323
// received. Notifies JS land about the priority change. Note that priorities
1324
// are considered advisory only, so this has no real effect other than to
1325
// simply let user code know that the priority has changed.
1326
16
void Http2Session::HandlePriorityFrame(const nghttp2_frame* frame) {
1327
16
  Isolate* isolate = env()->isolate();
1328
16
  HandleScope scope(isolate);
1329
16
  Local<Context> context = env()->context();
1330
  Context::Scope context_scope(context);
1331
1332
16
  nghttp2_priority priority_frame = frame->priority;
1333
16
  int32_t id = GetFrameID(frame);
1334
16
  Debug(this, "handle priority frame for stream %d", id);
1335
  // Priority frame stream ID should never be <= 0. nghttp2 handles this for us
1336
16
  nghttp2_priority_spec spec = priority_frame.pri_spec;
1337
1338
  Local<Value> argv[4] = {
1339
    Integer::New(isolate, id),
1340
    Integer::New(isolate, spec.stream_id),
1341
    Integer::New(isolate, spec.weight),
1342
    Boolean::New(isolate, spec.exclusive)
1343
80
  };
1344
  MakeCallback(env()->http2session_on_priority_function(),
1345
32
               arraysize(argv), argv);
1346
16
}
1347
1348
1349
// Called by OnFrameReceived when a complete DATA frame has been received.
1350
// If we know that this was the last DATA frame (because the END_STREAM flag
1351
// is set), then we'll terminate the readable side of the StreamBase.
1352
23670
void Http2Session::HandleDataFrame(const nghttp2_frame* frame) {
1353
23670
  int32_t id = GetFrameID(frame);
1354
23670
  Debug(this, "handling data frame for stream %d", id);
1355
23670
  Http2Stream* stream = FindStream(id);
1356
1357

23670
  if (!stream->IsDestroyed() && frame->hd.flags & NGHTTP2_FLAG_END_STREAM)
1358
12503
    stream->EmitRead(UV_EOF);
1359
23670
}
1360
1361
1362
// Called by OnFrameReceived when a complete GOAWAY frame has been received.
1363
267
void Http2Session::HandleGoawayFrame(const nghttp2_frame* frame) {
1364
267
  Isolate* isolate = env()->isolate();
1365
267
  HandleScope scope(isolate);
1366
267
  Local<Context> context = env()->context();
1367
  Context::Scope context_scope(context);
1368
1369
267
  nghttp2_goaway goaway_frame = frame->goaway;
1370
267
  Debug(this, "handling goaway frame");
1371
1372
  Local<Value> argv[3] = {
1373
    Integer::NewFromUnsigned(isolate, goaway_frame.error_code),
1374
    Integer::New(isolate, goaway_frame.last_stream_id),
1375
    Undefined(isolate)
1376
1068
  };
1377
1378
267
  size_t length = goaway_frame.opaque_data_len;
1379
267
  if (length > 0) {
1380
    argv[2] = Buffer::Copy(isolate,
1381
                           reinterpret_cast<char*>(goaway_frame.opaque_data),
1382
4
                           length).ToLocalChecked();
1383
  }
1384
1385
  MakeCallback(env()->http2session_on_goaway_data_function(),
1386
534
               arraysize(argv), argv);
1387
267
}
1388
1389
// Called by OnFrameReceived when a complete ALTSVC frame has been received.
1390
4
void Http2Session::HandleAltSvcFrame(const nghttp2_frame* frame) {
1391
4
  Isolate* isolate = env()->isolate();
1392
4
  HandleScope scope(isolate);
1393
4
  Local<Context> context = env()->context();
1394
  Context::Scope context_scope(context);
1395
1396
4
  int32_t id = GetFrameID(frame);
1397
1398
4
  nghttp2_extension ext = frame->ext;
1399
4
  nghttp2_ext_altsvc* altsvc = static_cast<nghttp2_ext_altsvc*>(ext.payload);
1400
4
  Debug(this, "handling altsvc frame");
1401
1402
  Local<Value> argv[3] = {
1403
    Integer::New(isolate, id),
1404
    String::NewFromOneByte(isolate,
1405
                           altsvc->origin,
1406
                           NewStringType::kNormal,
1407
4
                           altsvc->origin_len).ToLocalChecked(),
1408
    String::NewFromOneByte(isolate,
1409
                           altsvc->field_value,
1410
                           NewStringType::kNormal,
1411
4
                           altsvc->field_value_len).ToLocalChecked(),
1412
24
  };
1413
1414
  MakeCallback(env()->http2session_on_altsvc_function(),
1415
8
               arraysize(argv), argv);
1416
4
}
1417
1418
5
void Http2Session::HandleOriginFrame(const nghttp2_frame* frame) {
1419
5
  Isolate* isolate = env()->isolate();
1420
5
  HandleScope scope(isolate);
1421
5
  Local<Context> context = env()->context();
1422
  Context::Scope context_scope(context);
1423
1424
5
  Debug(this, "handling origin frame");
1425
1426
5
  nghttp2_extension ext = frame->ext;
1427
5
  nghttp2_ext_origin* origin = static_cast<nghttp2_ext_origin*>(ext.payload);
1428
1429
5
  size_t nov = origin->nov;
1430
10
  std::vector<Local<Value>> origin_v(nov);
1431
1432
14
  for (size_t i = 0; i < nov; ++i) {
1433
9
    const nghttp2_origin_entry& entry = origin->ov[i];
1434
9
    origin_v[i] =
1435
        String::NewFromOneByte(
1436
9
            isolate, entry.origin, NewStringType::kNormal, entry.origin_len)
1437
18
            .ToLocalChecked();
1438
  }
1439
5
  Local<Value> holder = Array::New(isolate, origin_v.data(), origin_v.size());
1440
10
  MakeCallback(env()->http2session_on_origin_function(), 1, &holder);
1441
5
}
1442
1443
// Called by OnFrameReceived when a complete PING frame has been received.
1444
13876
void Http2Session::HandlePingFrame(const nghttp2_frame* frame) {
1445
13876
  Isolate* isolate = env()->isolate();
1446
13876
  HandleScope scope(isolate);
1447
13876
  Local<Context> context = env()->context();
1448
13865
  Context::Scope context_scope(context);
1449
  Local<Value> arg;
1450
13876
  bool ack = frame->hd.flags & NGHTTP2_FLAG_ACK;
1451
13876
  if (ack) {
1452
11
    std::unique_ptr<Http2Ping> ping = PopPing();
1453
1454
11
    if (!ping) {
1455
      // PING Ack is unsolicited. Treat as a connection error. The HTTP/2
1456
      // spec does not require this, but there is no legitimate reason to
1457
      // receive an unsolicited PING ack on a connection. Either the peer
1458
      // is buggy or malicious, and we're not going to tolerate such
1459
      // nonsense.
1460
2
      arg = Integer::New(isolate, NGHTTP2_ERR_PROTO);
1461
1
      MakeCallback(env()->http2session_on_error_function(), 1, &arg);
1462
1
      return;
1463
    }
1464
1465
10
    ping->Done(true, frame->ping.opaque_data);
1466
10
    return;
1467
  }
1468
1469
  // Notify the session that a ping occurred
1470
  arg = Buffer::Copy(env(),
1471
                      reinterpret_cast<const char*>(frame->ping.opaque_data),
1472
27730
                      8).ToLocalChecked();
1473
27730
  MakeCallback(env()->http2session_on_ping_function(), 1, &arg);
1474
}
1475
1476
// Called by OnFrameReceived when a complete SETTINGS frame has been received.
1477
18283
void Http2Session::HandleSettingsFrame(const nghttp2_frame* frame) {
1478
18283
  bool ack = frame->hd.flags & NGHTTP2_FLAG_ACK;
1479
18283
  if (!ack) {
1480
    // This is not a SETTINGS acknowledgement, notify and return
1481
17807
    MakeCallback(env()->http2session_on_settings_function(), 0, nullptr);
1482
17807
    return;
1483
  }
1484
1485
  // If this is an acknowledgement, we should have an Http2Settings
1486
  // object for it.
1487
476
  std::unique_ptr<Http2Settings> settings = PopSettings();
1488
476
  if (settings) {
1489
476
    settings->Done(true);
1490
476
    return;
1491
  }
1492
  // SETTINGS Ack is unsolicited. Treat as a connection error. The HTTP/2
1493
  // spec does not require this, but there is no legitimate reason to
1494
  // receive an unsolicited SETTINGS ack on a connection. Either the peer
1495
  // is buggy or malicious, and we're not going to tolerate such
1496
  // nonsense.
1497
  // Note that nghttp2 currently prevents this from happening for SETTINGS
1498
  // frames, so this block is purely defensive just in case that behavior
1499
  // changes. Specifically, unlike unsolicited PING acks, unsolicited
1500
  // SETTINGS acks should *never* make it this far.
1501
  Isolate* isolate = env()->isolate();
1502
  HandleScope scope(isolate);
1503
  Local<Context> context = env()->context();
1504
  Context::Scope context_scope(context);
1505
  Local<Value> arg = Integer::New(isolate, NGHTTP2_ERR_PROTO);
1506
  MakeCallback(env()->http2session_on_error_function(), 1, &arg);
1507
}
1508
1509
// Callback used when data has been written to the stream.
1510
583
void Http2Session::OnStreamAfterWrite(WriteWrap* w, int status) {
1511
583
  Debug(this, "write finished with status %d", status);
1512
1513
  // Inform all pending writes about their completion.
1514
583
  ClearOutgoing(status);
1515
1516
583
  if (!(flags_ & SESSION_STATE_WRITE_SCHEDULED)) {
1517
    // Schedule a new write if nghttp2 wants to send data.
1518
462
    MaybeScheduleWrite();
1519
  }
1520
583
}
1521
1522
// If the underlying nghttp2_session struct has data pending in its outbound
1523
// queue, MaybeScheduleWrite will schedule a SendPendingData() call to occur
1524
// on the next iteration of the Node.js event loop (using the SetImmediate
1525
// queue), but only if a write has not already been scheduled.
1526
30758
void Http2Session::MaybeScheduleWrite() {
1527
30758
  CHECK_EQ(flags_ & SESSION_STATE_WRITE_SCHEDULED, 0);
1528
30758
  if (UNLIKELY(session_ == nullptr))
1529
30758
    return;
1530
1531
30758
  if (nghttp2_session_want_write(session_)) {
1532
2519
    HandleScope handle_scope(env()->isolate());
1533
2519
    Debug(this, "scheduling write");
1534
2519
    flags_ |= SESSION_STATE_WRITE_SCHEDULED;
1535
7557
    env()->SetImmediate([](Environment* env, void* data) {
1536
2519
      Http2Session* session = static_cast<Http2Session*>(data);
1537

5038
      if (session->session_ == nullptr ||
1538
2519
          !(session->flags_ & SESSION_STATE_WRITE_SCHEDULED)) {
1539
        // This can happen e.g. when a stream was reset before this turn
1540
        // of the event loop, in which case SendPendingData() is called early,
1541
        // or the session was destroyed in the meantime.
1542
2762
        return;
1543
      }
1544
1545
      // Sending data may call arbitrary JS code, so keep track of
1546
      // async context.
1547
2276
      HandleScope handle_scope(env->isolate());
1548
4552
      InternalCallbackScope callback_scope(session);
1549
2276
      session->SendPendingData();
1550
9833
    }, static_cast<void*>(this), object());
1551
  }
1552
}
1553
1554
60114
void Http2Session::MaybeStopReading() {
1555
60114
  int want_read = nghttp2_session_want_read(session_);
1556
60114
  Debug(this, "wants read? %d", want_read);
1557
60114
  if (want_read == 0)
1558
987
    stream_->ReadStop();
1559
60114
}
1560
1561
// Unset the sending state, finish up all current writes, and reset
1562
// storage for data and metadata that was associated with these writes.
1563
31397
void Http2Session::ClearOutgoing(int status) {
1564
31397
  CHECK_NE(flags_ & SESSION_STATE_SENDING, 0);
1565
1566
31397
  flags_ &= ~SESSION_STATE_SENDING;
1567
1568
31397
  if (outgoing_buffers_.size() > 0) {
1569
30745
    outgoing_storage_.clear();
1570
1571
30745
    std::vector<nghttp2_stream_write> current_outgoing_buffers_;
1572
30745
    current_outgoing_buffers_.swap(outgoing_buffers_);
1573
122922
    for (const nghttp2_stream_write& wr : current_outgoing_buffers_) {
1574
92177
      WriteWrap* wrap = wr.req_wrap;
1575
92177
      if (wrap != nullptr) {
1576
        // TODO(addaleax): Pass `status` instead of 0, so that we actually error
1577
        // out with the error from the write to the underlying protocol,
1578
        // if one occurred.
1579
3554
        wrap->Done(0);
1580
      }
1581
30745
    }
1582
  }
1583
1584
  // Now that we've finished sending queued data, if there are any pending
1585
  // RstStreams we should try sending again and then flush them one by one.
1586
31397
  if (pending_rst_streams_.size() > 0) {
1587
3
    std::vector<int32_t> current_pending_rst_streams;
1588
3
    pending_rst_streams_.swap(current_pending_rst_streams);
1589
1590
3
    SendPendingData();
1591
1592
6
    for (int32_t stream_id : current_pending_rst_streams) {
1593
3
      Http2Stream* stream = FindStream(stream_id);
1594
3
      if (LIKELY(stream != nullptr))
1595
3
        stream->FlushRstStream();
1596
3
    }
1597
  }
1598
31397
}
1599
1600
// Queue a given block of data for sending. This always creates a copy,
1601
// so it is used for the cases in which nghttp2 requests sending of a
1602
// small chunk of data.
1603
63776
void Http2Session::CopyDataIntoOutgoing(const uint8_t* src, size_t src_length) {
1604
63776
  size_t offset = outgoing_storage_.size();
1605
63776
  outgoing_storage_.resize(offset + src_length);
1606
63776
  memcpy(&outgoing_storage_[offset], src, src_length);
1607
1608
  // Store with a base of `nullptr` initially, since future resizes
1609
  // of the outgoing_buffers_ vector may invalidate the pointer.
1610
  // The correct base pointers will be set later, before writing to the
1611
  // underlying socket.
1612
  outgoing_buffers_.emplace_back(nghttp2_stream_write {
1613
    uv_buf_init(nullptr, src_length)
1614
63776
  });
1615
63776
}
1616
1617
// Prompts nghttp2 to begin serializing it's pending data and pushes each
1618
// chunk out to the i/o socket to be sent. This is a particularly hot method
1619
// that will generally be called at least twice be event loop iteration.
1620
// This is a potential performance optimization target later.
1621
// Returns non-zero value if a write is already in progress.
1622
31836
uint8_t Http2Session::SendPendingData() {
1623
31836
  Debug(this, "sending pending data");
1624
  // Do not attempt to send data on the socket if the destroying flag has
1625
  // been set. That means everything is shutting down and the socket
1626
  // will not be usable.
1627
31836
  if (IsDestroyed())
1628
28
    return 0;
1629
31808
  flags_ &= ~SESSION_STATE_WRITE_SCHEDULED;
1630
1631
  // SendPendingData should not be called recursively.
1632
31808
  if (flags_ & SESSION_STATE_SENDING)
1633
408
    return 1;
1634
  // This is cleared by ClearOutgoing().
1635
31400
  flags_ |= SESSION_STATE_SENDING;
1636
1637
  ssize_t src_length;
1638
  const uint8_t* src;
1639
1640
31400
  CHECK_EQ(outgoing_buffers_.size(), 0);
1641
31400
  CHECK_EQ(outgoing_storage_.size(), 0);
1642
1643
  // Part One: Gather data from nghttp2
1644
1645
115385
  while ((src_length = nghttp2_session_mem_send(session_, &src)) > 0) {
1646
52585
    Debug(this, "nghttp2 has %d bytes to send", src_length);
1647
52585
    CopyDataIntoOutgoing(src, src_length);
1648
  }
1649
1650
31400
  CHECK_NE(src_length, NGHTTP2_ERR_NOMEM);
1651
1652
31400
  if (stream_ == nullptr) {
1653
    // It would seem nice to bail out earlier, but `nghttp2_session_mem_send()`
1654
    // does take care of things like closing the individual streams after
1655
    // a socket has been torn down, so we still need to call it.
1656
12
    ClearOutgoing(UV_ECANCELED);
1657
12
    return 0;
1658
  }
1659
1660
  // Part Two: Pass Data to the underlying stream
1661
1662
31388
  size_t count = outgoing_buffers_.size();
1663
31388
  if (count == 0) {
1664
652
    ClearOutgoing(0);
1665
652
    return 0;
1666
  }
1667
30736
  MaybeStackBuffer<uv_buf_t, 32> bufs;
1668
30736
  bufs.AllocateSufficientStorage(count);
1669
1670
  // Set the buffer base pointers for copied data that ended up in the
1671
  // sessions's own storage since it might have shifted around during gathering.
1672
  // (Those are marked by having .base == nullptr.)
1673
30736
  size_t offset = 0;
1674
30736
  size_t i = 0;
1675
122899
  for (const nghttp2_stream_write& write : outgoing_buffers_) {
1676
92163
    statistics_.data_sent += write.buf.len;
1677
92163
    if (write.buf.base == nullptr) {
1678
63757
      bufs[i++] = uv_buf_init(
1679
63757
          reinterpret_cast<char*>(outgoing_storage_.data() + offset),
1680
127514
          write.buf.len);
1681
63757
      offset += write.buf.len;
1682
    } else {
1683
28406
      bufs[i++] = write.buf;
1684
    }
1685
  }
1686
1687
30736
  chunks_sent_since_last_write_++;
1688
1689
30736
  StreamWriteResult res = underlying_stream()->Write(*bufs, count);
1690
30736
  if (!res.async) {
1691
30150
    ClearOutgoing(res.err);
1692
  }
1693
1694
30736
  MaybeStopReading();
1695
1696
30736
  return 0;
1697
}
1698
1699
1700
// This callback is called from nghttp2 when it wants to send DATA frames for a
1701
// given Http2Stream, when we set the `NGHTTP2_DATA_FLAG_NO_COPY` flag earlier
1702
// in the Http2Stream::Provider::Stream::OnRead callback.
1703
// We take the write information directly out of the stream's data queue.
1704
11189
int Http2Session::OnSendData(
1705
      nghttp2_session* session_,
1706
      nghttp2_frame* frame,
1707
      const uint8_t* framehd,
1708
      size_t length,
1709
      nghttp2_data_source* source,
1710
      void* user_data) {
1711
11189
  Http2Session* session = static_cast<Http2Session*>(user_data);
1712
11189
  Http2Stream* stream = GetStream(session, frame->hd.stream_id, source);
1713
1714
  // Send the frame header + a byte that indicates padding length.
1715
11189
  session->CopyDataIntoOutgoing(framehd, 9);
1716
11189
  if (frame->data.padlen > 0) {
1717
2
    uint8_t padding_byte = frame->data.padlen - 1;
1718
2
    CHECK_EQ(padding_byte, frame->data.padlen - 1);
1719
2
    session->CopyDataIntoOutgoing(&padding_byte, 1);
1720
  }
1721
1722
  Debug(session, "nghttp2 has %d bytes to send directly", length);
1723
43148
  while (length > 0) {
1724
    // nghttp2 thinks that there is data available (length > 0), which means
1725
    // we told it so, which means that we *should* have data available.
1726
28404
    CHECK(!stream->queue_.empty());
1727
1728
28404
    nghttp2_stream_write& write = stream->queue_.front();
1729
28404
    if (write.buf.len <= length) {
1730
      // This write does not suffice by itself, so we can consume it completely.
1731
20770
      length -= write.buf.len;
1732
20770
      session->outgoing_buffers_.emplace_back(std::move(write));
1733
20770
      stream->queue_.pop();
1734
20770
      continue;
1735
    }
1736
1737
    // Slice off `length` bytes of the first write in the queue.
1738
    session->outgoing_buffers_.emplace_back(nghttp2_stream_write {
1739
      uv_buf_init(write.buf.base, length)
1740
7634
    });
1741
7634
    write.buf.base += length;
1742
7634
    write.buf.len -= length;
1743
7634
    break;
1744
  }
1745
1746
11189
  if (frame->data.padlen > 0) {
1747
    // Send padding if that was requested.
1748
    session->outgoing_buffers_.emplace_back(nghttp2_stream_write {
1749
      uv_buf_init(const_cast<char*>(zero_bytes_256), frame->data.padlen - 1)
1750
2
    });
1751
  }
1752
1753
11189
  return 0;
1754
}
1755
1756
// Creates a new Http2Stream and submits a new http2 request.
1757
11623
Http2Stream* Http2Session::SubmitRequest(
1758
    nghttp2_priority_spec* prispec,
1759
    nghttp2_nv* nva,
1760
    size_t len,
1761
    int32_t* ret,
1762
    int options) {
1763
11623
  Debug(this, "submitting request");
1764
11623
  Http2Scope h2scope(this);
1765
11623
  Http2Stream* stream = nullptr;
1766
23246
  Http2Stream::Provider::Stream prov(options);
1767
11623
  *ret = nghttp2_submit_request(session_, prispec, nva, len, *prov, nullptr);
1768
11623
  CHECK_NE(*ret, NGHTTP2_ERR_NOMEM);
1769
11623
  if (LIKELY(*ret > 0))
1770
11622
    stream = Http2Stream::New(this, *ret, NGHTTP2_HCAT_HEADERS, options);
1771
23246
  return stream;
1772
}
1773
1774
29398
uv_buf_t Http2Session::OnStreamAlloc(size_t suggested_size) {
1775
29398
  return env()->AllocateManaged(suggested_size).release();
1776
}
1777
1778
// Callback used to receive inbound data from the i/o stream
1779
29407
void Http2Session::OnStreamRead(ssize_t nread, const uv_buf_t& buf_) {
1780
29407
  HandleScope handle_scope(env()->isolate());
1781
58785
  Context::Scope context_scope(env()->context());
1782
58785
  Http2Scope h2scope(this);
1783
29407
  CHECK_NOT_NULL(stream_);
1784
29407
  Debug(this, "receiving %d bytes", nread);
1785
58814
  CHECK(stream_buf_ab_.IsEmpty());
1786
58785
  AllocatedBuffer buf(env(), buf_);
1787
1788
  // Only pass data on if nread > 0
1789
29407
  if (nread <= 0) {
1790
25
    if (nread < 0) {
1791
25
      PassReadErrorToPreviousListener(nread);
1792
    }
1793
25
    return;
1794
  }
1795
1796
  // Shrink to the actual amount of used data.
1797
29382
  buf.Resize(nread);
1798
1799
29382
  IncrementCurrentSessionMemory(nread);
1800
29382
  OnScopeLeave on_scope_leave([&]() {
1801
    // Once finished handling this write, reset the stream buffer.
1802
    // The memory has either been free()d or was handed over to V8.
1803
    // We use `nread` instead of `buf.size()` here, because the buffer is
1804
    // cleared as part of the `.ToArrayBuffer()` call below.
1805
29382
    DecrementCurrentSessionMemory(nread);
1806
58764
    stream_buf_ab_ = Local<ArrayBuffer>();
1807
29382
    stream_buf_ = uv_buf_init(nullptr, 0);
1808
88142
  });
1809
1810
  // Make sure that there was no read previously active.
1811
29382
  CHECK_NULL(stream_buf_.base);
1812
29382
  CHECK_EQ(stream_buf_.len, 0);
1813
1814
  // Remember the current buffer, so that OnDataChunkReceived knows the
1815
  // offset of a DATA frame's data into the socket read buffer.
1816
29382
  stream_buf_ = uv_buf_init(buf.data(), nread);
1817
1818
29382
  Isolate* isolate = env()->isolate();
1819
1820
  // Create an array buffer for the read data. DATA frames will be emitted
1821
  // as slices of this array buffer to avoid having to copy memory.
1822
29382
  stream_buf_ab_ = buf.ToArrayBuffer();
1823
1824
29382
  statistics_.data_received += nread;
1825
29382
  ssize_t ret = Write(&stream_buf_, 1);
1826
1827
29382
  if (UNLIKELY(ret < 0)) {
1828
4
    Debug(this, "fatal error receiving data: %d", ret);
1829
4
    Local<Value> arg = Integer::New(isolate, ret);
1830
4
    MakeCallback(env()->http2session_on_error_function(), 1, &arg);
1831
4
    return;
1832
  }
1833
1834
58756
  MaybeStopReading();
1835
}
1836
1837
23250
bool Http2Session::HasWritesOnSocketForStream(Http2Stream* stream) {
1838
26563
  for (const nghttp2_stream_write& wr : outgoing_buffers_) {
1839


3315
    if (wr.req_wrap != nullptr && wr.req_wrap->stream() == stream)
1840
2
      return true;
1841
  }
1842
23248
  return false;
1843
}
1844
1845
// Every Http2Session session is tightly bound to a single i/o StreamBase
1846
// (typically a net.Socket or tls.TLSSocket). The lifecycle of the two is
1847
// tightly coupled with all data transfer between the two happening at the
1848
// C++ layer via the StreamBase API.
1849
577
void Http2Session::Consume(Local<Object> stream_obj) {
1850
577
  StreamBase* stream = StreamBase::FromObject(stream_obj);
1851
577
  stream->PushStreamListener(this);
1852
577
  Debug(this, "i/o stream consumed");
1853
577
}
1854
1855
23258
Http2Stream* Http2Stream::New(Http2Session* session,
1856
                              int32_t id,
1857
                              nghttp2_headers_category category,
1858
                              int options) {
1859
  Local<Object> obj;
1860
46516
  if (!session->env()
1861
23258
           ->http2stream_constructor_template()
1862
93032
           ->NewInstance(session->env()->context())
1863
69774
           .ToLocal(&obj)) {
1864
    return nullptr;
1865
  }
1866
23258
  return new Http2Stream(session, obj, id, category, options);
1867
}
1868
1869
23258
Http2Stream::Http2Stream(Http2Session* session,
1870
                         Local<Object> obj,
1871
                         int32_t id,
1872
                         nghttp2_headers_category category,
1873
                         int options)
1874
    : AsyncWrap(session->env(), obj, AsyncWrap::PROVIDER_HTTP2STREAM),
1875
      StreamBase(session->env()),
1876
      session_(session),
1877
      id_(id),
1878
23258
      current_headers_category_(category) {
1879
23258
  MakeWeak();
1880
23258
  StreamBase::AttachToObject(GetObject());
1881
23258
  statistics_.start_time = uv_hrtime();
1882
1883
  // Limit the number of header pairs
1884
23258
  max_header_pairs_ = session->GetMaxHeaderPairs();
1885
23258
  if (max_header_pairs_ == 0) {
1886
    max_header_pairs_ = DEFAULT_MAX_HEADER_LIST_PAIRS;
1887
  }
1888
23258
  current_headers_.reserve(max_header_pairs_);
1889
1890
  // Limit the number of header octets
1891
  max_header_length_ =
1892
      std::min(
1893
        nghttp2_session_get_local_settings(
1894
          session->session(),
1895
23258
          NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE),
1896
46516
      MAX_MAX_HEADER_LIST_SIZE);
1897
1898
23258
  if (options & STREAM_OPTION_GET_TRAILERS)
1899
2
    flags_ |= NGHTTP2_STREAM_FLAG_TRAILERS;
1900
1901
23258
  PushStreamListener(&stream_listener_);
1902
1903
23258
  if (options & STREAM_OPTION_EMPTY_PAYLOAD)
1904
453
    Shutdown();
1905
23258
  session->AddStream(this);
1906
23258
}
1907
1908
1909


69774
Http2Stream::~Http2Stream() {
1910
23367
  for (nghttp2_header& header : current_headers_) {
1911
109
    nghttp2_rcbuf_decref(header.name);
1912
109
    nghttp2_rcbuf_decref(header.value);
1913
  }
1914
1915
23258
  if (session_ == nullptr)
1916
2
    return;
1917
23256
  Debug(this, "tearing down stream");
1918
23256
  session_->RemoveStream(this);
1919
23256
  session_ = nullptr;
1920
46514
}
1921
1922
17
std::string Http2Stream::diagnostic_name() const {
1923
34
  return "HttpStream " + std::to_string(id()) + " (" +
1924
68
      std::to_string(static_cast<int64_t>(get_async_id())) + ") [" +
1925
51
      session()->diagnostic_name() + "]";
1926
}
1927
1928
// Notify the Http2Stream that a new block of HEADERS is being processed.
1929
11554
void Http2Stream::StartHeaders(nghttp2_headers_category category) {
1930
11554
  Debug(this, "starting headers, category: %d", id_, category);
1931
11554
  CHECK(!this->IsDestroyed());
1932
11554
  current_headers_length_ = 0;
1933
11554
  current_headers_.clear();
1934
11554
  current_headers_category_ = category;
1935
11554
}
1936
1937
1938
11
nghttp2_stream* Http2Stream::operator*() {
1939
11
  return nghttp2_session_find_stream(**session_, id_);
1940
}
1941
1942
23183
void Http2Stream::Close(int32_t code) {
1943
23183
  CHECK(!this->IsDestroyed());
1944
23183
  flags_ |= NGHTTP2_STREAM_FLAG_CLOSED;
1945
23183
  code_ = code;
1946
23183
  Debug(this, "closed with code %d", code);
1947
23183
}
1948
1949
23620
int Http2Stream::DoShutdown(ShutdownWrap* req_wrap) {
1950
23620
  if (IsDestroyed())
1951
    return UV_EPIPE;
1952
1953
  {
1954
23620
    Http2Scope h2scope(this);
1955
23620
    flags_ |= NGHTTP2_STREAM_FLAG_SHUT;
1956
23620
    CHECK_NE(nghttp2_session_resume_data(**session_, id_),
1957
             NGHTTP2_ERR_NOMEM);
1958
47240
    Debug(this, "writable side shutdown");
1959
  }
1960
23620
  return 1;
1961
}
1962
1963
// Destroy the Http2Stream and render it unusable. Actual resources for the
1964
// Stream will not be freed until the next tick of the Node.js event loop
1965
// using the SetImmediate queue.
1966
23250
void Http2Stream::Destroy() {
1967
  // Do nothing if this stream instance is already destroyed
1968
23250
  if (IsDestroyed())
1969
23250
    return;
1970
23250
  if (session_->HasPendingRstStream(id_))
1971
2
    FlushRstStream();
1972
23250
  flags_ |= NGHTTP2_STREAM_FLAG_DESTROYED;
1973
1974
23250
  Debug(this, "destroying stream");
1975
1976
  // Wait until the start of the next loop to delete because there
1977
  // may still be some pending operations queued for this stream.
1978
69750
  env()->SetImmediate([](Environment* env, void* data) {
1979
23250
    Http2Stream* stream = static_cast<Http2Stream*>(data);
1980
    // Free any remaining outgoing data chunks here. This should be done
1981
    // here because it's possible for destroy to have been called while
1982
    // we still have queued outbound writes.
1983
46503
    while (!stream->queue_.empty()) {
1984
3
      nghttp2_stream_write& head = stream->queue_.front();
1985
3
      if (head.req_wrap != nullptr)
1986
3
        head.req_wrap->Done(UV_ECANCELED);
1987
3
      stream->queue_.pop();
1988
    }
1989
1990
    // We can destroy the stream now if there are no writes for it
1991
    // already on the socket. Otherwise, we'll wait for the garbage collector
1992
    // to take care of cleaning up.
1993

46500
    if (stream->session() == nullptr ||
1994
23250
        !stream->session()->HasWritesOnSocketForStream(stream))
1995
23248
      delete stream;
1996
93000
  }, this, this->object());
1997
1998
23250
  statistics_.end_time = uv_hrtime();
1999
  session_->statistics_.stream_average_duration =
2000
23250
      ((statistics_.end_time - statistics_.start_time) /
2001
46500
          session_->statistics_.stream_count) / 1e6;
2002
23250
  EmitStatistics();
2003
}
2004
2005
2006
// Initiates a response on the Http2Stream using data provided via the
2007
// StreamBase Streams API.
2008
11552
int Http2Stream::SubmitResponse(nghttp2_nv* nva, size_t len, int options) {
2009
11552
  CHECK(!this->IsDestroyed());
2010
11552
  Http2Scope h2scope(this);
2011
11552
  Debug(this, "submitting response");
2012
11552
  if (options & STREAM_OPTION_GET_TRAILERS)
2013
184
    flags_ |= NGHTTP2_STREAM_FLAG_TRAILERS;
2014
2015
11552
  if (!IsWritable())
2016
8
    options |= STREAM_OPTION_EMPTY_PAYLOAD;
2017
2018
23104
  Http2Stream::Provider::Stream prov(this, options);
2019
11552
  int ret = nghttp2_submit_response(**session_, id_, nva, len, *prov);
2020
11552
  CHECK_NE(ret, NGHTTP2_ERR_NOMEM);
2021
23104
  return ret;
2022
}
2023
2024
2025
// Submit informational headers for a stream.
2026
5
int Http2Stream::SubmitInfo(nghttp2_nv* nva, size_t len) {
2027
5
  CHECK(!this->IsDestroyed());
2028
5
  Http2Scope h2scope(this);
2029
5
  Debug(this, "sending %d informational headers", len);
2030
  int ret = nghttp2_submit_headers(**session_,
2031
                                   NGHTTP2_FLAG_NONE,
2032
                                   id_, nullptr,
2033
5
                                   nva, len, nullptr);
2034
5
  CHECK_NE(ret, NGHTTP2_ERR_NOMEM);
2035
5
  return ret;
2036
}
2037
2038
31
void Http2Stream::OnTrailers() {
2039
31
  Debug(this, "let javascript know we are ready for trailers");
2040
31
  CHECK(!this->IsDestroyed());
2041
31
  Isolate* isolate = env()->isolate();
2042
31
  HandleScope scope(isolate);
2043
31
  Local<Context> context = env()->context();
2044
  Context::Scope context_scope(context);
2045
31
  flags_ &= ~NGHTTP2_STREAM_FLAG_TRAILERS;
2046
62
  MakeCallback(env()->http2session_on_stream_trailers_function(), 0, nullptr);
2047
31
}
2048
2049
// Submit informational headers for a stream.
2050
29
int Http2Stream::SubmitTrailers(nghttp2_nv* nva, size_t len) {
2051
29
  CHECK(!this->IsDestroyed());
2052
29
  Http2Scope h2scope(this);
2053
29
  Debug(this, "sending %d trailers", len);
2054
  int ret;
2055
  // Sending an empty trailers frame poses problems in Safari, Edge & IE.
2056
  // Instead we can just send an empty data frame with NGHTTP2_FLAG_END_STREAM
2057
  // to indicate that the stream is ready to be closed.
2058
29
  if (len == 0) {
2059
24
    Http2Stream::Provider::Stream prov(this, 0);
2060
24
    ret = nghttp2_submit_data(**session_, NGHTTP2_FLAG_END_STREAM, id_, *prov);
2061
  } else {
2062
5
    ret = nghttp2_submit_trailer(**session_, id_, nva, len);
2063
  }
2064
29
  CHECK_NE(ret, NGHTTP2_ERR_NOMEM);
2065
29
  return ret;
2066
}
2067
2068
// Submit a PRIORITY frame to the connected peer.
2069
6
int Http2Stream::SubmitPriority(nghttp2_priority_spec* prispec,
2070
                                bool silent) {
2071
6
  CHECK(!this->IsDestroyed());
2072
6
  Http2Scope h2scope(this);
2073
6
  Debug(this, "sending priority spec");
2074
  int ret = silent ?
2075
      nghttp2_session_change_stream_priority(**session_,
2076
                                             id_, prispec) :
2077
      nghttp2_submit_priority(**session_,
2078
                              NGHTTP2_FLAG_NONE,
2079
6
                              id_, prispec);
2080
6
  CHECK_NE(ret, NGHTTP2_ERR_NOMEM);
2081
6
  return ret;
2082
}
2083
2084
// Closes the Http2Stream by submitting an RST_STREAM frame to the connected
2085
// peer.
2086
121
void Http2Stream::SubmitRstStream(const uint32_t code) {
2087
121
  CHECK(!this->IsDestroyed());
2088
121
  code_ = code;
2089
  // If possible, force a purge of any currently pending data here to make sure
2090
  // it is sent before closing the stream. If it returns non-zero then we need
2091
  // to wait until the current write finishes and try again to avoid nghttp2
2092
  // behaviour where it prioritizes RstStream over everything else.
2093
121
  if (session_->SendPendingData() != 0) {
2094
3
    session_->AddPendingRstStream(id_);
2095
124
    return;
2096
  }
2097
2098
118
  FlushRstStream();
2099
}
2100
2101
123
void Http2Stream::FlushRstStream() {
2102
123
  if (IsDestroyed())
2103
146
    return;
2104
100
  Http2Scope h2scope(this);
2105
100
  CHECK_EQ(nghttp2_submit_rst_stream(**session_, NGHTTP2_FLAG_NONE,
2106
100
                                     id_, code_), 0);
2107
}
2108
2109
2110
// Submit a push promise and create the associated Http2Stream if successful.
2111
8
Http2Stream* Http2Stream::SubmitPushPromise(nghttp2_nv* nva,
2112
                                            size_t len,
2113
                                            int32_t* ret,
2114
                                            int options) {
2115
8
  CHECK(!this->IsDestroyed());
2116
8
  Http2Scope h2scope(this);
2117
8
  Debug(this, "sending push promise");
2118
  *ret = nghttp2_submit_push_promise(**session_, NGHTTP2_FLAG_NONE,
2119
8
                                     id_, nva, len, nullptr);
2120
8
  CHECK_NE(*ret, NGHTTP2_ERR_NOMEM);
2121
8
  Http2Stream* stream = nullptr;
2122
8
  if (*ret > 0)
2123
8
    stream = Http2Stream::New(session_, *ret, NGHTTP2_HCAT_HEADERS, options);
2124
2125
8
  return stream;
2126
}
2127
2128
// Switch the StreamBase into flowing mode to begin pushing chunks of data
2129
// out to JS land.
2130
21175
int Http2Stream::ReadStart() {
2131
21175
  Http2Scope h2scope(this);
2132
21175
  CHECK(!this->IsDestroyed());
2133
21175
  flags_ |= NGHTTP2_STREAM_FLAG_READ_START;
2134
21175
  flags_ &= ~NGHTTP2_STREAM_FLAG_READ_PAUSED;
2135
2136
21175
  Debug(this, "reading starting");
2137
2138
  // Tell nghttp2 about our consumption of the data that was handed
2139
  // off to JS land.
2140
  nghttp2_session_consume_stream(**session_,
2141
                                 id_,
2142
21175
                                 inbound_consumed_data_while_paused_);
2143
21175
  inbound_consumed_data_while_paused_ = 0;
2144
2145
21175
  return 0;
2146
}
2147
2148
// Switch the StreamBase into paused mode.
2149
134
int Http2Stream::ReadStop() {
2150
134
  CHECK(!this->IsDestroyed());
2151
134
  if (!IsReading())
2152
94
    return 0;
2153
40
  flags_ |= NGHTTP2_STREAM_FLAG_READ_PAUSED;
2154
40
  Debug(this, "reading stopped");
2155
40
  return 0;
2156
}
2157
2158
// The Http2Stream class is a subclass of StreamBase. The DoWrite method
2159
// receives outbound chunks of data to send as outbound DATA frames. These
2160
// are queued in an internal linked list of uv_buf_t structs that are sent
2161
// when nghttp2 is ready to serialize the data frame.
2162
//
2163
// Queue the given set of uv_but_t handles for writing to an
2164
// nghttp2_stream. The WriteWrap's Done callback will be invoked once the
2165
// chunks of data have been flushed to the underlying nghttp2_session.
2166
// Note that this does *not* mean that the data has been flushed
2167
// to the socket yet.
2168
3563
int Http2Stream::DoWrite(WriteWrap* req_wrap,
2169
                         uv_buf_t* bufs,
2170
                         size_t nbufs,
2171
                         uv_stream_t* send_handle) {
2172
3563
  CHECK_NULL(send_handle);
2173
3563
  Http2Scope h2scope(this);
2174

3563
  if (!IsWritable() || IsDestroyed()) {
2175
    req_wrap->Done(UV_EOF);
2176
    return 0;
2177
  }
2178
3563
  Debug(this, "queuing %d buffers to send", id_, nbufs);
2179
24343
  for (size_t i = 0; i < nbufs; ++i) {
2180
    // Store the req_wrap on the last write info in the queue, so that it is
2181
    // only marked as finished once all buffers associated with it are finished.
2182
    queue_.emplace(nghttp2_stream_write {
2183
20780
      i == nbufs - 1 ? req_wrap : nullptr,
2184
      bufs[i]
2185
20780
    });
2186
20780
    IncrementAvailableOutboundLength(bufs[i].len);
2187
  }
2188
3563
  CHECK_NE(nghttp2_session_resume_data(**session_, id_), NGHTTP2_ERR_NOMEM);
2189
3563
  return 0;
2190
}
2191
2192
// Ads a header to the Http2Stream. Note that the header name and value are
2193
// provided using a buffer structure provided by nghttp2 that allows us to
2194
// avoid unnecessary memcpy's. Those buffers are ref counted. The ref count
2195
// is incremented here and are decremented when the header name and values
2196
// are garbage collected later.
2197
70846
bool Http2Stream::AddHeader(nghttp2_rcbuf* name,
2198
                            nghttp2_rcbuf* value,
2199
                            uint8_t flags) {
2200
70846
  CHECK(!this->IsDestroyed());
2201
70846
  if (this->statistics_.first_header == 0)
2202
23164
    this->statistics_.first_header = uv_hrtime();
2203
70846
  size_t length = nghttp2_rcbuf_get_buf(name).len +
2204
70846
                  nghttp2_rcbuf_get_buf(value).len + 32;
2205
  // A header can only be added if we have not exceeded the maximum number
2206
  // of headers and the session has memory available for it.
2207

212538
  if (!session_->IsAvailableSessionMemory(length) ||
2208

141691
      current_headers_.size() == max_header_pairs_ ||
2209
70845
      current_headers_length_ + length > max_header_length_) {
2210
2
    return false;
2211
  }
2212
70844
  nghttp2_header header;
2213
70844
  header.name = name;
2214
70844
  header.value = value;
2215
70844
  header.flags = flags;
2216
70844
  current_headers_.push_back(header);
2217
70844
  nghttp2_rcbuf_incref(name);
2218
70844
  nghttp2_rcbuf_incref(value);
2219
70844
  current_headers_length_ += length;
2220
70844
  return true;
2221
}
2222
2223
// A Provider is the thing that provides outbound DATA frame data.
2224
11576
Http2Stream::Provider::Provider(Http2Stream* stream, int options) {
2225
11576
  CHECK(!stream->IsDestroyed());
2226
11576
  provider_.source.ptr = stream;
2227
11576
  empty_ = options & STREAM_OPTION_EMPTY_PAYLOAD;
2228
11576
}
2229
2230
11623
Http2Stream::Provider::Provider(int options) {
2231
11623
  provider_.source.ptr = nullptr;
2232
11623
  empty_ = options & STREAM_OPTION_EMPTY_PAYLOAD;
2233
11623
}
2234
2235
23199
Http2Stream::Provider::~Provider() {
2236
23199
  provider_.source.ptr = nullptr;
2237
23199
}
2238
2239
// The Stream Provider pulls data from a linked list of uv_buf_t structs
2240
// built via the StreamBase API and the Streams js API.
2241
11623
Http2Stream::Provider::Stream::Stream(int options)
2242
11623
    : Http2Stream::Provider(options) {
2243
11623
  provider_.read_callback = Http2Stream::Provider::Stream::OnRead;
2244
11623
}
2245
2246
11576
Http2Stream::Provider::Stream::Stream(Http2Stream* stream, int options)
2247
11576
    : Http2Stream::Provider(stream, options) {
2248
11576
  provider_.read_callback = Http2Stream::Provider::Stream::OnRead;
2249
11576
}
2250
2251
27331
ssize_t Http2Stream::Provider::Stream::OnRead(nghttp2_session* handle,
2252
                                              int32_t id,
2253
                                              uint8_t* buf,
2254
                                              size_t length,
2255
                                              uint32_t* flags,
2256
                                              nghttp2_data_source* source,
2257
                                              void* user_data) {
2258
27331
  Http2Session* session = static_cast<Http2Session*>(user_data);
2259
  Debug(session, "reading outbound data for stream %d", id);
2260
27331
  Http2Stream* stream = GetStream(session, id, source);
2261
27331
  if (stream->statistics_.first_byte_sent == 0)
2262
12563
    stream->statistics_.first_byte_sent = uv_hrtime();
2263
27331
  CHECK_EQ(id, stream->id());
2264
2265
27331
  size_t amount = 0;          // amount of data being sent in this data frame.
2266
2267
  // Remove all empty chunks from the head of the queue.
2268
  // This is done here so that .write('', cb) is still a meaningful way to
2269
  // find out when the HTTP2 stream wants to consume data, and because the
2270
  // StreamBase API allows empty input chunks.
2271

54666
  while (!stream->queue_.empty() && stream->queue_.front().buf.len == 0) {
2272
4
    WriteWrap* finished = stream->queue_.front().req_wrap;
2273
4
    stream->queue_.pop();
2274
4
    if (finished != nullptr)
2275
2
      finished->Done(0);
2276
  }
2277
2278
27331
  if (!stream->queue_.empty()) {
2279
    Debug(session, "stream %d has pending outbound data", id);
2280
11189
    amount = std::min(stream->available_outbound_length_, length);
2281
    Debug(session, "sending %d bytes for data frame on stream %d", amount, id);
2282
11189
    if (amount > 0) {
2283
      // Just return the length, let Http2Session::OnSendData take care of
2284
      // actually taking the buffers out of the queue.
2285
11189
      *flags |= NGHTTP2_DATA_FLAG_NO_COPY;
2286
11189
      stream->DecrementAvailableOutboundLength(amount);
2287
    }
2288
  }
2289
2290

27331
  if (amount == 0 && stream->IsWritable()) {
2291
3591
    CHECK(stream->queue_.empty());
2292
    Debug(session, "deferring stream %d", id);
2293
3591
    stream->EmitWantsWrite(length);
2294

3591
    if (stream->available_outbound_length_ > 0 || !stream->IsWritable()) {
2295
      // EmitWantsWrite() did something interesting synchronously, restart:
2296
      return OnRead(handle, id, buf, length, flags, source, user_data);
2297
    }
2298
3591
    return NGHTTP2_ERR_DEFERRED;
2299
  }
2300
2301

23740
  if (stream->queue_.empty() && !stream->IsWritable()) {
2302
    Debug(session, "no more data for stream %d", id);
2303
12551
    *flags |= NGHTTP2_DATA_FLAG_EOF;
2304
12551
    if (stream->HasTrailers()) {
2305
31
      *flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
2306
31
      stream->OnTrailers();
2307
    }
2308
  }
2309
2310
23740
  stream->statistics_.sent_bytes += amount;
2311
23740
  return amount;
2312
}
2313
2314
20780
inline void Http2Stream::IncrementAvailableOutboundLength(size_t amount) {
2315
20780
  available_outbound_length_ += amount;
2316
20780
  session_->IncrementCurrentSessionMemory(amount);
2317
20780
}
2318
2319
11189
inline void Http2Stream::DecrementAvailableOutboundLength(size_t amount) {
2320
11189
  available_outbound_length_ -= amount;
2321
11189
  session_->DecrementCurrentSessionMemory(amount);
2322
11189
}
2323
2324
2325
// Implementation of the JavaScript API
2326
2327
// Fetches the string description of a nghttp2 error code and passes that
2328
// back to JS land
2329
52
void HttpErrorString(const FunctionCallbackInfo<Value>& args) {
2330
52
  Environment* env = Environment::GetCurrent(args);
2331
208
  uint32_t val = args[0]->Uint32Value(env->context()).ToChecked();
2332
  args.GetReturnValue().Set(
2333
      String::NewFromOneByte(
2334
          env->isolate(),
2335
52
          reinterpret_cast<const uint8_t*>(nghttp2_strerror(val)),
2336
156
          NewStringType::kInternalized).ToLocalChecked());
2337
52
}
2338
2339
2340
// Serializes the settings object into a Buffer instance that
2341
// would be suitable, for instance, for creating the Base64
2342
// output for an HTTP2-Settings header field.
2343
15
void PackSettings(const FunctionCallbackInfo<Value>& args) {
2344
15
  Environment* env = Environment::GetCurrent(args);
2345
  // TODO(addaleax): We should not be creating a full AsyncWrap for this.
2346
  Local<Object> obj;
2347
30
  if (!env->http2settings_constructor_template()
2348
45
           ->NewInstance(env->context())
2349
45
           .ToLocal(&obj)) {
2350
15
    return;
2351
  }
2352
15
  Http2Session::Http2Settings settings(env, nullptr, obj);
2353
45
  args.GetReturnValue().Set(settings.Pack());
2354
}
2355
2356
// A TypedArray instance is shared between C++ and JS land to contain the
2357
// default SETTINGS. RefreshDefaultSettings updates that TypedArray with the
2358
// default values.
2359
3
void RefreshDefaultSettings(const FunctionCallbackInfo<Value>& args) {
2360
3
  Environment* env = Environment::GetCurrent(args);
2361
3
  Http2Session::Http2Settings::RefreshDefaults(env);
2362
3
}
2363
2364
// Sets the next stream ID the Http2Session. If successful, returns true.
2365
1
void Http2Session::SetNextStreamID(const FunctionCallbackInfo<Value>& args) {
2366
1
  Environment* env = Environment::GetCurrent(args);
2367
  Http2Session* session;
2368
1
  ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
2369
4
  int32_t id = args[0]->Int32Value(env->context()).ToChecked();
2370
1
  if (nghttp2_session_set_next_stream_id(**session, id) < 0) {
2371
    Debug(session, "failed to set next stream id to %d", id);
2372
    return args.GetReturnValue().Set(false);
2373
  }
2374
2
  args.GetReturnValue().Set(true);
2375
1
  Debug(session, "set next stream id to %d", id);
2376
}
2377
2378
// A TypedArray instance is shared between C++ and JS land to contain the
2379
// SETTINGS (either remote or local). RefreshSettings updates the current
2380
// values established for each of the settings so those can be read in JS land.
2381
template <get_setting fn>
2382
18280
void Http2Session::RefreshSettings(const FunctionCallbackInfo<Value>& args) {
2383
18280
  Environment* env = Environment::GetCurrent(args);
2384
  Http2Session* session;
2385

36560
  ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
2386
18280
  Http2Settings::Update(env, session, fn);
2387
18280
  Debug(session, "settings refreshed for session");
2388
}
2389
2390
// A TypedArray instance is shared between C++ and JS land to contain state
2391
// information of the current Http2Session. This updates the values in the
2392
// TypedArray so those can be read in JS land.
2393
3
void Http2Session::RefreshState(const FunctionCallbackInfo<Value>& args) {
2394
3
  Environment* env = Environment::GetCurrent(args);
2395
  Http2Session* session;
2396
6
  ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
2397
3
  Debug(session, "refreshing state");
2398
2399
3
  AliasedFloat64Array& buffer = env->http2_state()->session_state_buffer;
2400
2401
3
  nghttp2_session* s = **session;
2402
2403
6
  buffer[IDX_SESSION_STATE_EFFECTIVE_LOCAL_WINDOW_SIZE] =
2404
6
      nghttp2_session_get_effective_local_window_size(s);
2405
6
  buffer[IDX_SESSION_STATE_EFFECTIVE_RECV_DATA_LENGTH] =
2406
6
      nghttp2_session_get_effective_recv_data_length(s);
2407
6
  buffer[IDX_SESSION_STATE_NEXT_STREAM_ID] =
2408
6
      nghttp2_session_get_next_stream_id(s);
2409
6
  buffer[IDX_SESSION_STATE_LOCAL_WINDOW_SIZE] =
2410
6
      nghttp2_session_get_local_window_size(s);
2411
6
  buffer[IDX_SESSION_STATE_LAST_PROC_STREAM_ID] =
2412
6
      nghttp2_session_get_last_proc_stream_id(s);
2413
6
  buffer[IDX_SESSION_STATE_REMOTE_WINDOW_SIZE] =
2414
6
      nghttp2_session_get_remote_window_size(s);
2415
6
  buffer[IDX_SESSION_STATE_OUTBOUND_QUEUE_SIZE] =
2416
6
      nghttp2_session_get_outbound_queue_size(s);
2417
6
  buffer[IDX_SESSION_STATE_HD_DEFLATE_DYNAMIC_TABLE_SIZE] =
2418
6
      nghttp2_session_get_hd_deflate_dynamic_table_size(s);
2419
6
  buffer[IDX_SESSION_STATE_HD_INFLATE_DYNAMIC_TABLE_SIZE] =
2420
6
      nghttp2_session_get_hd_inflate_dynamic_table_size(s);
2421
}
2422
2423
2424
// Constructor for new Http2Session instances.
2425
577
void Http2Session::New(const FunctionCallbackInfo<Value>& args) {
2426
577
  Environment* env = Environment::GetCurrent(args);
2427
577
  CHECK(args.IsConstructCall());
2428
2308
  int val = args[0]->IntegerValue(env->context()).ToChecked();
2429
577
  nghttp2_session_type type = static_cast<nghttp2_session_type>(val);
2430
577
  Http2Session* session = new Http2Session(env, args.This(), type);
2431
577
  session->get_async_id();  // avoid compiler warning
2432
  Debug(session, "session created");
2433
577
}
2434
2435
2436
// Binds the Http2Session with a StreamBase used for i/o
2437
577
void Http2Session::Consume(const FunctionCallbackInfo<Value>& args) {
2438
  Http2Session* session;
2439
1154
  ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
2440
1154
  CHECK(args[0]->IsObject());
2441
1154
  session->Consume(args[0].As<Object>());
2442
}
2443
2444
// Destroys the Http2Session instance and renders it unusable
2445
551
void Http2Session::Destroy(const FunctionCallbackInfo<Value>& args) {
2446
  Http2Session* session;
2447
1102
  ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
2448
551
  Debug(session, "destroying session");
2449
551
  Environment* env = Environment::GetCurrent(args);
2450
551
  Local<Context> context = env->context();
2451
2452
1653
  uint32_t code = args[0]->Uint32Value(context).ToChecked();
2453
1653
  bool socketDestroyed = args[1]->BooleanValue(env->isolate());
2454
2455
551
  session->Close(code, socketDestroyed);
2456
}
2457
2458
// Submits a new request on the Http2Session and returns either an error code
2459
// or the Http2Stream object.
2460
11623
void Http2Session::Request(const FunctionCallbackInfo<Value>& args) {
2461
  Http2Session* session;
2462
11624
  ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
2463
11623
  Environment* env = session->env();
2464
11623
  Local<Context> context = env->context();
2465
11623
  Isolate* isolate = env->isolate();
2466
2467
23246
  Local<Array> headers = args[0].As<Array>();
2468
34869
  int options = args[1]->IntegerValue(context).ToChecked();
2469
11623
  Http2Priority priority(env, args[2], args[3], args[4]);
2470
2471
11623
  Headers list(isolate, context, headers);
2472
2473
11623
  Debug(session, "request submitted");
2474
2475
11623
  int32_t ret = 0;
2476
  Http2Stream* stream =
2477
      session->Http2Session::SubmitRequest(*priority, *list, list.length(),
2478
11623
                                           &ret, options);
2479
2480

11623
  if (ret <= 0 || stream == nullptr) {
2481
2
    Debug(session, "could not submit request: %s", nghttp2_strerror(ret));
2482
3
    return args.GetReturnValue().Set(ret);
2483
  }
2484
2485
23244
  Debug(session, "request submitted, new stream id %d", stream->id());
2486
34866
  args.GetReturnValue().Set(stream->object());
2487
}
2488
2489
// Submits a GOAWAY frame to signal that the Http2Session is in the process
2490
// of shutting down. Note that this function does not actually alter the
2491
// state of the Http2Session, it's simply a notification.
2492
501
void Http2Session::Goaway(uint32_t code,
2493
                          int32_t lastStreamID,
2494
                          const uint8_t* data,
2495
                          size_t len) {
2496
501
  if (IsDestroyed())
2497
501
    return;
2498
2499
501
  Http2Scope h2scope(this);
2500
  // the last proc stream id is the most recently created Http2Stream.
2501
501
  if (lastStreamID <= 0)
2502
501
    lastStreamID = nghttp2_session_get_last_proc_stream_id(session_);
2503
501
  Debug(this, "submitting goaway");
2504
  nghttp2_submit_goaway(session_, NGHTTP2_FLAG_NONE,
2505
501
                        lastStreamID, code, data, len);
2506
}
2507
2508
// Submits a GOAWAY frame to signal that the Http2Session is in the process
2509
// of shutting down. The opaque data argument is an optional TypedArray that
2510
// can be used to send debugging data to the connected peer.
2511
501
void Http2Session::Goaway(const FunctionCallbackInfo<Value>& args) {
2512
501
  Environment* env = Environment::GetCurrent(args);
2513
501
  Local<Context> context = env->context();
2514
  Http2Session* session;
2515
1002
  ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
2516
2517
1503
  uint32_t code = args[0]->Uint32Value(context).ToChecked();
2518
1503
  int32_t lastStreamID = args[1]->Int32Value(context).ToChecked();
2519
501
  ArrayBufferViewContents<uint8_t> opaque_data;
2520
2521
1002
  if (args[2]->IsArrayBufferView()) {
2522
2
    opaque_data.Read(args[2].As<ArrayBufferView>());
2523
  }
2524
2525
501
  session->Goaway(code, lastStreamID, opaque_data.data(), opaque_data.length());
2526
}
2527
2528
// Update accounting of data chunks. This is used primarily to manage timeout
2529
// logic when using the FD Provider.
2530
10
void Http2Session::UpdateChunksSent(const FunctionCallbackInfo<Value>& args) {
2531
10
  Environment* env = Environment::GetCurrent(args);
2532
10
  Isolate* isolate = env->isolate();
2533
10
  HandleScope scope(isolate);
2534
  Http2Session* session;
2535
20
  ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
2536
2537
10
  uint32_t length = session->chunks_sent_since_last_write_;
2538
2539
10
  session->object()->Set(env->context(),
2540
                         env->chunks_sent_since_last_write_string(),
2541
60
                         Integer::NewFromUnsigned(isolate, length)).Check();
2542
2543
20
  args.GetReturnValue().Set(length);
2544
}
2545
2546
// Submits an RST_STREAM frame effectively closing the Http2Stream. Note that
2547
// this *WILL* alter the state of the stream, causing the OnStreamClose
2548
// callback to the triggered.
2549
119
void Http2Stream::RstStream(const FunctionCallbackInfo<Value>& args) {
2550
119
  Environment* env = Environment::GetCurrent(args);
2551
119
  Local<Context> context = env->context();
2552
  Http2Stream* stream;
2553
238
  ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder());
2554
357
  uint32_t code = args[0]->Uint32Value(context).ToChecked();
2555
119
  Debug(stream, "sending rst_stream with code %d", code);
2556
119
  stream->SubmitRstStream(code);
2557
}
2558
2559
// Initiates a response on the Http2Stream using the StreamBase API to provide
2560
// outbound DATA frames.
2561
11552
void Http2Stream::Respond(const FunctionCallbackInfo<Value>& args) {
2562
11552
  Environment* env = Environment::GetCurrent(args);
2563
11552
  Local<Context> context = env->context();
2564
11552
  Isolate* isolate = env->isolate();
2565
  Http2Stream* stream;
2566
23104
  ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder());
2567
2568
23104
  Local<Array> headers = args[0].As<Array>();
2569
34656
  int options = args[1]->IntegerValue(context).ToChecked();
2570
2571
11552
  Headers list(isolate, context, headers);
2572
2573
  args.GetReturnValue().Set(
2574
34656
      stream->SubmitResponse(*list, list.length(), options));
2575
23104
  Debug(stream, "response submitted");
2576
}
2577
2578
2579
// Submits informational headers on the Http2Stream
2580
5
void Http2Stream::Info(const FunctionCallbackInfo<Value>& args) {
2581
5
  Environment* env = Environment::GetCurrent(args);
2582
5
  Local<Context> context = env->context();
2583
5
  Isolate* isolate = env->isolate();
2584
  Http2Stream* stream;
2585
10
  ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder());
2586
2587
10
  Local<Array> headers = args[0].As<Array>();
2588
2589
5
  Headers list(isolate, context, headers);
2590
15
  args.GetReturnValue().Set(stream->SubmitInfo(*list, list.length()));
2591
10
  Debug(stream, "%d informational headers sent", list.length());
2592
}
2593
2594
// Submits trailing headers on the Http2Stream
2595
29
void Http2Stream::Trailers(const FunctionCallbackInfo<Value>& args) {
2596
29
  Environment* env = Environment::GetCurrent(args);
2597
29
  Local<Context> context = env->context();
2598
29
  Isolate* isolate = env->isolate();
2599
  Http2Stream* stream;
2600
58
  ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder());
2601
2602
58
  Local<Array> headers = args[0].As<Array>();
2603
2604
29
  Headers list(isolate, context, headers);
2605
87
  args.GetReturnValue().Set(stream->SubmitTrailers(*list, list.length()));
2606
58
  Debug(stream, "%d trailing headers sent", list.length());
2607
}
2608
2609
// Grab the numeric id of the Http2Stream
2610
11630
void Http2Stream::GetID(const FunctionCallbackInfo<Value>& args) {
2611
  Http2Stream* stream;
2612
23260
  ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder());
2613
34890
  args.GetReturnValue().Set(stream->id());
2614
}
2615
2616
// Destroy the Http2Stream, rendering it no longer usable
2617
23214
void Http2Stream::Destroy(const FunctionCallbackInfo<Value>& args) {
2618
  Http2Stream* stream;
2619
46428
  ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder());
2620
23214
  Debug(stream, "destroying stream");
2621
23214
  stream->Destroy();
2622
}
2623
2624
// Prompt the Http2Stream to begin sending data to the JS land.
2625
void Http2Stream::FlushData(const FunctionCallbackInfo<Value>& args) {
2626
  Http2Stream* stream;
2627
  ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder());
2628
  stream->ReadStart();
2629
  Debug(stream, "data flushed to js");
2630
}
2631
2632
// Initiate a Push Promise and create the associated Http2Stream
2633
8
void Http2Stream::PushPromise(const FunctionCallbackInfo<Value>& args) {
2634
8
  Environment* env = Environment::GetCurrent(args);
2635
8
  Local<Context> context = env->context();
2636
8
  Isolate* isolate = env->isolate();
2637
  Http2Stream* parent;
2638
8
  ASSIGN_OR_RETURN_UNWRAP(&parent, args.Holder());
2639
2640
16
  Local<Array> headers = args[0].As<Array>();
2641
24
  int options = args[1]->IntegerValue(context).ToChecked();
2642
2643
8
  Headers list(isolate, context, headers);
2644
2645
8
  Debug(parent, "creating push promise");
2646
2647
8
  int32_t ret = 0;
2648
  Http2Stream* stream = parent->SubmitPushPromise(*list, list.length(),
2649
8
                                                  &ret, options);
2650

8
  if (ret <= 0 || stream == nullptr) {
2651
    Debug(parent, "failed to create push stream: %d", ret);
2652
    return args.GetReturnValue().Set(ret);
2653
  }
2654
16
  Debug(parent, "push stream %d created", stream->id());
2655
24
  args.GetReturnValue().Set(stream->object());
2656
}
2657
2658
// Send a PRIORITY frame
2659
6
void Http2Stream::Priority(const FunctionCallbackInfo<Value>& args) {
2660
6
  Environment* env = Environment::GetCurrent(args);
2661
  Http2Stream* stream;
2662
12
  ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder());
2663
2664
6
  Http2Priority priority(env, args[0], args[1], args[2]);
2665
18
  bool silent = args[3]->BooleanValue(env->isolate());
2666
2667
6
  CHECK_EQ(stream->SubmitPriority(*priority, silent), 0);
2668
6
  Debug(stream, "priority submitted");
2669
}
2670
2671
// A TypedArray shared by C++ and JS land is used to communicate state
2672
// information about the Http2Stream. This updates the values in that
2673
// TypedArray so that the state can be read by JS.
2674
11
void Http2Stream::RefreshState(const FunctionCallbackInfo<Value>& args) {
2675
11
  Environment* env = Environment::GetCurrent(args);
2676
  Http2Stream* stream;
2677
22
  ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder());
2678
2679
11
  Debug(stream, "refreshing state");
2680
2681
11
  AliasedFloat64Array& buffer = env->http2_state()->stream_state_buffer;
2682
2683
11
  nghttp2_stream* str = **stream;
2684
11
  nghttp2_session* s = **(stream->session());
2685
2686
11
  if (str == nullptr) {
2687
1
    buffer[IDX_STREAM_STATE] = NGHTTP2_STREAM_STATE_IDLE;
2688
2
    buffer[IDX_STREAM_STATE_WEIGHT] =
2689
2
        buffer[IDX_STREAM_STATE_SUM_DEPENDENCY_WEIGHT] =
2690
2
        buffer[IDX_STREAM_STATE_LOCAL_CLOSE] =
2691
2
        buffer[IDX_STREAM_STATE_REMOTE_CLOSE] =
2692
3
        buffer[IDX_STREAM_STATE_LOCAL_WINDOW_SIZE] = 0;
2693
  } else {
2694
20
    buffer[IDX_STREAM_STATE] =
2695
20
        nghttp2_stream_get_state(str);
2696
20
    buffer[IDX_STREAM_STATE_WEIGHT] =
2697
20
        nghttp2_stream_get_weight(str);
2698
20
    buffer[IDX_STREAM_STATE_SUM_DEPENDENCY_WEIGHT] =
2699
20
        nghttp2_stream_get_sum_dependency_weight(str);
2700
20
    buffer[IDX_STREAM_STATE_LOCAL_CLOSE] =
2701
20
        nghttp2_session_get_stream_local_close(s, stream->id());
2702
20
    buffer[IDX_STREAM_STATE_REMOTE_CLOSE] =
2703
20
        nghttp2_session_get_stream_remote_close(s, stream->id());
2704
20
    buffer[IDX_STREAM_STATE_LOCAL_WINDOW_SIZE] =
2705
20
        nghttp2_session_get_stream_local_window_size(s, stream->id());
2706
  }
2707
}
2708
2709
5
void Http2Session::AltSvc(int32_t id,
2710
                          uint8_t* origin,
2711
                          size_t origin_len,
2712
                          uint8_t* value,
2713
                          size_t value_len) {
2714
5
  Http2Scope h2scope(this);
2715
5
  CHECK_EQ(nghttp2_submit_altsvc(session_, NGHTTP2_FLAG_NONE, id,
2716
5
                                 origin, origin_len, value, value_len), 0);
2717
5
}
2718
2719
5
void Http2Session::Origin(nghttp2_origin_entry* ov, size_t count) {
2720
5
  Http2Scope h2scope(this);
2721
5
  CHECK_EQ(nghttp2_submit_origin(session_, NGHTTP2_FLAG_NONE, ov, count), 0);
2722
5
}
2723
2724
// Submits an AltSvc frame to be sent to the connected peer.
2725
5
void Http2Session::AltSvc(const FunctionCallbackInfo<Value>& args) {
2726
5
  Environment* env = Environment::GetCurrent(args);
2727
  Http2Session* session;
2728
10
  ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
2729
2730
20
  int32_t id = args[0]->Int32Value(env->context()).ToChecked();
2731
2732
  // origin and value are both required to be ASCII, handle them as such.
2733
20
  Local<String> origin_str = args[1]->ToString(env->context()).ToLocalChecked();
2734
20
  Local<String> value_str = args[2]->ToString(env->context()).ToLocalChecked();
2735
2736
5
  size_t origin_len = origin_str->Length();
2737
5
  size_t value_len = value_str->Length();
2738
2739
5
  CHECK_LE(origin_len + value_len, 16382);  // Max permitted for ALTSVC
2740
  // Verify that origin len != 0 if stream id == 0, or
2741
  // that origin len == 0 if stream id != 0
2742


5
  CHECK((origin_len != 0 && id == 0) || (origin_len == 0 && id != 0));
2743
2744
5
  MaybeStackBuffer<uint8_t> origin(origin_len);
2745
10
  MaybeStackBuffer<uint8_t> value(value_len);
2746
10
  origin_str->WriteOneByte(env->isolate(), *origin);
2747
10
  value_str->WriteOneByte(env->isolate(), *value);
2748
2749
10
  session->AltSvc(id, *origin, origin_len, *value, value_len);
2750
}
2751
2752
5
void Http2Session::Origin(const FunctionCallbackInfo<Value>& args) {
2753
5
  Environment* env = Environment::GetCurrent(args);
2754
5
  Local<Context> context = env->context();
2755
  Http2Session* session;
2756
10
  ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
2757
2758
10
  Local<String> origin_string = args[0].As<String>();
2759
15
  int count = args[1]->IntegerValue(context).ToChecked();
2760
2761
2762
  Origins origins(env->isolate(),
2763
                  env->context(),
2764
                  origin_string,
2765
5
                  count);
2766
2767
5
  session->Origin(*origins, origins.length());
2768
}
2769
2770
// Submits a PING frame to be sent to the connected peer.
2771
13
void Http2Session::Ping(const FunctionCallbackInfo<Value>& args) {
2772
13
  Environment* env = Environment::GetCurrent(args);
2773
  Http2Session* session;
2774
15
  ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
2775
2776
  // A PING frame may have exactly 8 bytes of payload data. If not provided,
2777
  // then the current hrtime will be used as the payload.
2778
13
  ArrayBufferViewContents<uint8_t, 8> payload;
2779
26
  if (args[0]->IsArrayBufferView()) {
2780
12
    payload.Read(args[0].As<ArrayBufferView>());
2781
6
    CHECK_EQ(payload.length(), 8);
2782
  }
2783
2784
  Local<Object> obj;
2785
26
  if (!env->http2ping_constructor_template()
2786
39
           ->NewInstance(env->context())
2787
39
           .ToLocal(&obj)) {
2788
    return;
2789
  }
2790
52
  if (obj->Set(env->context(), env->ondone_string(), args[1]).IsNothing())
2791
    return;
2792
2793
13
  Http2Ping* ping = session->AddPing(std::make_unique<Http2Ping>(session, obj));
2794
  // To prevent abuse, we strictly limit the number of unacknowledged PING
2795
  // frames that may be sent at any given time. This is configurable in the
2796
  // Options when creating a Http2Session.
2797
17
  if (ping == nullptr) return args.GetReturnValue().Set(false);
2798
2799
  // The Ping itself is an Async resource. When the acknowledgement is received,
2800
  // the callback will be invoked and a notification sent out to JS land. The
2801
  // notification will include the duration of the ping, allowing the round
2802
  // trip to be measured.
2803
11
  ping->Send(payload.data());
2804
22
  args.GetReturnValue().Set(true);
2805
}
2806
2807
// Submits a SETTINGS frame for the Http2Session
2808
586
void Http2Session::Settings(const FunctionCallbackInfo<Value>& args) {
2809
586
  Environment* env = Environment::GetCurrent(args);
2810
  Http2Session* session;
2811
588
  ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
2812
2813
  Local<Object> obj;
2814
1172
  if (!env->http2settings_constructor_template()
2815
1758
           ->NewInstance(env->context())
2816
1758
           .ToLocal(&obj)) {
2817
    return;
2818
  }
2819
2344
  if (obj->Set(env->context(), env->ondone_string(), args[0]).IsNothing())
2820
    return;
2821
2822
  Http2Session::Http2Settings* settings = session->AddSettings(
2823
586
      std::make_unique<Http2Settings>(session->env(), session, obj, 0));
2824
590
  if (settings == nullptr) return args.GetReturnValue().Set(false);
2825
2826
584
  settings->Send();
2827
1168
  args.GetReturnValue().Set(true);
2828
}
2829
2830
563
std::unique_ptr<Http2Session::Http2Ping> Http2Session::PopPing() {
2831
563
  std::unique_ptr<Http2Ping> ping;
2832
563
  if (!outstanding_pings_.empty()) {
2833
11
    ping = std::move(outstanding_pings_.front());
2834
11
    outstanding_pings_.pop();
2835
11
    DecrementCurrentSessionMemory(sizeof(*ping));
2836
  }
2837
563
  return ping;
2838
}
2839
2840
13
Http2Session::Http2Ping* Http2Session::AddPing(
2841
    std::unique_ptr<Http2Session::Http2Ping> ping) {
2842
13
  if (outstanding_pings_.size() == max_outstanding_pings_) {
2843
2
    ping->Done(false);
2844
2
    return nullptr;
2845
  }
2846
11
  Http2Ping* ptr = ping.get();
2847
11
  outstanding_pings_.emplace(std::move(ping));
2848
11
  IncrementCurrentSessionMemory(sizeof(*ping));
2849
11
  return ptr;
2850
}
2851
2852
476
std::unique_ptr<Http2Session::Http2Settings> Http2Session::PopSettings() {
2853
476
  std::unique_ptr<Http2Settings> settings;
2854
476
  if (!outstanding_settings_.empty()) {
2855
476
    settings = std::move(outstanding_settings_.front());
2856
476
    outstanding_settings_.pop();
2857
476
    DecrementCurrentSessionMemory(sizeof(*settings));
2858
  }
2859
476
  return settings;
2860
}
2861
2862
586
Http2Session::Http2Settings* Http2Session::AddSettings(
2863
    std::unique_ptr<Http2Session::Http2Settings> settings) {
2864
586
  if (outstanding_settings_.size() == max_outstanding_settings_) {
2865
2
    settings->Done(false);
2866
2
    return nullptr;
2867
  }
2868
584
  Http2Settings* ptr = settings.get();
2869
584
  outstanding_settings_.emplace(std::move(settings));
2870
584
  IncrementCurrentSessionMemory(sizeof(*settings));
2871
584
  return ptr;
2872
}
2873
2874
13
Http2Session::Http2Ping::Http2Ping(Http2Session* session, Local<Object> obj)
2875
    : AsyncWrap(session->env(), obj, AsyncWrap::PROVIDER_HTTP2PING),
2876
      session_(session),
2877
13
      startTime_(uv_hrtime()) {
2878
13
  RemoveCleanupHook();  // This object is owned by the Http2Session.
2879
13
}
2880
2881
11
void Http2Session::Http2Ping::Send(const uint8_t* payload) {
2882
11
  CHECK_NOT_NULL(session_);
2883
  uint8_t data[8];
2884
11
  if (payload == nullptr) {
2885
5
    memcpy(&data, &startTime_, arraysize(data));
2886
5
    payload = data;
2887
  }
2888
11
  Http2Scope h2scope(session_);
2889
11
  CHECK_EQ(nghttp2_submit_ping(**session_, NGHTTP2_FLAG_NONE, payload), 0);
2890
11
}
2891
2892
13
void Http2Session::Http2Ping::Done(bool ack, const uint8_t* payload) {
2893
13
  uint64_t duration_ns = uv_hrtime() - startTime_;
2894
13
  double duration_ms = duration_ns / 1e6;
2895
13
  if (session_ != nullptr) session_->statistics_.ping_rtt = duration_ns;
2896
2897
13
  HandleScope handle_scope(env()->isolate());
2898
13
  Context::Scope context_scope(env()->context());
2899
2900
13
  Local<Value> buf = Undefined(env()->isolate());
2901
13
  if (payload != nullptr) {
2902
    buf = Buffer::Copy(env()->isolate(),
2903
                       reinterpret_cast<const char*>(payload),
2904
20
                       8).ToLocalChecked();
2905
  }
2906
2907
  Local<Value> argv[] = {
2908
    Boolean::New(env()->isolate(), ack),
2909
    Number::New(env()->isolate(), duration_ms),
2910
    buf
2911
39
  };
2912
26
  MakeCallback(env()->ondone_string(), arraysize(argv), argv);
2913
13
}
2914
2915
1
void Http2Session::Http2Ping::DetachFromSession() {
2916
1
  session_ = nullptr;
2917
1
}
2918
2919
void nghttp2_stream_write::MemoryInfo(MemoryTracker* tracker) const {
2920
  if (req_wrap != nullptr)
2921
    tracker->TrackField("req_wrap", req_wrap->GetAsyncWrap());
2922
  tracker->TrackField("buf", buf);
2923
}
2924
2925
2926
void nghttp2_header::MemoryInfo(MemoryTracker* tracker) const {
2927
  tracker->TrackFieldWithSize("name", nghttp2_rcbuf_get_buf(name).len);
2928
  tracker->TrackFieldWithSize("value", nghttp2_rcbuf_get_buf(value).len);
2929
}
2930
2931
221
void SetCallbackFunctions(const FunctionCallbackInfo<Value>& args) {
2932
221
  Environment* env = Environment::GetCurrent(args);
2933
221
  CHECK_EQ(args.Length(), 12);
2934
2935
#define SET_FUNCTION(arg, name)                                               \
2936
  CHECK(args[arg]->IsFunction());                                             \
2937
  env->set_http2session_on_ ## name ## _function(args[arg].As<Function>());
2938
2939
884
  SET_FUNCTION(0, error)
2940
884
  SET_FUNCTION(1, priority)
2941
884
  SET_FUNCTION(2, settings)
2942
884
  SET_FUNCTION(3, ping)
2943
884
  SET_FUNCTION(4, headers)
2944
884
  SET_FUNCTION(5, frame_error)
2945
884
  SET_FUNCTION(6, goaway_data)
2946
884
  SET_FUNCTION(7, altsvc)
2947
884
  SET_FUNCTION(8, origin)
2948
884
  SET_FUNCTION(9, select_padding)
2949
884
  SET_FUNCTION(10, stream_trailers)
2950
884
  SET_FUNCTION(11, stream_close)
2951
2952
#undef SET_FUNCTION
2953
221
}
2954
2955
// Set up the process.binding('http2') binding.
2956
227
void Initialize(Local<Object> target,
2957
                Local<Value> unused,
2958
                Local<Context> context,
2959
                void* priv) {
2960
227
  Environment* env = Environment::GetCurrent(context);
2961
227
  Isolate* isolate = env->isolate();
2962
227
  HandleScope scope(isolate);
2963
2964
454
  std::unique_ptr<Http2State> state(new Http2State(isolate));
2965
2966
#define SET_STATE_TYPEDARRAY(name, field)             \
2967
  target->Set(context,                                \
2968
              FIXED_ONE_BYTE_STRING(isolate, (name)), \
2969
              (field)).FromJust()
2970
2971
  // Initialize the buffer used for padding callbacks
2972
908
  SET_STATE_TYPEDARRAY(
2973
    "paddingBuffer", state->padding_buffer.GetJSArray());
2974
  // Initialize the buffer used to store the session state
2975
908
  SET_STATE_TYPEDARRAY(
2976
    "sessionState", state->session_state_buffer.GetJSArray());
2977
  // Initialize the buffer used to store the stream state
2978
908
  SET_STATE_TYPEDARRAY(
2979
    "streamState", state->stream_state_buffer.GetJSArray());
2980
908
  SET_STATE_TYPEDARRAY(
2981
    "settingsBuffer", state->settings_buffer.GetJSArray());
2982
908
  SET_STATE_TYPEDARRAY(
2983
    "optionsBuffer", state->options_buffer.GetJSArray());
2984
908
  SET_STATE_TYPEDARRAY(
2985
    "streamStats", state->stream_stats_buffer.GetJSArray());
2986
908
  SET_STATE_TYPEDARRAY(
2987
    "sessionStats", state->session_stats_buffer.GetJSArray());
2988
#undef SET_STATE_TYPEDARRAY
2989
2990
227
  env->set_http2_state(std::move(state));
2991
2992
908
  NODE_DEFINE_CONSTANT(target, PADDING_BUF_FRAME_LENGTH);
2993
908
  NODE_DEFINE_CONSTANT(target, PADDING_BUF_MAX_PAYLOAD_LENGTH);
2994
908
  NODE_DEFINE_CONSTANT(target, PADDING_BUF_RETURN_VALUE);
2995
2996
  // Method to fetch the nghttp2 string description of an nghttp2 error code
2997
227
  env->SetMethod(target, "nghttp2ErrorString", HttpErrorString);
2998
2999
  Local<String> http2SessionClassName =
3000
227
    FIXED_ONE_BYTE_STRING(isolate, "Http2Session");
3001
3002
227
  Local<FunctionTemplate> ping = FunctionTemplate::New(env->isolate());
3003
454
  ping->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "Http2Ping"));
3004
454
  ping->Inherit(AsyncWrap::GetConstructorTemplate(env));
3005
227
  Local<ObjectTemplate> pingt = ping->InstanceTemplate();
3006
227
  pingt->SetInternalFieldCount(1);
3007
227
  env->set_http2ping_constructor_template(pingt);
3008
3009
227
  Local<FunctionTemplate> setting = FunctionTemplate::New(env->isolate());
3010
454
  setting->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "Http2Setting"));
3011
454
  setting->Inherit(AsyncWrap::GetConstructorTemplate(env));
3012
227
  Local<ObjectTemplate> settingt = setting->InstanceTemplate();
3013
227
  settingt->SetInternalFieldCount(1);
3014
227
  env->set_http2settings_constructor_template(settingt);
3015
3016
227
  Local<FunctionTemplate> stream = FunctionTemplate::New(env->isolate());
3017
454
  stream->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "Http2Stream"));
3018
227
  env->SetProtoMethod(stream, "id", Http2Stream::GetID);
3019
227
  env->SetProtoMethod(stream, "destroy", Http2Stream::Destroy);
3020
227
  env->SetProtoMethod(stream, "flushData", Http2Stream::FlushData);
3021
227
  env->SetProtoMethod(stream, "priority", Http2Stream::Priority);
3022
227
  env->SetProtoMethod(stream, "pushPromise", Http2Stream::PushPromise);
3023
227
  env->SetProtoMethod(stream, "info", Http2Stream::Info);
3024
227
  env->SetProtoMethod(stream, "trailers", Http2Stream::Trailers);
3025
227
  env->SetProtoMethod(stream, "respond", Http2Stream::Respond);
3026
227
  env->SetProtoMethod(stream, "rstStream", Http2Stream::RstStream);
3027
227
  env->SetProtoMethod(stream, "refreshState", Http2Stream::RefreshState);
3028
454
  stream->Inherit(AsyncWrap::GetConstructorTemplate(env));
3029
227
  StreamBase::AddMethods(env, stream);
3030
227
  Local<ObjectTemplate> streamt = stream->InstanceTemplate();
3031
227
  streamt->SetInternalFieldCount(StreamBase::kStreamBaseFieldCount);
3032
227
  env->set_http2stream_constructor_template(streamt);
3033
  target->Set(context,
3034
              FIXED_ONE_BYTE_STRING(env->isolate(), "Http2Stream"),
3035
1135
              stream->GetFunction(env->context()).ToLocalChecked()).Check();
3036
3037
  Local<FunctionTemplate> session =
3038
227
      env->NewFunctionTemplate(Http2Session::New);
3039
227
  session->SetClassName(http2SessionClassName);
3040
454
  session->InstanceTemplate()->SetInternalFieldCount(1);
3041
454
  session->Inherit(AsyncWrap::GetConstructorTemplate(env));
3042
227
  env->SetProtoMethod(session, "origin", Http2Session::Origin);
3043
227
  env->SetProtoMethod(session, "altsvc", Http2Session::AltSvc);
3044
227
  env->SetProtoMethod(session, "ping", Http2Session::Ping);
3045
227
  env->SetProtoMethod(session, "consume", Http2Session::Consume);
3046
227
  env->SetProtoMethod(session, "destroy", Http2Session::Destroy);
3047
227
  env->SetProtoMethod(session, "goaway", Http2Session::Goaway);
3048
227
  env->SetProtoMethod(session, "settings", Http2Session::Settings);
3049
227
  env->SetProtoMethod(session, "request", Http2Session::Request);
3050
  env->SetProtoMethod(session, "setNextStreamID",
3051
227
                      Http2Session::SetNextStreamID);
3052
  env->SetProtoMethod(session, "updateChunksSent",
3053
227
                      Http2Session::UpdateChunksSent);
3054
227
  env->SetProtoMethod(session, "refreshState", Http2Session::RefreshState);
3055
  env->SetProtoMethod(
3056
      session, "localSettings",
3057
227
      Http2Session::RefreshSettings<nghttp2_session_get_local_settings>);
3058
  env->SetProtoMethod(
3059
      session, "remoteSettings",
3060
227
      Http2Session::RefreshSettings<nghttp2_session_get_remote_settings>);
3061
  target->Set(context,
3062
              http2SessionClassName,
3063
908
              session->GetFunction(env->context()).ToLocalChecked()).Check();
3064
3065
227
  Local<Object> constants = Object::New(isolate);
3066
227
  Local<Array> name_for_error_code = Array::New(isolate);
3067
3068
#define NODE_NGHTTP2_ERROR_CODES(V)                       \
3069
  V(NGHTTP2_SESSION_SERVER);                              \
3070
  V(NGHTTP2_SESSION_CLIENT);                              \
3071
  V(NGHTTP2_STREAM_STATE_IDLE);                           \
3072
  V(NGHTTP2_STREAM_STATE_OPEN);                           \
3073
  V(NGHTTP2_STREAM_STATE_RESERVED_LOCAL);                 \
3074
  V(NGHTTP2_STREAM_STATE_RESERVED_REMOTE);                \
3075
  V(NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL);              \
3076
  V(NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE);             \
3077
  V(NGHTTP2_STREAM_STATE_CLOSED);                         \
3078
  V(NGHTTP2_NO_ERROR);                                    \
3079
  V(NGHTTP2_PROTOCOL_ERROR);                              \
3080
  V(NGHTTP2_INTERNAL_ERROR);                              \
3081
  V(NGHTTP2_FLOW_CONTROL_ERROR);                          \
3082
  V(NGHTTP2_SETTINGS_TIMEOUT);                            \
3083
  V(NGHTTP2_STREAM_CLOSED);                               \
3084
  V(NGHTTP2_FRAME_SIZE_ERROR);                            \
3085
  V(NGHTTP2_REFUSED_STREAM);                              \
3086
  V(NGHTTP2_CANCEL);                                      \
3087
  V(NGHTTP2_COMPRESSION_ERROR);                           \
3088
  V(NGHTTP2_CONNECT_ERROR);                               \
3089
  V(NGHTTP2_ENHANCE_YOUR_CALM);                           \
3090
  V(NGHTTP2_INADEQUATE_SECURITY);                         \
3091
  V(NGHTTP2_HTTP_1_1_REQUIRED);                           \
3092
3093
#define V(name)                                                         \
3094
  NODE_DEFINE_CONSTANT(constants, name);                                \
3095
  name_for_error_code->Set(env->context(),                              \
3096
                           static_cast<int>(name),                      \
3097
                           FIXED_ONE_BYTE_STRING(isolate,               \
3098
                                                 #name)).Check();
3099
36547
  NODE_NGHTTP2_ERROR_CODES(V)
3100
#undef V
3101
3102
908
  NODE_DEFINE_HIDDEN_CONSTANT(constants, NGHTTP2_HCAT_REQUEST);
3103
908
  NODE_DEFINE_HIDDEN_CONSTANT(constants, NGHTTP2_HCAT_RESPONSE);
3104
908
  NODE_DEFINE_HIDDEN_CONSTANT(constants, NGHTTP2_HCAT_PUSH_RESPONSE);
3105
908
  NODE_DEFINE_HIDDEN_CONSTANT(constants, NGHTTP2_HCAT_HEADERS);
3106
908
  NODE_DEFINE_HIDDEN_CONSTANT(constants, NGHTTP2_NV_FLAG_NONE);
3107
908
  NODE_DEFINE_HIDDEN_CONSTANT(constants, NGHTTP2_NV_FLAG_NO_INDEX);
3108
908
  NODE_DEFINE_HIDDEN_CONSTANT(constants, NGHTTP2_ERR_DEFERRED);
3109
908
  NODE_DEFINE_HIDDEN_CONSTANT(constants, NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE);
3110
908
  NODE_DEFINE_HIDDEN_CONSTANT(constants, NGHTTP2_ERR_INVALID_ARGUMENT);
3111
908
  NODE_DEFINE_HIDDEN_CONSTANT(constants, NGHTTP2_ERR_STREAM_CLOSED);
3112
908
  NODE_DEFINE_CONSTANT(constants, NGHTTP2_ERR_FRAME_SIZE_ERROR);
3113
3114
908
  NODE_DEFINE_HIDDEN_CONSTANT(constants, STREAM_OPTION_EMPTY_PAYLOAD);
3115
908
  NODE_DEFINE_HIDDEN_CONSTANT(constants, STREAM_OPTION_GET_TRAILERS);
3116
3117
908
  NODE_DEFINE_CONSTANT(constants, NGHTTP2_FLAG_NONE);
3118
908
  NODE_DEFINE_CONSTANT(constants, NGHTTP2_FLAG_END_STREAM);
3119
908
  NODE_DEFINE_CONSTANT(constants, NGHTTP2_FLAG_END_HEADERS);
3120
908
  NODE_DEFINE_CONSTANT(constants, NGHTTP2_FLAG_ACK);
3121
908
  NODE_DEFINE_CONSTANT(constants, NGHTTP2_FLAG_PADDED);
3122
908
  NODE_DEFINE_CONSTANT(constants, NGHTTP2_FLAG_PRIORITY);
3123
3124
908
  NODE_DEFINE_CONSTANT(constants, DEFAULT_SETTINGS_HEADER_TABLE_SIZE);
3125
908
  NODE_DEFINE_CONSTANT(constants, DEFAULT_SETTINGS_ENABLE_PUSH);
3126
908
  NODE_DEFINE_CONSTANT(constants, DEFAULT_SETTINGS_INITIAL_WINDOW_SIZE);
3127
908
  NODE_DEFINE_CONSTANT(constants, DEFAULT_SETTINGS_MAX_FRAME_SIZE);
3128
908
  NODE_DEFINE_CONSTANT(constants, MAX_MAX_FRAME_SIZE);
3129
908
  NODE_DEFINE_CONSTANT(constants, MIN_MAX_FRAME_SIZE);
3130
908
  NODE_DEFINE_CONSTANT(constants, MAX_INITIAL_WINDOW_SIZE);
3131
908
  NODE_DEFINE_CONSTANT(constants, NGHTTP2_DEFAULT_WEIGHT);
3132
3133
908
  NODE_DEFINE_CONSTANT(constants, NGHTTP2_SETTINGS_HEADER_TABLE_SIZE);
3134
908
  NODE_DEFINE_CONSTANT(constants, NGHTTP2_SETTINGS_ENABLE_PUSH);
3135
908
  NODE_DEFINE_CONSTANT(constants, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
3136
908
  NODE_DEFINE_CONSTANT(constants, NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE);
3137
908
  NODE_DEFINE_CONSTANT(constants, NGHTTP2_SETTINGS_MAX_FRAME_SIZE);
3138
908
  NODE_DEFINE_CONSTANT(constants, NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE);
3139
908
  NODE_DEFINE_CONSTANT(constants, NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL);
3140
3141
908
  NODE_DEFINE_CONSTANT(constants, PADDING_STRATEGY_NONE);
3142
908
  NODE_DEFINE_CONSTANT(constants, PADDING_STRATEGY_ALIGNED);
3143
908
  NODE_DEFINE_CONSTANT(constants, PADDING_STRATEGY_MAX);
3144
908
  NODE_DEFINE_CONSTANT(constants, PADDING_STRATEGY_CALLBACK);
3145
3146
#define STRING_CONSTANT(NAME, VALUE)                                          \
3147
  NODE_DEFINE_STRING_CONSTANT(constants, "HTTP2_HEADER_" # NAME, VALUE);
3148
103512
HTTP_KNOWN_HEADERS(STRING_CONSTANT)
3149
#undef STRING_CONSTANT
3150
3151
#define STRING_CONSTANT(NAME, VALUE)                                          \
3152
  NODE_DEFINE_STRING_CONSTANT(constants, "HTTP2_METHOD_" # NAME, VALUE);
3153
53118
HTTP_KNOWN_METHODS(STRING_CONSTANT)
3154
#undef STRING_CONSTANT
3155
3156
#define V(name, _) NODE_DEFINE_CONSTANT(constants, HTTP_STATUS_##name);
3157
57204
HTTP_STATUS_CODES(V)
3158
#undef V
3159
3160
227
  env->SetMethod(target, "refreshDefaultSettings", RefreshDefaultSettings);
3161
227
  env->SetMethod(target, "packSettings", PackSettings);
3162
227
  env->SetMethod(target, "setCallbackFunctions", SetCallbackFunctions);
3163
3164
  target->Set(context,
3165
              env->constants_string(),
3166
681
              constants).Check();
3167
  target->Set(context,
3168
              FIXED_ONE_BYTE_STRING(isolate, "nameForErrorCode"),
3169
908
              name_for_error_code).Check();
3170
227
}
3171
}  // namespace http2
3172
}  // namespace node
3173
3174

19311
NODE_MODULE_CONTEXT_AWARE_INTERNAL(http2, node::http2::Initialize)