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