GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: node_http2.h Lines: 145 157 92.4 %
Date: 2022-06-07 04:15:38 Branches: 33 40 82.5 %

Line Branch Exec Source
1
#ifndef SRC_NODE_HTTP2_H_
2
#define SRC_NODE_HTTP2_H_
3
4
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
5
6
// FIXME(joyeecheung): nghttp2.h needs stdint.h to compile on Windows
7
#include <cstdint>
8
#include "nghttp2/nghttp2.h"
9
10
#include "env.h"
11
#include "aliased_struct.h"
12
#include "node_http2_state.h"
13
#include "node_http_common.h"
14
#include "node_mem.h"
15
#include "node_perf.h"
16
#include "stream_base.h"
17
#include "string_bytes.h"
18
19
#include <algorithm>
20
#include <queue>
21
22
namespace node {
23
namespace http2 {
24
25
// Constants in all caps are exported as user-facing constants
26
// in JavaScript. Constants using the kName pattern are internal
27
// only.
28
29
// We strictly limit the number of outstanding unacknowledged PINGS a user
30
// may send in order to prevent abuse. The current default cap is 10. The
31
// user may set a different limit using a per Http2Session configuration
32
// option.
33
constexpr size_t kDefaultMaxPings = 10;
34
35
// Also strictly limit the number of outstanding SETTINGS frames a user sends
36
constexpr size_t kDefaultMaxSettings = 10;
37
38
// Default maximum total memory cap for Http2Session.
39
constexpr uint64_t kDefaultMaxSessionMemory = 10000000;
40
41
// These are the standard HTTP/2 defaults as specified by the RFC
42
constexpr uint32_t DEFAULT_SETTINGS_HEADER_TABLE_SIZE = 4096;
43
constexpr uint32_t DEFAULT_SETTINGS_ENABLE_PUSH = 1;
44
constexpr uint32_t DEFAULT_SETTINGS_MAX_CONCURRENT_STREAMS = 0xffffffffu;
45
constexpr uint32_t DEFAULT_SETTINGS_INITIAL_WINDOW_SIZE = 65535;
46
constexpr uint32_t DEFAULT_SETTINGS_MAX_FRAME_SIZE = 16384;
47
constexpr uint32_t DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE = 65535;
48
constexpr uint32_t DEFAULT_SETTINGS_ENABLE_CONNECT_PROTOCOL = 0;
49
constexpr uint32_t MAX_MAX_FRAME_SIZE = 16777215;
50
constexpr uint32_t MIN_MAX_FRAME_SIZE = DEFAULT_SETTINGS_MAX_FRAME_SIZE;
51
constexpr uint32_t MAX_INITIAL_WINDOW_SIZE = 2147483647;
52
53
// Stream is not going to have any DATA frames
54
constexpr int STREAM_OPTION_EMPTY_PAYLOAD = 0x1;
55
56
// Stream might have trailing headers
57
constexpr int STREAM_OPTION_GET_TRAILERS = 0x2;
58
59
// Http2Stream internal states
60
constexpr int kStreamStateNone = 0x0;
61
constexpr int kStreamStateShut = 0x1;
62
constexpr int kStreamStateReadStart = 0x2;
63
constexpr int kStreamStateReadPaused = 0x4;
64
constexpr int kStreamStateClosed = 0x8;
65
constexpr int kStreamStateDestroyed = 0x10;
66
constexpr int kStreamStateTrailers = 0x20;
67
68
// Http2Session internal states
69
constexpr int kSessionStateNone = 0x0;
70
constexpr int kSessionStateHasScope = 0x1;
71
constexpr int kSessionStateWriteScheduled = 0x2;
72
constexpr int kSessionStateClosed = 0x4;
73
constexpr int kSessionStateClosing = 0x8;
74
constexpr int kSessionStateSending = 0x10;
75
constexpr int kSessionStateWriteInProgress = 0x20;
76
constexpr int kSessionStateReadingStopped = 0x40;
77
constexpr int kSessionStateReceivePaused = 0x80;
78
79
// The Padding Strategy determines the method by which extra padding is
80
// selected for HEADERS and DATA frames. These are configurable via the
81
// options passed in to a Http2Session object.
82
enum PaddingStrategy {
83
  // No padding strategy. This is the default.
84
  PADDING_STRATEGY_NONE,
85
  // Attempts to ensure that the frame is 8-byte aligned
86
  PADDING_STRATEGY_ALIGNED,
87
  // Padding will ensure all data frames are maxFrameSize
88
  PADDING_STRATEGY_MAX,
89
  // Removed and turned into an alias because it is unreasonably expensive for
90
  // very little benefit.
91
  PADDING_STRATEGY_CALLBACK = PADDING_STRATEGY_ALIGNED
92
};
93
94
enum SessionType {
95
  NGHTTP2_SESSION_SERVER,
96
  NGHTTP2_SESSION_CLIENT
97
};
98
99
template <typename T, void(*fn)(T*)>
100
struct Nghttp2Deleter {
101
25196
  void operator()(T* ptr) const noexcept { fn(ptr); }
102
};
103
104
using Nghttp2OptionPointer =
105
    std::unique_ptr<nghttp2_option,
106
                    Nghttp2Deleter<nghttp2_option, nghttp2_option_del>>;
107
108
using Nghttp2SessionPointer =
109
    std::unique_ptr<nghttp2_session,
110
                    Nghttp2Deleter<nghttp2_session, nghttp2_session_del>>;
111
112
using Nghttp2SessionCallbacksPointer =
113
    std::unique_ptr<nghttp2_session_callbacks,
114
                    Nghttp2Deleter<nghttp2_session_callbacks,
115
                                   nghttp2_session_callbacks_del>>;
116
117
struct Http2HeadersTraits {
118
  typedef nghttp2_nv nv_t;
119
};
120
121
struct Http2RcBufferPointerTraits {
122
  typedef nghttp2_rcbuf rcbuf_t;
123
  typedef nghttp2_vec vector_t;
124
125
333808
  static void inc(rcbuf_t* buf) {
126
333808
    CHECK_NOT_NULL(buf);
127
333808
    nghttp2_rcbuf_incref(buf);
128
333808
  }
129
333808
  static void dec(rcbuf_t* buf) {
130
333808
    CHECK_NOT_NULL(buf);
131
333808
    nghttp2_rcbuf_decref(buf);
132
333808
  }
133
549480
  static vector_t get_vec(rcbuf_t* buf) {
134
549480
    CHECK_NOT_NULL(buf);
135
549480
    return nghttp2_rcbuf_get_buf(buf);
136
  }
137
153716
  static bool is_static(const rcbuf_t* buf) {
138
153716
    CHECK_NOT_NULL(buf);
139
153716
    return nghttp2_rcbuf_is_static(buf);
140
  }
141
};
142
143
using Http2Headers = NgHeaders<Http2HeadersTraits>;
144
using Http2RcBufferPointer = NgRcBufPointer<Http2RcBufferPointerTraits>;
145
146
struct NgHttp2StreamWrite : public MemoryRetainer {
147
  BaseObjectPtr<AsyncWrap> req_wrap;
148
  uv_buf_t buf;
149
150
69566
  inline explicit NgHttp2StreamWrite(uv_buf_t buf_) : buf(buf_) {}
151
28191
  inline NgHttp2StreamWrite(BaseObjectPtr<AsyncWrap> req_wrap, uv_buf_t buf_) :
152
28191
      req_wrap(std::move(req_wrap)), buf(buf_) {}
153
154
  void MemoryInfo(MemoryTracker* tracker) const override;
155
  SET_MEMORY_INFO_NAME(NgHttp2StreamWrite)
156
  SET_SELF_SIZE(NgHttp2StreamWrite)
157
};
158
159
typedef uint32_t(*get_setting)(nghttp2_session* session,
160
                               nghttp2_settings_id id);
161
162
class Http2Ping;
163
class Http2Session;
164
class Http2Settings;
165
class Http2Stream;
166
class Origins;
167
168
// This scope should be present when any call into nghttp2 that may schedule
169
// data to be written to the underlying transport is made, and schedules
170
// such a write automatically once the scope is exited.
171
class Http2Scope {
172
 public:
173
  explicit Http2Scope(Http2Stream* stream);
174
  explicit Http2Scope(Http2Session* session);
175
  ~Http2Scope();
176
177
 private:
178
  BaseObjectPtr<Http2Session> session_;
179
};
180
181
// The Http2Options class is used to parse the options object passed in to
182
// a Http2Session object and convert those into an appropriate nghttp2_option
183
// struct. This is the primary mechanism by which the Http2Session object is
184
// configured.
185
class Http2Options {
186
 public:
187
  Http2Options(Http2State* http2_state,
188
               SessionType type);
189
190
1108
  ~Http2Options() = default;
191
192
1108
  nghttp2_option* operator*() const {
193
1108
    return options_.get();
194
  }
195
196
1
  void set_max_header_pairs(uint32_t max) {
197
1
    max_header_pairs_ = max;
198
1
  }
199
200
1108
  uint32_t max_header_pairs() const {
201
1108
    return max_header_pairs_;
202
  }
203
204
2
  void set_padding_strategy(PaddingStrategy val) {
205
2
    padding_strategy_ = val;
206
2
  }
207
208
1108
  PaddingStrategy padding_strategy() const {
209
1108
    return padding_strategy_;
210
  }
211
212
2
  void set_max_outstanding_pings(size_t max) {
213
2
    max_outstanding_pings_ = max;
214
2
  }
215
216
1108
  size_t max_outstanding_pings() const {
217
1108
    return max_outstanding_pings_;
218
  }
219
220
2
  void set_max_outstanding_settings(size_t max) {
221
2
    max_outstanding_settings_ = max;
222
2
  }
223
224
1108
  size_t max_outstanding_settings() const {
225
1108
    return max_outstanding_settings_;
226
  }
227
228
8
  void set_max_session_memory(uint64_t max) {
229
8
    max_session_memory_ = max;
230
8
  }
231
232
1108
  uint64_t max_session_memory() const {
233
1108
    return max_session_memory_;
234
  }
235
236
 private:
237
  Nghttp2OptionPointer options_;
238
  uint64_t max_session_memory_ = kDefaultMaxSessionMemory;
239
  uint32_t max_header_pairs_ = DEFAULT_MAX_HEADER_LIST_PAIRS;
240
  PaddingStrategy padding_strategy_ = PADDING_STRATEGY_NONE;
241
  size_t max_outstanding_pings_ = kDefaultMaxPings;
242
  size_t max_outstanding_settings_ = kDefaultMaxSettings;
243
};
244
245
struct Http2Priority : public nghttp2_priority_spec {
246
  Http2Priority(Environment* env,
247
                v8::Local<v8::Value> parent,
248
                v8::Local<v8::Value> weight,
249
                v8::Local<v8::Value> exclusive);
250
};
251
252
class Http2StreamListener : public StreamListener {
253
 public:
254
  uv_buf_t OnStreamAlloc(size_t suggested_size) override;
255
  void OnStreamRead(ssize_t nread, const uv_buf_t& buf) override;
256
};
257
258
struct Http2HeaderTraits {
259
  typedef Http2RcBufferPointer rcbufferpointer_t;
260
  typedef Http2Session allocator_t;
261
262
  // HTTP/2 does not support identifying header names by token id.
263
  // HTTP/3 will, however, so we prepare for that now.
264
76858
  static const char* ToHttpHeaderName(int32_t token) { return nullptr; }
265
};
266
267
using Http2Header = NgHeader<Http2HeaderTraits>;
268
269
class Http2Stream : public AsyncWrap,
270
                    public StreamBase {
271
 public:
272
  static Http2Stream* New(
273
      Http2Session* session,
274
      int32_t id,
275
      nghttp2_headers_category category = NGHTTP2_HCAT_HEADERS,
276
      int options = 0);
277
  ~Http2Stream() override;
278
279
  nghttp2_stream* operator*() const;
280
281
  nghttp2_stream* stream() const;
282
283
165788
  Http2Session* session() { return session_.get(); }
284
17
  const Http2Session* session() const { return session_.get(); }
285
286
  // Required for StreamBase
287
  int ReadStart() override;
288
289
  // Required for StreamBase
290
  int ReadStop() override;
291
292
  // Required for StreamBase
293
  ShutdownWrap* CreateShutdownWrap(v8::Local<v8::Object> object) override;
294
  int DoShutdown(ShutdownWrap* req_wrap) override;
295
296
15
  bool HasWantsWrite() const override { return true; }
297
298
  // Initiate a response on this stream.
299
  int SubmitResponse(const Http2Headers& headers, int options);
300
301
  // Submit informational headers for this stream
302
  int SubmitInfo(const Http2Headers& headers);
303
304
  // Submit trailing headers for this stream
305
  int SubmitTrailers(const Http2Headers& headers);
306
  void OnTrailers();
307
308
  // Submit a PRIORITY frame for this stream
309
  int SubmitPriority(const Http2Priority& priority, bool silent = false);
310
311
  // Submits an RST_STREAM frame using the given code
312
  void SubmitRstStream(const uint32_t code);
313
314
  void FlushRstStream();
315
316
  // Submits a PUSH_PROMISE frame with this stream as the parent.
317
  Http2Stream* SubmitPushPromise(
318
      const Http2Headers& headers,
319
      int32_t* ret,
320
      int options = 0);
321
322
323
  void Close(int32_t code);
324
325
  // Destroy this stream instance and free all held memory.
326
  void Destroy();
327
328
396631
  bool is_destroyed() const {
329
396631
    return flags_ & kStreamStateDestroyed;
330
  }
331
332
49572
  bool is_writable() const {
333
49572
    return !(flags_ & kStreamStateShut);
334
  }
335
336
17886
  bool is_paused() const {
337
17886
    return flags_ & kStreamStateReadPaused;
338
  }
339
340
  bool is_closed() const {
341
    return flags_ & kStreamStateClosed;
342
  }
343
344
12773
  bool has_trailers() const {
345
12773
    return flags_ & kStreamStateTrailers;
346
  }
347
348
202
  void set_has_trailers(bool on = true) {
349
202
    if (on)
350
162
      flags_ |= kStreamStateTrailers;
351
    else
352
40
      flags_ &= ~kStreamStateTrailers;
353
202
  }
354
355
23619
  void set_closed() {
356
23619
    flags_ |= kStreamStateClosed;
357
23619
  }
358
359
23695
  void set_destroyed() {
360
23695
    flags_ |= kStreamStateDestroyed;
361
23695
  }
362
363
27934
  void set_not_writable() {
364
27934
    flags_ |= kStreamStateShut;
365
27934
  }
366
367
23482
  void set_reading(bool on = true) {
368
23482
    if (on) {
369
23482
      flags_ |= kStreamStateReadStart;
370
23482
      set_paused(false);
371
    } else {}
372
23482
  }
373
374
28482
  void set_paused(bool on = true) {
375
28482
    if (on)
376
5000
      flags_ |= kStreamStateReadPaused;
377
    else
378
23482
      flags_ &= ~kStreamStateReadPaused;
379
28482
  }
380
381
  // Returns true if this stream is in the reading state, which occurs when
382
  // the kStreamStateReadStart flag has been set and the
383
  // kStreamStateReadPaused flag is *not* set.
384
18922
  bool is_reading() const {
385

18922
    return flags_ & kStreamStateReadStart && !is_paused();
386
  }
387
388
  // Returns the RST_STREAM code used to close this stream
389
  int32_t code() const { return code_; }
390
391
  // Returns the stream identifier for this stream
392
82696
  int32_t id() const { return id_; }
393
394
  void IncrementAvailableOutboundLength(size_t amount);
395
  void DecrementAvailableOutboundLength(size_t amount);
396
397
  bool AddHeader(nghttp2_rcbuf* name, nghttp2_rcbuf* value, uint8_t flags);
398
399
  template <typename Fn>
400
24720
  void TransferHeaders(Fn&& fn) {
401
24720
    size_t i = 0;
402
101578
    for (const auto& header : current_headers_ )
403
76858
      fn(header, i++);
404
24720
    ClearHeaders();
405
24720
  }
406
407
24721
  void ClearHeaders() {
408
24721
    current_headers_.clear();
409
24721
  }
410
411
74269
  size_t headers_count() const {
412
74269
    return current_headers_.size();
413
  }
414
415
24720
  nghttp2_headers_category headers_category() const {
416
24720
    return current_headers_category_;
417
  }
418
419
  void StartHeaders(nghttp2_headers_category category);
420
421
  // Required for StreamBase
422
58234
  bool IsAlive() override {
423
58234
    return true;
424
  }
425
426
  // Required for StreamBase
427
  bool IsClosing() override {
428
    return false;
429
  }
430
431
147894
  AsyncWrap* GetAsyncWrap() override { return this; }
432
433
  int DoWrite(WriteWrap* w, uv_buf_t* bufs, size_t count,
434
              uv_stream_t* send_handle) override;
435
436
  void MemoryInfo(MemoryTracker* tracker) const override;
437
  SET_MEMORY_INFO_NAME(Http2Stream)
438
  SET_SELF_SIZE(Http2Stream)
439
440
  std::string diagnostic_name() const override;
441
442
  // JavaScript API
443
  static void GetID(const v8::FunctionCallbackInfo<v8::Value>& args);
444
  static void Destroy(const v8::FunctionCallbackInfo<v8::Value>& args);
445
  static void Priority(const v8::FunctionCallbackInfo<v8::Value>& args);
446
  static void PushPromise(const v8::FunctionCallbackInfo<v8::Value>& args);
447
  static void RefreshState(const v8::FunctionCallbackInfo<v8::Value>& args);
448
  static void Info(const v8::FunctionCallbackInfo<v8::Value>& args);
449
  static void Trailers(const v8::FunctionCallbackInfo<v8::Value>& args);
450
  static void Respond(const v8::FunctionCallbackInfo<v8::Value>& args);
451
  static void RstStream(const v8::FunctionCallbackInfo<v8::Value>& args);
452
453
  class Provider;
454
455
  struct Statistics {
456
    uint64_t start_time;
457
    uint64_t end_time;
458
    uint64_t first_header;     // Time first header was received
459
    uint64_t first_byte;       // Time first DATA frame byte was received
460
    uint64_t first_byte_sent;  // Time first DATA frame byte was sent
461
    uint64_t sent_bytes;
462
    uint64_t received_bytes;
463
    uint64_t id;
464
  };
465
466
  Statistics statistics_ = {};
467
468
 private:
469
  Http2Stream(Http2Session* session,
470
              v8::Local<v8::Object> obj,
471
              int32_t id,
472
              nghttp2_headers_category category,
473
              int options);
474
475
  void EmitStatistics();
476
477
  BaseObjectWeakPtr<Http2Session> session_;     // The Parent HTTP/2 Session
478
  int32_t id_ = 0;                              // The Stream Identifier
479
  int32_t code_ = NGHTTP2_NO_ERROR;             // The RST_STREAM code (if any)
480
  int flags_ = kStreamStateNone;        // Internal state flags
481
482
  uint32_t max_header_pairs_ = DEFAULT_MAX_HEADER_LIST_PAIRS;
483
  uint32_t max_header_length_ = DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE;
484
485
  // The Current Headers block... As headers are received for this stream,
486
  // they are temporarily stored here until the OnFrameReceived is called
487
  // signalling the end of the HEADERS frame
488
  nghttp2_headers_category current_headers_category_ = NGHTTP2_HCAT_HEADERS;
489
  uint32_t current_headers_length_ = 0;  // total number of octets
490
  std::vector<Http2Header> current_headers_;
491
492
  // This keeps track of the amount of data read from the socket while the
493
  // socket was in paused mode. When `ReadStart()` is called (and not before
494
  // then), we tell nghttp2 that we consumed that data to get proper
495
  // backpressure handling.
496
  size_t inbound_consumed_data_while_paused_ = 0;
497
498
  // Outbound Data... This is the data written by the JS layer that is
499
  // waiting to be written out to the socket.
500
  std::queue<NgHttp2StreamWrite> queue_;
501
  size_t available_outbound_length_ = 0;
502
503
  Http2StreamListener stream_listener_;
504
505
  friend class Http2Session;
506
};
507
508
class Http2Stream::Provider {
509
 public:
510
  Provider(Http2Stream* stream, int options);
511
  explicit Provider(int options);
512
  virtual ~Provider();
513
514
25880
  nghttp2_data_provider* operator*() {
515
25880
    return !empty_ ? &provider_ : nullptr;
516
  }
517
518
  class FD;
519
  class Stream;
520
 protected:
521
  nghttp2_data_provider provider_;
522
523
 private:
524
  bool empty_ = false;
525
};
526
527
class Http2Stream::Provider::Stream : public Http2Stream::Provider {
528
 public:
529
  Stream(Http2Stream* stream, int options);
530
  explicit Stream(int options);
531
532
  static ssize_t OnRead(nghttp2_session* session,
533
                        int32_t id,
534
                        uint8_t* buf,
535
                        size_t length,
536
                        uint32_t* flags,
537
                        nghttp2_data_source* source,
538
                        void* user_data);
539
};
540
541
struct SessionJSFields {
542
  uint8_t bitfield;
543
  uint8_t priority_listener_count;
544
  uint8_t frame_error_listener_count;
545
  uint32_t max_invalid_frames = 1000;
546
  uint32_t max_rejected_streams = 100;
547
};
548
549
// Indices for js_fields_, which serves as a way to communicate data with JS
550
// land fast. In particular, we store information about the number/presence
551
// of certain event listeners in JS, and skip calls from C++ into JS if they
552
// are missing.
553
enum SessionUint8Fields {
554
  kBitfield = offsetof(SessionJSFields, bitfield),  // See below
555
  kSessionPriorityListenerCount =
556
      offsetof(SessionJSFields, priority_listener_count),
557
  kSessionFrameErrorListenerCount =
558
      offsetof(SessionJSFields, frame_error_listener_count),
559
  kSessionMaxInvalidFrames = offsetof(SessionJSFields, max_invalid_frames),
560
  kSessionMaxRejectedStreams = offsetof(SessionJSFields, max_rejected_streams),
561
  kSessionUint8FieldCount = sizeof(SessionJSFields)
562
};
563
564
enum SessionBitfieldFlags {
565
  kSessionHasRemoteSettingsListeners,
566
  kSessionRemoteSettingsIsUpToDate,
567
  kSessionHasPingListeners,
568
  kSessionHasAltsvcListeners
569
};
570
571
class Http2Session : public AsyncWrap,
572
                     public StreamListener,
573
                     public mem::NgLibMemoryManager<Http2Session, nghttp2_mem> {
574
 public:
575
  Http2Session(Http2State* http2_state,
576
               v8::Local<v8::Object> wrap,
577
               SessionType type = NGHTTP2_SESSION_SERVER);
578
  ~Http2Session() override;
579
580
32688
  StreamBase* underlying_stream() {
581
32688
    return static_cast<StreamBase*>(stream_);
582
  }
583
584
  void Close(uint32_t code = NGHTTP2_NO_ERROR,
585
             bool socket_closed = false);
586
587
  void Consume(v8::Local<v8::Object> stream);
588
589
  void Goaway(uint32_t code, int32_t lastStreamID,
590
              const uint8_t* data, size_t len);
591
592
  void AltSvc(int32_t id,
593
              uint8_t* origin,
594
              size_t origin_len,
595
              uint8_t* value,
596
              size_t value_len);
597
598
  void Origin(const Origins& origins);
599
600
  uint8_t SendPendingData();
601
602
  // Submits a new request. If the request is a success, assigned
603
  // will be a pointer to the Http2Stream instance assigned.
604
  // This only works if the session is a client session.
605
  Http2Stream* SubmitRequest(
606
      const Http2Priority& priority,
607
      const Http2Headers& headers,
608
      int32_t* ret,
609
      int options = 0);
610
611
  SessionType type() const { return session_type_; }
612
613
103317
  nghttp2_session* session() const { return session_.get(); }
614
615
  nghttp2_session* operator*() { return session_.get(); }
616
617
26949
  uint32_t max_header_pairs() const { return max_header_pairs_; }
618
619
  const char* TypeName() const;
620
621
73636
  bool is_destroyed() {
622

73636
    return (flags_ & kSessionStateClosed) || session_ == nullptr;
623
  }
624
625
667
  void set_destroyed() {
626
667
    flags_ |= kSessionStateClosed;
627
667
  }
628
629
#define IS_FLAG(name, flag)                                                    \
630
  bool is_##name() const { return flags_ & flag; }                             \
631
  void set_##name(bool on = true) {                                            \
632
    if (on)                                                                    \
633
      flags_ |= flag;                                                          \
634
    else                                                                       \
635
      flags_ &= ~flag;                                                         \
636
  }
637
638
379664
  IS_FLAG(in_scope, kSessionStateHasScope)
639
162914
  IS_FLAG(write_scheduled, kSessionStateWriteScheduled)
640
1334
  IS_FLAG(closing, kSessionStateClosing)
641
141106
  IS_FLAG(sending, kSessionStateSending)
642
179943
  IS_FLAG(write_in_progress, kSessionStateWriteInProgress)
643
75053
  IS_FLAG(reading_stopped, kSessionStateReadingStopped)
644
68277
  IS_FLAG(receive_paused, kSessionStateReceivePaused)
645
646
#undef IS_FLAG
647
648
  // Schedule a write if nghttp2 indicates it wants to write to the socket.
649
  void MaybeScheduleWrite();
650
651
  // Stop reading if nghttp2 doesn't want to anymore.
652
  void MaybeStopReading();
653
654
  // Returns pointer to the stream, or nullptr if stream does not exist
655
  BaseObjectPtr<Http2Stream> FindStream(int32_t id);
656
657
  bool CanAddStream();
658
659
  // Adds a stream instance to this session
660
  void AddStream(Http2Stream* stream);
661
662
  // Removes a stream instance from this session
663
  BaseObjectPtr<Http2Stream> RemoveStream(int32_t id);
664
665
  // Indicates whether there currently exist outgoing buffers for this stream.
666
  bool HasWritesOnSocketForStream(Http2Stream* stream);
667
668
  // Write data from stream_buf_ to the session.
669
  // This will call the error callback if an error occurs.
670
  void ConsumeHTTP2Data();
671
672
  void MemoryInfo(MemoryTracker* tracker) const override;
673
  SET_MEMORY_INFO_NAME(Http2Session)
674
  SET_SELF_SIZE(Http2Session)
675
676
  std::string diagnostic_name() const override;
677
678
  // Schedule an RstStream for after the current write finishes.
679
11
  void AddPendingRstStream(int32_t stream_id) {
680
11
    pending_rst_streams_.emplace_back(stream_id);
681
11
  }
682
683
23695
  bool has_pending_rststream(int32_t stream_id) {
684
23695
    return pending_rst_streams_.end() !=
685
23695
        std::find(pending_rst_streams_.begin(),
686
            pending_rst_streams_.end(),
687
47390
            stream_id);
688
  }
689
690
  // Handle reads/writes from the underlying network transport.
691
  uv_buf_t OnStreamAlloc(size_t suggested_size) override;
692
  void OnStreamRead(ssize_t nread, const uv_buf_t& buf) override;
693
  void OnStreamAfterWrite(WriteWrap* w, int status) override;
694
695
  // Implementation for mem::NgLibMemoryManager
696
  void CheckAllocatedSize(size_t previous_size) const;
697
  void IncreaseAllocatedSize(size_t size);
698
  void DecreaseAllocatedSize(size_t size);
699
700
  // The JavaScript API
701
  static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
702
  static void Consume(const v8::FunctionCallbackInfo<v8::Value>& args);
703
  static void Receive(const v8::FunctionCallbackInfo<v8::Value>& args);
704
  static void Destroy(const v8::FunctionCallbackInfo<v8::Value>& args);
705
  static void Settings(const v8::FunctionCallbackInfo<v8::Value>& args);
706
  static void Request(const v8::FunctionCallbackInfo<v8::Value>& args);
707
  static void SetNextStreamID(const v8::FunctionCallbackInfo<v8::Value>& args);
708
  static void SetLocalWindowSize(
709
      const v8::FunctionCallbackInfo<v8::Value>& args);
710
  static void Goaway(const v8::FunctionCallbackInfo<v8::Value>& args);
711
  static void UpdateChunksSent(const v8::FunctionCallbackInfo<v8::Value>& args);
712
  static void RefreshState(const v8::FunctionCallbackInfo<v8::Value>& args);
713
  static void Ping(const v8::FunctionCallbackInfo<v8::Value>& args);
714
  static void AltSvc(const v8::FunctionCallbackInfo<v8::Value>& args);
715
  static void Origin(const v8::FunctionCallbackInfo<v8::Value>& args);
716
717
  template <get_setting fn>
718
  static void RefreshSettings(const v8::FunctionCallbackInfo<v8::Value>& args);
719
720
  uv_loop_t* event_loop() const {
721
    return env()->event_loop();
722
  }
723
724
2141
  Http2State* http2_state() const { return http2_state_.get(); }
725
726
  BaseObjectPtr<Http2Ping> PopPing();
727
  bool AddPing(const uint8_t* data, v8::Local<v8::Function> callback);
728
729
  BaseObjectPtr<Http2Settings> PopSettings();
730
  bool AddSettings(v8::Local<v8::Function> callback);
731
732
166694
  void IncrementCurrentSessionMemory(uint64_t amount) {
733
166694
    current_session_memory_ += amount;
734
166694
  }
735
736
108482
  void DecrementCurrentSessionMemory(uint64_t amount) {
737
    DCHECK_LE(amount, current_session_memory_);
738
108482
    current_session_memory_ -= amount;
739
108482
  }
740
741
  // Tell our custom memory allocator that this rcbuf is independent of
742
  // this session now, and may outlive it.
743
  void StopTrackingRcbuf(nghttp2_rcbuf* buf);
744
745
  // Returns the current session memory including memory allocated by nghttp2,
746
  // the current outbound storage queue, and pending writes.
747
90136
  uint64_t current_session_memory() const {
748
90136
    uint64_t total = current_session_memory_ + sizeof(Http2Session);
749
90136
    total += current_nghttp2_memory_;
750
90136
    total += outgoing_storage_.size();
751
90136
    return total;
752
  }
753
754
  // Return true if current_session_memory + amount is less than the max
755
90136
  bool has_available_session_memory(uint64_t amount) const {
756
90136
    return current_session_memory() + amount <= max_session_memory_;
757
  }
758
759
  struct Statistics {
760
    uint64_t start_time;
761
    uint64_t end_time;
762
    uint64_t ping_rtt;
763
    uint64_t data_sent;
764
    uint64_t data_received;
765
    uint32_t frame_count;
766
    uint32_t frame_sent;
767
    int32_t stream_count;
768
    size_t max_concurrent_streams;
769
    double stream_average_duration;
770
    SessionType session_type;
771
  };
772
773
  Statistics statistics_ = {};
774
775
 private:
776
  void EmitStatistics();
777
778
  // Frame Padding Strategies
779
  ssize_t OnDWordAlignedPadding(size_t frameLength,
780
                                size_t maxPayloadLen);
781
  ssize_t OnMaxFrameSizePadding(size_t frameLength,
782
                                size_t maxPayloadLen);
783
784
  // Frame Handler
785
  int HandleDataFrame(const nghttp2_frame* frame);
786
  void HandleGoawayFrame(const nghttp2_frame* frame);
787
  void HandleHeadersFrame(const nghttp2_frame* frame);
788
  void HandlePriorityFrame(const nghttp2_frame* frame);
789
  void HandleSettingsFrame(const nghttp2_frame* frame);
790
  void HandlePingFrame(const nghttp2_frame* frame);
791
  void HandleAltSvcFrame(const nghttp2_frame* frame);
792
  void HandleOriginFrame(const nghttp2_frame* frame);
793
794
  void DecrefHeaders(const nghttp2_frame* frame);
795
796
  // nghttp2 callbacks
797
  static int OnBeginHeadersCallback(
798
      nghttp2_session* session,
799
      const nghttp2_frame* frame,
800
      void* user_data);
801
  static int OnHeaderCallback(
802
      nghttp2_session* session,
803
      const nghttp2_frame* frame,
804
      nghttp2_rcbuf* name,
805
      nghttp2_rcbuf* value,
806
      uint8_t flags,
807
      void* user_data);
808
  static int OnFrameReceive(
809
      nghttp2_session* session,
810
      const nghttp2_frame* frame,
811
      void* user_data);
812
  static int OnFrameNotSent(
813
      nghttp2_session* session,
814
      const nghttp2_frame* frame,
815
      int error_code,
816
      void* user_data);
817
  static int OnFrameSent(
818
      nghttp2_session* session,
819
      const nghttp2_frame* frame,
820
      void* user_data);
821
  static int OnStreamClose(
822
      nghttp2_session* session,
823
      int32_t id,
824
      uint32_t code,
825
      void* user_data);
826
  static int OnInvalidHeader(
827
      nghttp2_session* session,
828
      const nghttp2_frame* frame,
829
      nghttp2_rcbuf* name,
830
      nghttp2_rcbuf* value,
831
      uint8_t flags,
832
      void* user_data);
833
  static int OnDataChunkReceived(
834
      nghttp2_session* session,
835
      uint8_t flags,
836
      int32_t id,
837
      const uint8_t* data,
838
      size_t len,
839
      void* user_data);
840
  static ssize_t OnSelectPadding(
841
      nghttp2_session* session,
842
      const nghttp2_frame* frame,
843
      size_t maxPayloadLen,
844
      void* user_data);
845
  static int OnNghttpError(
846
      nghttp2_session* session,
847
      const char* message,
848
      size_t len,
849
      void* user_data);
850
  static int OnSendData(
851
      nghttp2_session* session,
852
      nghttp2_frame* frame,
853
      const uint8_t* framehd,
854
      size_t length,
855
      nghttp2_data_source* source,
856
      void* user_data);
857
  static int OnInvalidFrame(
858
      nghttp2_session* session,
859
      const nghttp2_frame* frame,
860
      int lib_error_code,
861
      void* user_data);
862
863
  struct Callbacks {
864
    explicit Callbacks(bool kHasGetPaddingCallback);
865
866
    Nghttp2SessionCallbacksPointer callbacks;
867
  };
868
869
  /* Use callback_struct_saved[kHasGetPaddingCallback ? 1 : 0] */
870
  static const Callbacks callback_struct_saved[2];
871
872
  // The underlying nghttp2_session handle
873
  Nghttp2SessionPointer session_;
874
875
  // JS-accessible numeric fields, as indexed by SessionUint8Fields.
876
  AliasedStruct<SessionJSFields> js_fields_;
877
878
  // The session type: client or server
879
  SessionType session_type_;
880
881
  // The maximum number of header pairs permitted for streams on this session
882
  uint32_t max_header_pairs_ = DEFAULT_MAX_HEADER_LIST_PAIRS;
883
884
  // The maximum amount of memory allocated for this session
885
  uint64_t max_session_memory_ = kDefaultMaxSessionMemory;
886
  uint64_t current_session_memory_ = 0;
887
  // The amount of memory allocated by nghttp2 internals
888
  uint64_t current_nghttp2_memory_ = 0;
889
890
  // The collection of active Http2Streams associated with this session
891
  std::unordered_map<int32_t, BaseObjectPtr<Http2Stream>> streams_;
892
893
  int flags_ = kSessionStateNone;
894
895
  // The StreamBase instance being used for i/o
896
  PaddingStrategy padding_strategy_ = PADDING_STRATEGY_NONE;
897
898
  // use this to allow timeout tracking during long-lasting writes
899
  uint32_t chunks_sent_since_last_write_ = 0;
900
901
  uv_buf_t stream_buf_ = uv_buf_init(nullptr, 0);
902
  // When processing input data, either stream_buf_ab_ or stream_buf_allocation_
903
  // will be set. stream_buf_ab_ is lazily created from stream_buf_allocation_.
904
  v8::Global<v8::ArrayBuffer> stream_buf_ab_;
905
  std::unique_ptr<v8::BackingStore> stream_buf_allocation_;
906
  size_t stream_buf_offset_ = 0;
907
  // Custom error code for errors that originated inside one of the callbacks
908
  // called by nghttp2_session_mem_recv.
909
  const char* custom_recv_error_code_ = nullptr;
910
911
  size_t max_outstanding_pings_ = kDefaultMaxPings;
912
  std::queue<BaseObjectPtr<Http2Ping>> outstanding_pings_;
913
914
  size_t max_outstanding_settings_ = kDefaultMaxSettings;
915
  std::queue<BaseObjectPtr<Http2Settings>> outstanding_settings_;
916
917
  std::vector<NgHttp2StreamWrite> outgoing_buffers_;
918
  std::vector<uint8_t> outgoing_storage_;
919
  size_t outgoing_length_ = 0;
920
  std::vector<int32_t> pending_rst_streams_;
921
  // Count streams that have been rejected while being opened. Exceeding a fixed
922
  // limit will result in the session being destroyed, as an indication of a
923
  // misbehaving peer. This counter is reset once new streams are being
924
  // accepted again.
925
  uint32_t rejected_stream_count_ = 0;
926
  // Also use the invalid frame count as a measure for rejecting input frames.
927
  uint32_t invalid_frame_count_ = 0;
928
929
  void PushOutgoingBuffer(NgHttp2StreamWrite&& write);
930
931
  BaseObjectPtr<Http2State> http2_state_;
932
933
  void CopyDataIntoOutgoing(const uint8_t* src, size_t src_length);
934
  void ClearOutgoing(int status);
935
936
  friend class Http2Scope;
937
  friend class Http2StreamListener;
938
};
939
940
struct Http2SessionPerformanceEntryTraits {
941
  static constexpr performance::PerformanceEntryType kType =
942
      performance::NODE_PERFORMANCE_ENTRY_TYPE_HTTP2;
943
944
  using Details = Http2Session::Statistics;
945
946
  static v8::MaybeLocal<v8::Object> GetDetails(
947
      Environment* env,
948
      const performance::PerformanceEntry<Http2SessionPerformanceEntryTraits>&
949
          entry);
950
};
951
952
struct Http2StreamPerformanceEntryTraits {
953
  static constexpr performance::PerformanceEntryType kType =
954
      performance::NODE_PERFORMANCE_ENTRY_TYPE_HTTP2;
955
956
  using Details = Http2Stream::Statistics;
957
958
  static v8::MaybeLocal<v8::Object> GetDetails(
959
      Environment* env,
960
      const performance::PerformanceEntry<Http2StreamPerformanceEntryTraits>&
961
          entry);
962
};
963
964
using Http2SessionPerformanceEntry =
965
    performance::PerformanceEntry<Http2SessionPerformanceEntryTraits>;
966
using Http2StreamPerformanceEntry =
967
    performance::PerformanceEntry<Http2StreamPerformanceEntryTraits>;
968
969
class Http2Ping : public AsyncWrap {
970
 public:
971
  explicit Http2Ping(
972
      Http2Session* session,
973
      v8::Local<v8::Object> obj,
974
      v8::Local<v8::Function> callback);
975
976
  void MemoryInfo(MemoryTracker* tracker) const override;
977
  SET_MEMORY_INFO_NAME(Http2Ping)
978
  SET_SELF_SIZE(Http2Ping)
979
980
  void Send(const uint8_t* payload);
981
  void Done(bool ack, const uint8_t* payload = nullptr);
982
  void DetachFromSession();
983
984
  v8::Local<v8::Function> callback() const;
985
986
 private:
987
  BaseObjectWeakPtr<Http2Session> session_;
988
  v8::Global<v8::Function> callback_;
989
  uint64_t startTime_;
990
};
991
992
// The Http2Settings class is used to parse the settings passed in for
993
// an Http2Session, converting those into an array of nghttp2_settings_entry
994
// structs.
995
class Http2Settings : public AsyncWrap {
996
 public:
997
  Http2Settings(Http2Session* session,
998
                v8::Local<v8::Object> obj,
999
                v8::Local<v8::Function> callback,
1000
                uint64_t start_time = uv_hrtime());
1001
1002
  void MemoryInfo(MemoryTracker* tracker) const override;
1003
  SET_MEMORY_INFO_NAME(Http2Settings)
1004
  SET_SELF_SIZE(Http2Settings)
1005
1006
  void Send();
1007
  void Done(bool ack);
1008
1009
  v8::Local<v8::Function> callback() const;
1010
1011
  // Returns a Buffer instance with the serialized SETTINGS payload
1012
  v8::Local<v8::Value> Pack();
1013
1014
  static v8::Local<v8::Value> Pack(Http2State* state);
1015
1016
  // Resets the default values in the settings buffer
1017
  static void RefreshDefaults(Http2State* http2_state);
1018
1019
  // Update the local or remote settings for the given session
1020
  static void Update(Http2Session* session,
1021
                     get_setting fn);
1022
1023
 private:
1024
  static size_t Init(
1025
      Http2State* http2_state,
1026
      nghttp2_settings_entry* entries);
1027
1028
  static v8::Local<v8::Value> Pack(
1029
      Environment* env,
1030
      size_t count,
1031
      const nghttp2_settings_entry* entries);
1032
1033
  BaseObjectWeakPtr<Http2Session> session_;
1034
  v8::Global<v8::Function> callback_;
1035
  uint64_t startTime_;
1036
  size_t count_ = 0;
1037
  nghttp2_settings_entry entries_[IDX_SETTINGS_COUNT];
1038
};
1039
1040
class Origins {
1041
 public:
1042
  Origins(Environment* env,
1043
          v8::Local<v8::String> origin_string,
1044
          size_t origin_count);
1045
5
  ~Origins() = default;
1046
1047
5
  const nghttp2_origin_entry* operator*() const {
1048
5
    return static_cast<const nghttp2_origin_entry*>(bs_->Data());
1049
  }
1050
1051
5
  size_t length() const {
1052
5
    return count_;
1053
  }
1054
1055
 private:
1056
  size_t count_;
1057
  std::unique_ptr<v8::BackingStore> bs_;
1058
};
1059
1060
#define HTTP2_HIDDEN_CONSTANTS(V)                                              \
1061
  V(NGHTTP2_HCAT_REQUEST)                                                      \
1062
  V(NGHTTP2_HCAT_RESPONSE)                                                     \
1063
  V(NGHTTP2_HCAT_PUSH_RESPONSE)                                                \
1064
  V(NGHTTP2_HCAT_HEADERS)                                                      \
1065
  V(NGHTTP2_NV_FLAG_NONE)                                                      \
1066
  V(NGHTTP2_NV_FLAG_NO_INDEX)                                                  \
1067
  V(NGHTTP2_ERR_DEFERRED)                                                      \
1068
  V(NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE)                                       \
1069
  V(NGHTTP2_ERR_INVALID_ARGUMENT)                                              \
1070
  V(NGHTTP2_ERR_STREAM_CLOSED)                                                 \
1071
  V(NGHTTP2_ERR_NOMEM)                                                         \
1072
  V(STREAM_OPTION_EMPTY_PAYLOAD)                                               \
1073
  V(STREAM_OPTION_GET_TRAILERS)
1074
1075
#define HTTP2_ERROR_CODES(V)                                                   \
1076
  V(NGHTTP2_NO_ERROR)                                                          \
1077
  V(NGHTTP2_PROTOCOL_ERROR)                                                    \
1078
  V(NGHTTP2_INTERNAL_ERROR)                                                    \
1079
  V(NGHTTP2_FLOW_CONTROL_ERROR)                                                \
1080
  V(NGHTTP2_SETTINGS_TIMEOUT)                                                  \
1081
  V(NGHTTP2_STREAM_CLOSED)                                                     \
1082
  V(NGHTTP2_FRAME_SIZE_ERROR)                                                  \
1083
  V(NGHTTP2_REFUSED_STREAM)                                                    \
1084
  V(NGHTTP2_CANCEL)                                                            \
1085
  V(NGHTTP2_COMPRESSION_ERROR)                                                 \
1086
  V(NGHTTP2_CONNECT_ERROR)                                                     \
1087
  V(NGHTTP2_ENHANCE_YOUR_CALM)                                                 \
1088
  V(NGHTTP2_INADEQUATE_SECURITY)                                               \
1089
  V(NGHTTP2_HTTP_1_1_REQUIRED)                                                 \
1090
1091
#define HTTP2_CONSTANTS(V)                                                     \
1092
  V(NGHTTP2_ERR_FRAME_SIZE_ERROR)                                              \
1093
  V(NGHTTP2_SESSION_SERVER)                                                    \
1094
  V(NGHTTP2_SESSION_CLIENT)                                                    \
1095
  V(NGHTTP2_STREAM_STATE_IDLE)                                                 \
1096
  V(NGHTTP2_STREAM_STATE_OPEN)                                                 \
1097
  V(NGHTTP2_STREAM_STATE_RESERVED_LOCAL)                                       \
1098
  V(NGHTTP2_STREAM_STATE_RESERVED_REMOTE)                                      \
1099
  V(NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL)                                    \
1100
  V(NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE)                                   \
1101
  V(NGHTTP2_STREAM_STATE_CLOSED)                                               \
1102
  V(NGHTTP2_FLAG_NONE)                                                         \
1103
  V(NGHTTP2_FLAG_END_STREAM)                                                   \
1104
  V(NGHTTP2_FLAG_END_HEADERS)                                                  \
1105
  V(NGHTTP2_FLAG_ACK)                                                          \
1106
  V(NGHTTP2_FLAG_PADDED)                                                       \
1107
  V(NGHTTP2_FLAG_PRIORITY)                                                     \
1108
  V(DEFAULT_SETTINGS_HEADER_TABLE_SIZE)                                        \
1109
  V(DEFAULT_SETTINGS_ENABLE_PUSH)                                              \
1110
  V(DEFAULT_SETTINGS_MAX_CONCURRENT_STREAMS)                                   \
1111
  V(DEFAULT_SETTINGS_INITIAL_WINDOW_SIZE)                                      \
1112
  V(DEFAULT_SETTINGS_MAX_FRAME_SIZE)                                           \
1113
  V(DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE)                                     \
1114
  V(DEFAULT_SETTINGS_ENABLE_CONNECT_PROTOCOL)                                  \
1115
  V(MAX_MAX_FRAME_SIZE)                                                        \
1116
  V(MIN_MAX_FRAME_SIZE)                                                        \
1117
  V(MAX_INITIAL_WINDOW_SIZE)                                                   \
1118
  V(NGHTTP2_SETTINGS_HEADER_TABLE_SIZE)                                        \
1119
  V(NGHTTP2_SETTINGS_ENABLE_PUSH)                                              \
1120
  V(NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS)                                   \
1121
  V(NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE)                                      \
1122
  V(NGHTTP2_SETTINGS_MAX_FRAME_SIZE)                                           \
1123
  V(NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE)                                     \
1124
  V(NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL)                                  \
1125
  V(PADDING_STRATEGY_NONE)                                                     \
1126
  V(PADDING_STRATEGY_ALIGNED)                                                  \
1127
  V(PADDING_STRATEGY_MAX)                                                      \
1128
  V(PADDING_STRATEGY_CALLBACK)                                                 \
1129
  HTTP2_ERROR_CODES(V)
1130
1131
#define HTTP2_SETTINGS(V)                                                      \
1132
  V(HEADER_TABLE_SIZE)                                                         \
1133
  V(ENABLE_PUSH)                                                               \
1134
  V(MAX_CONCURRENT_STREAMS)                                                    \
1135
  V(INITIAL_WINDOW_SIZE)                                                       \
1136
  V(MAX_FRAME_SIZE)                                                            \
1137
  V(MAX_HEADER_LIST_SIZE)                                                      \
1138
  V(ENABLE_CONNECT_PROTOCOL)                                                   \
1139
1140
}  // namespace http2
1141
}  // namespace node
1142
1143
#endif  // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
1144
1145
#endif  // SRC_NODE_HTTP2_H_