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