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