/src/node/src/node_http2.h
Line | Count | 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 | | // clang-format off |
7 | | #include "node.h" // nghttp2.h needs ssize_t |
8 | | // clang-format on |
9 | | #include "nghttp2/nghttp2.h" |
10 | | |
11 | | #include "env.h" |
12 | | #include "aliased_struct.h" |
13 | | #include "node_http2_state.h" |
14 | | #include "node_http_common.h" |
15 | | #include "node_mem.h" |
16 | | #include "node_perf.h" |
17 | | #include "stream_base.h" |
18 | | #include "string_bytes.h" |
19 | | |
20 | | #include <algorithm> |
21 | | #include <queue> |
22 | | |
23 | | namespace node { |
24 | | namespace http2 { |
25 | | |
26 | | // Constants in all caps are exported as user-facing constants |
27 | | // in JavaScript. Constants using the kName pattern are internal |
28 | | // only. |
29 | | |
30 | | // We strictly limit the number of outstanding unacknowledged PINGS a user |
31 | | // may send in order to prevent abuse. The current default cap is 10. The |
32 | | // user may set a different limit using a per Http2Session configuration |
33 | | // option. |
34 | | constexpr size_t kDefaultMaxPings = 10; |
35 | | |
36 | | // Also strictly limit the number of outstanding SETTINGS frames a user sends |
37 | | constexpr size_t kDefaultMaxSettings = 10; |
38 | | |
39 | | // Default maximum total memory cap for Http2Session. |
40 | | constexpr uint64_t kDefaultMaxSessionMemory = 10000000; |
41 | | |
42 | | // These are the standard HTTP/2 defaults as specified by the RFC |
43 | | constexpr uint32_t DEFAULT_SETTINGS_HEADER_TABLE_SIZE = 4096; |
44 | | constexpr uint32_t DEFAULT_SETTINGS_ENABLE_PUSH = 1; |
45 | | constexpr uint32_t DEFAULT_SETTINGS_MAX_CONCURRENT_STREAMS = 0xffffffffu; |
46 | | constexpr uint32_t DEFAULT_SETTINGS_INITIAL_WINDOW_SIZE = 65535; |
47 | | constexpr uint32_t DEFAULT_SETTINGS_MAX_FRAME_SIZE = 16384; |
48 | | constexpr uint32_t DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE = 65535; |
49 | | constexpr uint32_t DEFAULT_SETTINGS_ENABLE_CONNECT_PROTOCOL = 0; |
50 | | constexpr uint32_t MAX_MAX_FRAME_SIZE = 16777215; |
51 | | constexpr uint32_t MIN_MAX_FRAME_SIZE = DEFAULT_SETTINGS_MAX_FRAME_SIZE; |
52 | | constexpr uint32_t MAX_INITIAL_WINDOW_SIZE = 2147483647; |
53 | | |
54 | | // Stream is not going to have any DATA frames |
55 | | constexpr int STREAM_OPTION_EMPTY_PAYLOAD = 0x1; |
56 | | |
57 | | // Stream might have trailing headers |
58 | | constexpr int STREAM_OPTION_GET_TRAILERS = 0x2; |
59 | | |
60 | | // Http2Stream internal states |
61 | | constexpr int kStreamStateNone = 0x0; |
62 | | constexpr int kStreamStateShut = 0x1; |
63 | | constexpr int kStreamStateReadStart = 0x2; |
64 | | constexpr int kStreamStateReadPaused = 0x4; |
65 | | constexpr int kStreamStateClosed = 0x8; |
66 | | constexpr int kStreamStateDestroyed = 0x10; |
67 | | constexpr int kStreamStateTrailers = 0x20; |
68 | | |
69 | | // Http2Session internal states |
70 | | constexpr int kSessionStateNone = 0x0; |
71 | | constexpr int kSessionStateHasScope = 0x1; |
72 | | constexpr int kSessionStateWriteScheduled = 0x2; |
73 | | constexpr int kSessionStateClosed = 0x4; |
74 | | constexpr int kSessionStateClosing = 0x8; |
75 | | constexpr int kSessionStateSending = 0x10; |
76 | | constexpr int kSessionStateWriteInProgress = 0x20; |
77 | | constexpr int kSessionStateReadingStopped = 0x40; |
78 | | constexpr int kSessionStateReceivePaused = 0x80; |
79 | | |
80 | | // The Padding Strategy determines the method by which extra padding is |
81 | | // selected for HEADERS and DATA frames. These are configurable via the |
82 | | // options passed in to a Http2Session object. |
83 | | enum PaddingStrategy { |
84 | | // No padding strategy. This is the default. |
85 | | PADDING_STRATEGY_NONE, |
86 | | // Attempts to ensure that the frame is 8-byte aligned |
87 | | PADDING_STRATEGY_ALIGNED, |
88 | | // Padding will ensure all data frames are maxFrameSize |
89 | | PADDING_STRATEGY_MAX, |
90 | | // Removed and turned into an alias because it is unreasonably expensive for |
91 | | // very little benefit. |
92 | | PADDING_STRATEGY_CALLBACK = PADDING_STRATEGY_ALIGNED |
93 | | }; |
94 | | |
95 | | enum SessionType { |
96 | | NGHTTP2_SESSION_SERVER, |
97 | | NGHTTP2_SESSION_CLIENT |
98 | | }; |
99 | | |
100 | | template <typename T, void(*fn)(T*)> |
101 | | struct Nghttp2Deleter { |
102 | 0 | void operator()(T* ptr) const noexcept { fn(ptr); }Unexecuted instantiation: node::http2::Nghttp2Deleter<nghttp2_option, &nghttp2_option_del>::operator()(nghttp2_option*) const Unexecuted instantiation: node::http2::Nghttp2Deleter<nghttp2_session_callbacks, &nghttp2_session_callbacks_del>::operator()(nghttp2_session_callbacks*) const Unexecuted instantiation: node::http2::Nghttp2Deleter<nghttp2_session, &nghttp2_session_del>::operator()(nghttp2_session*) const |
103 | | }; |
104 | | |
105 | | using Nghttp2OptionPointer = |
106 | | std::unique_ptr<nghttp2_option, |
107 | | Nghttp2Deleter<nghttp2_option, nghttp2_option_del>>; |
108 | | |
109 | | using Nghttp2SessionPointer = |
110 | | std::unique_ptr<nghttp2_session, |
111 | | Nghttp2Deleter<nghttp2_session, nghttp2_session_del>>; |
112 | | |
113 | | using Nghttp2SessionCallbacksPointer = |
114 | | std::unique_ptr<nghttp2_session_callbacks, |
115 | | Nghttp2Deleter<nghttp2_session_callbacks, |
116 | | nghttp2_session_callbacks_del>>; |
117 | | |
118 | | struct Http2HeadersTraits { |
119 | | typedef nghttp2_nv nv_t; |
120 | | }; |
121 | | |
122 | | struct Http2RcBufferPointerTraits { |
123 | | typedef nghttp2_rcbuf rcbuf_t; |
124 | | typedef nghttp2_vec vector_t; |
125 | | |
126 | 0 | static void inc(rcbuf_t* buf) { |
127 | 0 | CHECK_NOT_NULL(buf); |
128 | 0 | nghttp2_rcbuf_incref(buf); |
129 | 0 | } |
130 | 0 | static void dec(rcbuf_t* buf) { |
131 | 0 | CHECK_NOT_NULL(buf); |
132 | 0 | nghttp2_rcbuf_decref(buf); |
133 | 0 | } |
134 | 0 | static vector_t get_vec(rcbuf_t* buf) { |
135 | 0 | CHECK_NOT_NULL(buf); |
136 | 0 | return nghttp2_rcbuf_get_buf(buf); |
137 | 0 | } |
138 | 0 | static bool is_static(const rcbuf_t* buf) { |
139 | 0 | CHECK_NOT_NULL(buf); |
140 | 0 | return nghttp2_rcbuf_is_static(buf); |
141 | 0 | } |
142 | | }; |
143 | | |
144 | | using Http2Headers = NgHeaders<Http2HeadersTraits>; |
145 | | using Http2RcBufferPointer = NgRcBufPointer<Http2RcBufferPointerTraits>; |
146 | | |
147 | | struct NgHttp2StreamWrite : public MemoryRetainer { |
148 | | BaseObjectPtr<AsyncWrap> req_wrap; |
149 | | uv_buf_t buf; |
150 | | |
151 | 0 | inline explicit NgHttp2StreamWrite(uv_buf_t buf_) : buf(buf_) {} |
152 | | inline NgHttp2StreamWrite(BaseObjectPtr<AsyncWrap> req_wrap, uv_buf_t buf_) : |
153 | 0 | req_wrap(std::move(req_wrap)), buf(buf_) {} |
154 | | |
155 | | void MemoryInfo(MemoryTracker* tracker) const override; |
156 | | SET_MEMORY_INFO_NAME(NgHttp2StreamWrite) |
157 | | SET_SELF_SIZE(NgHttp2StreamWrite) |
158 | | }; |
159 | | |
160 | | typedef uint32_t(*get_setting)(nghttp2_session* session, |
161 | | nghttp2_settings_id id); |
162 | | |
163 | | class Http2Ping; |
164 | | class Http2Session; |
165 | | class Http2Settings; |
166 | | class Http2Stream; |
167 | | class Origins; |
168 | | |
169 | | // This scope should be present when any call into nghttp2 that may schedule |
170 | | // data to be written to the underlying transport is made, and schedules |
171 | | // such a write automatically once the scope is exited. |
172 | | class Http2Scope { |
173 | | public: |
174 | | explicit Http2Scope(Http2Stream* stream); |
175 | | explicit Http2Scope(Http2Session* session); |
176 | | ~Http2Scope(); |
177 | | |
178 | | private: |
179 | | BaseObjectPtr<Http2Session> session_; |
180 | | }; |
181 | | |
182 | | // The Http2Options class is used to parse the options object passed in to |
183 | | // a Http2Session object and convert those into an appropriate nghttp2_option |
184 | | // struct. This is the primary mechanism by which the Http2Session object is |
185 | | // configured. |
186 | | class Http2Options { |
187 | | public: |
188 | | Http2Options(Http2State* http2_state, |
189 | | SessionType type); |
190 | | |
191 | 0 | ~Http2Options() = default; |
192 | | |
193 | 0 | nghttp2_option* operator*() const { |
194 | 0 | return options_.get(); |
195 | 0 | } |
196 | | |
197 | 0 | void set_max_header_pairs(uint32_t max) { |
198 | 0 | max_header_pairs_ = max; |
199 | 0 | } |
200 | | |
201 | 0 | uint32_t max_header_pairs() const { |
202 | 0 | return max_header_pairs_; |
203 | 0 | } |
204 | | |
205 | 0 | void set_padding_strategy(PaddingStrategy val) { |
206 | 0 | padding_strategy_ = val; |
207 | 0 | } |
208 | | |
209 | 0 | PaddingStrategy padding_strategy() const { |
210 | 0 | return padding_strategy_; |
211 | 0 | } |
212 | | |
213 | 0 | void set_max_outstanding_pings(size_t max) { |
214 | 0 | max_outstanding_pings_ = max; |
215 | 0 | } |
216 | | |
217 | 0 | size_t max_outstanding_pings() const { |
218 | 0 | return max_outstanding_pings_; |
219 | 0 | } |
220 | | |
221 | 0 | void set_max_outstanding_settings(size_t max) { |
222 | 0 | max_outstanding_settings_ = max; |
223 | 0 | } |
224 | | |
225 | 0 | size_t max_outstanding_settings() const { |
226 | 0 | return max_outstanding_settings_; |
227 | 0 | } |
228 | | |
229 | 0 | void set_max_session_memory(uint64_t max) { |
230 | 0 | max_session_memory_ = max; |
231 | 0 | } |
232 | | |
233 | 0 | uint64_t max_session_memory() const { |
234 | 0 | return max_session_memory_; |
235 | 0 | } |
236 | | |
237 | | private: |
238 | | Nghttp2OptionPointer options_; |
239 | | uint64_t max_session_memory_ = kDefaultMaxSessionMemory; |
240 | | uint32_t max_header_pairs_ = DEFAULT_MAX_HEADER_LIST_PAIRS; |
241 | | PaddingStrategy padding_strategy_ = PADDING_STRATEGY_NONE; |
242 | | size_t max_outstanding_pings_ = kDefaultMaxPings; |
243 | | size_t max_outstanding_settings_ = kDefaultMaxSettings; |
244 | | }; |
245 | | |
246 | | struct Http2Priority : public nghttp2_priority_spec { |
247 | | Http2Priority(Environment* env, |
248 | | v8::Local<v8::Value> parent, |
249 | | v8::Local<v8::Value> weight, |
250 | | v8::Local<v8::Value> exclusive); |
251 | | }; |
252 | | |
253 | | class Http2StreamListener : public StreamListener { |
254 | | public: |
255 | | uv_buf_t OnStreamAlloc(size_t suggested_size) override; |
256 | | void OnStreamRead(ssize_t nread, const uv_buf_t& buf) override; |
257 | | }; |
258 | | |
259 | | struct Http2HeaderTraits { |
260 | | typedef Http2RcBufferPointer rcbufferpointer_t; |
261 | | typedef Http2Session allocator_t; |
262 | | |
263 | | // HTTP/2 does not support identifying header names by token id. |
264 | | // HTTP/3 will, however, so we prepare for that now. |
265 | 0 | static const char* ToHttpHeaderName(int32_t token) { return nullptr; } |
266 | | }; |
267 | | |
268 | | using Http2Header = NgHeader<Http2HeaderTraits>; |
269 | | |
270 | | class Http2Stream : public AsyncWrap, |
271 | | public StreamBase { |
272 | | public: |
273 | | static Http2Stream* New( |
274 | | Http2Session* session, |
275 | | int32_t id, |
276 | | nghttp2_headers_category category = NGHTTP2_HCAT_HEADERS, |
277 | | int options = 0); |
278 | | ~Http2Stream() override; |
279 | | |
280 | | nghttp2_stream* operator*() const; |
281 | | |
282 | | nghttp2_stream* stream() const; |
283 | | |
284 | 0 | Http2Session* session() { return session_.get(); } |
285 | 0 | const Http2Session* session() const { return session_.get(); } |
286 | | |
287 | | // Required for StreamBase |
288 | | int ReadStart() override; |
289 | | |
290 | | // Required for StreamBase |
291 | | int ReadStop() override; |
292 | | |
293 | | // Required for StreamBase |
294 | | ShutdownWrap* CreateShutdownWrap(v8::Local<v8::Object> object) override; |
295 | | int DoShutdown(ShutdownWrap* req_wrap) override; |
296 | | |
297 | 0 | bool HasWantsWrite() const override { return true; } |
298 | | |
299 | | // Initiate a response on this stream. |
300 | | int SubmitResponse(const Http2Headers& headers, int options); |
301 | | |
302 | | // Submit informational headers for this stream |
303 | | int SubmitInfo(const Http2Headers& headers); |
304 | | |
305 | | // Submit trailing headers for this stream |
306 | | int SubmitTrailers(const Http2Headers& headers); |
307 | | void OnTrailers(); |
308 | | |
309 | | // Submit a PRIORITY frame for this stream |
310 | | int SubmitPriority(const Http2Priority& priority, bool silent = false); |
311 | | |
312 | | // Submits an RST_STREAM frame using the given code |
313 | | void SubmitRstStream(const uint32_t code); |
314 | | |
315 | | void FlushRstStream(); |
316 | | |
317 | | // Submits a PUSH_PROMISE frame with this stream as the parent. |
318 | | Http2Stream* SubmitPushPromise( |
319 | | const Http2Headers& headers, |
320 | | int32_t* ret, |
321 | | int options = 0); |
322 | | |
323 | | |
324 | | void Close(int32_t code); |
325 | | |
326 | | // Destroy this stream instance and free all held memory. |
327 | | void Destroy(); |
328 | | |
329 | 0 | bool is_destroyed() const { |
330 | 0 | return flags_ & kStreamStateDestroyed; |
331 | 0 | } |
332 | | |
333 | 0 | bool is_writable() const { |
334 | 0 | return !(flags_ & kStreamStateShut); |
335 | 0 | } |
336 | | |
337 | 0 | bool is_paused() const { |
338 | 0 | return flags_ & kStreamStateReadPaused; |
339 | 0 | } |
340 | | |
341 | 0 | bool is_closed() const { |
342 | 0 | return flags_ & kStreamStateClosed; |
343 | 0 | } |
344 | | |
345 | 0 | bool has_trailers() const { |
346 | 0 | return flags_ & kStreamStateTrailers; |
347 | 0 | } |
348 | | |
349 | 0 | void set_has_trailers(bool on = true) { |
350 | 0 | if (on) |
351 | 0 | flags_ |= kStreamStateTrailers; |
352 | 0 | else |
353 | 0 | flags_ &= ~kStreamStateTrailers; |
354 | 0 | } |
355 | | |
356 | 0 | void set_closed() { |
357 | 0 | flags_ |= kStreamStateClosed; |
358 | 0 | } |
359 | | |
360 | 0 | void set_destroyed() { |
361 | 0 | flags_ |= kStreamStateDestroyed; |
362 | 0 | } |
363 | | |
364 | 0 | void set_not_writable() { |
365 | 0 | flags_ |= kStreamStateShut; |
366 | 0 | } |
367 | | |
368 | 0 | void set_reading(bool on = true) { |
369 | 0 | if (on) { |
370 | 0 | flags_ |= kStreamStateReadStart; |
371 | 0 | set_paused(false); |
372 | 0 | } else {} |
373 | 0 | } |
374 | | |
375 | 0 | void set_paused(bool on = true) { |
376 | 0 | if (on) |
377 | 0 | flags_ |= kStreamStateReadPaused; |
378 | 0 | else |
379 | 0 | flags_ &= ~kStreamStateReadPaused; |
380 | 0 | } |
381 | | |
382 | | // Returns true if this stream is in the reading state, which occurs when |
383 | | // the kStreamStateReadStart flag has been set and the |
384 | | // kStreamStateReadPaused flag is *not* set. |
385 | 0 | bool is_reading() const { |
386 | 0 | return flags_ & kStreamStateReadStart && !is_paused(); |
387 | 0 | } |
388 | | |
389 | | // Returns the RST_STREAM code used to close this stream |
390 | 0 | int32_t code() const { return code_; } |
391 | | |
392 | | // Returns the stream identifier for this stream |
393 | 0 | int32_t id() const { return id_; } |
394 | | |
395 | | void IncrementAvailableOutboundLength(size_t amount); |
396 | | void DecrementAvailableOutboundLength(size_t amount); |
397 | | |
398 | | bool AddHeader(nghttp2_rcbuf* name, nghttp2_rcbuf* value, uint8_t flags); |
399 | | |
400 | | template <typename Fn> |
401 | 0 | void TransferHeaders(Fn&& fn) { |
402 | 0 | size_t i = 0; |
403 | 0 | for (const auto& header : current_headers_ ) |
404 | 0 | fn(header, i++); |
405 | 0 | ClearHeaders(); |
406 | 0 | } |
407 | | |
408 | 0 | void ClearHeaders() { |
409 | 0 | current_headers_.clear(); |
410 | 0 | } |
411 | | |
412 | 0 | size_t headers_count() const { |
413 | 0 | return current_headers_.size(); |
414 | 0 | } |
415 | | |
416 | 0 | nghttp2_headers_category headers_category() const { |
417 | 0 | return current_headers_category_; |
418 | 0 | } |
419 | | |
420 | | void StartHeaders(nghttp2_headers_category category); |
421 | | |
422 | | // Required for StreamBase |
423 | 0 | bool IsAlive() override { |
424 | 0 | return true; |
425 | 0 | } |
426 | | |
427 | | // Required for StreamBase |
428 | 0 | bool IsClosing() override { |
429 | 0 | return false; |
430 | 0 | } |
431 | | |
432 | 0 | AsyncWrap* GetAsyncWrap() override { return this; } |
433 | | |
434 | | int DoWrite(WriteWrap* w, uv_buf_t* bufs, size_t count, |
435 | | uv_stream_t* send_handle) override; |
436 | | |
437 | | void MemoryInfo(MemoryTracker* tracker) const override; |
438 | | SET_MEMORY_INFO_NAME(Http2Stream) |
439 | | SET_SELF_SIZE(Http2Stream) |
440 | | |
441 | | std::string diagnostic_name() const override; |
442 | | |
443 | | // JavaScript API |
444 | | static void GetID(const v8::FunctionCallbackInfo<v8::Value>& args); |
445 | | static void Destroy(const v8::FunctionCallbackInfo<v8::Value>& args); |
446 | | static void Priority(const v8::FunctionCallbackInfo<v8::Value>& args); |
447 | | static void PushPromise(const v8::FunctionCallbackInfo<v8::Value>& args); |
448 | | static void RefreshState(const v8::FunctionCallbackInfo<v8::Value>& args); |
449 | | static void Info(const v8::FunctionCallbackInfo<v8::Value>& args); |
450 | | static void Trailers(const v8::FunctionCallbackInfo<v8::Value>& args); |
451 | | static void Respond(const v8::FunctionCallbackInfo<v8::Value>& args); |
452 | | static void RstStream(const v8::FunctionCallbackInfo<v8::Value>& args); |
453 | | |
454 | | class Provider; |
455 | | |
456 | | struct Statistics { |
457 | | uint64_t start_time; |
458 | | uint64_t end_time; |
459 | | uint64_t first_header; // Time first header was received |
460 | | uint64_t first_byte; // Time first DATA frame byte was received |
461 | | uint64_t first_byte_sent; // Time first DATA frame byte was sent |
462 | | uint64_t sent_bytes; |
463 | | uint64_t received_bytes; |
464 | | uint64_t id; |
465 | | }; |
466 | | |
467 | | Statistics statistics_ = {}; |
468 | | |
469 | | private: |
470 | | Http2Stream(Http2Session* session, |
471 | | v8::Local<v8::Object> obj, |
472 | | int32_t id, |
473 | | nghttp2_headers_category category, |
474 | | int options); |
475 | | |
476 | | void EmitStatistics(); |
477 | | |
478 | | BaseObjectWeakPtr<Http2Session> session_; // The Parent HTTP/2 Session |
479 | | int32_t id_ = 0; // The Stream Identifier |
480 | | int32_t code_ = NGHTTP2_NO_ERROR; // The RST_STREAM code (if any) |
481 | | int flags_ = kStreamStateNone; // Internal state flags |
482 | | |
483 | | uint32_t max_header_pairs_ = DEFAULT_MAX_HEADER_LIST_PAIRS; |
484 | | uint32_t max_header_length_ = DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE; |
485 | | |
486 | | // The Current Headers block... As headers are received for this stream, |
487 | | // they are temporarily stored here until the OnFrameReceived is called |
488 | | // signalling the end of the HEADERS frame |
489 | | nghttp2_headers_category current_headers_category_ = NGHTTP2_HCAT_HEADERS; |
490 | | uint32_t current_headers_length_ = 0; // total number of octets |
491 | | std::vector<Http2Header> current_headers_; |
492 | | |
493 | | // This keeps track of the amount of data read from the socket while the |
494 | | // socket was in paused mode. When `ReadStart()` is called (and not before |
495 | | // then), we tell nghttp2 that we consumed that data to get proper |
496 | | // backpressure handling. |
497 | | size_t inbound_consumed_data_while_paused_ = 0; |
498 | | |
499 | | // Outbound Data... This is the data written by the JS layer that is |
500 | | // waiting to be written out to the socket. |
501 | | std::queue<NgHttp2StreamWrite> queue_; |
502 | | size_t available_outbound_length_ = 0; |
503 | | |
504 | | Http2StreamListener stream_listener_; |
505 | | |
506 | | friend class Http2Session; |
507 | | }; |
508 | | |
509 | | class Http2Stream::Provider { |
510 | | public: |
511 | | Provider(Http2Stream* stream, int options); |
512 | | explicit Provider(int options); |
513 | | virtual ~Provider(); |
514 | | |
515 | 0 | nghttp2_data_provider* operator*() { |
516 | 0 | return !empty_ ? &provider_ : nullptr; |
517 | 0 | } |
518 | | |
519 | | class FD; |
520 | | class Stream; |
521 | | protected: |
522 | | nghttp2_data_provider provider_; |
523 | | |
524 | | private: |
525 | | bool empty_ = false; |
526 | | }; |
527 | | |
528 | | class Http2Stream::Provider::Stream : public Http2Stream::Provider { |
529 | | public: |
530 | | Stream(Http2Stream* stream, int options); |
531 | | explicit Stream(int options); |
532 | | |
533 | | static ssize_t OnRead(nghttp2_session* session, |
534 | | int32_t id, |
535 | | uint8_t* buf, |
536 | | size_t length, |
537 | | uint32_t* flags, |
538 | | nghttp2_data_source* source, |
539 | | void* user_data); |
540 | | }; |
541 | | |
542 | | struct SessionJSFields { |
543 | | uint8_t bitfield; |
544 | | uint8_t priority_listener_count; |
545 | | uint8_t frame_error_listener_count; |
546 | | uint32_t max_invalid_frames = 1000; |
547 | | uint32_t max_rejected_streams = 100; |
548 | | }; |
549 | | |
550 | | // Indices for js_fields_, which serves as a way to communicate data with JS |
551 | | // land fast. In particular, we store information about the number/presence |
552 | | // of certain event listeners in JS, and skip calls from C++ into JS if they |
553 | | // are missing. |
554 | | enum SessionUint8Fields { |
555 | | kBitfield = offsetof(SessionJSFields, bitfield), // See below |
556 | | kSessionPriorityListenerCount = |
557 | | offsetof(SessionJSFields, priority_listener_count), |
558 | | kSessionFrameErrorListenerCount = |
559 | | offsetof(SessionJSFields, frame_error_listener_count), |
560 | | kSessionMaxInvalidFrames = offsetof(SessionJSFields, max_invalid_frames), |
561 | | kSessionMaxRejectedStreams = offsetof(SessionJSFields, max_rejected_streams), |
562 | | kSessionUint8FieldCount = sizeof(SessionJSFields) |
563 | | }; |
564 | | |
565 | | enum SessionBitfieldFlags { |
566 | | kSessionHasRemoteSettingsListeners, |
567 | | kSessionRemoteSettingsIsUpToDate, |
568 | | kSessionHasPingListeners, |
569 | | kSessionHasAltsvcListeners |
570 | | }; |
571 | | |
572 | | class Http2Session : public AsyncWrap, |
573 | | public StreamListener, |
574 | | public mem::NgLibMemoryManager<Http2Session, nghttp2_mem> { |
575 | | public: |
576 | | Http2Session(Http2State* http2_state, |
577 | | v8::Local<v8::Object> wrap, |
578 | | SessionType type = NGHTTP2_SESSION_SERVER); |
579 | | ~Http2Session() override; |
580 | | |
581 | 0 | StreamBase* underlying_stream() { |
582 | 0 | return static_cast<StreamBase*>(stream_); |
583 | 0 | } |
584 | | |
585 | | void Close(uint32_t code = NGHTTP2_NO_ERROR, |
586 | | bool socket_closed = false); |
587 | | |
588 | | void Consume(v8::Local<v8::Object> stream); |
589 | | |
590 | | void Goaway(uint32_t code, int32_t lastStreamID, |
591 | | const uint8_t* data, size_t len); |
592 | | |
593 | | void AltSvc(int32_t id, |
594 | | uint8_t* origin, |
595 | | size_t origin_len, |
596 | | uint8_t* value, |
597 | | size_t value_len); |
598 | | |
599 | | void Origin(const Origins& origins); |
600 | | |
601 | | uint8_t SendPendingData(); |
602 | | |
603 | | // Submits a new request. If the request is a success, assigned |
604 | | // will be a pointer to the Http2Stream instance assigned. |
605 | | // This only works if the session is a client session. |
606 | | Http2Stream* SubmitRequest( |
607 | | const Http2Priority& priority, |
608 | | const Http2Headers& headers, |
609 | | int32_t* ret, |
610 | | int options = 0); |
611 | | |
612 | 0 | SessionType type() const { return session_type_; } |
613 | | |
614 | 0 | nghttp2_session* session() const { return session_.get(); } |
615 | | |
616 | 0 | nghttp2_session* operator*() { return session_.get(); } |
617 | | |
618 | 0 | uint32_t max_header_pairs() const { return max_header_pairs_; } |
619 | | |
620 | | const char* TypeName() const; |
621 | | |
622 | 0 | bool is_destroyed() { |
623 | 0 | return (flags_ & kSessionStateClosed) || session_ == nullptr; |
624 | 0 | } |
625 | | |
626 | 0 | void set_destroyed() { |
627 | 0 | flags_ |= kSessionStateClosed; |
628 | 0 | } |
629 | | |
630 | | struct custom_settings_state { |
631 | | size_t number; |
632 | | nghttp2_settings_entry entries[MAX_ADDITIONAL_SETTINGS]; |
633 | | }; |
634 | | |
635 | 0 | custom_settings_state& custom_settings(bool local) { |
636 | 0 | return local ? local_custom_settings_ : remote_custom_settings_; |
637 | 0 | } |
638 | | |
639 | | #define IS_FLAG(name, flag) \ |
640 | 0 | bool is_##name() const { return flags_ & flag; } \Unexecuted instantiation: node::http2::Http2Session::is_in_scope() const Unexecuted instantiation: node::http2::Http2Session::is_write_scheduled() const Unexecuted instantiation: node::http2::Http2Session::is_closing() const Unexecuted instantiation: node::http2::Http2Session::is_write_in_progress() const Unexecuted instantiation: node::http2::Http2Session::is_receive_paused() const Unexecuted instantiation: node::http2::Http2Session::is_reading_stopped() const Unexecuted instantiation: node::http2::Http2Session::is_sending() const |
641 | 0 | void set_##name(bool on = true) { \ |
642 | 0 | if (on) \ |
643 | 0 | flags_ |= flag; \ |
644 | 0 | else \ |
645 | 0 | flags_ &= ~flag; \ |
646 | 0 | } Unexecuted instantiation: node::http2::Http2Session::set_in_scope(bool) Unexecuted instantiation: node::http2::Http2Session::set_closing(bool) Unexecuted instantiation: node::http2::Http2Session::set_reading_stopped(bool) Unexecuted instantiation: node::http2::Http2Session::set_receive_paused(bool) Unexecuted instantiation: node::http2::Http2Session::set_write_in_progress(bool) Unexecuted instantiation: node::http2::Http2Session::set_write_scheduled(bool) Unexecuted instantiation: node::http2::Http2Session::set_sending(bool) |
647 | | |
648 | | IS_FLAG(in_scope, kSessionStateHasScope) |
649 | | IS_FLAG(write_scheduled, kSessionStateWriteScheduled) |
650 | | IS_FLAG(closing, kSessionStateClosing) |
651 | | IS_FLAG(sending, kSessionStateSending) |
652 | | IS_FLAG(write_in_progress, kSessionStateWriteInProgress) |
653 | | IS_FLAG(reading_stopped, kSessionStateReadingStopped) |
654 | | IS_FLAG(receive_paused, kSessionStateReceivePaused) |
655 | | |
656 | | #undef IS_FLAG |
657 | | |
658 | | // Schedule a write if nghttp2 indicates it wants to write to the socket. |
659 | | void MaybeScheduleWrite(); |
660 | | |
661 | | // Stop reading if nghttp2 doesn't want to anymore. |
662 | | void MaybeStopReading(); |
663 | | |
664 | | // Returns pointer to the stream, or nullptr if stream does not exist |
665 | | BaseObjectPtr<Http2Stream> FindStream(int32_t id); |
666 | | |
667 | | bool CanAddStream(); |
668 | | |
669 | | // Adds a stream instance to this session |
670 | | void AddStream(Http2Stream* stream); |
671 | | |
672 | | // Removes a stream instance from this session |
673 | | BaseObjectPtr<Http2Stream> RemoveStream(int32_t id); |
674 | | |
675 | | // Indicates whether there currently exist outgoing buffers for this stream. |
676 | | bool HasWritesOnSocketForStream(Http2Stream* stream); |
677 | | |
678 | | // Write data from stream_buf_ to the session. |
679 | | // This will call the error callback if an error occurs. |
680 | | void ConsumeHTTP2Data(); |
681 | | |
682 | | void MemoryInfo(MemoryTracker* tracker) const override; |
683 | | SET_MEMORY_INFO_NAME(Http2Session) |
684 | | SET_SELF_SIZE(Http2Session) |
685 | | |
686 | | std::string diagnostic_name() const override; |
687 | | |
688 | | // Schedule an RstStream for after the current write finishes. |
689 | 0 | void AddPendingRstStream(int32_t stream_id) { |
690 | 0 | pending_rst_streams_.emplace_back(stream_id); |
691 | 0 | } |
692 | | |
693 | 0 | bool has_pending_rststream(int32_t stream_id) { |
694 | 0 | return pending_rst_streams_.end() != |
695 | 0 | std::ranges::find(pending_rst_streams_, stream_id); |
696 | 0 | } |
697 | | |
698 | | // Handle reads/writes from the underlying network transport. |
699 | | uv_buf_t OnStreamAlloc(size_t suggested_size) override; |
700 | | void OnStreamRead(ssize_t nread, const uv_buf_t& buf) override; |
701 | | void OnStreamAfterWrite(WriteWrap* w, int status) override; |
702 | | |
703 | | // Implementation for mem::NgLibMemoryManager |
704 | | void CheckAllocatedSize(size_t previous_size) const; |
705 | | void IncreaseAllocatedSize(size_t size); |
706 | | void DecreaseAllocatedSize(size_t size); |
707 | | |
708 | | // The JavaScript API |
709 | | static void New(const v8::FunctionCallbackInfo<v8::Value>& args); |
710 | | static void Consume(const v8::FunctionCallbackInfo<v8::Value>& args); |
711 | | static void Receive(const v8::FunctionCallbackInfo<v8::Value>& args); |
712 | | static void Destroy(const v8::FunctionCallbackInfo<v8::Value>& args); |
713 | | static void HasPendingData(const v8::FunctionCallbackInfo<v8::Value>& args); |
714 | | static void Settings(const v8::FunctionCallbackInfo<v8::Value>& args); |
715 | | static void Request(const v8::FunctionCallbackInfo<v8::Value>& args); |
716 | | static void SetNextStreamID(const v8::FunctionCallbackInfo<v8::Value>& args); |
717 | | static void SetLocalWindowSize( |
718 | | const v8::FunctionCallbackInfo<v8::Value>& args); |
719 | | static void Goaway(const v8::FunctionCallbackInfo<v8::Value>& args); |
720 | | static void UpdateChunksSent(const v8::FunctionCallbackInfo<v8::Value>& args); |
721 | | static void RefreshState(const v8::FunctionCallbackInfo<v8::Value>& args); |
722 | | static void Ping(const v8::FunctionCallbackInfo<v8::Value>& args); |
723 | | static void AltSvc(const v8::FunctionCallbackInfo<v8::Value>& args); |
724 | | static void Origin(const v8::FunctionCallbackInfo<v8::Value>& args); |
725 | | static void SetGracefulClose(const v8::FunctionCallbackInfo<v8::Value>& args); |
726 | | |
727 | | template <get_setting fn, bool local> |
728 | | static void RefreshSettings(const v8::FunctionCallbackInfo<v8::Value>& args); |
729 | | |
730 | 0 | uv_loop_t* event_loop() const { |
731 | 0 | return env()->event_loop(); |
732 | 0 | } |
733 | | |
734 | 0 | Http2State* http2_state() const { return http2_state_.get(); } |
735 | | |
736 | | BaseObjectPtr<Http2Ping> PopPing(); |
737 | | bool AddPing(const uint8_t* data, v8::Local<v8::Function> callback); |
738 | | bool HasPendingData() const; |
739 | | |
740 | | BaseObjectPtr<Http2Settings> PopSettings(); |
741 | | bool AddSettings(v8::Local<v8::Function> callback); |
742 | | |
743 | 0 | void IncrementCurrentSessionMemory(uint64_t amount) { |
744 | 0 | current_session_memory_ += amount; |
745 | 0 | } |
746 | | |
747 | 0 | void DecrementCurrentSessionMemory(uint64_t amount) { |
748 | 0 | DCHECK_LE(amount, current_session_memory_); |
749 | 0 | current_session_memory_ -= amount; |
750 | 0 | } |
751 | | |
752 | | void UpdateLocalCustomSettings(size_t count_, |
753 | | nghttp2_settings_entry* entries_); |
754 | | |
755 | | // Tell our custom memory allocator that this rcbuf is independent of |
756 | | // this session now, and may outlive it. |
757 | | void StopTrackingRcbuf(nghttp2_rcbuf* buf); |
758 | | |
759 | | // Returns the current session memory including memory allocated by nghttp2, |
760 | | // the current outbound storage queue, and pending writes. |
761 | 0 | uint64_t current_session_memory() const { |
762 | 0 | uint64_t total = current_session_memory_ + sizeof(Http2Session); |
763 | 0 | total += current_nghttp2_memory_; |
764 | 0 | total += outgoing_storage_.size(); |
765 | 0 | return total; |
766 | 0 | } |
767 | | |
768 | | // Return true if current_session_memory + amount is less than the max |
769 | 0 | bool has_available_session_memory(uint64_t amount) const { |
770 | 0 | return current_session_memory() + amount <= max_session_memory_; |
771 | 0 | } |
772 | | |
773 | | struct Statistics { |
774 | | uint64_t start_time; |
775 | | uint64_t end_time; |
776 | | uint64_t ping_rtt; |
777 | | uint64_t data_sent; |
778 | | uint64_t data_received; |
779 | | uint32_t frame_count; |
780 | | uint32_t frame_sent; |
781 | | int32_t stream_count; |
782 | | size_t max_concurrent_streams; |
783 | | double stream_average_duration; |
784 | | SessionType session_type; |
785 | | }; |
786 | | |
787 | | Statistics statistics_ = {}; |
788 | | |
789 | 0 | bool IsGracefulCloseInitiated() const { |
790 | 0 | return graceful_close_initiated_; |
791 | 0 | } |
792 | 0 | void SetGracefulCloseInitiated(bool value) { |
793 | 0 | graceful_close_initiated_ = value; |
794 | 0 | } |
795 | | |
796 | | private: |
797 | | void EmitStatistics(); |
798 | | |
799 | | void FetchAllowedRemoteCustomSettings(); |
800 | | |
801 | | // Frame Padding Strategies |
802 | | ssize_t OnDWordAlignedPadding(size_t frameLength, |
803 | | size_t maxPayloadLen); |
804 | | ssize_t OnMaxFrameSizePadding(size_t frameLength, |
805 | | size_t maxPayloadLen); |
806 | | |
807 | | // Frame Handler |
808 | | int HandleDataFrame(const nghttp2_frame* frame); |
809 | | void HandleGoawayFrame(const nghttp2_frame* frame); |
810 | | void HandleHeadersFrame(const nghttp2_frame* frame); |
811 | | void HandlePriorityFrame(const nghttp2_frame* frame); |
812 | | void HandleSettingsFrame(const nghttp2_frame* frame); |
813 | | void HandlePingFrame(const nghttp2_frame* frame); |
814 | | void HandleAltSvcFrame(const nghttp2_frame* frame); |
815 | | void HandleOriginFrame(const nghttp2_frame* frame); |
816 | | |
817 | | void DecrefHeaders(const nghttp2_frame* frame); |
818 | | |
819 | | // nghttp2 callbacks |
820 | | static int OnBeginHeadersCallback( |
821 | | nghttp2_session* session, |
822 | | const nghttp2_frame* frame, |
823 | | void* user_data); |
824 | | static int OnHeaderCallback( |
825 | | nghttp2_session* session, |
826 | | const nghttp2_frame* frame, |
827 | | nghttp2_rcbuf* name, |
828 | | nghttp2_rcbuf* value, |
829 | | uint8_t flags, |
830 | | void* user_data); |
831 | | static int OnFrameReceive( |
832 | | nghttp2_session* session, |
833 | | const nghttp2_frame* frame, |
834 | | void* user_data); |
835 | | static int OnFrameNotSent( |
836 | | nghttp2_session* session, |
837 | | const nghttp2_frame* frame, |
838 | | int error_code, |
839 | | void* user_data); |
840 | | static int OnFrameSent( |
841 | | nghttp2_session* session, |
842 | | const nghttp2_frame* frame, |
843 | | void* user_data); |
844 | | static int OnStreamClose( |
845 | | nghttp2_session* session, |
846 | | int32_t id, |
847 | | uint32_t code, |
848 | | void* user_data); |
849 | | static int OnInvalidHeader( |
850 | | nghttp2_session* session, |
851 | | const nghttp2_frame* frame, |
852 | | nghttp2_rcbuf* name, |
853 | | nghttp2_rcbuf* value, |
854 | | uint8_t flags, |
855 | | void* user_data); |
856 | | static int OnDataChunkReceived( |
857 | | nghttp2_session* session, |
858 | | uint8_t flags, |
859 | | int32_t id, |
860 | | const uint8_t* data, |
861 | | size_t len, |
862 | | void* user_data); |
863 | | static ssize_t OnSelectPadding( |
864 | | nghttp2_session* session, |
865 | | const nghttp2_frame* frame, |
866 | | size_t maxPayloadLen, |
867 | | void* user_data); |
868 | | static int OnNghttpError(nghttp2_session* session, |
869 | | int lib_error_code, |
870 | | const char* message, |
871 | | size_t len, |
872 | | void* user_data); |
873 | | static int OnSendData( |
874 | | nghttp2_session* session, |
875 | | nghttp2_frame* frame, |
876 | | const uint8_t* framehd, |
877 | | size_t length, |
878 | | nghttp2_data_source* source, |
879 | | void* user_data); |
880 | | static int OnInvalidFrame( |
881 | | nghttp2_session* session, |
882 | | const nghttp2_frame* frame, |
883 | | int lib_error_code, |
884 | | void* user_data); |
885 | | |
886 | | struct Callbacks { |
887 | | explicit Callbacks(bool kHasGetPaddingCallback); |
888 | | |
889 | | Nghttp2SessionCallbacksPointer callbacks; |
890 | | }; |
891 | | |
892 | | /* Use callback_struct_saved[kHasGetPaddingCallback ? 1 : 0] */ |
893 | | static const Callbacks callback_struct_saved[2]; |
894 | | |
895 | | // The underlying nghttp2_session handle |
896 | | Nghttp2SessionPointer session_; |
897 | | |
898 | | // JS-accessible numeric fields, as indexed by SessionUint8Fields. |
899 | | AliasedStruct<SessionJSFields> js_fields_; |
900 | | |
901 | | // The session type: client or server |
902 | | SessionType session_type_; |
903 | | |
904 | | // The maximum number of header pairs permitted for streams on this session |
905 | | uint32_t max_header_pairs_ = DEFAULT_MAX_HEADER_LIST_PAIRS; |
906 | | |
907 | | // The maximum amount of memory allocated for this session |
908 | | uint64_t max_session_memory_ = kDefaultMaxSessionMemory; |
909 | | uint64_t current_session_memory_ = 0; |
910 | | // The amount of memory allocated by nghttp2 internals |
911 | | uint64_t current_nghttp2_memory_ = 0; |
912 | | |
913 | | // The collection of active Http2Streams associated with this session |
914 | | std::unordered_map<int32_t, BaseObjectPtr<Http2Stream>> streams_; |
915 | | |
916 | | int flags_ = kSessionStateNone; |
917 | | |
918 | | // The StreamBase instance being used for i/o |
919 | | PaddingStrategy padding_strategy_ = PADDING_STRATEGY_NONE; |
920 | | |
921 | | // use this to allow timeout tracking during long-lasting writes |
922 | | uint32_t chunks_sent_since_last_write_ = 0; |
923 | | |
924 | | uv_buf_t stream_buf_ = uv_buf_init(nullptr, 0); |
925 | | // When processing input data, either stream_buf_ab_ or stream_buf_allocation_ |
926 | | // will be set. stream_buf_ab_ is lazily created from stream_buf_allocation_. |
927 | | v8::Global<v8::ArrayBuffer> stream_buf_ab_; |
928 | | std::unique_ptr<v8::BackingStore> stream_buf_allocation_; |
929 | | size_t stream_buf_offset_ = 0; |
930 | | // Custom error code for errors that originated inside one of the callbacks |
931 | | // called by nghttp2_session_mem_recv. |
932 | | const char* custom_recv_error_code_ = nullptr; |
933 | | |
934 | | size_t max_outstanding_pings_ = kDefaultMaxPings; |
935 | | std::queue<BaseObjectPtr<Http2Ping>> outstanding_pings_; |
936 | | |
937 | | size_t max_outstanding_settings_ = kDefaultMaxSettings; |
938 | | std::queue<BaseObjectPtr<Http2Settings>> outstanding_settings_; |
939 | | |
940 | | struct custom_settings_state local_custom_settings_; |
941 | | struct custom_settings_state remote_custom_settings_; |
942 | | |
943 | | std::vector<NgHttp2StreamWrite> outgoing_buffers_; |
944 | | std::vector<uint8_t> outgoing_storage_; |
945 | | size_t outgoing_length_ = 0; |
946 | | std::vector<int32_t> pending_rst_streams_; |
947 | | // Count streams that have been rejected while being opened. Exceeding a fixed |
948 | | // limit will result in the session being destroyed, as an indication of a |
949 | | // misbehaving peer. This counter is reset once new streams are being |
950 | | // accepted again. |
951 | | uint32_t rejected_stream_count_ = 0; |
952 | | // Also use the invalid frame count as a measure for rejecting input frames. |
953 | | uint32_t invalid_frame_count_ = 0; |
954 | | |
955 | | void PushOutgoingBuffer(NgHttp2StreamWrite&& write); |
956 | | |
957 | | BaseObjectPtr<Http2State> http2_state_; |
958 | | |
959 | | void CopyDataIntoOutgoing(const uint8_t* src, size_t src_length); |
960 | | void ClearOutgoing(int status); |
961 | | |
962 | | void MaybeNotifyGracefulCloseComplete(); |
963 | | |
964 | | friend class Http2Scope; |
965 | | friend class Http2StreamListener; |
966 | | |
967 | | // Flag to indicate that JavaScript has initiated a graceful closure |
968 | | bool graceful_close_initiated_ = false; |
969 | | }; |
970 | | |
971 | | struct Http2SessionPerformanceEntryTraits { |
972 | | static constexpr performance::PerformanceEntryType kType = |
973 | | performance::NODE_PERFORMANCE_ENTRY_TYPE_HTTP2; |
974 | | |
975 | | using Details = Http2Session::Statistics; |
976 | | |
977 | | static v8::MaybeLocal<v8::Object> GetDetails( |
978 | | Environment* env, |
979 | | const performance::PerformanceEntry<Http2SessionPerformanceEntryTraits>& |
980 | | entry); |
981 | | }; |
982 | | |
983 | | struct Http2StreamPerformanceEntryTraits { |
984 | | static constexpr performance::PerformanceEntryType kType = |
985 | | performance::NODE_PERFORMANCE_ENTRY_TYPE_HTTP2; |
986 | | |
987 | | using Details = Http2Stream::Statistics; |
988 | | |
989 | | static v8::MaybeLocal<v8::Object> GetDetails( |
990 | | Environment* env, |
991 | | const performance::PerformanceEntry<Http2StreamPerformanceEntryTraits>& |
992 | | entry); |
993 | | }; |
994 | | |
995 | | using Http2SessionPerformanceEntry = |
996 | | performance::PerformanceEntry<Http2SessionPerformanceEntryTraits>; |
997 | | using Http2StreamPerformanceEntry = |
998 | | performance::PerformanceEntry<Http2StreamPerformanceEntryTraits>; |
999 | | |
1000 | | class Http2Ping : public AsyncWrap { |
1001 | | public: |
1002 | | explicit Http2Ping( |
1003 | | Http2Session* session, |
1004 | | v8::Local<v8::Object> obj, |
1005 | | v8::Local<v8::Function> callback); |
1006 | | |
1007 | | void MemoryInfo(MemoryTracker* tracker) const override; |
1008 | | SET_MEMORY_INFO_NAME(Http2Ping) |
1009 | | SET_SELF_SIZE(Http2Ping) |
1010 | | |
1011 | | void Send(const uint8_t* payload); |
1012 | | void Done(bool ack, const uint8_t* payload = nullptr); |
1013 | | void DetachFromSession(); |
1014 | | |
1015 | | v8::Local<v8::Function> callback() const; |
1016 | | |
1017 | | private: |
1018 | | BaseObjectWeakPtr<Http2Session> session_; |
1019 | | v8::Global<v8::Function> callback_; |
1020 | | uint64_t startTime_; |
1021 | | }; |
1022 | | |
1023 | | // The Http2Settings class is used to parse the settings passed in for |
1024 | | // an Http2Session, converting those into an array of nghttp2_settings_entry |
1025 | | // structs. |
1026 | | class Http2Settings : public AsyncWrap { |
1027 | | public: |
1028 | | Http2Settings(Http2Session* session, |
1029 | | v8::Local<v8::Object> obj, |
1030 | | v8::Local<v8::Function> callback, |
1031 | | uint64_t start_time = uv_hrtime()); |
1032 | | |
1033 | | void MemoryInfo(MemoryTracker* tracker) const override; |
1034 | | SET_MEMORY_INFO_NAME(Http2Settings) |
1035 | | SET_SELF_SIZE(Http2Settings) |
1036 | | |
1037 | | void Send(); |
1038 | | void Done(bool ack); |
1039 | | |
1040 | | v8::Local<v8::Function> callback() const; |
1041 | | |
1042 | | // Returns a Buffer instance with the serialized SETTINGS payload |
1043 | | v8::Local<v8::Value> Pack(); |
1044 | | |
1045 | | static v8::Local<v8::Value> Pack(Http2State* state); |
1046 | | |
1047 | | // Resets the default values in the settings buffer |
1048 | | static void RefreshDefaults(Http2State* http2_state); |
1049 | | |
1050 | | // Update the local or remote settings for the given session |
1051 | | static void Update(Http2Session* session, get_setting fn, bool local); |
1052 | | |
1053 | | private: |
1054 | | static size_t Init( |
1055 | | Http2State* http2_state, |
1056 | | nghttp2_settings_entry* entries); |
1057 | | |
1058 | | static v8::Local<v8::Value> Pack( |
1059 | | Environment* env, |
1060 | | size_t count, |
1061 | | const nghttp2_settings_entry* entries); |
1062 | | |
1063 | | BaseObjectWeakPtr<Http2Session> session_; |
1064 | | v8::Global<v8::Function> callback_; |
1065 | | uint64_t startTime_; |
1066 | | size_t count_ = 0; |
1067 | | nghttp2_settings_entry entries_[IDX_SETTINGS_COUNT + MAX_ADDITIONAL_SETTINGS]; |
1068 | | }; |
1069 | | |
1070 | | class Origins { |
1071 | | public: |
1072 | | Origins(Environment* env, |
1073 | | v8::Local<v8::String> origin_string, |
1074 | | size_t origin_count); |
1075 | 0 | ~Origins() = default; |
1076 | | |
1077 | 0 | const nghttp2_origin_entry* operator*() const { |
1078 | 0 | return static_cast<const nghttp2_origin_entry*>(bs_->Data()); |
1079 | 0 | } |
1080 | | |
1081 | 0 | size_t length() const { |
1082 | 0 | return count_; |
1083 | 0 | } |
1084 | | |
1085 | | private: |
1086 | | size_t count_; |
1087 | | std::unique_ptr<v8::BackingStore> bs_; |
1088 | | }; |
1089 | | |
1090 | | #define HTTP2_HIDDEN_CONSTANTS(V) \ |
1091 | 0 | V(NGHTTP2_HCAT_REQUEST) \ |
1092 | 0 | V(NGHTTP2_HCAT_RESPONSE) \ |
1093 | 0 | V(NGHTTP2_HCAT_PUSH_RESPONSE) \ |
1094 | 0 | V(NGHTTP2_HCAT_HEADERS) \ |
1095 | 0 | V(NGHTTP2_NV_FLAG_NONE) \ |
1096 | 0 | V(NGHTTP2_NV_FLAG_NO_INDEX) \ |
1097 | 0 | V(NGHTTP2_ERR_DEFERRED) \ |
1098 | 0 | V(NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE) \ |
1099 | 0 | V(NGHTTP2_ERR_INVALID_ARGUMENT) \ |
1100 | 0 | V(NGHTTP2_ERR_STREAM_CLOSED) \ |
1101 | 0 | V(NGHTTP2_ERR_NOMEM) \ |
1102 | 0 | V(STREAM_OPTION_EMPTY_PAYLOAD) \ |
1103 | 0 | V(STREAM_OPTION_GET_TRAILERS) |
1104 | | |
1105 | | #define HTTP2_ERROR_CODES(V) \ |
1106 | 0 | V(NGHTTP2_NO_ERROR) \ |
1107 | 0 | V(NGHTTP2_PROTOCOL_ERROR) \ |
1108 | 0 | V(NGHTTP2_INTERNAL_ERROR) \ |
1109 | 0 | V(NGHTTP2_FLOW_CONTROL_ERROR) \ |
1110 | 0 | V(NGHTTP2_SETTINGS_TIMEOUT) \ |
1111 | 0 | V(NGHTTP2_STREAM_CLOSED) \ |
1112 | 0 | V(NGHTTP2_FRAME_SIZE_ERROR) \ |
1113 | 0 | V(NGHTTP2_REFUSED_STREAM) \ |
1114 | 0 | V(NGHTTP2_CANCEL) \ |
1115 | 0 | V(NGHTTP2_COMPRESSION_ERROR) \ |
1116 | 0 | V(NGHTTP2_CONNECT_ERROR) \ |
1117 | 0 | V(NGHTTP2_ENHANCE_YOUR_CALM) \ |
1118 | 0 | V(NGHTTP2_INADEQUATE_SECURITY) \ |
1119 | 0 | V(NGHTTP2_HTTP_1_1_REQUIRED) \ |
1120 | | |
1121 | | #define HTTP2_CONSTANTS(V) \ |
1122 | 0 | V(NGHTTP2_ERR_FRAME_SIZE_ERROR) \ |
1123 | 0 | V(NGHTTP2_SESSION_SERVER) \ |
1124 | 0 | V(NGHTTP2_SESSION_CLIENT) \ |
1125 | 0 | V(NGHTTP2_STREAM_STATE_IDLE) \ |
1126 | 0 | V(NGHTTP2_STREAM_STATE_OPEN) \ |
1127 | 0 | V(NGHTTP2_STREAM_STATE_RESERVED_LOCAL) \ |
1128 | 0 | V(NGHTTP2_STREAM_STATE_RESERVED_REMOTE) \ |
1129 | 0 | V(NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL) \ |
1130 | 0 | V(NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE) \ |
1131 | 0 | V(NGHTTP2_STREAM_STATE_CLOSED) \ |
1132 | 0 | V(NGHTTP2_FLAG_NONE) \ |
1133 | 0 | V(NGHTTP2_FLAG_END_STREAM) \ |
1134 | 0 | V(NGHTTP2_FLAG_END_HEADERS) \ |
1135 | 0 | V(NGHTTP2_FLAG_ACK) \ |
1136 | 0 | V(NGHTTP2_FLAG_PADDED) \ |
1137 | 0 | V(NGHTTP2_FLAG_PRIORITY) \ |
1138 | 0 | V(DEFAULT_SETTINGS_HEADER_TABLE_SIZE) \ |
1139 | 0 | V(DEFAULT_SETTINGS_ENABLE_PUSH) \ |
1140 | 0 | V(DEFAULT_SETTINGS_MAX_CONCURRENT_STREAMS) \ |
1141 | 0 | V(DEFAULT_SETTINGS_INITIAL_WINDOW_SIZE) \ |
1142 | 0 | V(DEFAULT_SETTINGS_MAX_FRAME_SIZE) \ |
1143 | 0 | V(DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE) \ |
1144 | 0 | V(DEFAULT_SETTINGS_ENABLE_CONNECT_PROTOCOL) \ |
1145 | 0 | V(MAX_MAX_FRAME_SIZE) \ |
1146 | 0 | V(MIN_MAX_FRAME_SIZE) \ |
1147 | 0 | V(MAX_INITIAL_WINDOW_SIZE) \ |
1148 | 0 | V(NGHTTP2_SETTINGS_HEADER_TABLE_SIZE) \ |
1149 | 0 | V(NGHTTP2_SETTINGS_ENABLE_PUSH) \ |
1150 | 0 | V(NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS) \ |
1151 | 0 | V(NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE) \ |
1152 | 0 | V(NGHTTP2_SETTINGS_MAX_FRAME_SIZE) \ |
1153 | 0 | V(NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE) \ |
1154 | 0 | V(NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL) \ |
1155 | 0 | V(PADDING_STRATEGY_NONE) \ |
1156 | 0 | V(PADDING_STRATEGY_ALIGNED) \ |
1157 | 0 | V(PADDING_STRATEGY_MAX) \ |
1158 | 0 | V(PADDING_STRATEGY_CALLBACK) \ |
1159 | 0 | HTTP2_ERROR_CODES(V) |
1160 | | |
1161 | | #define HTTP2_SETTINGS(V) \ |
1162 | 0 | V(HEADER_TABLE_SIZE) \ |
1163 | 0 | V(ENABLE_PUSH) \ |
1164 | 0 | V(MAX_CONCURRENT_STREAMS) \ |
1165 | 0 | V(INITIAL_WINDOW_SIZE) \ |
1166 | 0 | V(MAX_FRAME_SIZE) \ |
1167 | 0 | V(MAX_HEADER_LIST_SIZE) \ |
1168 | 0 | V(ENABLE_CONNECT_PROTOCOL) \ |
1169 | | |
1170 | | } // namespace http2 |
1171 | | } // namespace node |
1172 | | |
1173 | | #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS |
1174 | | |
1175 | | #endif // SRC_NODE_HTTP2_H_ |