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 current_headers_.clear(); 405 } 406 headers_count()407 size_t headers_count() const { 408 return current_headers_.size(); 409 } 410 headers_category()411 nghttp2_headers_category headers_category() const { 412 return current_headers_category_; 413 } 414 415 void StartHeaders(nghttp2_headers_category category); 416 417 // Required for StreamBase IsAlive()418 bool IsAlive() override { 419 return true; 420 } 421 422 // Required for StreamBase IsClosing()423 bool IsClosing() override { 424 return false; 425 } 426 GetAsyncWrap()427 AsyncWrap* GetAsyncWrap() override { return this; } 428 429 int DoWrite(WriteWrap* w, uv_buf_t* bufs, size_t count, 430 uv_stream_t* send_handle) override; 431 432 void MemoryInfo(MemoryTracker* tracker) const override; 433 SET_MEMORY_INFO_NAME(Http2Stream) 434 SET_SELF_SIZE(Http2Stream) 435 436 std::string diagnostic_name() const override; 437 438 // JavaScript API 439 static void GetID(const v8::FunctionCallbackInfo<v8::Value>& args); 440 static void Destroy(const v8::FunctionCallbackInfo<v8::Value>& args); 441 static void Priority(const v8::FunctionCallbackInfo<v8::Value>& args); 442 static void PushPromise(const v8::FunctionCallbackInfo<v8::Value>& args); 443 static void RefreshState(const v8::FunctionCallbackInfo<v8::Value>& args); 444 static void Info(const v8::FunctionCallbackInfo<v8::Value>& args); 445 static void Trailers(const v8::FunctionCallbackInfo<v8::Value>& args); 446 static void Respond(const v8::FunctionCallbackInfo<v8::Value>& args); 447 static void RstStream(const v8::FunctionCallbackInfo<v8::Value>& args); 448 449 class Provider; 450 451 struct Statistics { 452 uint64_t start_time; 453 uint64_t end_time; 454 uint64_t first_header; // Time first header was received 455 uint64_t first_byte; // Time first DATA frame byte was received 456 uint64_t first_byte_sent; // Time first DATA frame byte was sent 457 uint64_t sent_bytes; 458 uint64_t received_bytes; 459 }; 460 461 Statistics statistics_ = {}; 462 463 private: 464 Http2Stream(Http2Session* session, 465 v8::Local<v8::Object> obj, 466 int32_t id, 467 nghttp2_headers_category category, 468 int options); 469 470 void EmitStatistics(); 471 472 BaseObjectWeakPtr<Http2Session> session_; // The Parent HTTP/2 Session 473 int32_t id_ = 0; // The Stream Identifier 474 int32_t code_ = NGHTTP2_NO_ERROR; // The RST_STREAM code (if any) 475 int flags_ = kStreamStateNone; // Internal state flags 476 477 uint32_t max_header_pairs_ = DEFAULT_MAX_HEADER_LIST_PAIRS; 478 uint32_t max_header_length_ = DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE; 479 480 // The Current Headers block... As headers are received for this stream, 481 // they are temporarily stored here until the OnFrameReceived is called 482 // signalling the end of the HEADERS frame 483 nghttp2_headers_category current_headers_category_ = NGHTTP2_HCAT_HEADERS; 484 uint32_t current_headers_length_ = 0; // total number of octets 485 std::vector<Http2Header> current_headers_; 486 487 // This keeps track of the amount of data read from the socket while the 488 // socket was in paused mode. When `ReadStart()` is called (and not before 489 // then), we tell nghttp2 that we consumed that data to get proper 490 // backpressure handling. 491 size_t inbound_consumed_data_while_paused_ = 0; 492 493 // Outbound Data... This is the data written by the JS layer that is 494 // waiting to be written out to the socket. 495 std::queue<NgHttp2StreamWrite> queue_; 496 size_t available_outbound_length_ = 0; 497 498 Http2StreamListener stream_listener_; 499 500 friend class Http2Session; 501 }; 502 503 class Http2Stream::Provider { 504 public: 505 Provider(Http2Stream* stream, int options); 506 explicit Provider(int options); 507 virtual ~Provider(); 508 509 nghttp2_data_provider* operator*() { 510 return !empty_ ? &provider_ : nullptr; 511 } 512 513 class FD; 514 class Stream; 515 protected: 516 nghttp2_data_provider provider_; 517 518 private: 519 bool empty_ = false; 520 }; 521 522 class Http2Stream::Provider::Stream : public Http2Stream::Provider { 523 public: 524 Stream(Http2Stream* stream, int options); 525 explicit Stream(int options); 526 527 static ssize_t OnRead(nghttp2_session* session, 528 int32_t id, 529 uint8_t* buf, 530 size_t length, 531 uint32_t* flags, 532 nghttp2_data_source* source, 533 void* user_data); 534 }; 535 536 struct SessionJSFields { 537 uint8_t bitfield; 538 uint8_t priority_listener_count; 539 uint8_t frame_error_listener_count; 540 uint32_t max_invalid_frames = 1000; 541 uint32_t max_rejected_streams = 100; 542 }; 543 544 // Indices for js_fields_, which serves as a way to communicate data with JS 545 // land fast. In particular, we store information about the number/presence 546 // of certain event listeners in JS, and skip calls from C++ into JS if they 547 // are missing. 548 enum SessionUint8Fields { 549 kBitfield = offsetof(SessionJSFields, bitfield), // See below 550 kSessionPriorityListenerCount = 551 offsetof(SessionJSFields, priority_listener_count), 552 kSessionFrameErrorListenerCount = 553 offsetof(SessionJSFields, frame_error_listener_count), 554 kSessionMaxInvalidFrames = offsetof(SessionJSFields, max_invalid_frames), 555 kSessionMaxRejectedStreams = offsetof(SessionJSFields, max_rejected_streams), 556 kSessionUint8FieldCount = sizeof(SessionJSFields) 557 }; 558 559 enum SessionBitfieldFlags { 560 kSessionHasRemoteSettingsListeners, 561 kSessionRemoteSettingsIsUpToDate, 562 kSessionHasPingListeners, 563 kSessionHasAltsvcListeners 564 }; 565 566 class Http2Session : public AsyncWrap, 567 public StreamListener, 568 public mem::NgLibMemoryManager<Http2Session, nghttp2_mem> { 569 public: 570 Http2Session(Http2State* http2_state, 571 v8::Local<v8::Object> wrap, 572 SessionType type = NGHTTP2_SESSION_SERVER); 573 ~Http2Session() override; 574 underlying_stream()575 StreamBase* underlying_stream() { 576 return static_cast<StreamBase*>(stream_); 577 } 578 579 void Close(uint32_t code = NGHTTP2_NO_ERROR, 580 bool socket_closed = false); 581 582 void Consume(v8::Local<v8::Object> stream); 583 584 void Goaway(uint32_t code, int32_t lastStreamID, 585 const uint8_t* data, size_t len); 586 587 void AltSvc(int32_t id, 588 uint8_t* origin, 589 size_t origin_len, 590 uint8_t* value, 591 size_t value_len); 592 593 void Origin(const Origins& origins); 594 595 uint8_t SendPendingData(); 596 597 // Submits a new request. If the request is a success, assigned 598 // will be a pointer to the Http2Stream instance assigned. 599 // This only works if the session is a client session. 600 Http2Stream* SubmitRequest( 601 const Http2Priority& priority, 602 const Http2Headers& headers, 603 int32_t* ret, 604 int options = 0); 605 type()606 SessionType type() const { return session_type_; } 607 session()608 nghttp2_session* session() const { return session_.get(); } 609 610 nghttp2_session* operator*() { return session_.get(); } 611 max_header_pairs()612 uint32_t max_header_pairs() const { return max_header_pairs_; } 613 614 const char* TypeName() const; 615 is_destroyed()616 bool is_destroyed() { 617 return (flags_ & kSessionStateClosed) || session_ == nullptr; 618 } 619 set_destroyed()620 void set_destroyed() { 621 flags_ |= kSessionStateClosed; 622 } 623 624 #define IS_FLAG(name, flag) \ 625 bool is_##name() const { return flags_ & flag; } \ 626 void set_##name(bool on = true) { \ 627 if (on) \ 628 flags_ |= flag; \ 629 else \ 630 flags_ &= ~flag; \ 631 } 632 633 IS_FLAG(in_scope, kSessionStateHasScope) 634 IS_FLAG(write_scheduled, kSessionStateWriteScheduled) 635 IS_FLAG(closing, kSessionStateClosing) 636 IS_FLAG(sending, kSessionStateSending) 637 IS_FLAG(write_in_progress, kSessionStateWriteInProgress) 638 IS_FLAG(reading_stopped, kSessionStateReadingStopped) 639 IS_FLAG(receive_paused, kSessionStateReceivePaused) 640 641 #undef IS_FLAG 642 643 // Schedule a write if nghttp2 indicates it wants to write to the socket. 644 void MaybeScheduleWrite(); 645 646 // Stop reading if nghttp2 doesn't want to anymore. 647 void MaybeStopReading(); 648 649 // Returns pointer to the stream, or nullptr if stream does not exist 650 BaseObjectPtr<Http2Stream> FindStream(int32_t id); 651 652 bool CanAddStream(); 653 654 // Adds a stream instance to this session 655 void AddStream(Http2Stream* stream); 656 657 // Removes a stream instance from this session 658 BaseObjectPtr<Http2Stream> RemoveStream(int32_t id); 659 660 // Indicates whether there currently exist outgoing buffers for this stream. 661 bool HasWritesOnSocketForStream(Http2Stream* stream); 662 663 // Write data from stream_buf_ to the session 664 ssize_t ConsumeHTTP2Data(); 665 666 void MemoryInfo(MemoryTracker* tracker) const override; 667 SET_MEMORY_INFO_NAME(Http2Session) 668 SET_SELF_SIZE(Http2Session) 669 670 std::string diagnostic_name() const override; 671 672 // Schedule an RstStream for after the current write finishes. AddPendingRstStream(int32_t stream_id)673 void AddPendingRstStream(int32_t stream_id) { 674 pending_rst_streams_.emplace_back(stream_id); 675 } 676 has_pending_rststream(int32_t stream_id)677 bool has_pending_rststream(int32_t stream_id) { 678 return pending_rst_streams_.end() != 679 std::find(pending_rst_streams_.begin(), 680 pending_rst_streams_.end(), 681 stream_id); 682 } 683 684 // Handle reads/writes from the underlying network transport. 685 uv_buf_t OnStreamAlloc(size_t suggested_size) override; 686 void OnStreamRead(ssize_t nread, const uv_buf_t& buf) override; 687 void OnStreamAfterWrite(WriteWrap* w, int status) override; 688 689 // Implementation for mem::NgLibMemoryManager 690 void CheckAllocatedSize(size_t previous_size) const; 691 void IncreaseAllocatedSize(size_t size); 692 void DecreaseAllocatedSize(size_t size); 693 694 // The JavaScript API 695 static void New(const v8::FunctionCallbackInfo<v8::Value>& args); 696 static void Consume(const v8::FunctionCallbackInfo<v8::Value>& args); 697 static void Receive(const v8::FunctionCallbackInfo<v8::Value>& args); 698 static void Destroy(const v8::FunctionCallbackInfo<v8::Value>& args); 699 static void Settings(const v8::FunctionCallbackInfo<v8::Value>& args); 700 static void Request(const v8::FunctionCallbackInfo<v8::Value>& args); 701 static void SetNextStreamID(const v8::FunctionCallbackInfo<v8::Value>& args); 702 static void SetLocalWindowSize( 703 const v8::FunctionCallbackInfo<v8::Value>& args); 704 static void Goaway(const v8::FunctionCallbackInfo<v8::Value>& args); 705 static void UpdateChunksSent(const v8::FunctionCallbackInfo<v8::Value>& args); 706 static void RefreshState(const v8::FunctionCallbackInfo<v8::Value>& args); 707 static void Ping(const v8::FunctionCallbackInfo<v8::Value>& args); 708 static void AltSvc(const v8::FunctionCallbackInfo<v8::Value>& args); 709 static void Origin(const v8::FunctionCallbackInfo<v8::Value>& args); 710 711 template <get_setting fn> 712 static void RefreshSettings(const v8::FunctionCallbackInfo<v8::Value>& args); 713 event_loop()714 uv_loop_t* event_loop() const { 715 return env()->event_loop(); 716 } 717 http2_state()718 Http2State* http2_state() const { return http2_state_.get(); } 719 720 BaseObjectPtr<Http2Ping> PopPing(); 721 bool AddPing(const uint8_t* data, v8::Local<v8::Function> callback); 722 723 BaseObjectPtr<Http2Settings> PopSettings(); 724 bool AddSettings(v8::Local<v8::Function> callback); 725 IncrementCurrentSessionMemory(uint64_t amount)726 void IncrementCurrentSessionMemory(uint64_t amount) { 727 current_session_memory_ += amount; 728 } 729 DecrementCurrentSessionMemory(uint64_t amount)730 void DecrementCurrentSessionMemory(uint64_t amount) { 731 DCHECK_LE(amount, current_session_memory_); 732 current_session_memory_ -= amount; 733 } 734 735 // Tell our custom memory allocator that this rcbuf is independent of 736 // this session now, and may outlive it. 737 void StopTrackingRcbuf(nghttp2_rcbuf* buf); 738 739 // Returns the current session memory including memory allocated by nghttp2, 740 // the current outbound storage queue, and pending writes. current_session_memory()741 uint64_t current_session_memory() const { 742 uint64_t total = current_session_memory_ + sizeof(Http2Session); 743 total += current_nghttp2_memory_; 744 total += outgoing_storage_.size(); 745 return total; 746 } 747 748 // Return true if current_session_memory + amount is less than the max has_available_session_memory(uint64_t amount)749 bool has_available_session_memory(uint64_t amount) const { 750 return current_session_memory() + amount <= max_session_memory_; 751 } 752 753 struct Statistics { 754 uint64_t start_time; 755 uint64_t end_time; 756 uint64_t ping_rtt; 757 uint64_t data_sent; 758 uint64_t data_received; 759 uint32_t frame_count; 760 uint32_t frame_sent; 761 int32_t stream_count; 762 size_t max_concurrent_streams; 763 double stream_average_duration; 764 }; 765 766 Statistics statistics_ = {}; 767 768 private: 769 void EmitStatistics(); 770 771 // Frame Padding Strategies 772 ssize_t OnDWordAlignedPadding(size_t frameLength, 773 size_t maxPayloadLen); 774 ssize_t OnMaxFrameSizePadding(size_t frameLength, 775 size_t maxPayloadLen); 776 777 // Frame Handler 778 int HandleDataFrame(const nghttp2_frame* frame); 779 void HandleGoawayFrame(const nghttp2_frame* frame); 780 void HandleHeadersFrame(const nghttp2_frame* frame); 781 void HandlePriorityFrame(const nghttp2_frame* frame); 782 void HandleSettingsFrame(const nghttp2_frame* frame); 783 void HandlePingFrame(const nghttp2_frame* frame); 784 void HandleAltSvcFrame(const nghttp2_frame* frame); 785 void HandleOriginFrame(const nghttp2_frame* frame); 786 787 // nghttp2 callbacks 788 static int OnBeginHeadersCallback( 789 nghttp2_session* session, 790 const nghttp2_frame* frame, 791 void* user_data); 792 static int OnHeaderCallback( 793 nghttp2_session* session, 794 const nghttp2_frame* frame, 795 nghttp2_rcbuf* name, 796 nghttp2_rcbuf* value, 797 uint8_t flags, 798 void* user_data); 799 static int OnFrameReceive( 800 nghttp2_session* session, 801 const nghttp2_frame* frame, 802 void* user_data); 803 static int OnFrameNotSent( 804 nghttp2_session* session, 805 const nghttp2_frame* frame, 806 int error_code, 807 void* user_data); 808 static int OnFrameSent( 809 nghttp2_session* session, 810 const nghttp2_frame* frame, 811 void* user_data); 812 static int OnStreamClose( 813 nghttp2_session* session, 814 int32_t id, 815 uint32_t code, 816 void* user_data); 817 static int OnInvalidHeader( 818 nghttp2_session* session, 819 const nghttp2_frame* frame, 820 nghttp2_rcbuf* name, 821 nghttp2_rcbuf* value, 822 uint8_t flags, 823 void* user_data); 824 static int OnDataChunkReceived( 825 nghttp2_session* session, 826 uint8_t flags, 827 int32_t id, 828 const uint8_t* data, 829 size_t len, 830 void* user_data); 831 static ssize_t OnSelectPadding( 832 nghttp2_session* session, 833 const nghttp2_frame* frame, 834 size_t maxPayloadLen, 835 void* user_data); 836 static int OnNghttpError( 837 nghttp2_session* session, 838 const char* message, 839 size_t len, 840 void* user_data); 841 static int OnSendData( 842 nghttp2_session* session, 843 nghttp2_frame* frame, 844 const uint8_t* framehd, 845 size_t length, 846 nghttp2_data_source* source, 847 void* user_data); 848 static int OnInvalidFrame( 849 nghttp2_session* session, 850 const nghttp2_frame* frame, 851 int lib_error_code, 852 void* user_data); 853 854 struct Callbacks { 855 explicit Callbacks(bool kHasGetPaddingCallback); 856 857 Nghttp2SessionCallbacksPointer callbacks; 858 }; 859 860 /* Use callback_struct_saved[kHasGetPaddingCallback ? 1 : 0] */ 861 static const Callbacks callback_struct_saved[2]; 862 863 // The underlying nghttp2_session handle 864 Nghttp2SessionPointer session_; 865 866 // JS-accessible numeric fields, as indexed by SessionUint8Fields. 867 AliasedStruct<SessionJSFields> js_fields_; 868 869 // The session type: client or server 870 SessionType session_type_; 871 872 // The maximum number of header pairs permitted for streams on this session 873 uint32_t max_header_pairs_ = DEFAULT_MAX_HEADER_LIST_PAIRS; 874 875 // The maximum amount of memory allocated for this session 876 uint64_t max_session_memory_ = kDefaultMaxSessionMemory; 877 uint64_t current_session_memory_ = 0; 878 // The amount of memory allocated by nghttp2 internals 879 uint64_t current_nghttp2_memory_ = 0; 880 881 // The collection of active Http2Streams associated with this session 882 std::unordered_map<int32_t, BaseObjectPtr<Http2Stream>> streams_; 883 884 int flags_ = kSessionStateNone; 885 886 // The StreamBase instance being used for i/o 887 PaddingStrategy padding_strategy_ = PADDING_STRATEGY_NONE; 888 889 // use this to allow timeout tracking during long-lasting writes 890 uint32_t chunks_sent_since_last_write_ = 0; 891 892 uv_buf_t stream_buf_ = uv_buf_init(nullptr, 0); 893 // When processing input data, either stream_buf_ab_ or stream_buf_allocation_ 894 // will be set. stream_buf_ab_ is lazily created from stream_buf_allocation_. 895 v8::Global<v8::ArrayBuffer> stream_buf_ab_; 896 AllocatedBuffer stream_buf_allocation_; 897 size_t stream_buf_offset_ = 0; 898 899 size_t max_outstanding_pings_ = kDefaultMaxPings; 900 std::queue<BaseObjectPtr<Http2Ping>> outstanding_pings_; 901 902 size_t max_outstanding_settings_ = kDefaultMaxSettings; 903 std::queue<BaseObjectPtr<Http2Settings>> outstanding_settings_; 904 905 std::vector<NgHttp2StreamWrite> outgoing_buffers_; 906 std::vector<uint8_t> outgoing_storage_; 907 size_t outgoing_length_ = 0; 908 std::vector<int32_t> pending_rst_streams_; 909 // Count streams that have been rejected while being opened. Exceeding a fixed 910 // limit will result in the session being destroyed, as an indication of a 911 // misbehaving peer. This counter is reset once new streams are being 912 // accepted again. 913 uint32_t rejected_stream_count_ = 0; 914 // Also use the invalid frame count as a measure for rejecting input frames. 915 uint32_t invalid_frame_count_ = 0; 916 917 void PushOutgoingBuffer(NgHttp2StreamWrite&& write); 918 919 BaseObjectPtr<Http2State> http2_state_; 920 921 void CopyDataIntoOutgoing(const uint8_t* src, size_t src_length); 922 void ClearOutgoing(int status); 923 924 friend class Http2Scope; 925 friend class Http2StreamListener; 926 }; 927 928 class Http2SessionPerformanceEntry : public performance::PerformanceEntry { 929 public: Http2SessionPerformanceEntry(Http2State * http2_state,const Http2Session::Statistics & stats,SessionType type)930 Http2SessionPerformanceEntry( 931 Http2State* http2_state, 932 const Http2Session::Statistics& stats, 933 SessionType type) : 934 performance::PerformanceEntry( 935 http2_state->env(), "Http2Session", "http2", 936 stats.start_time, 937 stats.end_time), 938 ping_rtt_(stats.ping_rtt), 939 data_sent_(stats.data_sent), 940 data_received_(stats.data_received), 941 frame_count_(stats.frame_count), 942 frame_sent_(stats.frame_sent), 943 stream_count_(stats.stream_count), 944 max_concurrent_streams_(stats.max_concurrent_streams), 945 stream_average_duration_(stats.stream_average_duration), 946 session_type_(type), 947 http2_state_(http2_state) { } 948 ping_rtt()949 uint64_t ping_rtt() const { return ping_rtt_; } data_sent()950 uint64_t data_sent() const { return data_sent_; } data_received()951 uint64_t data_received() const { return data_received_; } frame_count()952 uint32_t frame_count() const { return frame_count_; } frame_sent()953 uint32_t frame_sent() const { return frame_sent_; } stream_count()954 int32_t stream_count() const { return stream_count_; } max_concurrent_streams()955 size_t max_concurrent_streams() const { return max_concurrent_streams_; } stream_average_duration()956 double stream_average_duration() const { return stream_average_duration_; } type()957 SessionType type() const { return session_type_; } http2_state()958 Http2State* http2_state() const { return http2_state_.get(); } 959 Notify(v8::Local<v8::Value> obj)960 void Notify(v8::Local<v8::Value> obj) { 961 performance::PerformanceEntry::Notify(env(), kind(), obj); 962 } 963 964 private: 965 uint64_t ping_rtt_; 966 uint64_t data_sent_; 967 uint64_t data_received_; 968 uint32_t frame_count_; 969 uint32_t frame_sent_; 970 int32_t stream_count_; 971 size_t max_concurrent_streams_; 972 double stream_average_duration_; 973 SessionType session_type_; 974 BaseObjectPtr<Http2State> http2_state_; 975 }; 976 977 class Http2StreamPerformanceEntry 978 : public performance::PerformanceEntry { 979 public: Http2StreamPerformanceEntry(Http2State * http2_state,int32_t id,const Http2Stream::Statistics & stats)980 Http2StreamPerformanceEntry( 981 Http2State* http2_state, 982 int32_t id, 983 const Http2Stream::Statistics& stats) : 984 performance::PerformanceEntry( 985 http2_state->env(), "Http2Stream", "http2", 986 stats.start_time, 987 stats.end_time), 988 id_(id), 989 first_header_(stats.first_header), 990 first_byte_(stats.first_byte), 991 first_byte_sent_(stats.first_byte_sent), 992 sent_bytes_(stats.sent_bytes), 993 received_bytes_(stats.received_bytes), 994 http2_state_(http2_state) { } 995 id()996 int32_t id() const { return id_; } first_header()997 uint64_t first_header() const { return first_header_; } first_byte()998 uint64_t first_byte() const { return first_byte_; } first_byte_sent()999 uint64_t first_byte_sent() const { return first_byte_sent_; } sent_bytes()1000 uint64_t sent_bytes() const { return sent_bytes_; } received_bytes()1001 uint64_t received_bytes() const { return received_bytes_; } http2_state()1002 Http2State* http2_state() const { return http2_state_.get(); } 1003 Notify(v8::Local<v8::Value> obj)1004 void Notify(v8::Local<v8::Value> obj) { 1005 performance::PerformanceEntry::Notify(env(), kind(), obj); 1006 } 1007 1008 private: 1009 int32_t id_; 1010 uint64_t first_header_; 1011 uint64_t first_byte_; 1012 uint64_t first_byte_sent_; 1013 uint64_t sent_bytes_; 1014 uint64_t received_bytes_; 1015 BaseObjectPtr<Http2State> http2_state_; 1016 }; 1017 1018 class Http2Ping : public AsyncWrap { 1019 public: 1020 explicit Http2Ping( 1021 Http2Session* session, 1022 v8::Local<v8::Object> obj, 1023 v8::Local<v8::Function> callback); 1024 1025 void MemoryInfo(MemoryTracker* tracker) const override; 1026 SET_MEMORY_INFO_NAME(Http2Ping) 1027 SET_SELF_SIZE(Http2Ping) 1028 1029 void Send(const uint8_t* payload); 1030 void Done(bool ack, const uint8_t* payload = nullptr); 1031 void DetachFromSession(); 1032 1033 v8::Local<v8::Function> callback() const; 1034 1035 private: 1036 BaseObjectWeakPtr<Http2Session> session_; 1037 v8::Global<v8::Function> callback_; 1038 uint64_t startTime_; 1039 }; 1040 1041 // The Http2Settings class is used to parse the settings passed in for 1042 // an Http2Session, converting those into an array of nghttp2_settings_entry 1043 // structs. 1044 class Http2Settings : public AsyncWrap { 1045 public: 1046 Http2Settings(Http2Session* session, 1047 v8::Local<v8::Object> obj, 1048 v8::Local<v8::Function> callback, 1049 uint64_t start_time = uv_hrtime()); 1050 1051 void MemoryInfo(MemoryTracker* tracker) const override; 1052 SET_MEMORY_INFO_NAME(Http2Settings) 1053 SET_SELF_SIZE(Http2Settings) 1054 1055 void Send(); 1056 void Done(bool ack); 1057 1058 v8::Local<v8::Function> callback() const; 1059 1060 // Returns a Buffer instance with the serialized SETTINGS payload 1061 v8::Local<v8::Value> Pack(); 1062 1063 static v8::Local<v8::Value> Pack(Http2State* state); 1064 1065 // Resets the default values in the settings buffer 1066 static void RefreshDefaults(Http2State* http2_state); 1067 1068 // Update the local or remote settings for the given session 1069 static void Update(Http2Session* session, 1070 get_setting fn); 1071 1072 private: 1073 static size_t Init( 1074 Http2State* http2_state, 1075 nghttp2_settings_entry* entries); 1076 1077 static v8::Local<v8::Value> Pack( 1078 Environment* env, 1079 size_t count, 1080 const nghttp2_settings_entry* entries); 1081 1082 BaseObjectWeakPtr<Http2Session> session_; 1083 v8::Global<v8::Function> callback_; 1084 uint64_t startTime_; 1085 size_t count_ = 0; 1086 nghttp2_settings_entry entries_[IDX_SETTINGS_COUNT]; 1087 }; 1088 1089 class Origins { 1090 public: 1091 Origins(Environment* env, 1092 v8::Local<v8::String> origin_string, 1093 size_t origin_count); 1094 ~Origins() = default; 1095 1096 const nghttp2_origin_entry* operator*() const { 1097 return reinterpret_cast<const nghttp2_origin_entry*>(buf_.data()); 1098 } 1099 length()1100 size_t length() const { 1101 return count_; 1102 } 1103 1104 private: 1105 size_t count_; 1106 AllocatedBuffer buf_; 1107 }; 1108 1109 #define HTTP2_HIDDEN_CONSTANTS(V) \ 1110 V(NGHTTP2_HCAT_REQUEST) \ 1111 V(NGHTTP2_HCAT_RESPONSE) \ 1112 V(NGHTTP2_HCAT_PUSH_RESPONSE) \ 1113 V(NGHTTP2_HCAT_HEADERS) \ 1114 V(NGHTTP2_NV_FLAG_NONE) \ 1115 V(NGHTTP2_NV_FLAG_NO_INDEX) \ 1116 V(NGHTTP2_ERR_DEFERRED) \ 1117 V(NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE) \ 1118 V(NGHTTP2_ERR_INVALID_ARGUMENT) \ 1119 V(NGHTTP2_ERR_STREAM_CLOSED) \ 1120 V(NGHTTP2_ERR_NOMEM) \ 1121 V(STREAM_OPTION_EMPTY_PAYLOAD) \ 1122 V(STREAM_OPTION_GET_TRAILERS) 1123 1124 #define HTTP2_ERROR_CODES(V) \ 1125 V(NGHTTP2_NO_ERROR) \ 1126 V(NGHTTP2_PROTOCOL_ERROR) \ 1127 V(NGHTTP2_INTERNAL_ERROR) \ 1128 V(NGHTTP2_FLOW_CONTROL_ERROR) \ 1129 V(NGHTTP2_SETTINGS_TIMEOUT) \ 1130 V(NGHTTP2_STREAM_CLOSED) \ 1131 V(NGHTTP2_FRAME_SIZE_ERROR) \ 1132 V(NGHTTP2_REFUSED_STREAM) \ 1133 V(NGHTTP2_CANCEL) \ 1134 V(NGHTTP2_COMPRESSION_ERROR) \ 1135 V(NGHTTP2_CONNECT_ERROR) \ 1136 V(NGHTTP2_ENHANCE_YOUR_CALM) \ 1137 V(NGHTTP2_INADEQUATE_SECURITY) \ 1138 V(NGHTTP2_HTTP_1_1_REQUIRED) \ 1139 1140 #define HTTP2_CONSTANTS(V) \ 1141 V(NGHTTP2_ERR_FRAME_SIZE_ERROR) \ 1142 V(NGHTTP2_SESSION_SERVER) \ 1143 V(NGHTTP2_SESSION_CLIENT) \ 1144 V(NGHTTP2_STREAM_STATE_IDLE) \ 1145 V(NGHTTP2_STREAM_STATE_OPEN) \ 1146 V(NGHTTP2_STREAM_STATE_RESERVED_LOCAL) \ 1147 V(NGHTTP2_STREAM_STATE_RESERVED_REMOTE) \ 1148 V(NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL) \ 1149 V(NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE) \ 1150 V(NGHTTP2_STREAM_STATE_CLOSED) \ 1151 V(NGHTTP2_FLAG_NONE) \ 1152 V(NGHTTP2_FLAG_END_STREAM) \ 1153 V(NGHTTP2_FLAG_END_HEADERS) \ 1154 V(NGHTTP2_FLAG_ACK) \ 1155 V(NGHTTP2_FLAG_PADDED) \ 1156 V(NGHTTP2_FLAG_PRIORITY) \ 1157 V(DEFAULT_SETTINGS_HEADER_TABLE_SIZE) \ 1158 V(DEFAULT_SETTINGS_ENABLE_PUSH) \ 1159 V(DEFAULT_SETTINGS_MAX_CONCURRENT_STREAMS) \ 1160 V(DEFAULT_SETTINGS_INITIAL_WINDOW_SIZE) \ 1161 V(DEFAULT_SETTINGS_MAX_FRAME_SIZE) \ 1162 V(DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE) \ 1163 V(DEFAULT_SETTINGS_ENABLE_CONNECT_PROTOCOL) \ 1164 V(MAX_MAX_FRAME_SIZE) \ 1165 V(MIN_MAX_FRAME_SIZE) \ 1166 V(MAX_INITIAL_WINDOW_SIZE) \ 1167 V(NGHTTP2_SETTINGS_HEADER_TABLE_SIZE) \ 1168 V(NGHTTP2_SETTINGS_ENABLE_PUSH) \ 1169 V(NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS) \ 1170 V(NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE) \ 1171 V(NGHTTP2_SETTINGS_MAX_FRAME_SIZE) \ 1172 V(NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE) \ 1173 V(NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL) \ 1174 V(PADDING_STRATEGY_NONE) \ 1175 V(PADDING_STRATEGY_ALIGNED) \ 1176 V(PADDING_STRATEGY_MAX) \ 1177 V(PADDING_STRATEGY_CALLBACK) \ 1178 HTTP2_ERROR_CODES(V) 1179 1180 #define HTTP2_SETTINGS(V) \ 1181 V(HEADER_TABLE_SIZE) \ 1182 V(ENABLE_PUSH) \ 1183 V(MAX_CONCURRENT_STREAMS) \ 1184 V(INITIAL_WINDOW_SIZE) \ 1185 V(MAX_FRAME_SIZE) \ 1186 V(MAX_HEADER_LIST_SIZE) \ 1187 V(ENABLE_CONNECT_PROTOCOL) \ 1188 1189 } // namespace http2 1190 } // namespace node 1191 1192 #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS 1193 1194 #endif // SRC_NODE_HTTP2_H_ 1195