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 |
|
25464 |
void operator()(T* ptr) const noexcept { fn(ptr); } |
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 |
|
315970 |
static void inc(rcbuf_t* buf) { |
127 |
✗✓ |
315970 |
CHECK_NOT_NULL(buf); |
128 |
|
315970 |
nghttp2_rcbuf_incref(buf); |
129 |
|
315970 |
} |
130 |
|
315968 |
static void dec(rcbuf_t* buf) { |
131 |
✗✓ |
315968 |
CHECK_NOT_NULL(buf); |
132 |
|
315968 |
nghttp2_rcbuf_decref(buf); |
133 |
|
315968 |
} |
134 |
|
519806 |
static vector_t get_vec(rcbuf_t* buf) { |
135 |
✗✓ |
519806 |
CHECK_NOT_NULL(buf); |
136 |
|
519806 |
return nghttp2_rcbuf_get_buf(buf); |
137 |
|
|
} |
138 |
|
145320 |
static bool is_static(const rcbuf_t* buf) { |
139 |
✗✓ |
145320 |
CHECK_NOT_NULL(buf); |
140 |
|
145320 |
return nghttp2_rcbuf_is_static(buf); |
141 |
|
|
} |
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 |
|
66686 |
inline explicit NgHttp2StreamWrite(uv_buf_t buf_) : buf(buf_) {} |
152 |
|
27819 |
inline NgHttp2StreamWrite(BaseObjectPtr<AsyncWrap> req_wrap, uv_buf_t buf_) : |
153 |
|
27819 |
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 |
|
770 |
~Http2Options() = default; |
192 |
|
|
|
193 |
|
770 |
nghttp2_option* operator*() const { |
194 |
|
770 |
return options_.get(); |
195 |
|
|
} |
196 |
|
|
|
197 |
|
1 |
void set_max_header_pairs(uint32_t max) { |
198 |
|
1 |
max_header_pairs_ = max; |
199 |
|
1 |
} |
200 |
|
|
|
201 |
|
770 |
uint32_t max_header_pairs() const { |
202 |
|
770 |
return max_header_pairs_; |
203 |
|
|
} |
204 |
|
|
|
205 |
|
2 |
void set_padding_strategy(PaddingStrategy val) { |
206 |
|
2 |
padding_strategy_ = val; |
207 |
|
2 |
} |
208 |
|
|
|
209 |
|
770 |
PaddingStrategy padding_strategy() const { |
210 |
|
770 |
return padding_strategy_; |
211 |
|
|
} |
212 |
|
|
|
213 |
|
2 |
void set_max_outstanding_pings(size_t max) { |
214 |
|
2 |
max_outstanding_pings_ = max; |
215 |
|
2 |
} |
216 |
|
|
|
217 |
|
770 |
size_t max_outstanding_pings() const { |
218 |
|
770 |
return max_outstanding_pings_; |
219 |
|
|
} |
220 |
|
|
|
221 |
|
2 |
void set_max_outstanding_settings(size_t max) { |
222 |
|
2 |
max_outstanding_settings_ = max; |
223 |
|
2 |
} |
224 |
|
|
|
225 |
|
770 |
size_t max_outstanding_settings() const { |
226 |
|
770 |
return max_outstanding_settings_; |
227 |
|
|
} |
228 |
|
|
|
229 |
|
8 |
void set_max_session_memory(uint64_t max) { |
230 |
|
8 |
max_session_memory_ = max; |
231 |
|
8 |
} |
232 |
|
|
|
233 |
|
770 |
uint64_t max_session_memory() const { |
234 |
|
770 |
return max_session_memory_; |
235 |
|
|
} |
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 |
|
72660 |
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 |
|
161793 |
Http2Session* session() { return session_.get(); } |
285 |
|
17 |
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 |
|
15 |
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 |
|
382791 |
bool is_destroyed() const { |
330 |
|
382791 |
return flags_ & kStreamStateDestroyed; |
331 |
|
|
} |
332 |
|
|
|
333 |
|
48028 |
bool is_writable() const { |
334 |
|
48028 |
return !(flags_ & kStreamStateShut); |
335 |
|
|
} |
336 |
|
|
|
337 |
|
17888 |
bool is_paused() const { |
338 |
|
17888 |
return flags_ & kStreamStateReadPaused; |
339 |
|
|
} |
340 |
|
|
|
341 |
|
|
bool is_closed() const { |
342 |
|
|
return flags_ & kStreamStateClosed; |
343 |
|
|
} |
344 |
|
|
|
345 |
|
12740 |
bool has_trailers() const { |
346 |
|
12740 |
return flags_ & kStreamStateTrailers; |
347 |
|
|
} |
348 |
|
|
|
349 |
|
210 |
void set_has_trailers(bool on = true) { |
350 |
✓✓ |
210 |
if (on) |
351 |
|
167 |
flags_ |= kStreamStateTrailers; |
352 |
|
|
else |
353 |
|
43 |
flags_ &= ~kStreamStateTrailers; |
354 |
|
210 |
} |
355 |
|
|
|
356 |
|
23590 |
void set_closed() { |
357 |
|
23590 |
flags_ |= kStreamStateClosed; |
358 |
|
23590 |
} |
359 |
|
|
|
360 |
|
23671 |
void set_destroyed() { |
361 |
|
23671 |
flags_ |= kStreamStateDestroyed; |
362 |
|
23671 |
} |
363 |
|
|
|
364 |
|
24744 |
void set_not_writable() { |
365 |
|
24744 |
flags_ |= kStreamStateShut; |
366 |
|
24744 |
} |
367 |
|
|
|
368 |
|
23490 |
void set_reading(bool on = true) { |
369 |
✓✗ |
23490 |
if (on) { |
370 |
|
23490 |
flags_ |= kStreamStateReadStart; |
371 |
|
23490 |
set_paused(false); |
372 |
|
|
} else {} |
373 |
|
23490 |
} |
374 |
|
|
|
375 |
|
28490 |
void set_paused(bool on = true) { |
376 |
✓✓ |
28490 |
if (on) |
377 |
|
5000 |
flags_ |= kStreamStateReadPaused; |
378 |
|
|
else |
379 |
|
23490 |
flags_ &= ~kStreamStateReadPaused; |
380 |
|
28490 |
} |
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 |
|
18924 |
bool is_reading() const { |
386 |
✓✓✓✓
|
18924 |
return flags_ & kStreamStateReadStart && !is_paused(); |
387 |
|
|
} |
388 |
|
|
|
389 |
|
|
// Returns the RST_STREAM code used to close this stream |
390 |
|
|
int32_t code() const { return code_; } |
391 |
|
|
|
392 |
|
|
// Returns the stream identifier for this stream |
393 |
|
76497 |
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 |
|
23670 |
void TransferHeaders(Fn&& fn) { |
402 |
|
23670 |
size_t i = 0; |
403 |
✓✓ |
96330 |
for (const auto& header : current_headers_ ) |
404 |
|
72660 |
fn(header, i++); |
405 |
|
23670 |
ClearHeaders(); |
406 |
|
23670 |
} |
407 |
|
|
|
408 |
|
23671 |
void ClearHeaders() { |
409 |
|
23671 |
current_headers_.clear(); |
410 |
|
23671 |
} |
411 |
|
|
|
412 |
|
71119 |
size_t headers_count() const { |
413 |
|
71119 |
return current_headers_.size(); |
414 |
|
|
} |
415 |
|
|
|
416 |
|
23670 |
nghttp2_headers_category headers_category() const { |
417 |
|
23670 |
return current_headers_category_; |
418 |
|
|
} |
419 |
|
|
|
420 |
|
|
void StartHeaders(nghttp2_headers_category category); |
421 |
|
|
|
422 |
|
|
// Required for StreamBase |
423 |
|
56256 |
bool IsAlive() override { |
424 |
|
56256 |
return true; |
425 |
|
|
} |
426 |
|
|
|
427 |
|
|
// Required for StreamBase |
428 |
|
|
bool IsClosing() override { |
429 |
|
|
return false; |
430 |
|
|
} |
431 |
|
|
|
432 |
|
139698 |
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 |
|
23919 |
nghttp2_data_provider* operator*() { |
516 |
✓✓ |
23919 |
return !empty_ ? &provider_ : nullptr; |
517 |
|
|
} |
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 |
|
31658 |
StreamBase* underlying_stream() { |
582 |
|
31658 |
return static_cast<StreamBase*>(stream_); |
583 |
|
|
} |
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 |
|
|
SessionType type() const { return session_type_; } |
613 |
|
|
|
614 |
|
94023 |
nghttp2_session* session() const { return session_.get(); } |
615 |
|
|
|
616 |
|
|
nghttp2_session* operator*() { return session_.get(); } |
617 |
|
|
|
618 |
|
24323 |
uint32_t max_header_pairs() const { return max_header_pairs_; } |
619 |
|
|
|
620 |
|
|
const char* TypeName() const; |
621 |
|
|
|
622 |
|
68226 |
bool is_destroyed() { |
623 |
✓✓✗✓
|
68226 |
return (flags_ & kSessionStateClosed) || session_ == nullptr; |
624 |
|
|
} |
625 |
|
|
|
626 |
|
673 |
void set_destroyed() { |
627 |
|
673 |
flags_ |= kSessionStateClosed; |
628 |
|
673 |
} |
629 |
|
|
|
630 |
|
|
#define IS_FLAG(name, flag) \ |
631 |
|
|
bool is_##name() const { return flags_ & flag; } \ |
632 |
|
|
void set_##name(bool on = true) { \ |
633 |
|
|
if (on) \ |
634 |
|
|
flags_ |= flag; \ |
635 |
|
|
else \ |
636 |
|
|
flags_ &= ~flag; \ |
637 |
|
|
} |
638 |
|
|
|
639 |
✓✓ |
353752 |
IS_FLAG(in_scope, kSessionStateHasScope) |
640 |
✓✓ |
145421 |
IS_FLAG(write_scheduled, kSessionStateWriteScheduled) |
641 |
✓✗ |
1346 |
IS_FLAG(closing, kSessionStateClosing) |
642 |
✓✓ |
131948 |
IS_FLAG(sending, kSessionStateSending) |
643 |
✓✓ |
173194 |
IS_FLAG(write_in_progress, kSessionStateWriteInProgress) |
644 |
✓✓ |
69969 |
IS_FLAG(reading_stopped, kSessionStateReadingStopped) |
645 |
✓✓ |
64453 |
IS_FLAG(receive_paused, kSessionStateReceivePaused) |
646 |
|
|
|
647 |
|
|
#undef IS_FLAG |
648 |
|
|
|
649 |
|
|
// Schedule a write if nghttp2 indicates it wants to write to the socket. |
650 |
|
|
void MaybeScheduleWrite(); |
651 |
|
|
|
652 |
|
|
// Stop reading if nghttp2 doesn't want to anymore. |
653 |
|
|
void MaybeStopReading(); |
654 |
|
|
|
655 |
|
|
// Returns pointer to the stream, or nullptr if stream does not exist |
656 |
|
|
BaseObjectPtr<Http2Stream> FindStream(int32_t id); |
657 |
|
|
|
658 |
|
|
bool CanAddStream(); |
659 |
|
|
|
660 |
|
|
// Adds a stream instance to this session |
661 |
|
|
void AddStream(Http2Stream* stream); |
662 |
|
|
|
663 |
|
|
// Removes a stream instance from this session |
664 |
|
|
BaseObjectPtr<Http2Stream> RemoveStream(int32_t id); |
665 |
|
|
|
666 |
|
|
// Indicates whether there currently exist outgoing buffers for this stream. |
667 |
|
|
bool HasWritesOnSocketForStream(Http2Stream* stream); |
668 |
|
|
|
669 |
|
|
// Write data from stream_buf_ to the session. |
670 |
|
|
// This will call the error callback if an error occurs. |
671 |
|
|
void ConsumeHTTP2Data(); |
672 |
|
|
|
673 |
|
|
void MemoryInfo(MemoryTracker* tracker) const override; |
674 |
|
|
SET_MEMORY_INFO_NAME(Http2Session) |
675 |
|
|
SET_SELF_SIZE(Http2Session) |
676 |
|
|
|
677 |
|
|
std::string diagnostic_name() const override; |
678 |
|
|
|
679 |
|
|
// Schedule an RstStream for after the current write finishes. |
680 |
|
11 |
void AddPendingRstStream(int32_t stream_id) { |
681 |
|
11 |
pending_rst_streams_.emplace_back(stream_id); |
682 |
|
11 |
} |
683 |
|
|
|
684 |
|
23671 |
bool has_pending_rststream(int32_t stream_id) { |
685 |
|
23671 |
return pending_rst_streams_.end() != |
686 |
|
23671 |
std::find(pending_rst_streams_.begin(), |
687 |
|
|
pending_rst_streams_.end(), |
688 |
|
47342 |
stream_id); |
689 |
|
|
} |
690 |
|
|
|
691 |
|
|
// Handle reads/writes from the underlying network transport. |
692 |
|
|
uv_buf_t OnStreamAlloc(size_t suggested_size) override; |
693 |
|
|
void OnStreamRead(ssize_t nread, const uv_buf_t& buf) override; |
694 |
|
|
void OnStreamAfterWrite(WriteWrap* w, int status) override; |
695 |
|
|
|
696 |
|
|
// Implementation for mem::NgLibMemoryManager |
697 |
|
|
void CheckAllocatedSize(size_t previous_size) const; |
698 |
|
|
void IncreaseAllocatedSize(size_t size); |
699 |
|
|
void DecreaseAllocatedSize(size_t size); |
700 |
|
|
|
701 |
|
|
// The JavaScript API |
702 |
|
|
static void New(const v8::FunctionCallbackInfo<v8::Value>& args); |
703 |
|
|
static void Consume(const v8::FunctionCallbackInfo<v8::Value>& args); |
704 |
|
|
static void Receive(const v8::FunctionCallbackInfo<v8::Value>& args); |
705 |
|
|
static void Destroy(const v8::FunctionCallbackInfo<v8::Value>& args); |
706 |
|
|
static void Settings(const v8::FunctionCallbackInfo<v8::Value>& args); |
707 |
|
|
static void Request(const v8::FunctionCallbackInfo<v8::Value>& args); |
708 |
|
|
static void SetNextStreamID(const v8::FunctionCallbackInfo<v8::Value>& args); |
709 |
|
|
static void SetLocalWindowSize( |
710 |
|
|
const v8::FunctionCallbackInfo<v8::Value>& args); |
711 |
|
|
static void Goaway(const v8::FunctionCallbackInfo<v8::Value>& args); |
712 |
|
|
static void UpdateChunksSent(const v8::FunctionCallbackInfo<v8::Value>& args); |
713 |
|
|
static void RefreshState(const v8::FunctionCallbackInfo<v8::Value>& args); |
714 |
|
|
static void Ping(const v8::FunctionCallbackInfo<v8::Value>& args); |
715 |
|
|
static void AltSvc(const v8::FunctionCallbackInfo<v8::Value>& args); |
716 |
|
|
static void Origin(const v8::FunctionCallbackInfo<v8::Value>& args); |
717 |
|
|
|
718 |
|
|
template <get_setting fn> |
719 |
|
|
static void RefreshSettings(const v8::FunctionCallbackInfo<v8::Value>& args); |
720 |
|
|
|
721 |
|
|
uv_loop_t* event_loop() const { |
722 |
|
|
return env()->event_loop(); |
723 |
|
|
} |
724 |
|
|
|
725 |
|
1461 |
Http2State* http2_state() const { return http2_state_.get(); } |
726 |
|
|
|
727 |
|
|
BaseObjectPtr<Http2Ping> PopPing(); |
728 |
|
|
bool AddPing(const uint8_t* data, v8::Local<v8::Function> callback); |
729 |
|
|
|
730 |
|
|
BaseObjectPtr<Http2Settings> PopSettings(); |
731 |
|
|
bool AddSettings(v8::Local<v8::Function> callback); |
732 |
|
|
|
733 |
|
157249 |
void IncrementCurrentSessionMemory(uint64_t amount) { |
734 |
|
157249 |
current_session_memory_ += amount; |
735 |
|
157249 |
} |
736 |
|
|
|
737 |
|
105123 |
void DecrementCurrentSessionMemory(uint64_t amount) { |
738 |
|
|
DCHECK_LE(amount, current_session_memory_); |
739 |
|
105123 |
current_session_memory_ -= amount; |
740 |
|
105123 |
} |
741 |
|
|
|
742 |
|
|
// Tell our custom memory allocator that this rcbuf is independent of |
743 |
|
|
// this session now, and may outlive it. |
744 |
|
|
void StopTrackingRcbuf(nghttp2_rcbuf* buf); |
745 |
|
|
|
746 |
|
|
// Returns the current session memory including memory allocated by nghttp2, |
747 |
|
|
// the current outbound storage queue, and pending writes. |
748 |
|
84889 |
uint64_t current_session_memory() const { |
749 |
|
84889 |
uint64_t total = current_session_memory_ + sizeof(Http2Session); |
750 |
|
84889 |
total += current_nghttp2_memory_; |
751 |
|
84889 |
total += outgoing_storage_.size(); |
752 |
|
84889 |
return total; |
753 |
|
|
} |
754 |
|
|
|
755 |
|
|
// Return true if current_session_memory + amount is less than the max |
756 |
|
84889 |
bool has_available_session_memory(uint64_t amount) const { |
757 |
|
84889 |
return current_session_memory() + amount <= max_session_memory_; |
758 |
|
|
} |
759 |
|
|
|
760 |
|
|
struct Statistics { |
761 |
|
|
uint64_t start_time; |
762 |
|
|
uint64_t end_time; |
763 |
|
|
uint64_t ping_rtt; |
764 |
|
|
uint64_t data_sent; |
765 |
|
|
uint64_t data_received; |
766 |
|
|
uint32_t frame_count; |
767 |
|
|
uint32_t frame_sent; |
768 |
|
|
int32_t stream_count; |
769 |
|
|
size_t max_concurrent_streams; |
770 |
|
|
double stream_average_duration; |
771 |
|
|
SessionType session_type; |
772 |
|
|
}; |
773 |
|
|
|
774 |
|
|
Statistics statistics_ = {}; |
775 |
|
|
|
776 |
|
|
private: |
777 |
|
|
void EmitStatistics(); |
778 |
|
|
|
779 |
|
|
// Frame Padding Strategies |
780 |
|
|
ssize_t OnDWordAlignedPadding(size_t frameLength, |
781 |
|
|
size_t maxPayloadLen); |
782 |
|
|
ssize_t OnMaxFrameSizePadding(size_t frameLength, |
783 |
|
|
size_t maxPayloadLen); |
784 |
|
|
|
785 |
|
|
// Frame Handler |
786 |
|
|
int HandleDataFrame(const nghttp2_frame* frame); |
787 |
|
|
void HandleGoawayFrame(const nghttp2_frame* frame); |
788 |
|
|
void HandleHeadersFrame(const nghttp2_frame* frame); |
789 |
|
|
void HandlePriorityFrame(const nghttp2_frame* frame); |
790 |
|
|
void HandleSettingsFrame(const nghttp2_frame* frame); |
791 |
|
|
void HandlePingFrame(const nghttp2_frame* frame); |
792 |
|
|
void HandleAltSvcFrame(const nghttp2_frame* frame); |
793 |
|
|
void HandleOriginFrame(const nghttp2_frame* frame); |
794 |
|
|
|
795 |
|
|
void DecrefHeaders(const nghttp2_frame* frame); |
796 |
|
|
|
797 |
|
|
// nghttp2 callbacks |
798 |
|
|
static int OnBeginHeadersCallback( |
799 |
|
|
nghttp2_session* session, |
800 |
|
|
const nghttp2_frame* frame, |
801 |
|
|
void* user_data); |
802 |
|
|
static int OnHeaderCallback( |
803 |
|
|
nghttp2_session* session, |
804 |
|
|
const nghttp2_frame* frame, |
805 |
|
|
nghttp2_rcbuf* name, |
806 |
|
|
nghttp2_rcbuf* value, |
807 |
|
|
uint8_t flags, |
808 |
|
|
void* user_data); |
809 |
|
|
static int OnFrameReceive( |
810 |
|
|
nghttp2_session* session, |
811 |
|
|
const nghttp2_frame* frame, |
812 |
|
|
void* user_data); |
813 |
|
|
static int OnFrameNotSent( |
814 |
|
|
nghttp2_session* session, |
815 |
|
|
const nghttp2_frame* frame, |
816 |
|
|
int error_code, |
817 |
|
|
void* user_data); |
818 |
|
|
static int OnFrameSent( |
819 |
|
|
nghttp2_session* session, |
820 |
|
|
const nghttp2_frame* frame, |
821 |
|
|
void* user_data); |
822 |
|
|
static int OnStreamClose( |
823 |
|
|
nghttp2_session* session, |
824 |
|
|
int32_t id, |
825 |
|
|
uint32_t code, |
826 |
|
|
void* user_data); |
827 |
|
|
static int OnInvalidHeader( |
828 |
|
|
nghttp2_session* session, |
829 |
|
|
const nghttp2_frame* frame, |
830 |
|
|
nghttp2_rcbuf* name, |
831 |
|
|
nghttp2_rcbuf* value, |
832 |
|
|
uint8_t flags, |
833 |
|
|
void* user_data); |
834 |
|
|
static int OnDataChunkReceived( |
835 |
|
|
nghttp2_session* session, |
836 |
|
|
uint8_t flags, |
837 |
|
|
int32_t id, |
838 |
|
|
const uint8_t* data, |
839 |
|
|
size_t len, |
840 |
|
|
void* user_data); |
841 |
|
|
static ssize_t OnSelectPadding( |
842 |
|
|
nghttp2_session* session, |
843 |
|
|
const nghttp2_frame* frame, |
844 |
|
|
size_t maxPayloadLen, |
845 |
|
|
void* user_data); |
846 |
|
|
static int OnNghttpError( |
847 |
|
|
nghttp2_session* session, |
848 |
|
|
const char* message, |
849 |
|
|
size_t len, |
850 |
|
|
void* user_data); |
851 |
|
|
static int OnSendData( |
852 |
|
|
nghttp2_session* session, |
853 |
|
|
nghttp2_frame* frame, |
854 |
|
|
const uint8_t* framehd, |
855 |
|
|
size_t length, |
856 |
|
|
nghttp2_data_source* source, |
857 |
|
|
void* user_data); |
858 |
|
|
static int OnInvalidFrame( |
859 |
|
|
nghttp2_session* session, |
860 |
|
|
const nghttp2_frame* frame, |
861 |
|
|
int lib_error_code, |
862 |
|
|
void* user_data); |
863 |
|
|
|
864 |
|
|
struct Callbacks { |
865 |
|
|
explicit Callbacks(bool kHasGetPaddingCallback); |
866 |
|
|
|
867 |
|
|
Nghttp2SessionCallbacksPointer callbacks; |
868 |
|
|
}; |
869 |
|
|
|
870 |
|
|
/* Use callback_struct_saved[kHasGetPaddingCallback ? 1 : 0] */ |
871 |
|
|
static const Callbacks callback_struct_saved[2]; |
872 |
|
|
|
873 |
|
|
// The underlying nghttp2_session handle |
874 |
|
|
Nghttp2SessionPointer session_; |
875 |
|
|
|
876 |
|
|
// JS-accessible numeric fields, as indexed by SessionUint8Fields. |
877 |
|
|
AliasedStruct<SessionJSFields> js_fields_; |
878 |
|
|
|
879 |
|
|
// The session type: client or server |
880 |
|
|
SessionType session_type_; |
881 |
|
|
|
882 |
|
|
// The maximum number of header pairs permitted for streams on this session |
883 |
|
|
uint32_t max_header_pairs_ = DEFAULT_MAX_HEADER_LIST_PAIRS; |
884 |
|
|
|
885 |
|
|
// The maximum amount of memory allocated for this session |
886 |
|
|
uint64_t max_session_memory_ = kDefaultMaxSessionMemory; |
887 |
|
|
uint64_t current_session_memory_ = 0; |
888 |
|
|
// The amount of memory allocated by nghttp2 internals |
889 |
|
|
uint64_t current_nghttp2_memory_ = 0; |
890 |
|
|
|
891 |
|
|
// The collection of active Http2Streams associated with this session |
892 |
|
|
std::unordered_map<int32_t, BaseObjectPtr<Http2Stream>> streams_; |
893 |
|
|
|
894 |
|
|
int flags_ = kSessionStateNone; |
895 |
|
|
|
896 |
|
|
// The StreamBase instance being used for i/o |
897 |
|
|
PaddingStrategy padding_strategy_ = PADDING_STRATEGY_NONE; |
898 |
|
|
|
899 |
|
|
// use this to allow timeout tracking during long-lasting writes |
900 |
|
|
uint32_t chunks_sent_since_last_write_ = 0; |
901 |
|
|
|
902 |
|
|
uv_buf_t stream_buf_ = uv_buf_init(nullptr, 0); |
903 |
|
|
// When processing input data, either stream_buf_ab_ or stream_buf_allocation_ |
904 |
|
|
// will be set. stream_buf_ab_ is lazily created from stream_buf_allocation_. |
905 |
|
|
v8::Global<v8::ArrayBuffer> stream_buf_ab_; |
906 |
|
|
std::unique_ptr<v8::BackingStore> stream_buf_allocation_; |
907 |
|
|
size_t stream_buf_offset_ = 0; |
908 |
|
|
// Custom error code for errors that originated inside one of the callbacks |
909 |
|
|
// called by nghttp2_session_mem_recv. |
910 |
|
|
const char* custom_recv_error_code_ = nullptr; |
911 |
|
|
|
912 |
|
|
size_t max_outstanding_pings_ = kDefaultMaxPings; |
913 |
|
|
std::queue<BaseObjectPtr<Http2Ping>> outstanding_pings_; |
914 |
|
|
|
915 |
|
|
size_t max_outstanding_settings_ = kDefaultMaxSettings; |
916 |
|
|
std::queue<BaseObjectPtr<Http2Settings>> outstanding_settings_; |
917 |
|
|
|
918 |
|
|
std::vector<NgHttp2StreamWrite> outgoing_buffers_; |
919 |
|
|
std::vector<uint8_t> outgoing_storage_; |
920 |
|
|
size_t outgoing_length_ = 0; |
921 |
|
|
std::vector<int32_t> pending_rst_streams_; |
922 |
|
|
// Count streams that have been rejected while being opened. Exceeding a fixed |
923 |
|
|
// limit will result in the session being destroyed, as an indication of a |
924 |
|
|
// misbehaving peer. This counter is reset once new streams are being |
925 |
|
|
// accepted again. |
926 |
|
|
uint32_t rejected_stream_count_ = 0; |
927 |
|
|
// Also use the invalid frame count as a measure for rejecting input frames. |
928 |
|
|
uint32_t invalid_frame_count_ = 0; |
929 |
|
|
|
930 |
|
|
void PushOutgoingBuffer(NgHttp2StreamWrite&& write); |
931 |
|
|
|
932 |
|
|
BaseObjectPtr<Http2State> http2_state_; |
933 |
|
|
|
934 |
|
|
void CopyDataIntoOutgoing(const uint8_t* src, size_t src_length); |
935 |
|
|
void ClearOutgoing(int status); |
936 |
|
|
|
937 |
|
|
friend class Http2Scope; |
938 |
|
|
friend class Http2StreamListener; |
939 |
|
|
}; |
940 |
|
|
|
941 |
|
|
struct Http2SessionPerformanceEntryTraits { |
942 |
|
|
static constexpr performance::PerformanceEntryType kType = |
943 |
|
|
performance::NODE_PERFORMANCE_ENTRY_TYPE_HTTP2; |
944 |
|
|
|
945 |
|
|
using Details = Http2Session::Statistics; |
946 |
|
|
|
947 |
|
|
static v8::MaybeLocal<v8::Object> GetDetails( |
948 |
|
|
Environment* env, |
949 |
|
|
const performance::PerformanceEntry<Http2SessionPerformanceEntryTraits>& |
950 |
|
|
entry); |
951 |
|
|
}; |
952 |
|
|
|
953 |
|
|
struct Http2StreamPerformanceEntryTraits { |
954 |
|
|
static constexpr performance::PerformanceEntryType kType = |
955 |
|
|
performance::NODE_PERFORMANCE_ENTRY_TYPE_HTTP2; |
956 |
|
|
|
957 |
|
|
using Details = Http2Stream::Statistics; |
958 |
|
|
|
959 |
|
|
static v8::MaybeLocal<v8::Object> GetDetails( |
960 |
|
|
Environment* env, |
961 |
|
|
const performance::PerformanceEntry<Http2StreamPerformanceEntryTraits>& |
962 |
|
|
entry); |
963 |
|
|
}; |
964 |
|
|
|
965 |
|
|
using Http2SessionPerformanceEntry = |
966 |
|
|
performance::PerformanceEntry<Http2SessionPerformanceEntryTraits>; |
967 |
|
|
using Http2StreamPerformanceEntry = |
968 |
|
|
performance::PerformanceEntry<Http2StreamPerformanceEntryTraits>; |
969 |
|
|
|
970 |
|
|
class Http2Ping : public AsyncWrap { |
971 |
|
|
public: |
972 |
|
|
explicit Http2Ping( |
973 |
|
|
Http2Session* session, |
974 |
|
|
v8::Local<v8::Object> obj, |
975 |
|
|
v8::Local<v8::Function> callback); |
976 |
|
|
|
977 |
|
|
void MemoryInfo(MemoryTracker* tracker) const override; |
978 |
|
|
SET_MEMORY_INFO_NAME(Http2Ping) |
979 |
|
|
SET_SELF_SIZE(Http2Ping) |
980 |
|
|
|
981 |
|
|
void Send(const uint8_t* payload); |
982 |
|
|
void Done(bool ack, const uint8_t* payload = nullptr); |
983 |
|
|
void DetachFromSession(); |
984 |
|
|
|
985 |
|
|
v8::Local<v8::Function> callback() const; |
986 |
|
|
|
987 |
|
|
private: |
988 |
|
|
BaseObjectWeakPtr<Http2Session> session_; |
989 |
|
|
v8::Global<v8::Function> callback_; |
990 |
|
|
uint64_t startTime_; |
991 |
|
|
}; |
992 |
|
|
|
993 |
|
|
// The Http2Settings class is used to parse the settings passed in for |
994 |
|
|
// an Http2Session, converting those into an array of nghttp2_settings_entry |
995 |
|
|
// structs. |
996 |
|
|
class Http2Settings : public AsyncWrap { |
997 |
|
|
public: |
998 |
|
|
Http2Settings(Http2Session* session, |
999 |
|
|
v8::Local<v8::Object> obj, |
1000 |
|
|
v8::Local<v8::Function> callback, |
1001 |
|
|
uint64_t start_time = uv_hrtime()); |
1002 |
|
|
|
1003 |
|
|
void MemoryInfo(MemoryTracker* tracker) const override; |
1004 |
|
|
SET_MEMORY_INFO_NAME(Http2Settings) |
1005 |
|
|
SET_SELF_SIZE(Http2Settings) |
1006 |
|
|
|
1007 |
|
|
void Send(); |
1008 |
|
|
void Done(bool ack); |
1009 |
|
|
|
1010 |
|
|
v8::Local<v8::Function> callback() const; |
1011 |
|
|
|
1012 |
|
|
// Returns a Buffer instance with the serialized SETTINGS payload |
1013 |
|
|
v8::Local<v8::Value> Pack(); |
1014 |
|
|
|
1015 |
|
|
static v8::Local<v8::Value> Pack(Http2State* state); |
1016 |
|
|
|
1017 |
|
|
// Resets the default values in the settings buffer |
1018 |
|
|
static void RefreshDefaults(Http2State* http2_state); |
1019 |
|
|
|
1020 |
|
|
// Update the local or remote settings for the given session |
1021 |
|
|
static void Update(Http2Session* session, |
1022 |
|
|
get_setting fn); |
1023 |
|
|
|
1024 |
|
|
private: |
1025 |
|
|
static size_t Init( |
1026 |
|
|
Http2State* http2_state, |
1027 |
|
|
nghttp2_settings_entry* entries); |
1028 |
|
|
|
1029 |
|
|
static v8::Local<v8::Value> Pack( |
1030 |
|
|
Environment* env, |
1031 |
|
|
size_t count, |
1032 |
|
|
const nghttp2_settings_entry* entries); |
1033 |
|
|
|
1034 |
|
|
BaseObjectWeakPtr<Http2Session> session_; |
1035 |
|
|
v8::Global<v8::Function> callback_; |
1036 |
|
|
uint64_t startTime_; |
1037 |
|
|
size_t count_ = 0; |
1038 |
|
|
nghttp2_settings_entry entries_[IDX_SETTINGS_COUNT]; |
1039 |
|
|
}; |
1040 |
|
|
|
1041 |
|
|
class Origins { |
1042 |
|
|
public: |
1043 |
|
|
Origins(Environment* env, |
1044 |
|
|
v8::Local<v8::String> origin_string, |
1045 |
|
|
size_t origin_count); |
1046 |
|
5 |
~Origins() = default; |
1047 |
|
|
|
1048 |
|
5 |
const nghttp2_origin_entry* operator*() const { |
1049 |
|
5 |
return static_cast<const nghttp2_origin_entry*>(bs_->Data()); |
1050 |
|
|
} |
1051 |
|
|
|
1052 |
|
5 |
size_t length() const { |
1053 |
|
5 |
return count_; |
1054 |
|
|
} |
1055 |
|
|
|
1056 |
|
|
private: |
1057 |
|
|
size_t count_; |
1058 |
|
|
std::unique_ptr<v8::BackingStore> bs_; |
1059 |
|
|
}; |
1060 |
|
|
|
1061 |
|
|
#define HTTP2_HIDDEN_CONSTANTS(V) \ |
1062 |
|
|
V(NGHTTP2_HCAT_REQUEST) \ |
1063 |
|
|
V(NGHTTP2_HCAT_RESPONSE) \ |
1064 |
|
|
V(NGHTTP2_HCAT_PUSH_RESPONSE) \ |
1065 |
|
|
V(NGHTTP2_HCAT_HEADERS) \ |
1066 |
|
|
V(NGHTTP2_NV_FLAG_NONE) \ |
1067 |
|
|
V(NGHTTP2_NV_FLAG_NO_INDEX) \ |
1068 |
|
|
V(NGHTTP2_ERR_DEFERRED) \ |
1069 |
|
|
V(NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE) \ |
1070 |
|
|
V(NGHTTP2_ERR_INVALID_ARGUMENT) \ |
1071 |
|
|
V(NGHTTP2_ERR_STREAM_CLOSED) \ |
1072 |
|
|
V(NGHTTP2_ERR_NOMEM) \ |
1073 |
|
|
V(STREAM_OPTION_EMPTY_PAYLOAD) \ |
1074 |
|
|
V(STREAM_OPTION_GET_TRAILERS) |
1075 |
|
|
|
1076 |
|
|
#define HTTP2_ERROR_CODES(V) \ |
1077 |
|
|
V(NGHTTP2_NO_ERROR) \ |
1078 |
|
|
V(NGHTTP2_PROTOCOL_ERROR) \ |
1079 |
|
|
V(NGHTTP2_INTERNAL_ERROR) \ |
1080 |
|
|
V(NGHTTP2_FLOW_CONTROL_ERROR) \ |
1081 |
|
|
V(NGHTTP2_SETTINGS_TIMEOUT) \ |
1082 |
|
|
V(NGHTTP2_STREAM_CLOSED) \ |
1083 |
|
|
V(NGHTTP2_FRAME_SIZE_ERROR) \ |
1084 |
|
|
V(NGHTTP2_REFUSED_STREAM) \ |
1085 |
|
|
V(NGHTTP2_CANCEL) \ |
1086 |
|
|
V(NGHTTP2_COMPRESSION_ERROR) \ |
1087 |
|
|
V(NGHTTP2_CONNECT_ERROR) \ |
1088 |
|
|
V(NGHTTP2_ENHANCE_YOUR_CALM) \ |
1089 |
|
|
V(NGHTTP2_INADEQUATE_SECURITY) \ |
1090 |
|
|
V(NGHTTP2_HTTP_1_1_REQUIRED) \ |
1091 |
|
|
|
1092 |
|
|
#define HTTP2_CONSTANTS(V) \ |
1093 |
|
|
V(NGHTTP2_ERR_FRAME_SIZE_ERROR) \ |
1094 |
|
|
V(NGHTTP2_SESSION_SERVER) \ |
1095 |
|
|
V(NGHTTP2_SESSION_CLIENT) \ |
1096 |
|
|
V(NGHTTP2_STREAM_STATE_IDLE) \ |
1097 |
|
|
V(NGHTTP2_STREAM_STATE_OPEN) \ |
1098 |
|
|
V(NGHTTP2_STREAM_STATE_RESERVED_LOCAL) \ |
1099 |
|
|
V(NGHTTP2_STREAM_STATE_RESERVED_REMOTE) \ |
1100 |
|
|
V(NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL) \ |
1101 |
|
|
V(NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE) \ |
1102 |
|
|
V(NGHTTP2_STREAM_STATE_CLOSED) \ |
1103 |
|
|
V(NGHTTP2_FLAG_NONE) \ |
1104 |
|
|
V(NGHTTP2_FLAG_END_STREAM) \ |
1105 |
|
|
V(NGHTTP2_FLAG_END_HEADERS) \ |
1106 |
|
|
V(NGHTTP2_FLAG_ACK) \ |
1107 |
|
|
V(NGHTTP2_FLAG_PADDED) \ |
1108 |
|
|
V(NGHTTP2_FLAG_PRIORITY) \ |
1109 |
|
|
V(DEFAULT_SETTINGS_HEADER_TABLE_SIZE) \ |
1110 |
|
|
V(DEFAULT_SETTINGS_ENABLE_PUSH) \ |
1111 |
|
|
V(DEFAULT_SETTINGS_MAX_CONCURRENT_STREAMS) \ |
1112 |
|
|
V(DEFAULT_SETTINGS_INITIAL_WINDOW_SIZE) \ |
1113 |
|
|
V(DEFAULT_SETTINGS_MAX_FRAME_SIZE) \ |
1114 |
|
|
V(DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE) \ |
1115 |
|
|
V(DEFAULT_SETTINGS_ENABLE_CONNECT_PROTOCOL) \ |
1116 |
|
|
V(MAX_MAX_FRAME_SIZE) \ |
1117 |
|
|
V(MIN_MAX_FRAME_SIZE) \ |
1118 |
|
|
V(MAX_INITIAL_WINDOW_SIZE) \ |
1119 |
|
|
V(NGHTTP2_SETTINGS_HEADER_TABLE_SIZE) \ |
1120 |
|
|
V(NGHTTP2_SETTINGS_ENABLE_PUSH) \ |
1121 |
|
|
V(NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS) \ |
1122 |
|
|
V(NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE) \ |
1123 |
|
|
V(NGHTTP2_SETTINGS_MAX_FRAME_SIZE) \ |
1124 |
|
|
V(NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE) \ |
1125 |
|
|
V(NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL) \ |
1126 |
|
|
V(PADDING_STRATEGY_NONE) \ |
1127 |
|
|
V(PADDING_STRATEGY_ALIGNED) \ |
1128 |
|
|
V(PADDING_STRATEGY_MAX) \ |
1129 |
|
|
V(PADDING_STRATEGY_CALLBACK) \ |
1130 |
|
|
HTTP2_ERROR_CODES(V) |
1131 |
|
|
|
1132 |
|
|
#define HTTP2_SETTINGS(V) \ |
1133 |
|
|
V(HEADER_TABLE_SIZE) \ |
1134 |
|
|
V(ENABLE_PUSH) \ |
1135 |
|
|
V(MAX_CONCURRENT_STREAMS) \ |
1136 |
|
|
V(INITIAL_WINDOW_SIZE) \ |
1137 |
|
|
V(MAX_FRAME_SIZE) \ |
1138 |
|
|
V(MAX_HEADER_LIST_SIZE) \ |
1139 |
|
|
V(ENABLE_CONNECT_PROTOCOL) \ |
1140 |
|
|
|
1141 |
|
|
} // namespace http2 |
1142 |
|
|
} // namespace node |
1143 |
|
|
|
1144 |
|
|
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS |
1145 |
|
|
|
1146 |
|
|
#endif // SRC_NODE_HTTP2_H_ |