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 "node_http2_state.h" |
11 |
|
|
#include "node_perf.h" |
12 |
|
|
#include "stream_base-inl.h" |
13 |
|
|
#include "string_bytes.h" |
14 |
|
|
|
15 |
|
|
#include <algorithm> |
16 |
|
|
#include <queue> |
17 |
|
|
|
18 |
|
|
namespace node { |
19 |
|
|
namespace http2 { |
20 |
|
|
|
21 |
|
|
using v8::Array; |
22 |
|
|
using v8::Context; |
23 |
|
|
using v8::Isolate; |
24 |
|
|
using v8::MaybeLocal; |
25 |
|
|
|
26 |
|
|
using performance::PerformanceEntry; |
27 |
|
|
|
28 |
|
|
// We strictly limit the number of outstanding unacknowledged PINGS a user |
29 |
|
|
// may send in order to prevent abuse. The current default cap is 10. The |
30 |
|
|
// user may set a different limit using a per Http2Session configuration |
31 |
|
|
// option. |
32 |
|
|
#define DEFAULT_MAX_PINGS 10 |
33 |
|
|
|
34 |
|
|
// Also strictly limit the number of outstanding SETTINGS frames a user sends |
35 |
|
|
#define DEFAULT_MAX_SETTINGS 10 |
36 |
|
|
|
37 |
|
|
// Default maximum total memory cap for Http2Session. |
38 |
|
|
#define DEFAULT_MAX_SESSION_MEMORY 1e7 |
39 |
|
|
|
40 |
|
|
// These are the standard HTTP/2 defaults as specified by the RFC |
41 |
|
|
#define DEFAULT_SETTINGS_HEADER_TABLE_SIZE 4096 |
42 |
|
|
#define DEFAULT_SETTINGS_ENABLE_PUSH 1 |
43 |
|
|
#define DEFAULT_SETTINGS_INITIAL_WINDOW_SIZE 65535 |
44 |
|
|
#define DEFAULT_SETTINGS_MAX_FRAME_SIZE 16384 |
45 |
|
|
#define DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE 65535 |
46 |
|
|
#define MAX_MAX_FRAME_SIZE 16777215 |
47 |
|
|
#define MIN_MAX_FRAME_SIZE DEFAULT_SETTINGS_MAX_FRAME_SIZE |
48 |
|
|
#define MAX_INITIAL_WINDOW_SIZE 2147483647 |
49 |
|
|
|
50 |
|
|
#define MAX_MAX_HEADER_LIST_SIZE 16777215u |
51 |
|
|
#define DEFAULT_MAX_HEADER_LIST_PAIRS 128u |
52 |
|
|
|
53 |
|
|
enum nghttp2_session_type { |
54 |
|
|
NGHTTP2_SESSION_SERVER, |
55 |
|
|
NGHTTP2_SESSION_CLIENT |
56 |
|
|
}; |
57 |
|
|
|
58 |
|
|
enum nghttp2_stream_flags { |
59 |
|
|
NGHTTP2_STREAM_FLAG_NONE = 0x0, |
60 |
|
|
// Writable side has ended |
61 |
|
|
NGHTTP2_STREAM_FLAG_SHUT = 0x1, |
62 |
|
|
// Reading has started |
63 |
|
|
NGHTTP2_STREAM_FLAG_READ_START = 0x2, |
64 |
|
|
// Reading is paused |
65 |
|
|
NGHTTP2_STREAM_FLAG_READ_PAUSED = 0x4, |
66 |
|
|
// Stream is closed |
67 |
|
|
NGHTTP2_STREAM_FLAG_CLOSED = 0x8, |
68 |
|
|
// Stream is destroyed |
69 |
|
|
NGHTTP2_STREAM_FLAG_DESTROYED = 0x10, |
70 |
|
|
// Stream has trailers |
71 |
|
|
NGHTTP2_STREAM_FLAG_TRAILERS = 0x20, |
72 |
|
|
// Stream has received all the data it can |
73 |
|
|
NGHTTP2_STREAM_FLAG_EOS = 0x40 |
74 |
|
|
}; |
75 |
|
|
|
76 |
|
|
enum nghttp2_stream_options { |
77 |
|
|
// Stream is not going to have any DATA frames |
78 |
|
|
STREAM_OPTION_EMPTY_PAYLOAD = 0x1, |
79 |
|
|
// Stream might have trailing headers |
80 |
|
|
STREAM_OPTION_GET_TRAILERS = 0x2, |
81 |
|
|
}; |
82 |
|
|
|
83 |
✗✓ |
499034 |
struct nghttp2_stream_write : public MemoryRetainer { |
84 |
|
|
WriteWrap* req_wrap = nullptr; |
85 |
|
|
uv_buf_t buf; |
86 |
|
|
|
87 |
|
63985 |
inline explicit nghttp2_stream_write(uv_buf_t buf_) : buf(buf_) {} |
88 |
|
30713 |
inline nghttp2_stream_write(WriteWrap* req, uv_buf_t buf_) : |
89 |
|
30713 |
req_wrap(req), buf(buf_) {} |
90 |
|
|
|
91 |
|
|
void MemoryInfo(MemoryTracker* tracker) const override; |
92 |
|
|
SET_MEMORY_INFO_NAME(nghttp2_stream_write) |
93 |
|
|
SET_SELF_SIZE(nghttp2_stream_write) |
94 |
|
|
}; |
95 |
|
|
|
96 |
✗✓ |
213966 |
struct nghttp2_header : public MemoryRetainer { |
97 |
|
|
nghttp2_rcbuf* name = nullptr; |
98 |
|
|
nghttp2_rcbuf* value = nullptr; |
99 |
|
|
uint8_t flags = 0; |
100 |
|
|
|
101 |
|
|
void MemoryInfo(MemoryTracker* tracker) const override; |
102 |
|
|
SET_MEMORY_INFO_NAME(nghttp2_header) |
103 |
|
|
SET_SELF_SIZE(nghttp2_header) |
104 |
|
|
}; |
105 |
|
|
|
106 |
|
|
|
107 |
|
|
// Unlike the HTTP/1 implementation, the HTTP/2 implementation is not limited |
108 |
|
|
// to a fixed number of known supported HTTP methods. These constants, therefore |
109 |
|
|
// are provided strictly as a convenience to users and are exposed via the |
110 |
|
|
// require('http2').constants object. |
111 |
|
|
#define HTTP_KNOWN_METHODS(V) \ |
112 |
|
|
V(ACL, "ACL") \ |
113 |
|
|
V(BASELINE_CONTROL, "BASELINE-CONTROL") \ |
114 |
|
|
V(BIND, "BIND") \ |
115 |
|
|
V(CHECKIN, "CHECKIN") \ |
116 |
|
|
V(CHECKOUT, "CHECKOUT") \ |
117 |
|
|
V(CONNECT, "CONNECT") \ |
118 |
|
|
V(COPY, "COPY") \ |
119 |
|
|
V(DELETE, "DELETE") \ |
120 |
|
|
V(GET, "GET") \ |
121 |
|
|
V(HEAD, "HEAD") \ |
122 |
|
|
V(LABEL, "LABEL") \ |
123 |
|
|
V(LINK, "LINK") \ |
124 |
|
|
V(LOCK, "LOCK") \ |
125 |
|
|
V(MERGE, "MERGE") \ |
126 |
|
|
V(MKACTIVITY, "MKACTIVITY") \ |
127 |
|
|
V(MKCALENDAR, "MKCALENDAR") \ |
128 |
|
|
V(MKCOL, "MKCOL") \ |
129 |
|
|
V(MKREDIRECTREF, "MKREDIRECTREF") \ |
130 |
|
|
V(MKWORKSPACE, "MKWORKSPACE") \ |
131 |
|
|
V(MOVE, "MOVE") \ |
132 |
|
|
V(OPTIONS, "OPTIONS") \ |
133 |
|
|
V(ORDERPATCH, "ORDERPATCH") \ |
134 |
|
|
V(PATCH, "PATCH") \ |
135 |
|
|
V(POST, "POST") \ |
136 |
|
|
V(PRI, "PRI") \ |
137 |
|
|
V(PROPFIND, "PROPFIND") \ |
138 |
|
|
V(PROPPATCH, "PROPPATCH") \ |
139 |
|
|
V(PUT, "PUT") \ |
140 |
|
|
V(REBIND, "REBIND") \ |
141 |
|
|
V(REPORT, "REPORT") \ |
142 |
|
|
V(SEARCH, "SEARCH") \ |
143 |
|
|
V(TRACE, "TRACE") \ |
144 |
|
|
V(UNBIND, "UNBIND") \ |
145 |
|
|
V(UNCHECKOUT, "UNCHECKOUT") \ |
146 |
|
|
V(UNLINK, "UNLINK") \ |
147 |
|
|
V(UNLOCK, "UNLOCK") \ |
148 |
|
|
V(UPDATE, "UPDATE") \ |
149 |
|
|
V(UPDATEREDIRECTREF, "UPDATEREDIRECTREF") \ |
150 |
|
|
V(VERSION_CONTROL, "VERSION-CONTROL") |
151 |
|
|
|
152 |
|
|
// These are provided strictly as a convenience to users and are exposed via the |
153 |
|
|
// require('http2').constants objects |
154 |
|
|
#define HTTP_KNOWN_HEADERS(V) \ |
155 |
|
|
V(STATUS, ":status") \ |
156 |
|
|
V(METHOD, ":method") \ |
157 |
|
|
V(AUTHORITY, ":authority") \ |
158 |
|
|
V(SCHEME, ":scheme") \ |
159 |
|
|
V(PATH, ":path") \ |
160 |
|
|
V(PROTOCOL, ":protocol") \ |
161 |
|
|
V(ACCEPT_CHARSET, "accept-charset") \ |
162 |
|
|
V(ACCEPT_ENCODING, "accept-encoding") \ |
163 |
|
|
V(ACCEPT_LANGUAGE, "accept-language") \ |
164 |
|
|
V(ACCEPT_RANGES, "accept-ranges") \ |
165 |
|
|
V(ACCEPT, "accept") \ |
166 |
|
|
V(ACCESS_CONTROL_ALLOW_CREDENTIALS, "access-control-allow-credentials") \ |
167 |
|
|
V(ACCESS_CONTROL_ALLOW_HEADERS, "access-control-allow-headers") \ |
168 |
|
|
V(ACCESS_CONTROL_ALLOW_METHODS, "access-control-allow-methods") \ |
169 |
|
|
V(ACCESS_CONTROL_ALLOW_ORIGIN, "access-control-allow-origin") \ |
170 |
|
|
V(ACCESS_CONTROL_EXPOSE_HEADERS, "access-control-expose-headers") \ |
171 |
|
|
V(ACCESS_CONTROL_MAX_AGE, "access-control-max-age") \ |
172 |
|
|
V(ACCESS_CONTROL_REQUEST_HEADERS, "access-control-request-headers") \ |
173 |
|
|
V(ACCESS_CONTROL_REQUEST_METHOD, "access-control-request-method") \ |
174 |
|
|
V(AGE, "age") \ |
175 |
|
|
V(ALLOW, "allow") \ |
176 |
|
|
V(AUTHORIZATION, "authorization") \ |
177 |
|
|
V(CACHE_CONTROL, "cache-control") \ |
178 |
|
|
V(CONNECTION, "connection") \ |
179 |
|
|
V(CONTENT_DISPOSITION, "content-disposition") \ |
180 |
|
|
V(CONTENT_ENCODING, "content-encoding") \ |
181 |
|
|
V(CONTENT_LANGUAGE, "content-language") \ |
182 |
|
|
V(CONTENT_LENGTH, "content-length") \ |
183 |
|
|
V(CONTENT_LOCATION, "content-location") \ |
184 |
|
|
V(CONTENT_MD5, "content-md5") \ |
185 |
|
|
V(CONTENT_RANGE, "content-range") \ |
186 |
|
|
V(CONTENT_TYPE, "content-type") \ |
187 |
|
|
V(COOKIE, "cookie") \ |
188 |
|
|
V(DATE, "date") \ |
189 |
|
|
V(DNT, "dnt") \ |
190 |
|
|
V(ETAG, "etag") \ |
191 |
|
|
V(EXPECT, "expect") \ |
192 |
|
|
V(EXPIRES, "expires") \ |
193 |
|
|
V(FORWARDED, "forwarded") \ |
194 |
|
|
V(FROM, "from") \ |
195 |
|
|
V(HOST, "host") \ |
196 |
|
|
V(IF_MATCH, "if-match") \ |
197 |
|
|
V(IF_MODIFIED_SINCE, "if-modified-since") \ |
198 |
|
|
V(IF_NONE_MATCH, "if-none-match") \ |
199 |
|
|
V(IF_RANGE, "if-range") \ |
200 |
|
|
V(IF_UNMODIFIED_SINCE, "if-unmodified-since") \ |
201 |
|
|
V(LAST_MODIFIED, "last-modified") \ |
202 |
|
|
V(LINK, "link") \ |
203 |
|
|
V(LOCATION, "location") \ |
204 |
|
|
V(MAX_FORWARDS, "max-forwards") \ |
205 |
|
|
V(PREFER, "prefer") \ |
206 |
|
|
V(PROXY_AUTHENTICATE, "proxy-authenticate") \ |
207 |
|
|
V(PROXY_AUTHORIZATION, "proxy-authorization") \ |
208 |
|
|
V(RANGE, "range") \ |
209 |
|
|
V(REFERER, "referer") \ |
210 |
|
|
V(REFRESH, "refresh") \ |
211 |
|
|
V(RETRY_AFTER, "retry-after") \ |
212 |
|
|
V(SERVER, "server") \ |
213 |
|
|
V(SET_COOKIE, "set-cookie") \ |
214 |
|
|
V(STRICT_TRANSPORT_SECURITY, "strict-transport-security") \ |
215 |
|
|
V(TRAILER, "trailer") \ |
216 |
|
|
V(TRANSFER_ENCODING, "transfer-encoding") \ |
217 |
|
|
V(TE, "te") \ |
218 |
|
|
V(TK, "tk") \ |
219 |
|
|
V(UPGRADE_INSECURE_REQUESTS, "upgrade-insecure-requests") \ |
220 |
|
|
V(UPGRADE, "upgrade") \ |
221 |
|
|
V(USER_AGENT, "user-agent") \ |
222 |
|
|
V(VARY, "vary") \ |
223 |
|
|
V(VIA, "via") \ |
224 |
|
|
V(WARNING, "warning") \ |
225 |
|
|
V(WWW_AUTHENTICATE, "www-authenticate") \ |
226 |
|
|
V(X_CONTENT_TYPE_OPTIONS, "x-content-type-options") \ |
227 |
|
|
V(X_FRAME_OPTIONS, "x-frame-options") \ |
228 |
|
|
V(HTTP2_SETTINGS, "http2-settings") \ |
229 |
|
|
V(KEEP_ALIVE, "keep-alive") \ |
230 |
|
|
V(PROXY_CONNECTION, "proxy-connection") |
231 |
|
|
|
232 |
|
|
enum http_known_headers { |
233 |
|
|
HTTP_KNOWN_HEADER_MIN, |
234 |
|
|
#define V(name, value) HTTP_HEADER_##name, |
235 |
|
|
HTTP_KNOWN_HEADERS(V) |
236 |
|
|
#undef V |
237 |
|
|
HTTP_KNOWN_HEADER_MAX |
238 |
|
|
}; |
239 |
|
|
|
240 |
|
|
// While some of these codes are used within the HTTP/2 implementation in |
241 |
|
|
// core, they are provided strictly as a convenience to users and are exposed |
242 |
|
|
// via the require('http2').constants object. |
243 |
|
|
#define HTTP_STATUS_CODES(V) \ |
244 |
|
|
V(CONTINUE, 100) \ |
245 |
|
|
V(SWITCHING_PROTOCOLS, 101) \ |
246 |
|
|
V(PROCESSING, 102) \ |
247 |
|
|
V(EARLY_HINTS, 103) \ |
248 |
|
|
V(OK, 200) \ |
249 |
|
|
V(CREATED, 201) \ |
250 |
|
|
V(ACCEPTED, 202) \ |
251 |
|
|
V(NON_AUTHORITATIVE_INFORMATION, 203) \ |
252 |
|
|
V(NO_CONTENT, 204) \ |
253 |
|
|
V(RESET_CONTENT, 205) \ |
254 |
|
|
V(PARTIAL_CONTENT, 206) \ |
255 |
|
|
V(MULTI_STATUS, 207) \ |
256 |
|
|
V(ALREADY_REPORTED, 208) \ |
257 |
|
|
V(IM_USED, 226) \ |
258 |
|
|
V(MULTIPLE_CHOICES, 300) \ |
259 |
|
|
V(MOVED_PERMANENTLY, 301) \ |
260 |
|
|
V(FOUND, 302) \ |
261 |
|
|
V(SEE_OTHER, 303) \ |
262 |
|
|
V(NOT_MODIFIED, 304) \ |
263 |
|
|
V(USE_PROXY, 305) \ |
264 |
|
|
V(TEMPORARY_REDIRECT, 307) \ |
265 |
|
|
V(PERMANENT_REDIRECT, 308) \ |
266 |
|
|
V(BAD_REQUEST, 400) \ |
267 |
|
|
V(UNAUTHORIZED, 401) \ |
268 |
|
|
V(PAYMENT_REQUIRED, 402) \ |
269 |
|
|
V(FORBIDDEN, 403) \ |
270 |
|
|
V(NOT_FOUND, 404) \ |
271 |
|
|
V(METHOD_NOT_ALLOWED, 405) \ |
272 |
|
|
V(NOT_ACCEPTABLE, 406) \ |
273 |
|
|
V(PROXY_AUTHENTICATION_REQUIRED, 407) \ |
274 |
|
|
V(REQUEST_TIMEOUT, 408) \ |
275 |
|
|
V(CONFLICT, 409) \ |
276 |
|
|
V(GONE, 410) \ |
277 |
|
|
V(LENGTH_REQUIRED, 411) \ |
278 |
|
|
V(PRECONDITION_FAILED, 412) \ |
279 |
|
|
V(PAYLOAD_TOO_LARGE, 413) \ |
280 |
|
|
V(URI_TOO_LONG, 414) \ |
281 |
|
|
V(UNSUPPORTED_MEDIA_TYPE, 415) \ |
282 |
|
|
V(RANGE_NOT_SATISFIABLE, 416) \ |
283 |
|
|
V(EXPECTATION_FAILED, 417) \ |
284 |
|
|
V(TEAPOT, 418) \ |
285 |
|
|
V(MISDIRECTED_REQUEST, 421) \ |
286 |
|
|
V(UNPROCESSABLE_ENTITY, 422) \ |
287 |
|
|
V(LOCKED, 423) \ |
288 |
|
|
V(FAILED_DEPENDENCY, 424) \ |
289 |
|
|
V(UNORDERED_COLLECTION, 425) \ |
290 |
|
|
V(UPGRADE_REQUIRED, 426) \ |
291 |
|
|
V(PRECONDITION_REQUIRED, 428) \ |
292 |
|
|
V(TOO_MANY_REQUESTS, 429) \ |
293 |
|
|
V(REQUEST_HEADER_FIELDS_TOO_LARGE, 431) \ |
294 |
|
|
V(UNAVAILABLE_FOR_LEGAL_REASONS, 451) \ |
295 |
|
|
V(INTERNAL_SERVER_ERROR, 500) \ |
296 |
|
|
V(NOT_IMPLEMENTED, 501) \ |
297 |
|
|
V(BAD_GATEWAY, 502) \ |
298 |
|
|
V(SERVICE_UNAVAILABLE, 503) \ |
299 |
|
|
V(GATEWAY_TIMEOUT, 504) \ |
300 |
|
|
V(HTTP_VERSION_NOT_SUPPORTED, 505) \ |
301 |
|
|
V(VARIANT_ALSO_NEGOTIATES, 506) \ |
302 |
|
|
V(INSUFFICIENT_STORAGE, 507) \ |
303 |
|
|
V(LOOP_DETECTED, 508) \ |
304 |
|
|
V(BANDWIDTH_LIMIT_EXCEEDED, 509) \ |
305 |
|
|
V(NOT_EXTENDED, 510) \ |
306 |
|
|
V(NETWORK_AUTHENTICATION_REQUIRED, 511) |
307 |
|
|
|
308 |
|
|
enum http_status_codes { |
309 |
|
|
#define V(name, code) HTTP_STATUS_##name = code, |
310 |
|
|
HTTP_STATUS_CODES(V) |
311 |
|
|
#undef V |
312 |
|
|
}; |
313 |
|
|
|
314 |
|
|
// The Padding Strategy determines the method by which extra padding is |
315 |
|
|
// selected for HEADERS and DATA frames. These are configurable via the |
316 |
|
|
// options passed in to a Http2Session object. |
317 |
|
|
enum padding_strategy_type { |
318 |
|
|
// No padding strategy. This is the default. |
319 |
|
|
PADDING_STRATEGY_NONE, |
320 |
|
|
// Attempts to ensure that the frame is 8-byte aligned |
321 |
|
|
PADDING_STRATEGY_ALIGNED, |
322 |
|
|
// Padding will ensure all data frames are maxFrameSize |
323 |
|
|
PADDING_STRATEGY_MAX, |
324 |
|
|
// Removed and turned into an alias because it is unreasonably expensive for |
325 |
|
|
// very little benefit. |
326 |
|
|
PADDING_STRATEGY_CALLBACK = PADDING_STRATEGY_ALIGNED |
327 |
|
|
}; |
328 |
|
|
|
329 |
|
|
enum session_state_flags { |
330 |
|
|
SESSION_STATE_NONE = 0x0, |
331 |
|
|
SESSION_STATE_HAS_SCOPE = 0x1, |
332 |
|
|
SESSION_STATE_WRITE_SCHEDULED = 0x2, |
333 |
|
|
SESSION_STATE_CLOSED = 0x4, |
334 |
|
|
SESSION_STATE_CLOSING = 0x8, |
335 |
|
|
SESSION_STATE_SENDING = 0x10, |
336 |
|
|
SESSION_STATE_WRITE_IN_PROGRESS = 0x20, |
337 |
|
|
SESSION_STATE_READING_STOPPED = 0x40, |
338 |
|
|
SESSION_STATE_NGHTTP2_RECV_PAUSED = 0x80 |
339 |
|
|
}; |
340 |
|
|
|
341 |
|
|
typedef uint32_t(*get_setting)(nghttp2_session* session, |
342 |
|
|
nghttp2_settings_id id); |
343 |
|
|
|
344 |
|
|
class Http2Session; |
345 |
|
|
class Http2Stream; |
346 |
|
|
|
347 |
|
|
// This scope should be present when any call into nghttp2 that may schedule |
348 |
|
|
// data to be written to the underlying transport is made, and schedules |
349 |
|
|
// such a write automatically once the scope is exited. |
350 |
|
|
class Http2Scope { |
351 |
|
|
public: |
352 |
|
|
explicit Http2Scope(Http2Stream* stream); |
353 |
|
|
explicit Http2Scope(Http2Session* session); |
354 |
|
|
~Http2Scope(); |
355 |
|
|
|
356 |
|
|
private: |
357 |
|
|
Http2Session* session_ = nullptr; |
358 |
|
|
Local<Object> session_handle_; |
359 |
|
|
}; |
360 |
|
|
|
361 |
|
|
// The Http2Options class is used to parse the options object passed in to |
362 |
|
|
// a Http2Session object and convert those into an appropriate nghttp2_option |
363 |
|
|
// struct. This is the primary mechanism by which the Http2Session object is |
364 |
|
|
// configured. |
365 |
|
|
class Http2Options { |
366 |
|
|
public: |
367 |
|
|
Http2Options(Environment* env, nghttp2_session_type type); |
368 |
|
|
|
369 |
|
582 |
~Http2Options() { |
370 |
|
582 |
nghttp2_option_del(options_); |
371 |
|
582 |
} |
372 |
|
|
|
373 |
|
582 |
nghttp2_option* operator*() const { |
374 |
|
582 |
return options_; |
375 |
|
|
} |
376 |
|
|
|
377 |
|
3 |
void SetMaxHeaderPairs(uint32_t max) { |
378 |
|
3 |
max_header_pairs_ = max; |
379 |
|
3 |
} |
380 |
|
|
|
381 |
|
582 |
uint32_t GetMaxHeaderPairs() const { |
382 |
|
582 |
return max_header_pairs_; |
383 |
|
|
} |
384 |
|
|
|
385 |
|
2 |
void SetPaddingStrategy(padding_strategy_type val) { |
386 |
|
2 |
padding_strategy_ = val; |
387 |
|
2 |
} |
388 |
|
|
|
389 |
|
582 |
padding_strategy_type GetPaddingStrategy() const { |
390 |
|
582 |
return padding_strategy_; |
391 |
|
|
} |
392 |
|
|
|
393 |
|
2 |
void SetMaxOutstandingPings(size_t max) { |
394 |
|
2 |
max_outstanding_pings_ = max; |
395 |
|
2 |
} |
396 |
|
|
|
397 |
|
582 |
size_t GetMaxOutstandingPings() { |
398 |
|
582 |
return max_outstanding_pings_; |
399 |
|
|
} |
400 |
|
|
|
401 |
|
2 |
void SetMaxOutstandingSettings(size_t max) { |
402 |
|
2 |
max_outstanding_settings_ = max; |
403 |
|
2 |
} |
404 |
|
|
|
405 |
|
582 |
size_t GetMaxOutstandingSettings() { |
406 |
|
582 |
return max_outstanding_settings_; |
407 |
|
|
} |
408 |
|
|
|
409 |
|
5 |
void SetMaxSessionMemory(uint64_t max) { |
410 |
|
5 |
max_session_memory_ = max; |
411 |
|
5 |
} |
412 |
|
|
|
413 |
|
582 |
uint64_t GetMaxSessionMemory() { |
414 |
|
582 |
return max_session_memory_; |
415 |
|
|
} |
416 |
|
|
|
417 |
|
|
private: |
418 |
|
|
nghttp2_option* options_; |
419 |
|
|
uint64_t max_session_memory_ = DEFAULT_MAX_SESSION_MEMORY; |
420 |
|
|
uint32_t max_header_pairs_ = DEFAULT_MAX_HEADER_LIST_PAIRS; |
421 |
|
|
padding_strategy_type padding_strategy_ = PADDING_STRATEGY_NONE; |
422 |
|
|
size_t max_outstanding_pings_ = DEFAULT_MAX_PINGS; |
423 |
|
|
size_t max_outstanding_settings_ = DEFAULT_MAX_SETTINGS; |
424 |
|
|
}; |
425 |
|
|
|
426 |
|
|
class Http2Priority { |
427 |
|
|
public: |
428 |
|
|
Http2Priority(Environment* env, |
429 |
|
|
Local<Value> parent, |
430 |
|
|
Local<Value> weight, |
431 |
|
|
Local<Value> exclusive); |
432 |
|
|
|
433 |
|
11686 |
nghttp2_priority_spec* operator*() { |
434 |
|
11686 |
return &spec; |
435 |
|
|
} |
436 |
|
|
private: |
437 |
|
|
nghttp2_priority_spec spec; |
438 |
|
|
}; |
439 |
|
|
|
440 |
✗✓ |
48748 |
class Http2StreamListener : public StreamListener { |
441 |
|
|
public: |
442 |
|
|
uv_buf_t OnStreamAlloc(size_t suggested_size) override; |
443 |
|
|
void OnStreamRead(ssize_t nread, const uv_buf_t& buf) override; |
444 |
|
|
}; |
445 |
|
|
|
446 |
|
|
class Http2Stream : public AsyncWrap, |
447 |
|
|
public StreamBase { |
448 |
|
|
public: |
449 |
|
|
static Http2Stream* New( |
450 |
|
|
Http2Session* session, |
451 |
|
|
int32_t id, |
452 |
|
|
nghttp2_headers_category category = NGHTTP2_HCAT_HEADERS, |
453 |
|
|
int options = 0); |
454 |
|
|
~Http2Stream() override; |
455 |
|
|
|
456 |
|
|
nghttp2_stream* operator*(); |
457 |
|
|
|
458 |
|
135391 |
Http2Session* session() { return session_; } |
459 |
|
17 |
const Http2Session* session() const { return session_; } |
460 |
|
|
|
461 |
|
|
void EmitStatistics(); |
462 |
|
|
|
463 |
|
|
// Required for StreamBase |
464 |
|
|
int ReadStart() override; |
465 |
|
|
|
466 |
|
|
// Required for StreamBase |
467 |
|
|
int ReadStop() override; |
468 |
|
|
|
469 |
|
|
// Required for StreamBase |
470 |
|
|
int DoShutdown(ShutdownWrap* req_wrap) override; |
471 |
|
|
|
472 |
|
15 |
bool HasWantsWrite() const override { return true; } |
473 |
|
|
|
474 |
|
|
// Initiate a response on this stream. |
475 |
|
|
int SubmitResponse(nghttp2_nv* nva, size_t len, int options); |
476 |
|
|
|
477 |
|
|
// Submit informational headers for this stream |
478 |
|
|
int SubmitInfo(nghttp2_nv* nva, size_t len); |
479 |
|
|
|
480 |
|
|
// Submit trailing headers for this stream |
481 |
|
|
int SubmitTrailers(nghttp2_nv* nva, size_t len); |
482 |
|
|
void OnTrailers(); |
483 |
|
|
|
484 |
|
|
// Submit a PRIORITY frame for this stream |
485 |
|
|
int SubmitPriority(nghttp2_priority_spec* prispec, bool silent = false); |
486 |
|
|
|
487 |
|
|
// Submits an RST_STREAM frame using the given code |
488 |
|
|
void SubmitRstStream(const uint32_t code); |
489 |
|
|
|
490 |
|
|
void FlushRstStream(); |
491 |
|
|
|
492 |
|
|
// Submits a PUSH_PROMISE frame with this stream as the parent. |
493 |
|
|
Http2Stream* SubmitPushPromise( |
494 |
|
|
nghttp2_nv* nva, |
495 |
|
|
size_t len, |
496 |
|
|
int32_t* ret, |
497 |
|
|
int options = 0); |
498 |
|
|
|
499 |
|
|
|
500 |
|
|
void Close(int32_t code); |
501 |
|
|
|
502 |
|
|
// Destroy this stream instance and free all held memory. |
503 |
|
|
void Destroy(); |
504 |
|
|
|
505 |
|
372731 |
inline bool IsDestroyed() const { |
506 |
|
372731 |
return flags_ & NGHTTP2_STREAM_FLAG_DESTROYED; |
507 |
|
|
} |
508 |
|
|
|
509 |
|
47562 |
inline bool IsWritable() const { |
510 |
|
47562 |
return !(flags_ & NGHTTP2_STREAM_FLAG_SHUT); |
511 |
|
|
} |
512 |
|
|
|
513 |
|
|
inline bool IsPaused() const { |
514 |
|
|
return flags_ & NGHTTP2_STREAM_FLAG_READ_PAUSED; |
515 |
|
|
} |
516 |
|
|
|
517 |
|
|
inline bool IsClosed() const { |
518 |
|
|
return flags_ & NGHTTP2_STREAM_FLAG_CLOSED; |
519 |
|
|
} |
520 |
|
|
|
521 |
|
12558 |
inline bool HasTrailers() const { |
522 |
|
12558 |
return flags_ & NGHTTP2_STREAM_FLAG_TRAILERS; |
523 |
|
|
} |
524 |
|
|
|
525 |
|
|
// Returns true if this stream is in the reading state, which occurs when |
526 |
|
|
// the NGHTTP2_STREAM_FLAG_READ_START flag has been set and the |
527 |
|
|
// NGHTTP2_STREAM_FLAG_READ_PAUSED flag is *not* set. |
528 |
|
12813 |
inline bool IsReading() const { |
529 |
✓✓✓✓
|
24590 |
return flags_ & NGHTTP2_STREAM_FLAG_READ_START && |
530 |
|
24590 |
!(flags_ & NGHTTP2_STREAM_FLAG_READ_PAUSED); |
531 |
|
|
} |
532 |
|
|
|
533 |
|
|
// Returns the RST_STREAM code used to close this stream |
534 |
|
|
inline int32_t code() const { return code_; } |
535 |
|
|
|
536 |
|
|
// Returns the stream identifier for this stream |
537 |
|
140707 |
inline int32_t id() const { return id_; } |
538 |
|
|
|
539 |
|
|
inline void IncrementAvailableOutboundLength(size_t amount); |
540 |
|
|
inline void DecrementAvailableOutboundLength(size_t amount); |
541 |
|
|
|
542 |
|
|
bool AddHeader(nghttp2_rcbuf* name, nghttp2_rcbuf* value, uint8_t flags); |
543 |
|
|
|
544 |
|
23259 |
inline std::vector<nghttp2_header> move_headers() { |
545 |
|
23259 |
return std::move(current_headers_); |
546 |
|
|
} |
547 |
|
|
|
548 |
|
23259 |
inline nghttp2_headers_category headers_category() const { |
549 |
|
23259 |
return current_headers_category_; |
550 |
|
|
} |
551 |
|
|
|
552 |
|
|
void StartHeaders(nghttp2_headers_category category); |
553 |
|
|
|
554 |
|
|
// Required for StreamBase |
555 |
|
49149 |
bool IsAlive() override { |
556 |
|
49149 |
return true; |
557 |
|
|
} |
558 |
|
|
|
559 |
|
|
// Required for StreamBase |
560 |
|
|
bool IsClosing() override { |
561 |
|
|
return false; |
562 |
|
|
} |
563 |
|
|
|
564 |
|
129632 |
AsyncWrap* GetAsyncWrap() override { return this; } |
565 |
|
|
|
566 |
|
|
int DoWrite(WriteWrap* w, uv_buf_t* bufs, size_t count, |
567 |
|
|
uv_stream_t* send_handle) override; |
568 |
|
|
|
569 |
|
4 |
void MemoryInfo(MemoryTracker* tracker) const override { |
570 |
|
4 |
tracker->TrackField("current_headers", current_headers_); |
571 |
|
4 |
tracker->TrackField("queue", queue_); |
572 |
|
4 |
} |
573 |
|
|
|
574 |
|
4 |
SET_MEMORY_INFO_NAME(Http2Stream) |
575 |
|
4 |
SET_SELF_SIZE(Http2Stream) |
576 |
|
|
|
577 |
|
|
std::string diagnostic_name() const override; |
578 |
|
|
|
579 |
|
|
// JavaScript API |
580 |
|
|
static void GetID(const FunctionCallbackInfo<Value>& args); |
581 |
|
|
static void Destroy(const FunctionCallbackInfo<Value>& args); |
582 |
|
|
static void Priority(const FunctionCallbackInfo<Value>& args); |
583 |
|
|
static void PushPromise(const FunctionCallbackInfo<Value>& args); |
584 |
|
|
static void RefreshState(const FunctionCallbackInfo<Value>& args); |
585 |
|
|
static void Info(const FunctionCallbackInfo<Value>& args); |
586 |
|
|
static void Trailers(const FunctionCallbackInfo<Value>& args); |
587 |
|
|
static void Respond(const FunctionCallbackInfo<Value>& args); |
588 |
|
|
static void RstStream(const FunctionCallbackInfo<Value>& args); |
589 |
|
|
|
590 |
|
|
class Provider; |
591 |
|
|
|
592 |
|
|
struct Statistics { |
593 |
|
|
uint64_t start_time; |
594 |
|
|
uint64_t end_time; |
595 |
|
|
uint64_t first_header; // Time first header was received |
596 |
|
|
uint64_t first_byte; // Time first DATA frame byte was received |
597 |
|
|
uint64_t first_byte_sent; // Time first DATA frame byte was sent |
598 |
|
|
uint64_t sent_bytes; |
599 |
|
|
uint64_t received_bytes; |
600 |
|
|
}; |
601 |
|
|
|
602 |
|
|
Statistics statistics_ = {}; |
603 |
|
|
|
604 |
|
|
private: |
605 |
|
|
Http2Stream(Http2Session* session, |
606 |
|
|
v8::Local<v8::Object> obj, |
607 |
|
|
int32_t id, |
608 |
|
|
nghttp2_headers_category category, |
609 |
|
|
int options); |
610 |
|
|
|
611 |
|
|
Http2Session* session_ = nullptr; // The Parent HTTP/2 Session |
612 |
|
|
int32_t id_ = 0; // The Stream Identifier |
613 |
|
|
int32_t code_ = NGHTTP2_NO_ERROR; // The RST_STREAM code (if any) |
614 |
|
|
int flags_ = NGHTTP2_STREAM_FLAG_NONE; // Internal state flags |
615 |
|
|
|
616 |
|
|
uint32_t max_header_pairs_ = DEFAULT_MAX_HEADER_LIST_PAIRS; |
617 |
|
|
uint32_t max_header_length_ = DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE; |
618 |
|
|
|
619 |
|
|
// The Current Headers block... As headers are received for this stream, |
620 |
|
|
// they are temporarily stored here until the OnFrameReceived is called |
621 |
|
|
// signalling the end of the HEADERS frame |
622 |
|
|
nghttp2_headers_category current_headers_category_ = NGHTTP2_HCAT_HEADERS; |
623 |
|
|
uint32_t current_headers_length_ = 0; // total number of octets |
624 |
|
|
std::vector<nghttp2_header> current_headers_; |
625 |
|
|
|
626 |
|
|
// This keeps track of the amount of data read from the socket while the |
627 |
|
|
// socket was in paused mode. When `ReadStart()` is called (and not before |
628 |
|
|
// then), we tell nghttp2 that we consumed that data to get proper |
629 |
|
|
// backpressure handling. |
630 |
|
|
size_t inbound_consumed_data_while_paused_ = 0; |
631 |
|
|
|
632 |
|
|
// Outbound Data... This is the data written by the JS layer that is |
633 |
|
|
// waiting to be written out to the socket. |
634 |
|
|
std::queue<nghttp2_stream_write> queue_; |
635 |
|
|
size_t available_outbound_length_ = 0; |
636 |
|
|
|
637 |
|
|
Http2StreamListener stream_listener_; |
638 |
|
|
|
639 |
|
|
friend class Http2Session; |
640 |
|
|
}; |
641 |
|
|
|
642 |
|
|
class Http2Stream::Provider { |
643 |
|
|
public: |
644 |
|
|
Provider(Http2Stream* stream, int options); |
645 |
|
|
explicit Provider(int options); |
646 |
|
|
virtual ~Provider(); |
647 |
|
|
|
648 |
|
23312 |
nghttp2_data_provider* operator*() { |
649 |
✓✓ |
23312 |
return !empty_ ? &provider_ : nullptr; |
650 |
|
|
} |
651 |
|
|
|
652 |
|
|
class FD; |
653 |
|
|
class Stream; |
654 |
|
|
protected: |
655 |
|
|
nghttp2_data_provider provider_; |
656 |
|
|
|
657 |
|
|
private: |
658 |
|
|
bool empty_ = false; |
659 |
|
|
}; |
660 |
|
|
|
661 |
✗✓ |
23312 |
class Http2Stream::Provider::Stream : public Http2Stream::Provider { |
662 |
|
|
public: |
663 |
|
|
Stream(Http2Stream* stream, int options); |
664 |
|
|
explicit Stream(int options); |
665 |
|
|
|
666 |
|
|
static ssize_t OnRead(nghttp2_session* session, |
667 |
|
|
int32_t id, |
668 |
|
|
uint8_t* buf, |
669 |
|
|
size_t length, |
670 |
|
|
uint32_t* flags, |
671 |
|
|
nghttp2_data_source* source, |
672 |
|
|
void* user_data); |
673 |
|
|
}; |
674 |
|
|
|
675 |
|
|
// Indices for js_fields_, which serves as a way to communicate data with JS |
676 |
|
|
// land fast. In particular, we store information about the number/presence |
677 |
|
|
// of certain event listeners in JS, and skip calls from C++ into JS if they |
678 |
|
|
// are missing. |
679 |
|
|
enum SessionUint8Fields { |
680 |
|
|
kBitfield, // See below |
681 |
|
|
kSessionPriorityListenerCount, |
682 |
|
|
kSessionFrameErrorListenerCount, |
683 |
|
|
kSessionUint8FieldCount |
684 |
|
|
}; |
685 |
|
|
|
686 |
|
|
enum SessionBitfieldFlags { |
687 |
|
|
kSessionHasRemoteSettingsListeners, |
688 |
|
|
kSessionRemoteSettingsIsUpToDate, |
689 |
|
|
kSessionHasPingListeners, |
690 |
|
|
kSessionHasAltsvcListeners |
691 |
|
|
}; |
692 |
|
|
|
693 |
|
|
class Http2Session : public AsyncWrap, public StreamListener { |
694 |
|
|
public: |
695 |
|
|
Http2Session(Environment* env, |
696 |
|
|
Local<Object> wrap, |
697 |
|
|
nghttp2_session_type type = NGHTTP2_SESSION_SERVER); |
698 |
|
|
~Http2Session() override; |
699 |
|
|
|
700 |
|
|
class Http2Ping; |
701 |
|
|
class Http2Settings; |
702 |
|
|
class MemoryAllocatorInfo; |
703 |
|
|
|
704 |
|
|
void EmitStatistics(); |
705 |
|
|
|
706 |
|
31410 |
inline StreamBase* underlying_stream() { |
707 |
|
31410 |
return static_cast<StreamBase*>(stream_); |
708 |
|
|
} |
709 |
|
|
|
710 |
|
|
void Close(uint32_t code = NGHTTP2_NO_ERROR, |
711 |
|
|
bool socket_closed = false); |
712 |
|
|
void Consume(Local<Object> stream); |
713 |
|
|
void Goaway(uint32_t code, int32_t lastStreamID, |
714 |
|
|
const uint8_t* data, size_t len); |
715 |
|
|
void AltSvc(int32_t id, |
716 |
|
|
uint8_t* origin, |
717 |
|
|
size_t origin_len, |
718 |
|
|
uint8_t* value, |
719 |
|
|
size_t value_len); |
720 |
|
|
void Origin(nghttp2_origin_entry* ov, size_t count); |
721 |
|
|
|
722 |
|
|
uint8_t SendPendingData(); |
723 |
|
|
|
724 |
|
|
// Submits a new request. If the request is a success, assigned |
725 |
|
|
// will be a pointer to the Http2Stream instance assigned. |
726 |
|
|
// This only works if the session is a client session. |
727 |
|
|
Http2Stream* SubmitRequest( |
728 |
|
|
nghttp2_priority_spec* prispec, |
729 |
|
|
nghttp2_nv* nva, |
730 |
|
|
size_t len, |
731 |
|
|
int32_t* ret, |
732 |
|
|
int options = 0); |
733 |
|
|
|
734 |
|
|
inline nghttp2_session_type type() const { return session_type_; } |
735 |
|
|
|
736 |
|
24374 |
inline nghttp2_session* session() const { return session_; } |
737 |
|
|
|
738 |
|
65758 |
inline nghttp2_session* operator*() { return session_; } |
739 |
|
|
|
740 |
|
24374 |
inline uint32_t GetMaxHeaderPairs() const { return max_header_pairs_; } |
741 |
|
|
|
742 |
|
|
inline const char* TypeName() const; |
743 |
|
|
|
744 |
|
62844 |
inline bool IsDestroyed() { |
745 |
✓✓✗✓
|
62844 |
return (flags_ & SESSION_STATE_CLOSED) || session_ == nullptr; |
746 |
|
|
} |
747 |
|
|
|
748 |
|
|
// Schedule a write if nghttp2 indicates it wants to write to the socket. |
749 |
|
|
void MaybeScheduleWrite(); |
750 |
|
|
|
751 |
|
|
// Stop reading if nghttp2 doesn't want to anymore. |
752 |
|
|
void MaybeStopReading(); |
753 |
|
|
|
754 |
|
|
// Returns pointer to the stream, or nullptr if stream does not exist |
755 |
|
|
inline Http2Stream* FindStream(int32_t id); |
756 |
|
|
|
757 |
|
|
inline bool CanAddStream(); |
758 |
|
|
|
759 |
|
|
// Adds a stream instance to this session |
760 |
|
|
inline void AddStream(Http2Stream* stream); |
761 |
|
|
|
762 |
|
|
// Removes a stream instance from this session |
763 |
|
|
inline void RemoveStream(Http2Stream* stream); |
764 |
|
|
|
765 |
|
|
// Indicates whether there currently exist outgoing buffers for this stream. |
766 |
|
|
bool HasWritesOnSocketForStream(Http2Stream* stream); |
767 |
|
|
|
768 |
|
|
// Write data from stream_buf_ to the session |
769 |
|
|
ssize_t ConsumeHTTP2Data(); |
770 |
|
|
|
771 |
|
6 |
void MemoryInfo(MemoryTracker* tracker) const override { |
772 |
|
6 |
tracker->TrackField("streams", streams_); |
773 |
|
6 |
tracker->TrackField("outstanding_pings", outstanding_pings_); |
774 |
|
6 |
tracker->TrackField("outstanding_settings", outstanding_settings_); |
775 |
|
6 |
tracker->TrackField("outgoing_buffers", outgoing_buffers_); |
776 |
|
6 |
tracker->TrackFieldWithSize("stream_buf", stream_buf_.len); |
777 |
|
6 |
tracker->TrackFieldWithSize("outgoing_storage", outgoing_storage_.size()); |
778 |
|
|
tracker->TrackFieldWithSize("pending_rst_streams", |
779 |
|
6 |
pending_rst_streams_.size() * sizeof(int32_t)); |
780 |
|
6 |
} |
781 |
|
|
|
782 |
|
6 |
SET_MEMORY_INFO_NAME(Http2Session) |
783 |
|
6 |
SET_SELF_SIZE(Http2Session) |
784 |
|
|
|
785 |
|
|
std::string diagnostic_name() const override; |
786 |
|
|
|
787 |
|
|
// Schedule an RstStream for after the current write finishes. |
788 |
|
8 |
inline void AddPendingRstStream(int32_t stream_id) { |
789 |
|
8 |
pending_rst_streams_.emplace_back(stream_id); |
790 |
|
8 |
} |
791 |
|
|
|
792 |
|
24286 |
inline bool HasPendingRstStream(int32_t stream_id) { |
793 |
|
48572 |
return pending_rst_streams_.end() != std::find(pending_rst_streams_.begin(), |
794 |
|
|
pending_rst_streams_.end(), |
795 |
|
72858 |
stream_id); |
796 |
|
|
} |
797 |
|
|
|
798 |
|
|
// Handle reads/writes from the underlying network transport. |
799 |
|
|
uv_buf_t OnStreamAlloc(size_t suggested_size) override; |
800 |
|
|
void OnStreamRead(ssize_t nread, const uv_buf_t& buf) override; |
801 |
|
|
void OnStreamAfterWrite(WriteWrap* w, int status) override; |
802 |
|
|
|
803 |
|
|
// The JavaScript API |
804 |
|
|
static void New(const FunctionCallbackInfo<Value>& args); |
805 |
|
|
static void Consume(const FunctionCallbackInfo<Value>& args); |
806 |
|
|
static void Destroy(const FunctionCallbackInfo<Value>& args); |
807 |
|
|
static void Settings(const FunctionCallbackInfo<Value>& args); |
808 |
|
|
static void Request(const FunctionCallbackInfo<Value>& args); |
809 |
|
|
static void SetNextStreamID(const FunctionCallbackInfo<Value>& args); |
810 |
|
|
static void Goaway(const FunctionCallbackInfo<Value>& args); |
811 |
|
|
static void UpdateChunksSent(const FunctionCallbackInfo<Value>& args); |
812 |
|
|
static void RefreshState(const FunctionCallbackInfo<Value>& args); |
813 |
|
|
static void Ping(const FunctionCallbackInfo<Value>& args); |
814 |
|
|
static void AltSvc(const FunctionCallbackInfo<Value>& args); |
815 |
|
|
static void Origin(const FunctionCallbackInfo<Value>& args); |
816 |
|
|
|
817 |
|
|
template <get_setting fn> |
818 |
|
|
static void RefreshSettings(const FunctionCallbackInfo<Value>& args); |
819 |
|
|
|
820 |
|
|
uv_loop_t* event_loop() const { |
821 |
|
|
return env()->event_loop(); |
822 |
|
|
} |
823 |
|
|
|
824 |
|
|
std::unique_ptr<Http2Ping> PopPing(); |
825 |
|
|
Http2Ping* AddPing(std::unique_ptr<Http2Ping> ping); |
826 |
|
|
|
827 |
|
|
std::unique_ptr<Http2Settings> PopSettings(); |
828 |
|
|
Http2Settings* AddSettings(std::unique_ptr<Http2Settings> settings); |
829 |
|
|
|
830 |
|
156929 |
void IncrementCurrentSessionMemory(uint64_t amount) { |
831 |
|
156929 |
current_session_memory_ += amount; |
832 |
|
156929 |
} |
833 |
|
|
|
834 |
|
126012 |
void DecrementCurrentSessionMemory(uint64_t amount) { |
835 |
|
|
DCHECK_LE(amount, current_session_memory_); |
836 |
|
126012 |
current_session_memory_ -= amount; |
837 |
|
126012 |
} |
838 |
|
|
|
839 |
|
|
// Tell our custom memory allocator that this rcbuf is independent of |
840 |
|
|
// this session now, and may outlive it. |
841 |
|
|
void StopTrackingRcbuf(nghttp2_rcbuf* buf); |
842 |
|
|
|
843 |
|
|
// Returns the current session memory including memory allocated by nghttp2, |
844 |
|
|
// the current outbound storage queue, and pending writes. |
845 |
|
83974 |
uint64_t GetCurrentSessionMemory() { |
846 |
|
83974 |
uint64_t total = current_session_memory_ + sizeof(Http2Session); |
847 |
|
83974 |
total += current_nghttp2_memory_; |
848 |
|
83974 |
total += outgoing_storage_.size(); |
849 |
|
83974 |
return total; |
850 |
|
|
} |
851 |
|
|
|
852 |
|
|
// Return true if current_session_memory + amount is less than the max |
853 |
|
83974 |
bool IsAvailableSessionMemory(uint64_t amount) { |
854 |
|
83974 |
return GetCurrentSessionMemory() + amount <= max_session_memory_; |
855 |
|
|
} |
856 |
|
|
|
857 |
|
|
struct Statistics { |
858 |
|
|
uint64_t start_time; |
859 |
|
|
uint64_t end_time; |
860 |
|
|
uint64_t ping_rtt; |
861 |
|
|
uint64_t data_sent; |
862 |
|
|
uint64_t data_received; |
863 |
|
|
uint32_t frame_count; |
864 |
|
|
uint32_t frame_sent; |
865 |
|
|
int32_t stream_count; |
866 |
|
|
size_t max_concurrent_streams; |
867 |
|
|
double stream_average_duration; |
868 |
|
|
}; |
869 |
|
|
|
870 |
|
|
Statistics statistics_ = {}; |
871 |
|
|
|
872 |
|
|
private: |
873 |
|
|
// Frame Padding Strategies |
874 |
|
|
ssize_t OnDWordAlignedPadding(size_t frameLength, |
875 |
|
|
size_t maxPayloadLen); |
876 |
|
|
ssize_t OnMaxFrameSizePadding(size_t frameLength, |
877 |
|
|
size_t maxPayloadLen); |
878 |
|
|
|
879 |
|
|
// Frame Handler |
880 |
|
|
int HandleDataFrame(const nghttp2_frame* frame); |
881 |
|
|
void HandleGoawayFrame(const nghttp2_frame* frame); |
882 |
|
|
void HandleHeadersFrame(const nghttp2_frame* frame); |
883 |
|
|
void HandlePriorityFrame(const nghttp2_frame* frame); |
884 |
|
|
void HandleSettingsFrame(const nghttp2_frame* frame); |
885 |
|
|
void HandlePingFrame(const nghttp2_frame* frame); |
886 |
|
|
void HandleAltSvcFrame(const nghttp2_frame* frame); |
887 |
|
|
void HandleOriginFrame(const nghttp2_frame* frame); |
888 |
|
|
|
889 |
|
|
// nghttp2 callbacks |
890 |
|
|
static int OnBeginHeadersCallback( |
891 |
|
|
nghttp2_session* session, |
892 |
|
|
const nghttp2_frame* frame, |
893 |
|
|
void* user_data); |
894 |
|
|
static int OnHeaderCallback( |
895 |
|
|
nghttp2_session* session, |
896 |
|
|
const nghttp2_frame* frame, |
897 |
|
|
nghttp2_rcbuf* name, |
898 |
|
|
nghttp2_rcbuf* value, |
899 |
|
|
uint8_t flags, |
900 |
|
|
void* user_data); |
901 |
|
|
static int OnFrameReceive( |
902 |
|
|
nghttp2_session* session, |
903 |
|
|
const nghttp2_frame* frame, |
904 |
|
|
void* user_data); |
905 |
|
|
static int OnFrameNotSent( |
906 |
|
|
nghttp2_session* session, |
907 |
|
|
const nghttp2_frame* frame, |
908 |
|
|
int error_code, |
909 |
|
|
void* user_data); |
910 |
|
|
static int OnFrameSent( |
911 |
|
|
nghttp2_session* session, |
912 |
|
|
const nghttp2_frame* frame, |
913 |
|
|
void* user_data); |
914 |
|
|
static int OnStreamClose( |
915 |
|
|
nghttp2_session* session, |
916 |
|
|
int32_t id, |
917 |
|
|
uint32_t code, |
918 |
|
|
void* user_data); |
919 |
|
|
static int OnInvalidHeader( |
920 |
|
|
nghttp2_session* session, |
921 |
|
|
const nghttp2_frame* frame, |
922 |
|
|
nghttp2_rcbuf* name, |
923 |
|
|
nghttp2_rcbuf* value, |
924 |
|
|
uint8_t flags, |
925 |
|
|
void* user_data); |
926 |
|
|
static int OnDataChunkReceived( |
927 |
|
|
nghttp2_session* session, |
928 |
|
|
uint8_t flags, |
929 |
|
|
int32_t id, |
930 |
|
|
const uint8_t* data, |
931 |
|
|
size_t len, |
932 |
|
|
void* user_data); |
933 |
|
|
static ssize_t OnSelectPadding( |
934 |
|
|
nghttp2_session* session, |
935 |
|
|
const nghttp2_frame* frame, |
936 |
|
|
size_t maxPayloadLen, |
937 |
|
|
void* user_data); |
938 |
|
|
static int OnNghttpError( |
939 |
|
|
nghttp2_session* session, |
940 |
|
|
const char* message, |
941 |
|
|
size_t len, |
942 |
|
|
void* user_data); |
943 |
|
|
static int OnSendData( |
944 |
|
|
nghttp2_session* session, |
945 |
|
|
nghttp2_frame* frame, |
946 |
|
|
const uint8_t* framehd, |
947 |
|
|
size_t length, |
948 |
|
|
nghttp2_data_source* source, |
949 |
|
|
void* user_data); |
950 |
|
|
static int OnInvalidFrame( |
951 |
|
|
nghttp2_session* session, |
952 |
|
|
const nghttp2_frame* frame, |
953 |
|
|
int lib_error_code, |
954 |
|
|
void* user_data); |
955 |
|
|
|
956 |
|
|
struct Callbacks { |
957 |
|
|
inline explicit Callbacks(bool kHasGetPaddingCallback); |
958 |
|
|
inline ~Callbacks(); |
959 |
|
|
|
960 |
|
|
nghttp2_session_callbacks* callbacks; |
961 |
|
|
}; |
962 |
|
|
|
963 |
|
|
/* Use callback_struct_saved[kHasGetPaddingCallback ? 1 : 0] */ |
964 |
|
|
static const Callbacks callback_struct_saved[2]; |
965 |
|
|
|
966 |
|
|
// The underlying nghttp2_session handle |
967 |
|
|
nghttp2_session* session_; |
968 |
|
|
|
969 |
|
|
// JS-accessible numeric fields, as indexed by SessionUint8Fields. |
970 |
|
|
uint8_t js_fields_[kSessionUint8FieldCount] = {}; |
971 |
|
|
|
972 |
|
|
// The session type: client or server |
973 |
|
|
nghttp2_session_type session_type_; |
974 |
|
|
|
975 |
|
|
// The maximum number of header pairs permitted for streams on this session |
976 |
|
|
uint32_t max_header_pairs_ = DEFAULT_MAX_HEADER_LIST_PAIRS; |
977 |
|
|
|
978 |
|
|
// The maximum amount of memory allocated for this session |
979 |
|
|
uint64_t max_session_memory_ = DEFAULT_MAX_SESSION_MEMORY; |
980 |
|
|
uint64_t current_session_memory_ = 0; |
981 |
|
|
// The amount of memory allocated by nghttp2 internals |
982 |
|
|
uint64_t current_nghttp2_memory_ = 0; |
983 |
|
|
|
984 |
|
|
// The collection of active Http2Streams associated with this session |
985 |
|
|
std::unordered_map<int32_t, Http2Stream*> streams_; |
986 |
|
|
|
987 |
|
|
int flags_ = SESSION_STATE_NONE; |
988 |
|
|
|
989 |
|
|
// The StreamBase instance being used for i/o |
990 |
|
|
padding_strategy_type padding_strategy_ = PADDING_STRATEGY_NONE; |
991 |
|
|
|
992 |
|
|
// use this to allow timeout tracking during long-lasting writes |
993 |
|
|
uint32_t chunks_sent_since_last_write_ = 0; |
994 |
|
|
|
995 |
|
|
uv_buf_t stream_buf_ = uv_buf_init(nullptr, 0); |
996 |
|
|
// When processing input data, either stream_buf_ab_ or stream_buf_allocation_ |
997 |
|
|
// will be set. stream_buf_ab_ is lazily created from stream_buf_allocation_. |
998 |
|
|
v8::Global<v8::ArrayBuffer> stream_buf_ab_; |
999 |
|
|
AllocatedBuffer stream_buf_allocation_; |
1000 |
|
|
size_t stream_buf_offset_ = 0; |
1001 |
|
|
|
1002 |
|
|
size_t max_outstanding_pings_ = DEFAULT_MAX_PINGS; |
1003 |
|
|
std::queue<std::unique_ptr<Http2Ping>> outstanding_pings_; |
1004 |
|
|
|
1005 |
|
|
size_t max_outstanding_settings_ = DEFAULT_MAX_SETTINGS; |
1006 |
|
|
std::queue<std::unique_ptr<Http2Settings>> outstanding_settings_; |
1007 |
|
|
|
1008 |
|
|
std::vector<nghttp2_stream_write> outgoing_buffers_; |
1009 |
|
|
std::vector<uint8_t> outgoing_storage_; |
1010 |
|
|
size_t outgoing_length_ = 0; |
1011 |
|
|
std::vector<int32_t> pending_rst_streams_; |
1012 |
|
|
// Count streams that have been rejected while being opened. Exceeding a fixed |
1013 |
|
|
// limit will result in the session being destroyed, as an indication of a |
1014 |
|
|
// misbehaving peer. This counter is reset once new streams are being |
1015 |
|
|
// accepted again. |
1016 |
|
|
int32_t rejected_stream_count_ = 0; |
1017 |
|
|
// Also use the invalid frame count as a measure for rejecting input frames. |
1018 |
|
|
int32_t invalid_frame_count_ = 0; |
1019 |
|
|
|
1020 |
|
|
void PushOutgoingBuffer(nghttp2_stream_write&& write); |
1021 |
|
|
void CopyDataIntoOutgoing(const uint8_t* src, size_t src_length); |
1022 |
|
|
void ClearOutgoing(int status); |
1023 |
|
|
|
1024 |
|
|
friend class Http2Scope; |
1025 |
|
|
friend class Http2StreamListener; |
1026 |
|
|
}; |
1027 |
|
|
|
1028 |
✗✓ |
4 |
class Http2SessionPerformanceEntry : public PerformanceEntry { |
1029 |
|
|
public: |
1030 |
|
2 |
Http2SessionPerformanceEntry( |
1031 |
|
|
Environment* env, |
1032 |
|
|
const Http2Session::Statistics& stats, |
1033 |
|
|
nghttp2_session_type type) : |
1034 |
|
|
PerformanceEntry(env, "Http2Session", "http2", |
1035 |
|
|
stats.start_time, |
1036 |
|
|
stats.end_time), |
1037 |
|
|
ping_rtt_(stats.ping_rtt), |
1038 |
|
|
data_sent_(stats.data_sent), |
1039 |
|
|
data_received_(stats.data_received), |
1040 |
|
|
frame_count_(stats.frame_count), |
1041 |
|
|
frame_sent_(stats.frame_sent), |
1042 |
|
|
stream_count_(stats.stream_count), |
1043 |
|
|
max_concurrent_streams_(stats.max_concurrent_streams), |
1044 |
|
|
stream_average_duration_(stats.stream_average_duration), |
1045 |
|
2 |
session_type_(type) { } |
1046 |
|
|
|
1047 |
|
2 |
uint64_t ping_rtt() const { return ping_rtt_; } |
1048 |
|
2 |
uint64_t data_sent() const { return data_sent_; } |
1049 |
|
2 |
uint64_t data_received() const { return data_received_; } |
1050 |
|
2 |
uint32_t frame_count() const { return frame_count_; } |
1051 |
|
2 |
uint32_t frame_sent() const { return frame_sent_; } |
1052 |
|
2 |
int32_t stream_count() const { return stream_count_; } |
1053 |
|
2 |
size_t max_concurrent_streams() const { return max_concurrent_streams_; } |
1054 |
|
2 |
double stream_average_duration() const { return stream_average_duration_; } |
1055 |
|
2 |
nghttp2_session_type type() const { return session_type_; } |
1056 |
|
|
|
1057 |
|
2 |
void Notify(Local<Value> obj) { |
1058 |
|
2 |
PerformanceEntry::Notify(env(), kind(), obj); |
1059 |
|
2 |
} |
1060 |
|
|
|
1061 |
|
|
private: |
1062 |
|
|
uint64_t ping_rtt_; |
1063 |
|
|
uint64_t data_sent_; |
1064 |
|
|
uint64_t data_received_; |
1065 |
|
|
uint32_t frame_count_; |
1066 |
|
|
uint32_t frame_sent_; |
1067 |
|
|
int32_t stream_count_; |
1068 |
|
|
size_t max_concurrent_streams_; |
1069 |
|
|
double stream_average_duration_; |
1070 |
|
|
nghttp2_session_type session_type_; |
1071 |
|
|
}; |
1072 |
|
|
|
1073 |
✗✓ |
4 |
class Http2StreamPerformanceEntry : public PerformanceEntry { |
1074 |
|
|
public: |
1075 |
|
2 |
Http2StreamPerformanceEntry( |
1076 |
|
|
Environment* env, |
1077 |
|
|
int32_t id, |
1078 |
|
|
const Http2Stream::Statistics& stats) : |
1079 |
|
|
PerformanceEntry(env, "Http2Stream", "http2", |
1080 |
|
|
stats.start_time, |
1081 |
|
|
stats.end_time), |
1082 |
|
|
id_(id), |
1083 |
|
|
first_header_(stats.first_header), |
1084 |
|
|
first_byte_(stats.first_byte), |
1085 |
|
|
first_byte_sent_(stats.first_byte_sent), |
1086 |
|
|
sent_bytes_(stats.sent_bytes), |
1087 |
|
2 |
received_bytes_(stats.received_bytes) { } |
1088 |
|
|
|
1089 |
|
2 |
int32_t id() const { return id_; } |
1090 |
|
4 |
uint64_t first_header() const { return first_header_; } |
1091 |
|
2 |
uint64_t first_byte() const { return first_byte_; } |
1092 |
|
3 |
uint64_t first_byte_sent() const { return first_byte_sent_; } |
1093 |
|
2 |
uint64_t sent_bytes() const { return sent_bytes_; } |
1094 |
|
2 |
uint64_t received_bytes() const { return received_bytes_; } |
1095 |
|
|
|
1096 |
|
2 |
void Notify(Local<Value> obj) { |
1097 |
|
2 |
PerformanceEntry::Notify(env(), kind(), obj); |
1098 |
|
2 |
} |
1099 |
|
|
|
1100 |
|
|
private: |
1101 |
|
|
int32_t id_; |
1102 |
|
|
uint64_t first_header_; |
1103 |
|
|
uint64_t first_byte_; |
1104 |
|
|
uint64_t first_byte_sent_; |
1105 |
|
|
uint64_t sent_bytes_; |
1106 |
|
|
uint64_t received_bytes_; |
1107 |
|
|
}; |
1108 |
|
|
|
1109 |
✗✓ |
26 |
class Http2Session::Http2Ping : public AsyncWrap { |
1110 |
|
|
public: |
1111 |
|
|
explicit Http2Ping(Http2Session* session, v8::Local<v8::Object> obj); |
1112 |
|
|
|
1113 |
|
|
void MemoryInfo(MemoryTracker* tracker) const override { |
1114 |
|
|
tracker->TrackField("session", session_); |
1115 |
|
|
} |
1116 |
|
|
|
1117 |
|
|
SET_MEMORY_INFO_NAME(Http2Ping) |
1118 |
|
|
SET_SELF_SIZE(Http2Ping) |
1119 |
|
|
|
1120 |
|
|
void Send(const uint8_t* payload); |
1121 |
|
|
void Done(bool ack, const uint8_t* payload = nullptr); |
1122 |
|
|
void DetachFromSession(); |
1123 |
|
|
|
1124 |
|
|
private: |
1125 |
|
|
Http2Session* session_; |
1126 |
|
|
uint64_t startTime_; |
1127 |
|
|
}; |
1128 |
|
|
|
1129 |
|
|
// The Http2Settings class is used to parse the settings passed in for |
1130 |
|
|
// an Http2Session, converting those into an array of nghttp2_settings_entry |
1131 |
|
|
// structs. |
1132 |
✗✓ |
1197 |
class Http2Session::Http2Settings : public AsyncWrap { |
1133 |
|
|
public: |
1134 |
|
|
Http2Settings(Environment* env, |
1135 |
|
|
Http2Session* session, |
1136 |
|
|
v8::Local<v8::Object> obj, |
1137 |
|
|
uint64_t start_time = uv_hrtime()); |
1138 |
|
|
|
1139 |
|
2 |
void MemoryInfo(MemoryTracker* tracker) const override { |
1140 |
|
2 |
tracker->TrackField("session", session_); |
1141 |
|
2 |
} |
1142 |
|
|
|
1143 |
|
2 |
SET_MEMORY_INFO_NAME(Http2Settings) |
1144 |
|
2 |
SET_SELF_SIZE(Http2Settings) |
1145 |
|
|
|
1146 |
|
|
void Send(); |
1147 |
|
|
void Done(bool ack); |
1148 |
|
|
|
1149 |
|
|
// Returns a Buffer instance with the serialized SETTINGS payload |
1150 |
|
|
Local<Value> Pack(); |
1151 |
|
|
|
1152 |
|
|
// Resets the default values in the settings buffer |
1153 |
|
|
static void RefreshDefaults(Environment* env); |
1154 |
|
|
|
1155 |
|
|
// Update the local or remote settings for the given session |
1156 |
|
|
static void Update(Environment* env, |
1157 |
|
|
Http2Session* session, |
1158 |
|
|
get_setting fn); |
1159 |
|
|
|
1160 |
|
|
private: |
1161 |
|
|
void Init(); |
1162 |
|
|
Http2Session* session_; |
1163 |
|
|
uint64_t startTime_; |
1164 |
|
|
size_t count_ = 0; |
1165 |
|
|
nghttp2_settings_entry entries_[IDX_SETTINGS_COUNT]; |
1166 |
|
|
}; |
1167 |
|
|
|
1168 |
|
|
class ExternalHeader : |
1169 |
|
|
public String::ExternalOneByteStringResource { |
1170 |
|
|
public: |
1171 |
|
24856 |
explicit ExternalHeader(nghttp2_rcbuf* buf) |
1172 |
|
24856 |
: buf_(buf), vec_(nghttp2_rcbuf_get_buf(buf)) { |
1173 |
|
24856 |
} |
1174 |
|
|
|
1175 |
|
74568 |
~ExternalHeader() override { |
1176 |
|
24856 |
nghttp2_rcbuf_decref(buf_); |
1177 |
|
24856 |
buf_ = nullptr; |
1178 |
✗✓ |
49712 |
} |
1179 |
|
|
|
1180 |
|
49837 |
const char* data() const override { |
1181 |
|
49837 |
return const_cast<const char*>(reinterpret_cast<char*>(vec_.base)); |
1182 |
|
|
} |
1183 |
|
|
|
1184 |
|
99424 |
size_t length() const override { |
1185 |
|
99424 |
return vec_.len; |
1186 |
|
|
} |
1187 |
|
|
|
1188 |
|
|
static inline |
1189 |
|
1811 |
MaybeLocal<String> GetInternalizedString(Environment* env, |
1190 |
|
|
const nghttp2_vec& vec) { |
1191 |
|
|
return String::NewFromOneByte(env->isolate(), |
1192 |
|
|
vec.base, |
1193 |
|
|
v8::NewStringType::kInternalized, |
1194 |
|
1811 |
vec.len); |
1195 |
|
|
} |
1196 |
|
|
|
1197 |
|
|
template <bool may_internalize> |
1198 |
|
142350 |
static MaybeLocal<String> New(Http2Session* session, nghttp2_rcbuf* buf) { |
1199 |
|
142350 |
Environment* env = session->env(); |
1200 |
✓✓✓✓
|
142350 |
if (nghttp2_rcbuf_is_static(buf)) { |
1201 |
|
117437 |
auto& static_str_map = env->isolate_data()->http2_static_strs; |
1202 |
|
117437 |
v8::Eternal<v8::String>& eternal = static_str_map[buf]; |
1203 |
✓✓✓✓
|
117437 |
if (eternal.IsEmpty()) { |
1204 |
|
|
Local<String> str = |
1205 |
|
3510 |
GetInternalizedString(env, nghttp2_rcbuf_get_buf(buf)) |
1206 |
|
3510 |
.ToLocalChecked(); |
1207 |
|
1755 |
eternal.Set(env->isolate(), str); |
1208 |
|
1755 |
return str; |
1209 |
|
|
} |
1210 |
|
231364 |
return eternal.Get(env->isolate()); |
1211 |
|
|
} |
1212 |
|
|
|
1213 |
|
24913 |
nghttp2_vec vec = nghttp2_rcbuf_get_buf(buf); |
1214 |
✓✓✗✓
|
24913 |
if (vec.len == 0) { |
1215 |
|
1 |
nghttp2_rcbuf_decref(buf); |
1216 |
|
2 |
return String::Empty(env->isolate()); |
1217 |
|
|
} |
1218 |
|
|
|
1219 |
✓✗ |
56 |
if (may_internalize && vec.len < 64) { |
1220 |
|
56 |
nghttp2_rcbuf_decref(buf); |
1221 |
|
|
// This is a short header name, so there is a good chance V8 already has |
1222 |
|
|
// it internalized. |
1223 |
|
56 |
return GetInternalizedString(env, vec); |
1224 |
|
|
} |
1225 |
|
|
|
1226 |
|
24856 |
session->StopTrackingRcbuf(buf); |
1227 |
|
24856 |
ExternalHeader* h_str = new ExternalHeader(buf); |
1228 |
|
24856 |
MaybeLocal<String> str = String::NewExternalOneByte(env->isolate(), h_str); |
1229 |
✗✓✗✗
|
24856 |
if (str.IsEmpty()) |
1230 |
|
|
delete h_str; |
1231 |
|
|
|
1232 |
|
24856 |
return str; |
1233 |
|
|
} |
1234 |
|
|
|
1235 |
|
|
private: |
1236 |
|
|
nghttp2_rcbuf* buf_; |
1237 |
|
|
nghttp2_vec vec_; |
1238 |
|
|
}; |
1239 |
|
|
|
1240 |
|
|
class Headers { |
1241 |
|
|
public: |
1242 |
|
|
Headers(Isolate* isolate, Local<Context> context, Local<Array> headers); |
1243 |
|
23330 |
~Headers() = default; |
1244 |
|
|
|
1245 |
|
23330 |
nghttp2_nv* operator*() { |
1246 |
|
23330 |
return reinterpret_cast<nghttp2_nv*>(*buf_); |
1247 |
|
|
} |
1248 |
|
|
|
1249 |
|
23364 |
size_t length() const { |
1250 |
|
23364 |
return count_; |
1251 |
|
|
} |
1252 |
|
|
|
1253 |
|
|
private: |
1254 |
|
|
size_t count_; |
1255 |
|
|
MaybeStackBuffer<char, 3000> buf_; |
1256 |
|
|
}; |
1257 |
|
|
|
1258 |
|
|
class Origins { |
1259 |
|
|
public: |
1260 |
|
|
Origins(Isolate* isolate, |
1261 |
|
|
Local<Context> context, |
1262 |
|
|
Local<v8::String> origin_string, |
1263 |
|
|
size_t origin_count); |
1264 |
|
5 |
~Origins() = default; |
1265 |
|
|
|
1266 |
|
5 |
nghttp2_origin_entry* operator*() { |
1267 |
|
5 |
return reinterpret_cast<nghttp2_origin_entry*>(*buf_); |
1268 |
|
|
} |
1269 |
|
|
|
1270 |
|
5 |
size_t length() const { |
1271 |
|
5 |
return count_; |
1272 |
|
|
} |
1273 |
|
|
|
1274 |
|
|
private: |
1275 |
|
|
size_t count_; |
1276 |
|
|
MaybeStackBuffer<char, 512> buf_; |
1277 |
|
|
}; |
1278 |
|
|
|
1279 |
|
|
} // namespace http2 |
1280 |
|
|
} // namespace node |
1281 |
|
|
|
1282 |
|
|
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS |
1283 |
|
|
|
1284 |
|
|
#endif // SRC_NODE_HTTP2_H_ |