11cb0ef41Sopenharmony_ci#include "node_http2.h" 21cb0ef41Sopenharmony_ci#include "aliased_buffer-inl.h" 31cb0ef41Sopenharmony_ci#include "aliased_struct-inl.h" 41cb0ef41Sopenharmony_ci#include "debug_utils-inl.h" 51cb0ef41Sopenharmony_ci#include "histogram-inl.h" 61cb0ef41Sopenharmony_ci#include "memory_tracker-inl.h" 71cb0ef41Sopenharmony_ci#include "node.h" 81cb0ef41Sopenharmony_ci#include "node_buffer.h" 91cb0ef41Sopenharmony_ci#include "node_http_common-inl.h" 101cb0ef41Sopenharmony_ci#include "node_mem-inl.h" 111cb0ef41Sopenharmony_ci#include "node_perf.h" 121cb0ef41Sopenharmony_ci#include "node_revert.h" 131cb0ef41Sopenharmony_ci#include "stream_base-inl.h" 141cb0ef41Sopenharmony_ci#include "util-inl.h" 151cb0ef41Sopenharmony_ci 161cb0ef41Sopenharmony_ci#include <algorithm> 171cb0ef41Sopenharmony_ci#include <memory> 181cb0ef41Sopenharmony_ci#include <string> 191cb0ef41Sopenharmony_ci#include <utility> 201cb0ef41Sopenharmony_ci#include <vector> 211cb0ef41Sopenharmony_ci 221cb0ef41Sopenharmony_cinamespace node { 231cb0ef41Sopenharmony_ci 241cb0ef41Sopenharmony_ciusing v8::Array; 251cb0ef41Sopenharmony_ciusing v8::ArrayBuffer; 261cb0ef41Sopenharmony_ciusing v8::ArrayBufferView; 271cb0ef41Sopenharmony_ciusing v8::BackingStore; 281cb0ef41Sopenharmony_ciusing v8::Boolean; 291cb0ef41Sopenharmony_ciusing v8::Context; 301cb0ef41Sopenharmony_ciusing v8::EscapableHandleScope; 311cb0ef41Sopenharmony_ciusing v8::Function; 321cb0ef41Sopenharmony_ciusing v8::FunctionCallbackInfo; 331cb0ef41Sopenharmony_ciusing v8::FunctionTemplate; 341cb0ef41Sopenharmony_ciusing v8::HandleScope; 351cb0ef41Sopenharmony_ciusing v8::Integer; 361cb0ef41Sopenharmony_ciusing v8::Isolate; 371cb0ef41Sopenharmony_ciusing v8::Local; 381cb0ef41Sopenharmony_ciusing v8::MaybeLocal; 391cb0ef41Sopenharmony_ciusing v8::NewStringType; 401cb0ef41Sopenharmony_ciusing v8::Number; 411cb0ef41Sopenharmony_ciusing v8::Object; 421cb0ef41Sopenharmony_ciusing v8::ObjectTemplate; 431cb0ef41Sopenharmony_ciusing v8::String; 441cb0ef41Sopenharmony_ciusing v8::Uint8Array; 451cb0ef41Sopenharmony_ciusing v8::Undefined; 461cb0ef41Sopenharmony_ciusing v8::Value; 471cb0ef41Sopenharmony_ci 481cb0ef41Sopenharmony_cinamespace http2 { 491cb0ef41Sopenharmony_ci 501cb0ef41Sopenharmony_cinamespace { 511cb0ef41Sopenharmony_ci 521cb0ef41Sopenharmony_ciconst char zero_bytes_256[256] = {}; 531cb0ef41Sopenharmony_ci 541cb0ef41Sopenharmony_cibool HasHttp2Observer(Environment* env) { 551cb0ef41Sopenharmony_ci AliasedUint32Array& observers = env->performance_state()->observers; 561cb0ef41Sopenharmony_ci return observers[performance::NODE_PERFORMANCE_ENTRY_TYPE_HTTP2] != 0; 571cb0ef41Sopenharmony_ci} 581cb0ef41Sopenharmony_ci 591cb0ef41Sopenharmony_ci} // anonymous namespace 601cb0ef41Sopenharmony_ci 611cb0ef41Sopenharmony_ci// These configure the callbacks required by nghttp2 itself. There are 621cb0ef41Sopenharmony_ci// two sets of callback functions, one that is used if a padding callback 631cb0ef41Sopenharmony_ci// is set, and other that does not include the padding callback. 641cb0ef41Sopenharmony_ciconst Http2Session::Callbacks Http2Session::callback_struct_saved[2] = { 651cb0ef41Sopenharmony_ci Callbacks(false), 661cb0ef41Sopenharmony_ci Callbacks(true)}; 671cb0ef41Sopenharmony_ci 681cb0ef41Sopenharmony_ci// The Http2Scope object is used to queue a write to the i/o stream. It is 691cb0ef41Sopenharmony_ci// used whenever any action is take on the underlying nghttp2 API that may 701cb0ef41Sopenharmony_ci// push data into nghttp2 outbound data queue. 711cb0ef41Sopenharmony_ci// 721cb0ef41Sopenharmony_ci// For example: 731cb0ef41Sopenharmony_ci// 741cb0ef41Sopenharmony_ci// Http2Scope h2scope(session); 751cb0ef41Sopenharmony_ci// nghttp2_submit_ping(session->session(), ... ); 761cb0ef41Sopenharmony_ci// 771cb0ef41Sopenharmony_ci// When the Http2Scope passes out of scope and is deconstructed, it will 781cb0ef41Sopenharmony_ci// call Http2Session::MaybeScheduleWrite(). 791cb0ef41Sopenharmony_ciHttp2Scope::Http2Scope(Http2Stream* stream) : Http2Scope(stream->session()) {} 801cb0ef41Sopenharmony_ci 811cb0ef41Sopenharmony_ciHttp2Scope::Http2Scope(Http2Session* session) : session_(session) { 821cb0ef41Sopenharmony_ci if (!session_) return; 831cb0ef41Sopenharmony_ci 841cb0ef41Sopenharmony_ci // If there is another scope further below on the stack, or 851cb0ef41Sopenharmony_ci // a write is already scheduled, there's nothing to do. 861cb0ef41Sopenharmony_ci if (session_->is_in_scope() || session_->is_write_scheduled()) { 871cb0ef41Sopenharmony_ci session_.reset(); 881cb0ef41Sopenharmony_ci return; 891cb0ef41Sopenharmony_ci } 901cb0ef41Sopenharmony_ci session_->set_in_scope(); 911cb0ef41Sopenharmony_ci} 921cb0ef41Sopenharmony_ci 931cb0ef41Sopenharmony_ciHttp2Scope::~Http2Scope() { 941cb0ef41Sopenharmony_ci if (!session_) return; 951cb0ef41Sopenharmony_ci session_->set_in_scope(false); 961cb0ef41Sopenharmony_ci if (!session_->is_write_scheduled()) 971cb0ef41Sopenharmony_ci session_->MaybeScheduleWrite(); 981cb0ef41Sopenharmony_ci} 991cb0ef41Sopenharmony_ci 1001cb0ef41Sopenharmony_ci// The Http2Options object is used during the construction of Http2Session 1011cb0ef41Sopenharmony_ci// instances to configure an appropriate nghttp2_options struct. The class 1021cb0ef41Sopenharmony_ci// uses a single TypedArray instance that is shared with the JavaScript side 1031cb0ef41Sopenharmony_ci// to more efficiently pass values back and forth. 1041cb0ef41Sopenharmony_ciHttp2Options::Http2Options(Http2State* http2_state, SessionType type) { 1051cb0ef41Sopenharmony_ci nghttp2_option* option; 1061cb0ef41Sopenharmony_ci CHECK_EQ(nghttp2_option_new(&option), 0); 1071cb0ef41Sopenharmony_ci CHECK_NOT_NULL(option); 1081cb0ef41Sopenharmony_ci options_.reset(option); 1091cb0ef41Sopenharmony_ci 1101cb0ef41Sopenharmony_ci // Make sure closed connections aren't kept around, taking up memory. 1111cb0ef41Sopenharmony_ci // Note that this breaks the priority tree, which we don't use. 1121cb0ef41Sopenharmony_ci nghttp2_option_set_no_closed_streams(option, 1); 1131cb0ef41Sopenharmony_ci 1141cb0ef41Sopenharmony_ci // We manually handle flow control within a session in order to 1151cb0ef41Sopenharmony_ci // implement backpressure -- that is, we only send WINDOW_UPDATE 1161cb0ef41Sopenharmony_ci // frames to the remote peer as data is actually consumed by user 1171cb0ef41Sopenharmony_ci // code. This ensures that the flow of data over the connection 1181cb0ef41Sopenharmony_ci // does not move too quickly and limits the amount of data we 1191cb0ef41Sopenharmony_ci // are required to buffer. 1201cb0ef41Sopenharmony_ci nghttp2_option_set_no_auto_window_update(option, 1); 1211cb0ef41Sopenharmony_ci 1221cb0ef41Sopenharmony_ci // Enable built in support for receiving ALTSVC and ORIGIN frames (but 1231cb0ef41Sopenharmony_ci // only on client side sessions 1241cb0ef41Sopenharmony_ci if (type == NGHTTP2_SESSION_CLIENT) { 1251cb0ef41Sopenharmony_ci nghttp2_option_set_builtin_recv_extension_type(option, NGHTTP2_ALTSVC); 1261cb0ef41Sopenharmony_ci nghttp2_option_set_builtin_recv_extension_type(option, NGHTTP2_ORIGIN); 1271cb0ef41Sopenharmony_ci } 1281cb0ef41Sopenharmony_ci 1291cb0ef41Sopenharmony_ci AliasedUint32Array& buffer = http2_state->options_buffer; 1301cb0ef41Sopenharmony_ci uint32_t flags = buffer[IDX_OPTIONS_FLAGS]; 1311cb0ef41Sopenharmony_ci 1321cb0ef41Sopenharmony_ci if (flags & (1 << IDX_OPTIONS_MAX_DEFLATE_DYNAMIC_TABLE_SIZE)) { 1331cb0ef41Sopenharmony_ci nghttp2_option_set_max_deflate_dynamic_table_size( 1341cb0ef41Sopenharmony_ci option, 1351cb0ef41Sopenharmony_ci buffer[IDX_OPTIONS_MAX_DEFLATE_DYNAMIC_TABLE_SIZE]); 1361cb0ef41Sopenharmony_ci } 1371cb0ef41Sopenharmony_ci 1381cb0ef41Sopenharmony_ci if (flags & (1 << IDX_OPTIONS_MAX_RESERVED_REMOTE_STREAMS)) { 1391cb0ef41Sopenharmony_ci nghttp2_option_set_max_reserved_remote_streams( 1401cb0ef41Sopenharmony_ci option, 1411cb0ef41Sopenharmony_ci buffer[IDX_OPTIONS_MAX_RESERVED_REMOTE_STREAMS]); 1421cb0ef41Sopenharmony_ci } 1431cb0ef41Sopenharmony_ci 1441cb0ef41Sopenharmony_ci if (flags & (1 << IDX_OPTIONS_MAX_SEND_HEADER_BLOCK_LENGTH)) { 1451cb0ef41Sopenharmony_ci nghttp2_option_set_max_send_header_block_length( 1461cb0ef41Sopenharmony_ci option, 1471cb0ef41Sopenharmony_ci buffer[IDX_OPTIONS_MAX_SEND_HEADER_BLOCK_LENGTH]); 1481cb0ef41Sopenharmony_ci } 1491cb0ef41Sopenharmony_ci 1501cb0ef41Sopenharmony_ci // Recommended default 1511cb0ef41Sopenharmony_ci nghttp2_option_set_peer_max_concurrent_streams(option, 100); 1521cb0ef41Sopenharmony_ci if (flags & (1 << IDX_OPTIONS_PEER_MAX_CONCURRENT_STREAMS)) { 1531cb0ef41Sopenharmony_ci nghttp2_option_set_peer_max_concurrent_streams( 1541cb0ef41Sopenharmony_ci option, 1551cb0ef41Sopenharmony_ci buffer[IDX_OPTIONS_PEER_MAX_CONCURRENT_STREAMS]); 1561cb0ef41Sopenharmony_ci } 1571cb0ef41Sopenharmony_ci 1581cb0ef41Sopenharmony_ci // The padding strategy sets the mechanism by which we determine how much 1591cb0ef41Sopenharmony_ci // additional frame padding to apply to DATA and HEADERS frames. Currently 1601cb0ef41Sopenharmony_ci // this is set on a per-session basis, but eventually we may switch to 1611cb0ef41Sopenharmony_ci // a per-stream setting, giving users greater control 1621cb0ef41Sopenharmony_ci if (flags & (1 << IDX_OPTIONS_PADDING_STRATEGY)) { 1631cb0ef41Sopenharmony_ci PaddingStrategy strategy = 1641cb0ef41Sopenharmony_ci static_cast<PaddingStrategy>( 1651cb0ef41Sopenharmony_ci buffer.GetValue(IDX_OPTIONS_PADDING_STRATEGY)); 1661cb0ef41Sopenharmony_ci set_padding_strategy(strategy); 1671cb0ef41Sopenharmony_ci } 1681cb0ef41Sopenharmony_ci 1691cb0ef41Sopenharmony_ci // The max header list pairs option controls the maximum number of 1701cb0ef41Sopenharmony_ci // header pairs the session may accept. This is a hard limit.. that is, 1711cb0ef41Sopenharmony_ci // if the remote peer sends more than this amount, the stream will be 1721cb0ef41Sopenharmony_ci // automatically closed with an RST_STREAM. 1731cb0ef41Sopenharmony_ci if (flags & (1 << IDX_OPTIONS_MAX_HEADER_LIST_PAIRS)) 1741cb0ef41Sopenharmony_ci set_max_header_pairs(buffer[IDX_OPTIONS_MAX_HEADER_LIST_PAIRS]); 1751cb0ef41Sopenharmony_ci 1761cb0ef41Sopenharmony_ci // The HTTP2 specification places no limits on the number of HTTP2 1771cb0ef41Sopenharmony_ci // PING frames that can be sent. In order to prevent PINGS from being 1781cb0ef41Sopenharmony_ci // abused as an attack vector, however, we place a strict upper limit 1791cb0ef41Sopenharmony_ci // on the number of unacknowledged PINGS that can be sent at any given 1801cb0ef41Sopenharmony_ci // time. 1811cb0ef41Sopenharmony_ci if (flags & (1 << IDX_OPTIONS_MAX_OUTSTANDING_PINGS)) 1821cb0ef41Sopenharmony_ci set_max_outstanding_pings(buffer[IDX_OPTIONS_MAX_OUTSTANDING_PINGS]); 1831cb0ef41Sopenharmony_ci 1841cb0ef41Sopenharmony_ci // The HTTP2 specification places no limits on the number of HTTP2 1851cb0ef41Sopenharmony_ci // SETTINGS frames that can be sent. In order to prevent PINGS from being 1861cb0ef41Sopenharmony_ci // abused as an attack vector, however, we place a strict upper limit 1871cb0ef41Sopenharmony_ci // on the number of unacknowledged SETTINGS that can be sent at any given 1881cb0ef41Sopenharmony_ci // time. 1891cb0ef41Sopenharmony_ci if (flags & (1 << IDX_OPTIONS_MAX_OUTSTANDING_SETTINGS)) 1901cb0ef41Sopenharmony_ci set_max_outstanding_settings(buffer[IDX_OPTIONS_MAX_OUTSTANDING_SETTINGS]); 1911cb0ef41Sopenharmony_ci 1921cb0ef41Sopenharmony_ci // The HTTP2 specification places no limits on the amount of memory 1931cb0ef41Sopenharmony_ci // that a session can consume. In order to prevent abuse, we place a 1941cb0ef41Sopenharmony_ci // cap on the amount of memory a session can consume at any given time. 1951cb0ef41Sopenharmony_ci // this is a credit based system. Existing streams may cause the limit 1961cb0ef41Sopenharmony_ci // to be temporarily exceeded but once over the limit, new streams cannot 1971cb0ef41Sopenharmony_ci // created. 1981cb0ef41Sopenharmony_ci // Important: The maxSessionMemory option in javascript is expressed in 1991cb0ef41Sopenharmony_ci // terms of MB increments (i.e. the value 1 == 1 MB) 2001cb0ef41Sopenharmony_ci if (flags & (1 << IDX_OPTIONS_MAX_SESSION_MEMORY)) 2011cb0ef41Sopenharmony_ci set_max_session_memory(buffer[IDX_OPTIONS_MAX_SESSION_MEMORY] * 2021cb0ef41Sopenharmony_ci static_cast<uint64_t>(1000000)); 2031cb0ef41Sopenharmony_ci 2041cb0ef41Sopenharmony_ci if (flags & (1 << IDX_OPTIONS_MAX_SETTINGS)) { 2051cb0ef41Sopenharmony_ci nghttp2_option_set_max_settings( 2061cb0ef41Sopenharmony_ci option, 2071cb0ef41Sopenharmony_ci static_cast<size_t>(buffer[IDX_OPTIONS_MAX_SETTINGS])); 2081cb0ef41Sopenharmony_ci } 2091cb0ef41Sopenharmony_ci} 2101cb0ef41Sopenharmony_ci 2111cb0ef41Sopenharmony_ci#define GRABSETTING(entries, count, name) \ 2121cb0ef41Sopenharmony_ci do { \ 2131cb0ef41Sopenharmony_ci if (flags & (1 << IDX_SETTINGS_ ## name)) { \ 2141cb0ef41Sopenharmony_ci uint32_t val = buffer[IDX_SETTINGS_ ## name]; \ 2151cb0ef41Sopenharmony_ci entries[count++] = \ 2161cb0ef41Sopenharmony_ci nghttp2_settings_entry {NGHTTP2_SETTINGS_ ## name, val}; \ 2171cb0ef41Sopenharmony_ci } } while (0) 2181cb0ef41Sopenharmony_ci 2191cb0ef41Sopenharmony_cisize_t Http2Settings::Init( 2201cb0ef41Sopenharmony_ci Http2State* http2_state, 2211cb0ef41Sopenharmony_ci nghttp2_settings_entry* entries) { 2221cb0ef41Sopenharmony_ci AliasedUint32Array& buffer = http2_state->settings_buffer; 2231cb0ef41Sopenharmony_ci uint32_t flags = buffer[IDX_SETTINGS_COUNT]; 2241cb0ef41Sopenharmony_ci 2251cb0ef41Sopenharmony_ci size_t count = 0; 2261cb0ef41Sopenharmony_ci 2271cb0ef41Sopenharmony_ci#define V(name) GRABSETTING(entries, count, name); 2281cb0ef41Sopenharmony_ci HTTP2_SETTINGS(V) 2291cb0ef41Sopenharmony_ci#undef V 2301cb0ef41Sopenharmony_ci 2311cb0ef41Sopenharmony_ci return count; 2321cb0ef41Sopenharmony_ci} 2331cb0ef41Sopenharmony_ci#undef GRABSETTING 2341cb0ef41Sopenharmony_ci 2351cb0ef41Sopenharmony_ci// The Http2Settings class is used to configure a SETTINGS frame that is 2361cb0ef41Sopenharmony_ci// to be sent to the connected peer. The settings are set using a TypedArray 2371cb0ef41Sopenharmony_ci// that is shared with the JavaScript side. 2381cb0ef41Sopenharmony_ciHttp2Settings::Http2Settings(Http2Session* session, 2391cb0ef41Sopenharmony_ci Local<Object> obj, 2401cb0ef41Sopenharmony_ci Local<Function> callback, 2411cb0ef41Sopenharmony_ci uint64_t start_time) 2421cb0ef41Sopenharmony_ci : AsyncWrap(session->env(), obj, PROVIDER_HTTP2SETTINGS), 2431cb0ef41Sopenharmony_ci session_(session), 2441cb0ef41Sopenharmony_ci startTime_(start_time) { 2451cb0ef41Sopenharmony_ci callback_.Reset(env()->isolate(), callback); 2461cb0ef41Sopenharmony_ci count_ = Init(session->http2_state(), entries_); 2471cb0ef41Sopenharmony_ci} 2481cb0ef41Sopenharmony_ci 2491cb0ef41Sopenharmony_ciLocal<Function> Http2Settings::callback() const { 2501cb0ef41Sopenharmony_ci return callback_.Get(env()->isolate()); 2511cb0ef41Sopenharmony_ci} 2521cb0ef41Sopenharmony_ci 2531cb0ef41Sopenharmony_civoid Http2Settings::MemoryInfo(MemoryTracker* tracker) const { 2541cb0ef41Sopenharmony_ci tracker->TrackField("callback", callback_); 2551cb0ef41Sopenharmony_ci} 2561cb0ef41Sopenharmony_ci 2571cb0ef41Sopenharmony_ci// Generates a Buffer that contains the serialized payload of a SETTINGS 2581cb0ef41Sopenharmony_ci// frame. This can be used, for instance, to create the Base64-encoded 2591cb0ef41Sopenharmony_ci// content of an Http2-Settings header field. 2601cb0ef41Sopenharmony_ciLocal<Value> Http2Settings::Pack() { 2611cb0ef41Sopenharmony_ci return Pack(session_->env(), count_, entries_); 2621cb0ef41Sopenharmony_ci} 2631cb0ef41Sopenharmony_ci 2641cb0ef41Sopenharmony_ciLocal<Value> Http2Settings::Pack(Http2State* state) { 2651cb0ef41Sopenharmony_ci nghttp2_settings_entry entries[IDX_SETTINGS_COUNT]; 2661cb0ef41Sopenharmony_ci size_t count = Init(state, entries); 2671cb0ef41Sopenharmony_ci return Pack(state->env(), count, entries); 2681cb0ef41Sopenharmony_ci} 2691cb0ef41Sopenharmony_ci 2701cb0ef41Sopenharmony_ciLocal<Value> Http2Settings::Pack( 2711cb0ef41Sopenharmony_ci Environment* env, 2721cb0ef41Sopenharmony_ci size_t count, 2731cb0ef41Sopenharmony_ci const nghttp2_settings_entry* entries) { 2741cb0ef41Sopenharmony_ci EscapableHandleScope scope(env->isolate()); 2751cb0ef41Sopenharmony_ci std::unique_ptr<BackingStore> bs; 2761cb0ef41Sopenharmony_ci { 2771cb0ef41Sopenharmony_ci NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); 2781cb0ef41Sopenharmony_ci bs = ArrayBuffer::NewBackingStore(env->isolate(), count * 6); 2791cb0ef41Sopenharmony_ci } 2801cb0ef41Sopenharmony_ci if (nghttp2_pack_settings_payload(static_cast<uint8_t*>(bs->Data()), 2811cb0ef41Sopenharmony_ci bs->ByteLength(), 2821cb0ef41Sopenharmony_ci entries, 2831cb0ef41Sopenharmony_ci count) < 0) { 2841cb0ef41Sopenharmony_ci return scope.Escape(Undefined(env->isolate())); 2851cb0ef41Sopenharmony_ci } 2861cb0ef41Sopenharmony_ci Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), std::move(bs)); 2871cb0ef41Sopenharmony_ci return scope.Escape(Buffer::New(env, ab, 0, ab->ByteLength()) 2881cb0ef41Sopenharmony_ci .FromMaybe(Local<Value>())); 2891cb0ef41Sopenharmony_ci} 2901cb0ef41Sopenharmony_ci 2911cb0ef41Sopenharmony_ci// Updates the shared TypedArray with the current remote or local settings for 2921cb0ef41Sopenharmony_ci// the session. 2931cb0ef41Sopenharmony_civoid Http2Settings::Update(Http2Session* session, get_setting fn) { 2941cb0ef41Sopenharmony_ci AliasedUint32Array& buffer = session->http2_state()->settings_buffer; 2951cb0ef41Sopenharmony_ci 2961cb0ef41Sopenharmony_ci#define V(name) \ 2971cb0ef41Sopenharmony_ci buffer[IDX_SETTINGS_ ## name] = \ 2981cb0ef41Sopenharmony_ci fn(session->session(), NGHTTP2_SETTINGS_ ## name); 2991cb0ef41Sopenharmony_ci HTTP2_SETTINGS(V) 3001cb0ef41Sopenharmony_ci#undef V 3011cb0ef41Sopenharmony_ci} 3021cb0ef41Sopenharmony_ci 3031cb0ef41Sopenharmony_ci// Initializes the shared TypedArray with the default settings values. 3041cb0ef41Sopenharmony_civoid Http2Settings::RefreshDefaults(Http2State* http2_state) { 3051cb0ef41Sopenharmony_ci AliasedUint32Array& buffer = http2_state->settings_buffer; 3061cb0ef41Sopenharmony_ci uint32_t flags = 0; 3071cb0ef41Sopenharmony_ci 3081cb0ef41Sopenharmony_ci#define V(name) \ 3091cb0ef41Sopenharmony_ci do { \ 3101cb0ef41Sopenharmony_ci buffer[IDX_SETTINGS_ ## name] = DEFAULT_SETTINGS_ ## name; \ 3111cb0ef41Sopenharmony_ci flags |= 1 << IDX_SETTINGS_ ## name; \ 3121cb0ef41Sopenharmony_ci } while (0); 3131cb0ef41Sopenharmony_ci HTTP2_SETTINGS(V) 3141cb0ef41Sopenharmony_ci#undef V 3151cb0ef41Sopenharmony_ci 3161cb0ef41Sopenharmony_ci buffer[IDX_SETTINGS_COUNT] = flags; 3171cb0ef41Sopenharmony_ci} 3181cb0ef41Sopenharmony_ci 3191cb0ef41Sopenharmony_ci 3201cb0ef41Sopenharmony_civoid Http2Settings::Send() { 3211cb0ef41Sopenharmony_ci Http2Scope h2scope(session_.get()); 3221cb0ef41Sopenharmony_ci CHECK_EQ(nghttp2_submit_settings( 3231cb0ef41Sopenharmony_ci session_->session(), 3241cb0ef41Sopenharmony_ci NGHTTP2_FLAG_NONE, 3251cb0ef41Sopenharmony_ci &entries_[0], 3261cb0ef41Sopenharmony_ci count_), 0); 3271cb0ef41Sopenharmony_ci} 3281cb0ef41Sopenharmony_ci 3291cb0ef41Sopenharmony_civoid Http2Settings::Done(bool ack) { 3301cb0ef41Sopenharmony_ci uint64_t end = uv_hrtime(); 3311cb0ef41Sopenharmony_ci double duration = (end - startTime_) / 1e6; 3321cb0ef41Sopenharmony_ci 3331cb0ef41Sopenharmony_ci Local<Value> argv[] = {Boolean::New(env()->isolate(), ack), 3341cb0ef41Sopenharmony_ci Number::New(env()->isolate(), duration)}; 3351cb0ef41Sopenharmony_ci MakeCallback(callback(), arraysize(argv), argv); 3361cb0ef41Sopenharmony_ci} 3371cb0ef41Sopenharmony_ci 3381cb0ef41Sopenharmony_ci// The Http2Priority class initializes an appropriate nghttp2_priority_spec 3391cb0ef41Sopenharmony_ci// struct used when either creating a stream or updating its priority 3401cb0ef41Sopenharmony_ci// settings. 3411cb0ef41Sopenharmony_ciHttp2Priority::Http2Priority(Environment* env, 3421cb0ef41Sopenharmony_ci Local<Value> parent, 3431cb0ef41Sopenharmony_ci Local<Value> weight, 3441cb0ef41Sopenharmony_ci Local<Value> exclusive) { 3451cb0ef41Sopenharmony_ci Local<Context> context = env->context(); 3461cb0ef41Sopenharmony_ci int32_t parent_ = parent->Int32Value(context).ToChecked(); 3471cb0ef41Sopenharmony_ci int32_t weight_ = weight->Int32Value(context).ToChecked(); 3481cb0ef41Sopenharmony_ci bool exclusive_ = exclusive->IsTrue(); 3491cb0ef41Sopenharmony_ci Debug(env, DebugCategory::HTTP2STREAM, 3501cb0ef41Sopenharmony_ci "Http2Priority: parent: %d, weight: %d, exclusive: %s\n", 3511cb0ef41Sopenharmony_ci parent_, weight_, exclusive_ ? "yes" : "no"); 3521cb0ef41Sopenharmony_ci nghttp2_priority_spec_init(this, parent_, weight_, exclusive_ ? 1 : 0); 3531cb0ef41Sopenharmony_ci} 3541cb0ef41Sopenharmony_ci 3551cb0ef41Sopenharmony_ci 3561cb0ef41Sopenharmony_ciconst char* Http2Session::TypeName() const { 3571cb0ef41Sopenharmony_ci switch (session_type_) { 3581cb0ef41Sopenharmony_ci case NGHTTP2_SESSION_SERVER: return "server"; 3591cb0ef41Sopenharmony_ci case NGHTTP2_SESSION_CLIENT: return "client"; 3601cb0ef41Sopenharmony_ci default: 3611cb0ef41Sopenharmony_ci // This should never happen 3621cb0ef41Sopenharmony_ci ABORT(); 3631cb0ef41Sopenharmony_ci } 3641cb0ef41Sopenharmony_ci} 3651cb0ef41Sopenharmony_ci 3661cb0ef41Sopenharmony_ciOrigins::Origins( 3671cb0ef41Sopenharmony_ci Environment* env, 3681cb0ef41Sopenharmony_ci Local<String> origin_string, 3691cb0ef41Sopenharmony_ci size_t origin_count) 3701cb0ef41Sopenharmony_ci : count_(origin_count) { 3711cb0ef41Sopenharmony_ci int origin_string_len = origin_string->Length(); 3721cb0ef41Sopenharmony_ci if (count_ == 0) { 3731cb0ef41Sopenharmony_ci CHECK_EQ(origin_string_len, 0); 3741cb0ef41Sopenharmony_ci return; 3751cb0ef41Sopenharmony_ci } 3761cb0ef41Sopenharmony_ci 3771cb0ef41Sopenharmony_ci { 3781cb0ef41Sopenharmony_ci NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); 3791cb0ef41Sopenharmony_ci bs_ = ArrayBuffer::NewBackingStore(env->isolate(), 3801cb0ef41Sopenharmony_ci alignof(nghttp2_origin_entry) - 1 + 3811cb0ef41Sopenharmony_ci count_ * sizeof(nghttp2_origin_entry) + 3821cb0ef41Sopenharmony_ci origin_string_len); 3831cb0ef41Sopenharmony_ci } 3841cb0ef41Sopenharmony_ci 3851cb0ef41Sopenharmony_ci // Make sure the start address is aligned appropriately for an nghttp2_nv*. 3861cb0ef41Sopenharmony_ci char* start = AlignUp(static_cast<char*>(bs_->Data()), 3871cb0ef41Sopenharmony_ci alignof(nghttp2_origin_entry)); 3881cb0ef41Sopenharmony_ci char* origin_contents = start + (count_ * sizeof(nghttp2_origin_entry)); 3891cb0ef41Sopenharmony_ci nghttp2_origin_entry* const nva = 3901cb0ef41Sopenharmony_ci reinterpret_cast<nghttp2_origin_entry*>(start); 3911cb0ef41Sopenharmony_ci 3921cb0ef41Sopenharmony_ci CHECK_LE(origin_contents + origin_string_len, 3931cb0ef41Sopenharmony_ci static_cast<char*>(bs_->Data()) + bs_->ByteLength()); 3941cb0ef41Sopenharmony_ci CHECK_EQ(origin_string->WriteOneByte( 3951cb0ef41Sopenharmony_ci env->isolate(), 3961cb0ef41Sopenharmony_ci reinterpret_cast<uint8_t*>(origin_contents), 3971cb0ef41Sopenharmony_ci 0, 3981cb0ef41Sopenharmony_ci origin_string_len, 3991cb0ef41Sopenharmony_ci String::NO_NULL_TERMINATION), 4001cb0ef41Sopenharmony_ci origin_string_len); 4011cb0ef41Sopenharmony_ci 4021cb0ef41Sopenharmony_ci size_t n = 0; 4031cb0ef41Sopenharmony_ci char* p; 4041cb0ef41Sopenharmony_ci for (p = origin_contents; p < origin_contents + origin_string_len; n++) { 4051cb0ef41Sopenharmony_ci if (n >= count_) { 4061cb0ef41Sopenharmony_ci static uint8_t zero = '\0'; 4071cb0ef41Sopenharmony_ci nva[0].origin = &zero; 4081cb0ef41Sopenharmony_ci nva[0].origin_len = 1; 4091cb0ef41Sopenharmony_ci count_ = 1; 4101cb0ef41Sopenharmony_ci return; 4111cb0ef41Sopenharmony_ci } 4121cb0ef41Sopenharmony_ci 4131cb0ef41Sopenharmony_ci nva[n].origin = reinterpret_cast<uint8_t*>(p); 4141cb0ef41Sopenharmony_ci nva[n].origin_len = strlen(p); 4151cb0ef41Sopenharmony_ci p += nva[n].origin_len + 1; 4161cb0ef41Sopenharmony_ci } 4171cb0ef41Sopenharmony_ci} 4181cb0ef41Sopenharmony_ci 4191cb0ef41Sopenharmony_ci// Sets the various callback functions that nghttp2 will use to notify us 4201cb0ef41Sopenharmony_ci// about significant events while processing http2 stuff. 4211cb0ef41Sopenharmony_ciHttp2Session::Callbacks::Callbacks(bool kHasGetPaddingCallback) { 4221cb0ef41Sopenharmony_ci nghttp2_session_callbacks* callbacks_; 4231cb0ef41Sopenharmony_ci CHECK_EQ(nghttp2_session_callbacks_new(&callbacks_), 0); 4241cb0ef41Sopenharmony_ci callbacks.reset(callbacks_); 4251cb0ef41Sopenharmony_ci 4261cb0ef41Sopenharmony_ci nghttp2_session_callbacks_set_on_begin_headers_callback( 4271cb0ef41Sopenharmony_ci callbacks_, OnBeginHeadersCallback); 4281cb0ef41Sopenharmony_ci nghttp2_session_callbacks_set_on_header_callback2( 4291cb0ef41Sopenharmony_ci callbacks_, OnHeaderCallback); 4301cb0ef41Sopenharmony_ci nghttp2_session_callbacks_set_on_frame_recv_callback( 4311cb0ef41Sopenharmony_ci callbacks_, OnFrameReceive); 4321cb0ef41Sopenharmony_ci nghttp2_session_callbacks_set_on_stream_close_callback( 4331cb0ef41Sopenharmony_ci callbacks_, OnStreamClose); 4341cb0ef41Sopenharmony_ci nghttp2_session_callbacks_set_on_data_chunk_recv_callback( 4351cb0ef41Sopenharmony_ci callbacks_, OnDataChunkReceived); 4361cb0ef41Sopenharmony_ci nghttp2_session_callbacks_set_on_frame_not_send_callback( 4371cb0ef41Sopenharmony_ci callbacks_, OnFrameNotSent); 4381cb0ef41Sopenharmony_ci nghttp2_session_callbacks_set_on_invalid_header_callback2( 4391cb0ef41Sopenharmony_ci callbacks_, OnInvalidHeader); 4401cb0ef41Sopenharmony_ci nghttp2_session_callbacks_set_error_callback2(callbacks_, OnNghttpError); 4411cb0ef41Sopenharmony_ci nghttp2_session_callbacks_set_send_data_callback( 4421cb0ef41Sopenharmony_ci callbacks_, OnSendData); 4431cb0ef41Sopenharmony_ci nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( 4441cb0ef41Sopenharmony_ci callbacks_, OnInvalidFrame); 4451cb0ef41Sopenharmony_ci nghttp2_session_callbacks_set_on_frame_send_callback( 4461cb0ef41Sopenharmony_ci callbacks_, OnFrameSent); 4471cb0ef41Sopenharmony_ci 4481cb0ef41Sopenharmony_ci if (kHasGetPaddingCallback) { 4491cb0ef41Sopenharmony_ci nghttp2_session_callbacks_set_select_padding_callback( 4501cb0ef41Sopenharmony_ci callbacks_, OnSelectPadding); 4511cb0ef41Sopenharmony_ci } 4521cb0ef41Sopenharmony_ci} 4531cb0ef41Sopenharmony_ci 4541cb0ef41Sopenharmony_civoid Http2Session::StopTrackingRcbuf(nghttp2_rcbuf* buf) { 4551cb0ef41Sopenharmony_ci StopTrackingMemory(buf); 4561cb0ef41Sopenharmony_ci} 4571cb0ef41Sopenharmony_ci 4581cb0ef41Sopenharmony_civoid Http2Session::CheckAllocatedSize(size_t previous_size) const { 4591cb0ef41Sopenharmony_ci CHECK_GE(current_nghttp2_memory_, previous_size); 4601cb0ef41Sopenharmony_ci} 4611cb0ef41Sopenharmony_ci 4621cb0ef41Sopenharmony_civoid Http2Session::IncreaseAllocatedSize(size_t size) { 4631cb0ef41Sopenharmony_ci current_nghttp2_memory_ += size; 4641cb0ef41Sopenharmony_ci} 4651cb0ef41Sopenharmony_ci 4661cb0ef41Sopenharmony_civoid Http2Session::DecreaseAllocatedSize(size_t size) { 4671cb0ef41Sopenharmony_ci current_nghttp2_memory_ -= size; 4681cb0ef41Sopenharmony_ci} 4691cb0ef41Sopenharmony_ci 4701cb0ef41Sopenharmony_ciHttp2Session::Http2Session(Http2State* http2_state, 4711cb0ef41Sopenharmony_ci Local<Object> wrap, 4721cb0ef41Sopenharmony_ci SessionType type) 4731cb0ef41Sopenharmony_ci : AsyncWrap(http2_state->env(), wrap, AsyncWrap::PROVIDER_HTTP2SESSION), 4741cb0ef41Sopenharmony_ci js_fields_(http2_state->env()->isolate()), 4751cb0ef41Sopenharmony_ci session_type_(type), 4761cb0ef41Sopenharmony_ci http2_state_(http2_state) { 4771cb0ef41Sopenharmony_ci MakeWeak(); 4781cb0ef41Sopenharmony_ci statistics_.session_type = type; 4791cb0ef41Sopenharmony_ci statistics_.start_time = uv_hrtime(); 4801cb0ef41Sopenharmony_ci 4811cb0ef41Sopenharmony_ci // Capture the configuration options for this session 4821cb0ef41Sopenharmony_ci Http2Options opts(http2_state, type); 4831cb0ef41Sopenharmony_ci 4841cb0ef41Sopenharmony_ci max_session_memory_ = opts.max_session_memory(); 4851cb0ef41Sopenharmony_ci 4861cb0ef41Sopenharmony_ci uint32_t maxHeaderPairs = opts.max_header_pairs(); 4871cb0ef41Sopenharmony_ci max_header_pairs_ = 4881cb0ef41Sopenharmony_ci type == NGHTTP2_SESSION_SERVER 4891cb0ef41Sopenharmony_ci ? GetServerMaxHeaderPairs(maxHeaderPairs) 4901cb0ef41Sopenharmony_ci : GetClientMaxHeaderPairs(maxHeaderPairs); 4911cb0ef41Sopenharmony_ci 4921cb0ef41Sopenharmony_ci max_outstanding_pings_ = opts.max_outstanding_pings(); 4931cb0ef41Sopenharmony_ci max_outstanding_settings_ = opts.max_outstanding_settings(); 4941cb0ef41Sopenharmony_ci 4951cb0ef41Sopenharmony_ci padding_strategy_ = opts.padding_strategy(); 4961cb0ef41Sopenharmony_ci 4971cb0ef41Sopenharmony_ci bool hasGetPaddingCallback = 4981cb0ef41Sopenharmony_ci padding_strategy_ != PADDING_STRATEGY_NONE; 4991cb0ef41Sopenharmony_ci 5001cb0ef41Sopenharmony_ci auto fn = type == NGHTTP2_SESSION_SERVER ? 5011cb0ef41Sopenharmony_ci nghttp2_session_server_new3 : 5021cb0ef41Sopenharmony_ci nghttp2_session_client_new3; 5031cb0ef41Sopenharmony_ci 5041cb0ef41Sopenharmony_ci nghttp2_mem alloc_info = MakeAllocator(); 5051cb0ef41Sopenharmony_ci 5061cb0ef41Sopenharmony_ci // This should fail only if the system is out of memory, which 5071cb0ef41Sopenharmony_ci // is going to cause lots of other problems anyway, or if any 5081cb0ef41Sopenharmony_ci // of the options are out of acceptable range, which we should 5091cb0ef41Sopenharmony_ci // be catching before it gets this far. Either way, crash if this 5101cb0ef41Sopenharmony_ci // fails. 5111cb0ef41Sopenharmony_ci nghttp2_session* session; 5121cb0ef41Sopenharmony_ci CHECK_EQ(fn( 5131cb0ef41Sopenharmony_ci &session, 5141cb0ef41Sopenharmony_ci callback_struct_saved[hasGetPaddingCallback ? 1 : 0].callbacks.get(), 5151cb0ef41Sopenharmony_ci this, 5161cb0ef41Sopenharmony_ci *opts, 5171cb0ef41Sopenharmony_ci &alloc_info), 0); 5181cb0ef41Sopenharmony_ci session_.reset(session); 5191cb0ef41Sopenharmony_ci 5201cb0ef41Sopenharmony_ci outgoing_storage_.reserve(1024); 5211cb0ef41Sopenharmony_ci outgoing_buffers_.reserve(32); 5221cb0ef41Sopenharmony_ci 5231cb0ef41Sopenharmony_ci Local<Uint8Array> uint8_arr = 5241cb0ef41Sopenharmony_ci Uint8Array::New(js_fields_.GetArrayBuffer(), 0, kSessionUint8FieldCount); 5251cb0ef41Sopenharmony_ci USE(wrap->Set(env()->context(), env()->fields_string(), uint8_arr)); 5261cb0ef41Sopenharmony_ci} 5271cb0ef41Sopenharmony_ci 5281cb0ef41Sopenharmony_ciHttp2Session::~Http2Session() { 5291cb0ef41Sopenharmony_ci CHECK(!is_in_scope()); 5301cb0ef41Sopenharmony_ci Debug(this, "freeing nghttp2 session"); 5311cb0ef41Sopenharmony_ci // Ensure that all `Http2Stream` instances and the memory they hold 5321cb0ef41Sopenharmony_ci // on to are destroyed before the nghttp2 session is. 5331cb0ef41Sopenharmony_ci for (const auto& [id, stream] : streams_) { 5341cb0ef41Sopenharmony_ci stream->Detach(); 5351cb0ef41Sopenharmony_ci } 5361cb0ef41Sopenharmony_ci streams_.clear(); 5371cb0ef41Sopenharmony_ci // Explicitly reset session_ so the subsequent 5381cb0ef41Sopenharmony_ci // current_nghttp2_memory_ check passes. 5391cb0ef41Sopenharmony_ci session_.reset(); 5401cb0ef41Sopenharmony_ci CHECK_EQ(current_nghttp2_memory_, 0); 5411cb0ef41Sopenharmony_ci} 5421cb0ef41Sopenharmony_ci 5431cb0ef41Sopenharmony_civoid Http2Session::MemoryInfo(MemoryTracker* tracker) const { 5441cb0ef41Sopenharmony_ci tracker->TrackField("streams", streams_); 5451cb0ef41Sopenharmony_ci tracker->TrackField("outstanding_pings", outstanding_pings_); 5461cb0ef41Sopenharmony_ci tracker->TrackField("outstanding_settings", outstanding_settings_); 5471cb0ef41Sopenharmony_ci tracker->TrackField("outgoing_buffers", outgoing_buffers_); 5481cb0ef41Sopenharmony_ci tracker->TrackFieldWithSize("stream_buf", stream_buf_.len); 5491cb0ef41Sopenharmony_ci tracker->TrackFieldWithSize("outgoing_storage", outgoing_storage_.size()); 5501cb0ef41Sopenharmony_ci tracker->TrackFieldWithSize("pending_rst_streams", 5511cb0ef41Sopenharmony_ci pending_rst_streams_.size() * sizeof(int32_t)); 5521cb0ef41Sopenharmony_ci tracker->TrackFieldWithSize("nghttp2_memory", current_nghttp2_memory_); 5531cb0ef41Sopenharmony_ci} 5541cb0ef41Sopenharmony_ci 5551cb0ef41Sopenharmony_cistd::string Http2Session::diagnostic_name() const { 5561cb0ef41Sopenharmony_ci return std::string("Http2Session ") + TypeName() + " (" + 5571cb0ef41Sopenharmony_ci std::to_string(static_cast<int64_t>(get_async_id())) + ")"; 5581cb0ef41Sopenharmony_ci} 5591cb0ef41Sopenharmony_ci 5601cb0ef41Sopenharmony_ciMaybeLocal<Object> Http2StreamPerformanceEntryTraits::GetDetails( 5611cb0ef41Sopenharmony_ci Environment* env, 5621cb0ef41Sopenharmony_ci const Http2StreamPerformanceEntry& entry) { 5631cb0ef41Sopenharmony_ci Local<Object> obj = Object::New(env->isolate()); 5641cb0ef41Sopenharmony_ci 5651cb0ef41Sopenharmony_ci#define SET(name, val) \ 5661cb0ef41Sopenharmony_ci if (!obj->Set( \ 5671cb0ef41Sopenharmony_ci env->context(), \ 5681cb0ef41Sopenharmony_ci env->name(), \ 5691cb0ef41Sopenharmony_ci Number::New( \ 5701cb0ef41Sopenharmony_ci env->isolate(), \ 5711cb0ef41Sopenharmony_ci static_cast<double>(entry.details.val))).IsJust()) { \ 5721cb0ef41Sopenharmony_ci return MaybeLocal<Object>(); \ 5731cb0ef41Sopenharmony_ci } 5741cb0ef41Sopenharmony_ci 5751cb0ef41Sopenharmony_ci SET(bytes_read_string, received_bytes) 5761cb0ef41Sopenharmony_ci SET(bytes_written_string, sent_bytes) 5771cb0ef41Sopenharmony_ci SET(id_string, id) 5781cb0ef41Sopenharmony_ci#undef SET 5791cb0ef41Sopenharmony_ci 5801cb0ef41Sopenharmony_ci#define SET(name, val) \ 5811cb0ef41Sopenharmony_ci if (!obj->Set( \ 5821cb0ef41Sopenharmony_ci env->context(), \ 5831cb0ef41Sopenharmony_ci env->name(), \ 5841cb0ef41Sopenharmony_ci Number::New( \ 5851cb0ef41Sopenharmony_ci env->isolate(), \ 5861cb0ef41Sopenharmony_ci (entry.details.val - entry.details.start_time) / 1e6)) \ 5871cb0ef41Sopenharmony_ci .IsJust()) { \ 5881cb0ef41Sopenharmony_ci return MaybeLocal<Object>(); \ 5891cb0ef41Sopenharmony_ci } 5901cb0ef41Sopenharmony_ci 5911cb0ef41Sopenharmony_ci SET(time_to_first_byte_string, first_byte) 5921cb0ef41Sopenharmony_ci SET(time_to_first_byte_sent_string, first_byte_sent) 5931cb0ef41Sopenharmony_ci SET(time_to_first_header_string, first_header) 5941cb0ef41Sopenharmony_ci#undef SET 5951cb0ef41Sopenharmony_ci 5961cb0ef41Sopenharmony_ci return obj; 5971cb0ef41Sopenharmony_ci} 5981cb0ef41Sopenharmony_ci 5991cb0ef41Sopenharmony_ciMaybeLocal<Object> Http2SessionPerformanceEntryTraits::GetDetails( 6001cb0ef41Sopenharmony_ci Environment* env, 6011cb0ef41Sopenharmony_ci const Http2SessionPerformanceEntry& entry) { 6021cb0ef41Sopenharmony_ci Local<Object> obj = Object::New(env->isolate()); 6031cb0ef41Sopenharmony_ci 6041cb0ef41Sopenharmony_ci#define SET(name, val) \ 6051cb0ef41Sopenharmony_ci if (!obj->Set( \ 6061cb0ef41Sopenharmony_ci env->context(), \ 6071cb0ef41Sopenharmony_ci env->name(), \ 6081cb0ef41Sopenharmony_ci Number::New( \ 6091cb0ef41Sopenharmony_ci env->isolate(), \ 6101cb0ef41Sopenharmony_ci static_cast<double>(entry.details.val))).IsJust()) { \ 6111cb0ef41Sopenharmony_ci return MaybeLocal<Object>(); \ 6121cb0ef41Sopenharmony_ci } 6131cb0ef41Sopenharmony_ci 6141cb0ef41Sopenharmony_ci SET(bytes_written_string, data_sent) 6151cb0ef41Sopenharmony_ci SET(bytes_read_string, data_received) 6161cb0ef41Sopenharmony_ci SET(frames_received_string, frame_count) 6171cb0ef41Sopenharmony_ci SET(frames_sent_string, frame_sent) 6181cb0ef41Sopenharmony_ci SET(max_concurrent_streams_string, max_concurrent_streams) 6191cb0ef41Sopenharmony_ci SET(ping_rtt_string, ping_rtt) 6201cb0ef41Sopenharmony_ci SET(stream_average_duration_string, stream_average_duration) 6211cb0ef41Sopenharmony_ci SET(stream_count_string, stream_count) 6221cb0ef41Sopenharmony_ci 6231cb0ef41Sopenharmony_ci if (!obj->Set( 6241cb0ef41Sopenharmony_ci env->context(), 6251cb0ef41Sopenharmony_ci env->type_string(), 6261cb0ef41Sopenharmony_ci OneByteString( 6271cb0ef41Sopenharmony_ci env->isolate(), 6281cb0ef41Sopenharmony_ci (entry.details.session_type == NGHTTP2_SESSION_SERVER) 6291cb0ef41Sopenharmony_ci ? "server" : "client")).IsJust()) { 6301cb0ef41Sopenharmony_ci return MaybeLocal<Object>(); 6311cb0ef41Sopenharmony_ci } 6321cb0ef41Sopenharmony_ci 6331cb0ef41Sopenharmony_ci#undef SET 6341cb0ef41Sopenharmony_ci return obj; 6351cb0ef41Sopenharmony_ci} 6361cb0ef41Sopenharmony_ci 6371cb0ef41Sopenharmony_civoid Http2Stream::EmitStatistics() { 6381cb0ef41Sopenharmony_ci CHECK_NOT_NULL(session()); 6391cb0ef41Sopenharmony_ci if (LIKELY(!HasHttp2Observer(env()))) 6401cb0ef41Sopenharmony_ci return; 6411cb0ef41Sopenharmony_ci 6421cb0ef41Sopenharmony_ci double start = statistics_.start_time / 1e6; 6431cb0ef41Sopenharmony_ci double duration = (PERFORMANCE_NOW() / 1e6) - start; 6441cb0ef41Sopenharmony_ci 6451cb0ef41Sopenharmony_ci std::unique_ptr<Http2StreamPerformanceEntry> entry = 6461cb0ef41Sopenharmony_ci std::make_unique<Http2StreamPerformanceEntry>( 6471cb0ef41Sopenharmony_ci "Http2Stream", 6481cb0ef41Sopenharmony_ci start - (node::performance::timeOrigin / 1e6), 6491cb0ef41Sopenharmony_ci duration, 6501cb0ef41Sopenharmony_ci statistics_); 6511cb0ef41Sopenharmony_ci 6521cb0ef41Sopenharmony_ci env()->SetImmediate([entry = std::move(entry)](Environment* env) { 6531cb0ef41Sopenharmony_ci if (HasHttp2Observer(env)) 6541cb0ef41Sopenharmony_ci entry->Notify(env); 6551cb0ef41Sopenharmony_ci }); 6561cb0ef41Sopenharmony_ci} 6571cb0ef41Sopenharmony_ci 6581cb0ef41Sopenharmony_civoid Http2Session::EmitStatistics() { 6591cb0ef41Sopenharmony_ci if (LIKELY(!HasHttp2Observer(env()))) 6601cb0ef41Sopenharmony_ci return; 6611cb0ef41Sopenharmony_ci 6621cb0ef41Sopenharmony_ci double start = statistics_.start_time / 1e6; 6631cb0ef41Sopenharmony_ci double duration = (PERFORMANCE_NOW() / 1e6) - start; 6641cb0ef41Sopenharmony_ci 6651cb0ef41Sopenharmony_ci std::unique_ptr<Http2SessionPerformanceEntry> entry = 6661cb0ef41Sopenharmony_ci std::make_unique<Http2SessionPerformanceEntry>( 6671cb0ef41Sopenharmony_ci "Http2Session", 6681cb0ef41Sopenharmony_ci start - (node::performance::timeOrigin / 1e6), 6691cb0ef41Sopenharmony_ci duration, 6701cb0ef41Sopenharmony_ci statistics_); 6711cb0ef41Sopenharmony_ci 6721cb0ef41Sopenharmony_ci env()->SetImmediate([entry = std::move(entry)](Environment* env) { 6731cb0ef41Sopenharmony_ci if (HasHttp2Observer(env)) 6741cb0ef41Sopenharmony_ci entry->Notify(env); 6751cb0ef41Sopenharmony_ci }); 6761cb0ef41Sopenharmony_ci} 6771cb0ef41Sopenharmony_ci 6781cb0ef41Sopenharmony_ci// Closes the session and frees the associated resources 6791cb0ef41Sopenharmony_civoid Http2Session::Close(uint32_t code, bool socket_closed) { 6801cb0ef41Sopenharmony_ci Debug(this, "closing session"); 6811cb0ef41Sopenharmony_ci 6821cb0ef41Sopenharmony_ci if (is_closing()) 6831cb0ef41Sopenharmony_ci return; 6841cb0ef41Sopenharmony_ci set_closing(); 6851cb0ef41Sopenharmony_ci 6861cb0ef41Sopenharmony_ci // Stop reading on the i/o stream 6871cb0ef41Sopenharmony_ci if (stream_ != nullptr) { 6881cb0ef41Sopenharmony_ci set_reading_stopped(); 6891cb0ef41Sopenharmony_ci stream_->ReadStop(); 6901cb0ef41Sopenharmony_ci } 6911cb0ef41Sopenharmony_ci 6921cb0ef41Sopenharmony_ci // If the socket is not closed, then attempt to send a closing GOAWAY 6931cb0ef41Sopenharmony_ci // frame. There is no guarantee that this GOAWAY will be received by 6941cb0ef41Sopenharmony_ci // the peer but the HTTP/2 spec recommends sending it anyway. We'll 6951cb0ef41Sopenharmony_ci // make a best effort. 6961cb0ef41Sopenharmony_ci if (!socket_closed) { 6971cb0ef41Sopenharmony_ci Debug(this, "terminating session with code %d", code); 6981cb0ef41Sopenharmony_ci CHECK_EQ(nghttp2_session_terminate_session(session_.get(), code), 0); 6991cb0ef41Sopenharmony_ci SendPendingData(); 7001cb0ef41Sopenharmony_ci } else if (stream_ != nullptr) { 7011cb0ef41Sopenharmony_ci stream_->RemoveStreamListener(this); 7021cb0ef41Sopenharmony_ci } 7031cb0ef41Sopenharmony_ci 7041cb0ef41Sopenharmony_ci set_destroyed(); 7051cb0ef41Sopenharmony_ci 7061cb0ef41Sopenharmony_ci // If we are writing we will get to make the callback in OnStreamAfterWrite. 7071cb0ef41Sopenharmony_ci if (!is_write_in_progress()) { 7081cb0ef41Sopenharmony_ci Debug(this, "make done session callback"); 7091cb0ef41Sopenharmony_ci HandleScope scope(env()->isolate()); 7101cb0ef41Sopenharmony_ci MakeCallback(env()->ondone_string(), 0, nullptr); 7111cb0ef41Sopenharmony_ci if (stream_ != nullptr) { 7121cb0ef41Sopenharmony_ci // Start reading again to detect the other end finishing. 7131cb0ef41Sopenharmony_ci set_reading_stopped(false); 7141cb0ef41Sopenharmony_ci stream_->ReadStart(); 7151cb0ef41Sopenharmony_ci } 7161cb0ef41Sopenharmony_ci } 7171cb0ef41Sopenharmony_ci 7181cb0ef41Sopenharmony_ci // If there are outstanding pings, those will need to be canceled, do 7191cb0ef41Sopenharmony_ci // so on the next iteration of the event loop to avoid calling out into 7201cb0ef41Sopenharmony_ci // javascript since this may be called during garbage collection. 7211cb0ef41Sopenharmony_ci while (BaseObjectPtr<Http2Ping> ping = PopPing()) { 7221cb0ef41Sopenharmony_ci ping->DetachFromSession(); 7231cb0ef41Sopenharmony_ci env()->SetImmediate( 7241cb0ef41Sopenharmony_ci [ping = std::move(ping)](Environment* env) { 7251cb0ef41Sopenharmony_ci ping->Done(false); 7261cb0ef41Sopenharmony_ci }); 7271cb0ef41Sopenharmony_ci } 7281cb0ef41Sopenharmony_ci 7291cb0ef41Sopenharmony_ci statistics_.end_time = uv_hrtime(); 7301cb0ef41Sopenharmony_ci EmitStatistics(); 7311cb0ef41Sopenharmony_ci} 7321cb0ef41Sopenharmony_ci 7331cb0ef41Sopenharmony_ci// Locates an existing known stream by ID. nghttp2 has a similar method 7341cb0ef41Sopenharmony_ci// but this is faster and does not fail if the stream is not found. 7351cb0ef41Sopenharmony_ciBaseObjectPtr<Http2Stream> Http2Session::FindStream(int32_t id) { 7361cb0ef41Sopenharmony_ci auto s = streams_.find(id); 7371cb0ef41Sopenharmony_ci return s != streams_.end() ? s->second : BaseObjectPtr<Http2Stream>(); 7381cb0ef41Sopenharmony_ci} 7391cb0ef41Sopenharmony_ci 7401cb0ef41Sopenharmony_cibool Http2Session::CanAddStream() { 7411cb0ef41Sopenharmony_ci uint32_t maxConcurrentStreams = 7421cb0ef41Sopenharmony_ci nghttp2_session_get_local_settings( 7431cb0ef41Sopenharmony_ci session_.get(), NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS); 7441cb0ef41Sopenharmony_ci size_t maxSize = 7451cb0ef41Sopenharmony_ci std::min(streams_.max_size(), static_cast<size_t>(maxConcurrentStreams)); 7461cb0ef41Sopenharmony_ci // We can add a new stream so long as we are less than the current 7471cb0ef41Sopenharmony_ci // maximum on concurrent streams and there's enough available memory 7481cb0ef41Sopenharmony_ci return streams_.size() < maxSize && 7491cb0ef41Sopenharmony_ci has_available_session_memory(sizeof(Http2Stream)); 7501cb0ef41Sopenharmony_ci} 7511cb0ef41Sopenharmony_ci 7521cb0ef41Sopenharmony_civoid Http2Session::AddStream(Http2Stream* stream) { 7531cb0ef41Sopenharmony_ci CHECK_GE(++statistics_.stream_count, 0); 7541cb0ef41Sopenharmony_ci streams_[stream->id()] = BaseObjectPtr<Http2Stream>(stream); 7551cb0ef41Sopenharmony_ci size_t size = streams_.size(); 7561cb0ef41Sopenharmony_ci if (size > statistics_.max_concurrent_streams) 7571cb0ef41Sopenharmony_ci statistics_.max_concurrent_streams = size; 7581cb0ef41Sopenharmony_ci IncrementCurrentSessionMemory(sizeof(*stream)); 7591cb0ef41Sopenharmony_ci} 7601cb0ef41Sopenharmony_ci 7611cb0ef41Sopenharmony_ci 7621cb0ef41Sopenharmony_ciBaseObjectPtr<Http2Stream> Http2Session::RemoveStream(int32_t id) { 7631cb0ef41Sopenharmony_ci BaseObjectPtr<Http2Stream> stream; 7641cb0ef41Sopenharmony_ci if (streams_.empty()) 7651cb0ef41Sopenharmony_ci return stream; 7661cb0ef41Sopenharmony_ci stream = FindStream(id); 7671cb0ef41Sopenharmony_ci if (stream) { 7681cb0ef41Sopenharmony_ci streams_.erase(id); 7691cb0ef41Sopenharmony_ci DecrementCurrentSessionMemory(sizeof(*stream)); 7701cb0ef41Sopenharmony_ci } 7711cb0ef41Sopenharmony_ci return stream; 7721cb0ef41Sopenharmony_ci} 7731cb0ef41Sopenharmony_ci 7741cb0ef41Sopenharmony_ci// Used as one of the Padding Strategy functions. Will attempt to ensure 7751cb0ef41Sopenharmony_ci// that the total frame size, including header bytes, are 8-byte aligned. 7761cb0ef41Sopenharmony_ci// If maxPayloadLen is smaller than the number of bytes necessary to align, 7771cb0ef41Sopenharmony_ci// will return maxPayloadLen instead. 7781cb0ef41Sopenharmony_cissize_t Http2Session::OnDWordAlignedPadding(size_t frameLen, 7791cb0ef41Sopenharmony_ci size_t maxPayloadLen) { 7801cb0ef41Sopenharmony_ci size_t r = (frameLen + 9) % 8; 7811cb0ef41Sopenharmony_ci if (r == 0) return frameLen; // If already a multiple of 8, return. 7821cb0ef41Sopenharmony_ci 7831cb0ef41Sopenharmony_ci size_t pad = frameLen + (8 - r); 7841cb0ef41Sopenharmony_ci 7851cb0ef41Sopenharmony_ci // If maxPayloadLen happens to be less than the calculated pad length, 7861cb0ef41Sopenharmony_ci // use the max instead, even tho this means the frame will not be 7871cb0ef41Sopenharmony_ci // aligned. 7881cb0ef41Sopenharmony_ci pad = std::min(maxPayloadLen, pad); 7891cb0ef41Sopenharmony_ci Debug(this, "using frame size padding: %d", pad); 7901cb0ef41Sopenharmony_ci return pad; 7911cb0ef41Sopenharmony_ci} 7921cb0ef41Sopenharmony_ci 7931cb0ef41Sopenharmony_ci// Used as one of the Padding Strategy functions. Uses the maximum amount 7941cb0ef41Sopenharmony_ci// of padding allowed for the current frame. 7951cb0ef41Sopenharmony_cissize_t Http2Session::OnMaxFrameSizePadding(size_t frameLen, 7961cb0ef41Sopenharmony_ci size_t maxPayloadLen) { 7971cb0ef41Sopenharmony_ci Debug(this, "using max frame size padding: %d", maxPayloadLen); 7981cb0ef41Sopenharmony_ci return maxPayloadLen; 7991cb0ef41Sopenharmony_ci} 8001cb0ef41Sopenharmony_ci 8011cb0ef41Sopenharmony_ci// Write data received from the i/o stream to the underlying nghttp2_session. 8021cb0ef41Sopenharmony_ci// On each call to nghttp2_session_mem_recv, nghttp2 will begin calling the 8031cb0ef41Sopenharmony_ci// various callback functions. Each of these will typically result in a call 8041cb0ef41Sopenharmony_ci// out to JavaScript so this particular function is rather hot and can be 8051cb0ef41Sopenharmony_ci// quite expensive. This is a potential performance optimization target later. 8061cb0ef41Sopenharmony_civoid Http2Session::ConsumeHTTP2Data() { 8071cb0ef41Sopenharmony_ci CHECK_NOT_NULL(stream_buf_.base); 8081cb0ef41Sopenharmony_ci CHECK_LE(stream_buf_offset_, stream_buf_.len); 8091cb0ef41Sopenharmony_ci size_t read_len = stream_buf_.len - stream_buf_offset_; 8101cb0ef41Sopenharmony_ci 8111cb0ef41Sopenharmony_ci // multiple side effects. 8121cb0ef41Sopenharmony_ci Debug(this, "receiving %d bytes [wants data? %d]", 8131cb0ef41Sopenharmony_ci read_len, 8141cb0ef41Sopenharmony_ci nghttp2_session_want_read(session_.get())); 8151cb0ef41Sopenharmony_ci set_receive_paused(false); 8161cb0ef41Sopenharmony_ci custom_recv_error_code_ = nullptr; 8171cb0ef41Sopenharmony_ci ssize_t ret = 8181cb0ef41Sopenharmony_ci nghttp2_session_mem_recv(session_.get(), 8191cb0ef41Sopenharmony_ci reinterpret_cast<uint8_t*>(stream_buf_.base) + 8201cb0ef41Sopenharmony_ci stream_buf_offset_, 8211cb0ef41Sopenharmony_ci read_len); 8221cb0ef41Sopenharmony_ci CHECK_NE(ret, NGHTTP2_ERR_NOMEM); 8231cb0ef41Sopenharmony_ci CHECK_IMPLIES(custom_recv_error_code_ != nullptr, ret < 0); 8241cb0ef41Sopenharmony_ci 8251cb0ef41Sopenharmony_ci if (is_receive_paused()) { 8261cb0ef41Sopenharmony_ci CHECK(is_reading_stopped()); 8271cb0ef41Sopenharmony_ci 8281cb0ef41Sopenharmony_ci CHECK_GT(ret, 0); 8291cb0ef41Sopenharmony_ci CHECK_LE(static_cast<size_t>(ret), read_len); 8301cb0ef41Sopenharmony_ci 8311cb0ef41Sopenharmony_ci // Mark the remainder of the data as available for later consumption. 8321cb0ef41Sopenharmony_ci // Even if all bytes were received, a paused stream may delay the 8331cb0ef41Sopenharmony_ci // nghttp2_on_frame_recv_callback which may have an END_STREAM flag. 8341cb0ef41Sopenharmony_ci stream_buf_offset_ += ret; 8351cb0ef41Sopenharmony_ci goto done; 8361cb0ef41Sopenharmony_ci } 8371cb0ef41Sopenharmony_ci 8381cb0ef41Sopenharmony_ci // We are done processing the current input chunk. 8391cb0ef41Sopenharmony_ci DecrementCurrentSessionMemory(stream_buf_.len); 8401cb0ef41Sopenharmony_ci stream_buf_offset_ = 0; 8411cb0ef41Sopenharmony_ci stream_buf_ab_.Reset(); 8421cb0ef41Sopenharmony_ci stream_buf_allocation_.reset(); 8431cb0ef41Sopenharmony_ci stream_buf_ = uv_buf_init(nullptr, 0); 8441cb0ef41Sopenharmony_ci 8451cb0ef41Sopenharmony_ci // Send any data that was queued up while processing the received data. 8461cb0ef41Sopenharmony_ci if (ret >= 0 && !is_destroyed()) { 8471cb0ef41Sopenharmony_ci SendPendingData(); 8481cb0ef41Sopenharmony_ci } 8491cb0ef41Sopenharmony_ci 8501cb0ef41Sopenharmony_cidone: 8511cb0ef41Sopenharmony_ci if (UNLIKELY(ret < 0)) { 8521cb0ef41Sopenharmony_ci Isolate* isolate = env()->isolate(); 8531cb0ef41Sopenharmony_ci Debug(this, 8541cb0ef41Sopenharmony_ci "fatal error receiving data: %d (%s)", 8551cb0ef41Sopenharmony_ci ret, 8561cb0ef41Sopenharmony_ci custom_recv_error_code_ != nullptr ? 8571cb0ef41Sopenharmony_ci custom_recv_error_code_ : "(no custom error code)"); 8581cb0ef41Sopenharmony_ci Local<Value> args[] = { 8591cb0ef41Sopenharmony_ci Integer::New(isolate, static_cast<int32_t>(ret)), 8601cb0ef41Sopenharmony_ci Null(isolate) 8611cb0ef41Sopenharmony_ci }; 8621cb0ef41Sopenharmony_ci if (custom_recv_error_code_ != nullptr) { 8631cb0ef41Sopenharmony_ci args[1] = String::NewFromUtf8( 8641cb0ef41Sopenharmony_ci isolate, 8651cb0ef41Sopenharmony_ci custom_recv_error_code_, 8661cb0ef41Sopenharmony_ci NewStringType::kInternalized).ToLocalChecked(); 8671cb0ef41Sopenharmony_ci } 8681cb0ef41Sopenharmony_ci MakeCallback( 8691cb0ef41Sopenharmony_ci env()->http2session_on_error_function(), 8701cb0ef41Sopenharmony_ci arraysize(args), 8711cb0ef41Sopenharmony_ci args); 8721cb0ef41Sopenharmony_ci } 8731cb0ef41Sopenharmony_ci} 8741cb0ef41Sopenharmony_ci 8751cb0ef41Sopenharmony_ci 8761cb0ef41Sopenharmony_ciint32_t GetFrameID(const nghttp2_frame* frame) { 8771cb0ef41Sopenharmony_ci // If this is a push promise, we want to grab the id of the promised stream 8781cb0ef41Sopenharmony_ci return (frame->hd.type == NGHTTP2_PUSH_PROMISE) ? 8791cb0ef41Sopenharmony_ci frame->push_promise.promised_stream_id : 8801cb0ef41Sopenharmony_ci frame->hd.stream_id; 8811cb0ef41Sopenharmony_ci} 8821cb0ef41Sopenharmony_ci 8831cb0ef41Sopenharmony_ci 8841cb0ef41Sopenharmony_ci// Called by nghttp2 at the start of receiving a HEADERS frame. We use this 8851cb0ef41Sopenharmony_ci// callback to determine if a new stream is being created or if we are simply 8861cb0ef41Sopenharmony_ci// adding a new block of headers to an existing stream. The header pairs 8871cb0ef41Sopenharmony_ci// themselves are set in the OnHeaderCallback 8881cb0ef41Sopenharmony_ciint Http2Session::OnBeginHeadersCallback(nghttp2_session* handle, 8891cb0ef41Sopenharmony_ci const nghttp2_frame* frame, 8901cb0ef41Sopenharmony_ci void* user_data) { 8911cb0ef41Sopenharmony_ci Http2Session* session = static_cast<Http2Session*>(user_data); 8921cb0ef41Sopenharmony_ci int32_t id = GetFrameID(frame); 8931cb0ef41Sopenharmony_ci Debug(session, "beginning headers for stream %d", id); 8941cb0ef41Sopenharmony_ci 8951cb0ef41Sopenharmony_ci BaseObjectPtr<Http2Stream> stream = session->FindStream(id); 8961cb0ef41Sopenharmony_ci // The common case is that we're creating a new stream. The less likely 8971cb0ef41Sopenharmony_ci // case is that we're receiving a set of trailers 8981cb0ef41Sopenharmony_ci if (LIKELY(!stream)) { 8991cb0ef41Sopenharmony_ci if (UNLIKELY(!session->CanAddStream() || 9001cb0ef41Sopenharmony_ci Http2Stream::New(session, id, frame->headers.cat) == 9011cb0ef41Sopenharmony_ci nullptr)) { 9021cb0ef41Sopenharmony_ci if (session->rejected_stream_count_++ > 9031cb0ef41Sopenharmony_ci session->js_fields_->max_rejected_streams) 9041cb0ef41Sopenharmony_ci return NGHTTP2_ERR_CALLBACK_FAILURE; 9051cb0ef41Sopenharmony_ci // Too many concurrent streams being opened 9061cb0ef41Sopenharmony_ci nghttp2_submit_rst_stream( 9071cb0ef41Sopenharmony_ci session->session(), 9081cb0ef41Sopenharmony_ci NGHTTP2_FLAG_NONE, 9091cb0ef41Sopenharmony_ci id, 9101cb0ef41Sopenharmony_ci NGHTTP2_ENHANCE_YOUR_CALM); 9111cb0ef41Sopenharmony_ci return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; 9121cb0ef41Sopenharmony_ci } 9131cb0ef41Sopenharmony_ci 9141cb0ef41Sopenharmony_ci session->rejected_stream_count_ = 0; 9151cb0ef41Sopenharmony_ci } else if (!stream->is_destroyed()) { 9161cb0ef41Sopenharmony_ci stream->StartHeaders(frame->headers.cat); 9171cb0ef41Sopenharmony_ci } 9181cb0ef41Sopenharmony_ci return 0; 9191cb0ef41Sopenharmony_ci} 9201cb0ef41Sopenharmony_ci 9211cb0ef41Sopenharmony_ci// Called by nghttp2 for each header name/value pair in a HEADERS block. 9221cb0ef41Sopenharmony_ci// This had to have been preceded by a call to OnBeginHeadersCallback so 9231cb0ef41Sopenharmony_ci// the Http2Stream is guaranteed to already exist. 9241cb0ef41Sopenharmony_ciint Http2Session::OnHeaderCallback(nghttp2_session* handle, 9251cb0ef41Sopenharmony_ci const nghttp2_frame* frame, 9261cb0ef41Sopenharmony_ci nghttp2_rcbuf* name, 9271cb0ef41Sopenharmony_ci nghttp2_rcbuf* value, 9281cb0ef41Sopenharmony_ci uint8_t flags, 9291cb0ef41Sopenharmony_ci void* user_data) { 9301cb0ef41Sopenharmony_ci Http2Session* session = static_cast<Http2Session*>(user_data); 9311cb0ef41Sopenharmony_ci int32_t id = GetFrameID(frame); 9321cb0ef41Sopenharmony_ci BaseObjectPtr<Http2Stream> stream = session->FindStream(id); 9331cb0ef41Sopenharmony_ci // If stream is null at this point, either something odd has happened 9341cb0ef41Sopenharmony_ci // or the stream was closed locally while header processing was occurring. 9351cb0ef41Sopenharmony_ci // either way, do not proceed and close the stream. 9361cb0ef41Sopenharmony_ci if (UNLIKELY(!stream)) 9371cb0ef41Sopenharmony_ci return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; 9381cb0ef41Sopenharmony_ci 9391cb0ef41Sopenharmony_ci // If the stream has already been destroyed, ignore. 9401cb0ef41Sopenharmony_ci if (!stream->is_destroyed() && !stream->AddHeader(name, value, flags)) { 9411cb0ef41Sopenharmony_ci // This will only happen if the connected peer sends us more 9421cb0ef41Sopenharmony_ci // than the allowed number of header items at any given time 9431cb0ef41Sopenharmony_ci stream->SubmitRstStream(NGHTTP2_ENHANCE_YOUR_CALM); 9441cb0ef41Sopenharmony_ci return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; 9451cb0ef41Sopenharmony_ci } 9461cb0ef41Sopenharmony_ci return 0; 9471cb0ef41Sopenharmony_ci} 9481cb0ef41Sopenharmony_ci 9491cb0ef41Sopenharmony_ci 9501cb0ef41Sopenharmony_ci// Called by nghttp2 when a complete HTTP2 frame has been received. There are 9511cb0ef41Sopenharmony_ci// only a handful of frame types that we care about handling here. 9521cb0ef41Sopenharmony_ciint Http2Session::OnFrameReceive(nghttp2_session* handle, 9531cb0ef41Sopenharmony_ci const nghttp2_frame* frame, 9541cb0ef41Sopenharmony_ci void* user_data) { 9551cb0ef41Sopenharmony_ci Http2Session* session = static_cast<Http2Session*>(user_data); 9561cb0ef41Sopenharmony_ci session->statistics_.frame_count++; 9571cb0ef41Sopenharmony_ci Debug(session, "complete frame received: type: %d", 9581cb0ef41Sopenharmony_ci frame->hd.type); 9591cb0ef41Sopenharmony_ci switch (frame->hd.type) { 9601cb0ef41Sopenharmony_ci case NGHTTP2_DATA: 9611cb0ef41Sopenharmony_ci return session->HandleDataFrame(frame); 9621cb0ef41Sopenharmony_ci case NGHTTP2_PUSH_PROMISE: 9631cb0ef41Sopenharmony_ci // Intentional fall-through, handled just like headers frames 9641cb0ef41Sopenharmony_ci case NGHTTP2_HEADERS: 9651cb0ef41Sopenharmony_ci session->HandleHeadersFrame(frame); 9661cb0ef41Sopenharmony_ci break; 9671cb0ef41Sopenharmony_ci case NGHTTP2_SETTINGS: 9681cb0ef41Sopenharmony_ci session->HandleSettingsFrame(frame); 9691cb0ef41Sopenharmony_ci break; 9701cb0ef41Sopenharmony_ci case NGHTTP2_PRIORITY: 9711cb0ef41Sopenharmony_ci session->HandlePriorityFrame(frame); 9721cb0ef41Sopenharmony_ci break; 9731cb0ef41Sopenharmony_ci case NGHTTP2_GOAWAY: 9741cb0ef41Sopenharmony_ci session->HandleGoawayFrame(frame); 9751cb0ef41Sopenharmony_ci break; 9761cb0ef41Sopenharmony_ci case NGHTTP2_PING: 9771cb0ef41Sopenharmony_ci session->HandlePingFrame(frame); 9781cb0ef41Sopenharmony_ci break; 9791cb0ef41Sopenharmony_ci case NGHTTP2_ALTSVC: 9801cb0ef41Sopenharmony_ci session->HandleAltSvcFrame(frame); 9811cb0ef41Sopenharmony_ci break; 9821cb0ef41Sopenharmony_ci case NGHTTP2_ORIGIN: 9831cb0ef41Sopenharmony_ci session->HandleOriginFrame(frame); 9841cb0ef41Sopenharmony_ci break; 9851cb0ef41Sopenharmony_ci default: 9861cb0ef41Sopenharmony_ci break; 9871cb0ef41Sopenharmony_ci } 9881cb0ef41Sopenharmony_ci return 0; 9891cb0ef41Sopenharmony_ci} 9901cb0ef41Sopenharmony_ci 9911cb0ef41Sopenharmony_ciint Http2Session::OnInvalidFrame(nghttp2_session* handle, 9921cb0ef41Sopenharmony_ci const nghttp2_frame* frame, 9931cb0ef41Sopenharmony_ci int lib_error_code, 9941cb0ef41Sopenharmony_ci void* user_data) { 9951cb0ef41Sopenharmony_ci Http2Session* session = static_cast<Http2Session*>(user_data); 9961cb0ef41Sopenharmony_ci const uint32_t max_invalid_frames = session->js_fields_->max_invalid_frames; 9971cb0ef41Sopenharmony_ci 9981cb0ef41Sopenharmony_ci Debug(session, 9991cb0ef41Sopenharmony_ci "invalid frame received (%u/%u), code: %d", 10001cb0ef41Sopenharmony_ci session->invalid_frame_count_, 10011cb0ef41Sopenharmony_ci max_invalid_frames, 10021cb0ef41Sopenharmony_ci lib_error_code); 10031cb0ef41Sopenharmony_ci if (session->invalid_frame_count_++ > max_invalid_frames) { 10041cb0ef41Sopenharmony_ci session->custom_recv_error_code_ = "ERR_HTTP2_TOO_MANY_INVALID_FRAMES"; 10051cb0ef41Sopenharmony_ci return 1; 10061cb0ef41Sopenharmony_ci } 10071cb0ef41Sopenharmony_ci 10081cb0ef41Sopenharmony_ci // If the error is fatal or if error code is ERR_STREAM_CLOSED... emit error 10091cb0ef41Sopenharmony_ci if (nghttp2_is_fatal(lib_error_code) || 10101cb0ef41Sopenharmony_ci lib_error_code == NGHTTP2_ERR_STREAM_CLOSED) { 10111cb0ef41Sopenharmony_ci Environment* env = session->env(); 10121cb0ef41Sopenharmony_ci Isolate* isolate = env->isolate(); 10131cb0ef41Sopenharmony_ci HandleScope scope(isolate); 10141cb0ef41Sopenharmony_ci Local<Context> context = env->context(); 10151cb0ef41Sopenharmony_ci Context::Scope context_scope(context); 10161cb0ef41Sopenharmony_ci Local<Value> arg = Integer::New(isolate, lib_error_code); 10171cb0ef41Sopenharmony_ci session->MakeCallback(env->http2session_on_error_function(), 1, &arg); 10181cb0ef41Sopenharmony_ci } 10191cb0ef41Sopenharmony_ci return 0; 10201cb0ef41Sopenharmony_ci} 10211cb0ef41Sopenharmony_ci 10221cb0ef41Sopenharmony_ci// Remove the headers reference. 10231cb0ef41Sopenharmony_ci// Implicitly calls nghttp2_rcbuf_decref 10241cb0ef41Sopenharmony_civoid Http2Session::DecrefHeaders(const nghttp2_frame* frame) { 10251cb0ef41Sopenharmony_ci int32_t id = GetFrameID(frame); 10261cb0ef41Sopenharmony_ci BaseObjectPtr<Http2Stream> stream = FindStream(id); 10271cb0ef41Sopenharmony_ci 10281cb0ef41Sopenharmony_ci if (stream && !stream->is_destroyed() && stream->headers_count() > 0) { 10291cb0ef41Sopenharmony_ci Debug(this, "freeing headers for stream %d", id); 10301cb0ef41Sopenharmony_ci stream->ClearHeaders(); 10311cb0ef41Sopenharmony_ci CHECK_EQ(stream->headers_count(), 0); 10321cb0ef41Sopenharmony_ci DecrementCurrentSessionMemory(stream->current_headers_length_); 10331cb0ef41Sopenharmony_ci stream->current_headers_length_ = 0; 10341cb0ef41Sopenharmony_ci } 10351cb0ef41Sopenharmony_ci} 10361cb0ef41Sopenharmony_ci 10371cb0ef41Sopenharmony_ciuint32_t TranslateNghttp2ErrorCode(const int libErrorCode) { 10381cb0ef41Sopenharmony_ci switch (libErrorCode) { 10391cb0ef41Sopenharmony_ci case NGHTTP2_ERR_STREAM_CLOSED: 10401cb0ef41Sopenharmony_ci return NGHTTP2_STREAM_CLOSED; 10411cb0ef41Sopenharmony_ci case NGHTTP2_ERR_HEADER_COMP: 10421cb0ef41Sopenharmony_ci return NGHTTP2_COMPRESSION_ERROR; 10431cb0ef41Sopenharmony_ci case NGHTTP2_ERR_FRAME_SIZE_ERROR: 10441cb0ef41Sopenharmony_ci return NGHTTP2_FRAME_SIZE_ERROR; 10451cb0ef41Sopenharmony_ci case NGHTTP2_ERR_FLOW_CONTROL: 10461cb0ef41Sopenharmony_ci return NGHTTP2_FLOW_CONTROL_ERROR; 10471cb0ef41Sopenharmony_ci case NGHTTP2_ERR_REFUSED_STREAM: 10481cb0ef41Sopenharmony_ci return NGHTTP2_REFUSED_STREAM; 10491cb0ef41Sopenharmony_ci case NGHTTP2_ERR_PROTO: 10501cb0ef41Sopenharmony_ci case NGHTTP2_ERR_HTTP_HEADER: 10511cb0ef41Sopenharmony_ci case NGHTTP2_ERR_HTTP_MESSAGING: 10521cb0ef41Sopenharmony_ci return NGHTTP2_PROTOCOL_ERROR; 10531cb0ef41Sopenharmony_ci default: 10541cb0ef41Sopenharmony_ci return NGHTTP2_INTERNAL_ERROR; 10551cb0ef41Sopenharmony_ci } 10561cb0ef41Sopenharmony_ci} 10571cb0ef41Sopenharmony_ci 10581cb0ef41Sopenharmony_ci// If nghttp2 is unable to send a queued up frame, it will call this callback 10591cb0ef41Sopenharmony_ci// to let us know. If the failure occurred because we are in the process of 10601cb0ef41Sopenharmony_ci// closing down the session or stream, we go ahead and ignore it. We don't 10611cb0ef41Sopenharmony_ci// really care about those and there's nothing we can reasonably do about it 10621cb0ef41Sopenharmony_ci// anyway. Other types of failures are reported up to JavaScript. This should 10631cb0ef41Sopenharmony_ci// be exceedingly rare. 10641cb0ef41Sopenharmony_ciint Http2Session::OnFrameNotSent(nghttp2_session* handle, 10651cb0ef41Sopenharmony_ci const nghttp2_frame* frame, 10661cb0ef41Sopenharmony_ci int error_code, 10671cb0ef41Sopenharmony_ci void* user_data) { 10681cb0ef41Sopenharmony_ci Http2Session* session = static_cast<Http2Session*>(user_data); 10691cb0ef41Sopenharmony_ci Environment* env = session->env(); 10701cb0ef41Sopenharmony_ci Debug(session, "frame type %d was not sent, code: %d", 10711cb0ef41Sopenharmony_ci frame->hd.type, error_code); 10721cb0ef41Sopenharmony_ci 10731cb0ef41Sopenharmony_ci // Do not report if the frame was not sent due to the session closing 10741cb0ef41Sopenharmony_ci if (error_code == NGHTTP2_ERR_SESSION_CLOSING || 10751cb0ef41Sopenharmony_ci error_code == NGHTTP2_ERR_STREAM_CLOSED || 10761cb0ef41Sopenharmony_ci error_code == NGHTTP2_ERR_STREAM_CLOSING) { 10771cb0ef41Sopenharmony_ci // Nghttp2 contains header limit of 65536. When this value is exceeded the 10781cb0ef41Sopenharmony_ci // pipeline is stopped and we should remove the current headers reference 10791cb0ef41Sopenharmony_ci // to destroy the session completely. 10801cb0ef41Sopenharmony_ci // Further information see: https://github.com/nodejs/node/issues/35233 10811cb0ef41Sopenharmony_ci session->DecrefHeaders(frame); 10821cb0ef41Sopenharmony_ci return 0; 10831cb0ef41Sopenharmony_ci } 10841cb0ef41Sopenharmony_ci 10851cb0ef41Sopenharmony_ci Isolate* isolate = env->isolate(); 10861cb0ef41Sopenharmony_ci HandleScope scope(isolate); 10871cb0ef41Sopenharmony_ci Local<Context> context = env->context(); 10881cb0ef41Sopenharmony_ci Context::Scope context_scope(context); 10891cb0ef41Sopenharmony_ci 10901cb0ef41Sopenharmony_ci Local<Value> argv[3] = { 10911cb0ef41Sopenharmony_ci Integer::New(isolate, frame->hd.stream_id), 10921cb0ef41Sopenharmony_ci Integer::New(isolate, frame->hd.type), 10931cb0ef41Sopenharmony_ci Integer::New(isolate, TranslateNghttp2ErrorCode(error_code)) 10941cb0ef41Sopenharmony_ci }; 10951cb0ef41Sopenharmony_ci session->MakeCallback( 10961cb0ef41Sopenharmony_ci env->http2session_on_frame_error_function(), 10971cb0ef41Sopenharmony_ci arraysize(argv), argv); 10981cb0ef41Sopenharmony_ci return 0; 10991cb0ef41Sopenharmony_ci} 11001cb0ef41Sopenharmony_ci 11011cb0ef41Sopenharmony_ciint Http2Session::OnFrameSent(nghttp2_session* handle, 11021cb0ef41Sopenharmony_ci const nghttp2_frame* frame, 11031cb0ef41Sopenharmony_ci void* user_data) { 11041cb0ef41Sopenharmony_ci Http2Session* session = static_cast<Http2Session*>(user_data); 11051cb0ef41Sopenharmony_ci session->statistics_.frame_sent += 1; 11061cb0ef41Sopenharmony_ci return 0; 11071cb0ef41Sopenharmony_ci} 11081cb0ef41Sopenharmony_ci 11091cb0ef41Sopenharmony_ci// Called by nghttp2 when a stream closes. 11101cb0ef41Sopenharmony_ciint Http2Session::OnStreamClose(nghttp2_session* handle, 11111cb0ef41Sopenharmony_ci int32_t id, 11121cb0ef41Sopenharmony_ci uint32_t code, 11131cb0ef41Sopenharmony_ci void* user_data) { 11141cb0ef41Sopenharmony_ci Http2Session* session = static_cast<Http2Session*>(user_data); 11151cb0ef41Sopenharmony_ci Environment* env = session->env(); 11161cb0ef41Sopenharmony_ci Isolate* isolate = env->isolate(); 11171cb0ef41Sopenharmony_ci HandleScope scope(isolate); 11181cb0ef41Sopenharmony_ci Local<Context> context = env->context(); 11191cb0ef41Sopenharmony_ci Context::Scope context_scope(context); 11201cb0ef41Sopenharmony_ci Debug(session, "stream %d closed with code: %d", id, code); 11211cb0ef41Sopenharmony_ci BaseObjectPtr<Http2Stream> stream = session->FindStream(id); 11221cb0ef41Sopenharmony_ci // Intentionally ignore the callback if the stream does not exist or has 11231cb0ef41Sopenharmony_ci // already been destroyed 11241cb0ef41Sopenharmony_ci if (!stream || stream->is_destroyed()) 11251cb0ef41Sopenharmony_ci return 0; 11261cb0ef41Sopenharmony_ci 11271cb0ef41Sopenharmony_ci stream->Close(code); 11281cb0ef41Sopenharmony_ci 11291cb0ef41Sopenharmony_ci // It is possible for the stream close to occur before the stream is 11301cb0ef41Sopenharmony_ci // ever passed on to the javascript side. If that happens, the callback 11311cb0ef41Sopenharmony_ci // will return false. 11321cb0ef41Sopenharmony_ci if (env->can_call_into_js()) { 11331cb0ef41Sopenharmony_ci Local<Value> arg = Integer::NewFromUnsigned(isolate, code); 11341cb0ef41Sopenharmony_ci MaybeLocal<Value> answer = stream->MakeCallback( 11351cb0ef41Sopenharmony_ci env->http2session_on_stream_close_function(), 1, &arg); 11361cb0ef41Sopenharmony_ci if (answer.IsEmpty() || answer.ToLocalChecked()->IsFalse()) { 11371cb0ef41Sopenharmony_ci // Skip to destroy 11381cb0ef41Sopenharmony_ci stream->Destroy(); 11391cb0ef41Sopenharmony_ci } 11401cb0ef41Sopenharmony_ci } 11411cb0ef41Sopenharmony_ci return 0; 11421cb0ef41Sopenharmony_ci} 11431cb0ef41Sopenharmony_ci 11441cb0ef41Sopenharmony_ci// Called by nghttp2 when an invalid header has been received. For now, we 11451cb0ef41Sopenharmony_ci// ignore these. If this callback was not provided, nghttp2 would handle 11461cb0ef41Sopenharmony_ci// invalid headers strictly and would shut down the stream. We are intentionally 11471cb0ef41Sopenharmony_ci// being more lenient here although we may want to revisit this choice later. 11481cb0ef41Sopenharmony_ciint Http2Session::OnInvalidHeader(nghttp2_session* session, 11491cb0ef41Sopenharmony_ci const nghttp2_frame* frame, 11501cb0ef41Sopenharmony_ci nghttp2_rcbuf* name, 11511cb0ef41Sopenharmony_ci nghttp2_rcbuf* value, 11521cb0ef41Sopenharmony_ci uint8_t flags, 11531cb0ef41Sopenharmony_ci void* user_data) { 11541cb0ef41Sopenharmony_ci // Ignore invalid header fields by default. 11551cb0ef41Sopenharmony_ci return 0; 11561cb0ef41Sopenharmony_ci} 11571cb0ef41Sopenharmony_ci 11581cb0ef41Sopenharmony_ci// When nghttp2 receives a DATA frame, it will deliver the data payload to 11591cb0ef41Sopenharmony_ci// us in discrete chunks. We push these into a linked list stored in the 11601cb0ef41Sopenharmony_ci// Http2Sttream which is flushed out to JavaScript as quickly as possible. 11611cb0ef41Sopenharmony_ci// This can be a particularly hot path. 11621cb0ef41Sopenharmony_ciint Http2Session::OnDataChunkReceived(nghttp2_session* handle, 11631cb0ef41Sopenharmony_ci uint8_t flags, 11641cb0ef41Sopenharmony_ci int32_t id, 11651cb0ef41Sopenharmony_ci const uint8_t* data, 11661cb0ef41Sopenharmony_ci size_t len, 11671cb0ef41Sopenharmony_ci void* user_data) { 11681cb0ef41Sopenharmony_ci Http2Session* session = static_cast<Http2Session*>(user_data); 11691cb0ef41Sopenharmony_ci Debug(session, "buffering data chunk for stream %d, size: " 11701cb0ef41Sopenharmony_ci "%d, flags: %d", id, len, flags); 11711cb0ef41Sopenharmony_ci Environment* env = session->env(); 11721cb0ef41Sopenharmony_ci HandleScope scope(env->isolate()); 11731cb0ef41Sopenharmony_ci 11741cb0ef41Sopenharmony_ci // We should never actually get a 0-length chunk so this check is 11751cb0ef41Sopenharmony_ci // only a precaution at this point. 11761cb0ef41Sopenharmony_ci if (len == 0) 11771cb0ef41Sopenharmony_ci return 0; 11781cb0ef41Sopenharmony_ci 11791cb0ef41Sopenharmony_ci // Notify nghttp2 that we've consumed a chunk of data on the connection 11801cb0ef41Sopenharmony_ci // so that it can send a WINDOW_UPDATE frame. This is a critical part of 11811cb0ef41Sopenharmony_ci // the flow control process in http2 11821cb0ef41Sopenharmony_ci CHECK_EQ(nghttp2_session_consume_connection(handle, len), 0); 11831cb0ef41Sopenharmony_ci BaseObjectPtr<Http2Stream> stream = session->FindStream(id); 11841cb0ef41Sopenharmony_ci 11851cb0ef41Sopenharmony_ci // If the stream has been destroyed, ignore this chunk 11861cb0ef41Sopenharmony_ci if (!stream || stream->is_destroyed()) 11871cb0ef41Sopenharmony_ci return 0; 11881cb0ef41Sopenharmony_ci 11891cb0ef41Sopenharmony_ci stream->statistics_.received_bytes += len; 11901cb0ef41Sopenharmony_ci 11911cb0ef41Sopenharmony_ci // Repeatedly ask the stream's owner for memory, and copy the read data 11921cb0ef41Sopenharmony_ci // into those buffers. 11931cb0ef41Sopenharmony_ci // The typical case is actually the exception here; Http2StreamListeners 11941cb0ef41Sopenharmony_ci // know about the HTTP2 session associated with this stream, so they know 11951cb0ef41Sopenharmony_ci // about the larger from-socket read buffer, so they do not require copying. 11961cb0ef41Sopenharmony_ci do { 11971cb0ef41Sopenharmony_ci uv_buf_t buf = stream->EmitAlloc(len); 11981cb0ef41Sopenharmony_ci ssize_t avail = len; 11991cb0ef41Sopenharmony_ci if (static_cast<ssize_t>(buf.len) < avail) 12001cb0ef41Sopenharmony_ci avail = buf.len; 12011cb0ef41Sopenharmony_ci 12021cb0ef41Sopenharmony_ci // `buf.base == nullptr` is the default Http2StreamListener's way 12031cb0ef41Sopenharmony_ci // of saying that it wants a pointer to the raw original. 12041cb0ef41Sopenharmony_ci // Since it has access to the original socket buffer from which the data 12051cb0ef41Sopenharmony_ci // was read in the first place, it can use that to minimize ArrayBuffer 12061cb0ef41Sopenharmony_ci // allocations. 12071cb0ef41Sopenharmony_ci if (LIKELY(buf.base == nullptr)) 12081cb0ef41Sopenharmony_ci buf.base = reinterpret_cast<char*>(const_cast<uint8_t*>(data)); 12091cb0ef41Sopenharmony_ci else 12101cb0ef41Sopenharmony_ci memcpy(buf.base, data, avail); 12111cb0ef41Sopenharmony_ci data += avail; 12121cb0ef41Sopenharmony_ci len -= avail; 12131cb0ef41Sopenharmony_ci stream->EmitRead(avail, buf); 12141cb0ef41Sopenharmony_ci 12151cb0ef41Sopenharmony_ci // If the stream owner (e.g. the JS Http2Stream) wants more data, just 12161cb0ef41Sopenharmony_ci // tell nghttp2 that all data has been consumed. Otherwise, defer until 12171cb0ef41Sopenharmony_ci // more data is being requested. 12181cb0ef41Sopenharmony_ci if (stream->is_reading()) 12191cb0ef41Sopenharmony_ci nghttp2_session_consume_stream(handle, id, avail); 12201cb0ef41Sopenharmony_ci else 12211cb0ef41Sopenharmony_ci stream->inbound_consumed_data_while_paused_ += avail; 12221cb0ef41Sopenharmony_ci 12231cb0ef41Sopenharmony_ci // If we have a gathered a lot of data for output, try sending it now. 12241cb0ef41Sopenharmony_ci if (session->outgoing_length_ > 4096 || 12251cb0ef41Sopenharmony_ci stream->available_outbound_length_ > 4096) { 12261cb0ef41Sopenharmony_ci session->SendPendingData(); 12271cb0ef41Sopenharmony_ci } 12281cb0ef41Sopenharmony_ci } while (len != 0); 12291cb0ef41Sopenharmony_ci 12301cb0ef41Sopenharmony_ci // If we are currently waiting for a write operation to finish, we should 12311cb0ef41Sopenharmony_ci // tell nghttp2 that we want to wait before we process more input data. 12321cb0ef41Sopenharmony_ci if (session->is_write_in_progress()) { 12331cb0ef41Sopenharmony_ci CHECK(session->is_reading_stopped()); 12341cb0ef41Sopenharmony_ci session->set_receive_paused(); 12351cb0ef41Sopenharmony_ci Debug(session, "receive paused"); 12361cb0ef41Sopenharmony_ci return NGHTTP2_ERR_PAUSE; 12371cb0ef41Sopenharmony_ci } 12381cb0ef41Sopenharmony_ci 12391cb0ef41Sopenharmony_ci return 0; 12401cb0ef41Sopenharmony_ci} 12411cb0ef41Sopenharmony_ci 12421cb0ef41Sopenharmony_ci// Called by nghttp2 when it needs to determine how much padding to use in 12431cb0ef41Sopenharmony_ci// a DATA or HEADERS frame. 12441cb0ef41Sopenharmony_cissize_t Http2Session::OnSelectPadding(nghttp2_session* handle, 12451cb0ef41Sopenharmony_ci const nghttp2_frame* frame, 12461cb0ef41Sopenharmony_ci size_t maxPayloadLen, 12471cb0ef41Sopenharmony_ci void* user_data) { 12481cb0ef41Sopenharmony_ci Http2Session* session = static_cast<Http2Session*>(user_data); 12491cb0ef41Sopenharmony_ci ssize_t padding = frame->hd.length; 12501cb0ef41Sopenharmony_ci 12511cb0ef41Sopenharmony_ci switch (session->padding_strategy_) { 12521cb0ef41Sopenharmony_ci case PADDING_STRATEGY_NONE: 12531cb0ef41Sopenharmony_ci // Fall-through 12541cb0ef41Sopenharmony_ci break; 12551cb0ef41Sopenharmony_ci case PADDING_STRATEGY_MAX: 12561cb0ef41Sopenharmony_ci padding = session->OnMaxFrameSizePadding(padding, maxPayloadLen); 12571cb0ef41Sopenharmony_ci break; 12581cb0ef41Sopenharmony_ci case PADDING_STRATEGY_ALIGNED: 12591cb0ef41Sopenharmony_ci padding = session->OnDWordAlignedPadding(padding, maxPayloadLen); 12601cb0ef41Sopenharmony_ci break; 12611cb0ef41Sopenharmony_ci } 12621cb0ef41Sopenharmony_ci return padding; 12631cb0ef41Sopenharmony_ci} 12641cb0ef41Sopenharmony_ci 12651cb0ef41Sopenharmony_ci// We use this currently to determine when an attempt is made to use the http2 12661cb0ef41Sopenharmony_ci// protocol with a non-http2 peer. 12671cb0ef41Sopenharmony_ciint Http2Session::OnNghttpError(nghttp2_session* handle, 12681cb0ef41Sopenharmony_ci int lib_error_code, 12691cb0ef41Sopenharmony_ci const char* message, 12701cb0ef41Sopenharmony_ci size_t len, 12711cb0ef41Sopenharmony_ci void* user_data) { 12721cb0ef41Sopenharmony_ci // Unfortunately, this is currently the only way for us to know if 12731cb0ef41Sopenharmony_ci // the session errored because the peer is not an http2 peer. 12741cb0ef41Sopenharmony_ci Http2Session* session = static_cast<Http2Session*>(user_data); 12751cb0ef41Sopenharmony_ci Debug(session, "Error '%s'", message); 12761cb0ef41Sopenharmony_ci if (lib_error_code == NGHTTP2_ERR_SETTINGS_EXPECTED) { 12771cb0ef41Sopenharmony_ci Environment* env = session->env(); 12781cb0ef41Sopenharmony_ci Isolate* isolate = env->isolate(); 12791cb0ef41Sopenharmony_ci HandleScope scope(isolate); 12801cb0ef41Sopenharmony_ci Local<Context> context = env->context(); 12811cb0ef41Sopenharmony_ci Context::Scope context_scope(context); 12821cb0ef41Sopenharmony_ci Local<Value> arg = Integer::New(isolate, NGHTTP2_ERR_PROTO); 12831cb0ef41Sopenharmony_ci session->MakeCallback(env->http2session_on_error_function(), 1, &arg); 12841cb0ef41Sopenharmony_ci } 12851cb0ef41Sopenharmony_ci return 0; 12861cb0ef41Sopenharmony_ci} 12871cb0ef41Sopenharmony_ci 12881cb0ef41Sopenharmony_ciuv_buf_t Http2StreamListener::OnStreamAlloc(size_t size) { 12891cb0ef41Sopenharmony_ci // See the comments in Http2Session::OnDataChunkReceived 12901cb0ef41Sopenharmony_ci // (which is the only possible call site for this method). 12911cb0ef41Sopenharmony_ci return uv_buf_init(nullptr, size); 12921cb0ef41Sopenharmony_ci} 12931cb0ef41Sopenharmony_ci 12941cb0ef41Sopenharmony_civoid Http2StreamListener::OnStreamRead(ssize_t nread, const uv_buf_t& buf) { 12951cb0ef41Sopenharmony_ci Http2Stream* stream = static_cast<Http2Stream*>(stream_); 12961cb0ef41Sopenharmony_ci Http2Session* session = stream->session(); 12971cb0ef41Sopenharmony_ci Environment* env = stream->env(); 12981cb0ef41Sopenharmony_ci HandleScope handle_scope(env->isolate()); 12991cb0ef41Sopenharmony_ci Context::Scope context_scope(env->context()); 13001cb0ef41Sopenharmony_ci 13011cb0ef41Sopenharmony_ci if (nread < 0) { 13021cb0ef41Sopenharmony_ci PassReadErrorToPreviousListener(nread); 13031cb0ef41Sopenharmony_ci return; 13041cb0ef41Sopenharmony_ci } 13051cb0ef41Sopenharmony_ci 13061cb0ef41Sopenharmony_ci Local<ArrayBuffer> ab; 13071cb0ef41Sopenharmony_ci if (session->stream_buf_ab_.IsEmpty()) { 13081cb0ef41Sopenharmony_ci ab = ArrayBuffer::New(env->isolate(), 13091cb0ef41Sopenharmony_ci std::move(session->stream_buf_allocation_)); 13101cb0ef41Sopenharmony_ci session->stream_buf_ab_.Reset(env->isolate(), ab); 13111cb0ef41Sopenharmony_ci } else { 13121cb0ef41Sopenharmony_ci ab = PersistentToLocal::Strong(session->stream_buf_ab_); 13131cb0ef41Sopenharmony_ci } 13141cb0ef41Sopenharmony_ci 13151cb0ef41Sopenharmony_ci // There is a single large array buffer for the entire data read from the 13161cb0ef41Sopenharmony_ci // network; create a slice of that array buffer and emit it as the 13171cb0ef41Sopenharmony_ci // received data buffer. 13181cb0ef41Sopenharmony_ci size_t offset = buf.base - session->stream_buf_.base; 13191cb0ef41Sopenharmony_ci 13201cb0ef41Sopenharmony_ci // Verify that the data offset is inside the current read buffer. 13211cb0ef41Sopenharmony_ci CHECK_GE(offset, session->stream_buf_offset_); 13221cb0ef41Sopenharmony_ci CHECK_LE(offset, session->stream_buf_.len); 13231cb0ef41Sopenharmony_ci CHECK_LE(offset + buf.len, session->stream_buf_.len); 13241cb0ef41Sopenharmony_ci 13251cb0ef41Sopenharmony_ci stream->CallJSOnreadMethod(nread, ab, offset); 13261cb0ef41Sopenharmony_ci} 13271cb0ef41Sopenharmony_ci 13281cb0ef41Sopenharmony_ci 13291cb0ef41Sopenharmony_ci// Called by OnFrameReceived to notify JavaScript land that a complete 13301cb0ef41Sopenharmony_ci// HEADERS frame has been received and processed. This method converts the 13311cb0ef41Sopenharmony_ci// received headers into a JavaScript array and pushes those out to JS. 13321cb0ef41Sopenharmony_civoid Http2Session::HandleHeadersFrame(const nghttp2_frame* frame) { 13331cb0ef41Sopenharmony_ci Isolate* isolate = env()->isolate(); 13341cb0ef41Sopenharmony_ci HandleScope scope(isolate); 13351cb0ef41Sopenharmony_ci Local<Context> context = env()->context(); 13361cb0ef41Sopenharmony_ci Context::Scope context_scope(context); 13371cb0ef41Sopenharmony_ci 13381cb0ef41Sopenharmony_ci int32_t id = GetFrameID(frame); 13391cb0ef41Sopenharmony_ci Debug(this, "handle headers frame for stream %d", id); 13401cb0ef41Sopenharmony_ci BaseObjectPtr<Http2Stream> stream = FindStream(id); 13411cb0ef41Sopenharmony_ci 13421cb0ef41Sopenharmony_ci // If the stream has already been destroyed, ignore. 13431cb0ef41Sopenharmony_ci if (!stream || stream->is_destroyed()) 13441cb0ef41Sopenharmony_ci return; 13451cb0ef41Sopenharmony_ci 13461cb0ef41Sopenharmony_ci // The headers are stored as a vector of Http2Header instances. 13471cb0ef41Sopenharmony_ci // The following converts that into a JS array with the structure: 13481cb0ef41Sopenharmony_ci // [name1, value1, name2, value2, name3, value3, name3, value4] and so on. 13491cb0ef41Sopenharmony_ci // That array is passed up to the JS layer and converted into an Object form 13501cb0ef41Sopenharmony_ci // like {name1: value1, name2: value2, name3: [value3, value4]}. We do it 13511cb0ef41Sopenharmony_ci // this way for performance reasons (it's faster to generate and pass an 13521cb0ef41Sopenharmony_ci // array than it is to generate and pass the object). 13531cb0ef41Sopenharmony_ci 13541cb0ef41Sopenharmony_ci MaybeStackBuffer<Local<Value>, 64> headers_v(stream->headers_count() * 2); 13551cb0ef41Sopenharmony_ci MaybeStackBuffer<Local<Value>, 32> sensitive_v(stream->headers_count()); 13561cb0ef41Sopenharmony_ci size_t sensitive_count = 0; 13571cb0ef41Sopenharmony_ci 13581cb0ef41Sopenharmony_ci stream->TransferHeaders([&](const Http2Header& header, size_t i) { 13591cb0ef41Sopenharmony_ci headers_v[i * 2] = header.GetName(this).ToLocalChecked(); 13601cb0ef41Sopenharmony_ci headers_v[i * 2 + 1] = header.GetValue(this).ToLocalChecked(); 13611cb0ef41Sopenharmony_ci if (header.flags() & NGHTTP2_NV_FLAG_NO_INDEX) 13621cb0ef41Sopenharmony_ci sensitive_v[sensitive_count++] = headers_v[i * 2]; 13631cb0ef41Sopenharmony_ci }); 13641cb0ef41Sopenharmony_ci CHECK_EQ(stream->headers_count(), 0); 13651cb0ef41Sopenharmony_ci 13661cb0ef41Sopenharmony_ci DecrementCurrentSessionMemory(stream->current_headers_length_); 13671cb0ef41Sopenharmony_ci stream->current_headers_length_ = 0; 13681cb0ef41Sopenharmony_ci 13691cb0ef41Sopenharmony_ci Local<Value> args[] = { 13701cb0ef41Sopenharmony_ci stream->object(), 13711cb0ef41Sopenharmony_ci Integer::New(isolate, id), 13721cb0ef41Sopenharmony_ci Integer::New(isolate, stream->headers_category()), 13731cb0ef41Sopenharmony_ci Integer::New(isolate, frame->hd.flags), 13741cb0ef41Sopenharmony_ci Array::New(isolate, headers_v.out(), headers_v.length()), 13751cb0ef41Sopenharmony_ci Array::New(isolate, sensitive_v.out(), sensitive_count), 13761cb0ef41Sopenharmony_ci }; 13771cb0ef41Sopenharmony_ci MakeCallback(env()->http2session_on_headers_function(), 13781cb0ef41Sopenharmony_ci arraysize(args), args); 13791cb0ef41Sopenharmony_ci} 13801cb0ef41Sopenharmony_ci 13811cb0ef41Sopenharmony_ci 13821cb0ef41Sopenharmony_ci// Called by OnFrameReceived when a complete PRIORITY frame has been 13831cb0ef41Sopenharmony_ci// received. Notifies JS land about the priority change. Note that priorities 13841cb0ef41Sopenharmony_ci// are considered advisory only, so this has no real effect other than to 13851cb0ef41Sopenharmony_ci// simply let user code know that the priority has changed. 13861cb0ef41Sopenharmony_civoid Http2Session::HandlePriorityFrame(const nghttp2_frame* frame) { 13871cb0ef41Sopenharmony_ci if (js_fields_->priority_listener_count == 0) return; 13881cb0ef41Sopenharmony_ci Isolate* isolate = env()->isolate(); 13891cb0ef41Sopenharmony_ci HandleScope scope(isolate); 13901cb0ef41Sopenharmony_ci Local<Context> context = env()->context(); 13911cb0ef41Sopenharmony_ci Context::Scope context_scope(context); 13921cb0ef41Sopenharmony_ci 13931cb0ef41Sopenharmony_ci nghttp2_priority priority_frame = frame->priority; 13941cb0ef41Sopenharmony_ci int32_t id = GetFrameID(frame); 13951cb0ef41Sopenharmony_ci Debug(this, "handle priority frame for stream %d", id); 13961cb0ef41Sopenharmony_ci // Priority frame stream ID should never be <= 0. nghttp2 handles this for us 13971cb0ef41Sopenharmony_ci nghttp2_priority_spec spec = priority_frame.pri_spec; 13981cb0ef41Sopenharmony_ci 13991cb0ef41Sopenharmony_ci Local<Value> argv[4] = { 14001cb0ef41Sopenharmony_ci Integer::New(isolate, id), 14011cb0ef41Sopenharmony_ci Integer::New(isolate, spec.stream_id), 14021cb0ef41Sopenharmony_ci Integer::New(isolate, spec.weight), 14031cb0ef41Sopenharmony_ci Boolean::New(isolate, spec.exclusive) 14041cb0ef41Sopenharmony_ci }; 14051cb0ef41Sopenharmony_ci MakeCallback(env()->http2session_on_priority_function(), 14061cb0ef41Sopenharmony_ci arraysize(argv), argv); 14071cb0ef41Sopenharmony_ci} 14081cb0ef41Sopenharmony_ci 14091cb0ef41Sopenharmony_ci 14101cb0ef41Sopenharmony_ci// Called by OnFrameReceived when a complete DATA frame has been received. 14111cb0ef41Sopenharmony_ci// If we know that this was the last DATA frame (because the END_STREAM flag 14121cb0ef41Sopenharmony_ci// is set), then we'll terminate the readable side of the StreamBase. 14131cb0ef41Sopenharmony_ciint Http2Session::HandleDataFrame(const nghttp2_frame* frame) { 14141cb0ef41Sopenharmony_ci int32_t id = GetFrameID(frame); 14151cb0ef41Sopenharmony_ci Debug(this, "handling data frame for stream %d", id); 14161cb0ef41Sopenharmony_ci BaseObjectPtr<Http2Stream> stream = FindStream(id); 14171cb0ef41Sopenharmony_ci 14181cb0ef41Sopenharmony_ci if (stream && 14191cb0ef41Sopenharmony_ci !stream->is_destroyed() && 14201cb0ef41Sopenharmony_ci frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { 14211cb0ef41Sopenharmony_ci stream->EmitRead(UV_EOF); 14221cb0ef41Sopenharmony_ci } else if (frame->hd.length == 0) { 14231cb0ef41Sopenharmony_ci if (invalid_frame_count_++ > js_fields_->max_invalid_frames) { 14241cb0ef41Sopenharmony_ci custom_recv_error_code_ = "ERR_HTTP2_TOO_MANY_INVALID_FRAMES"; 14251cb0ef41Sopenharmony_ci Debug(this, "rejecting empty-frame-without-END_STREAM flood\n"); 14261cb0ef41Sopenharmony_ci // Consider a flood of 0-length frames without END_STREAM an error. 14271cb0ef41Sopenharmony_ci return 1; 14281cb0ef41Sopenharmony_ci } 14291cb0ef41Sopenharmony_ci } 14301cb0ef41Sopenharmony_ci return 0; 14311cb0ef41Sopenharmony_ci} 14321cb0ef41Sopenharmony_ci 14331cb0ef41Sopenharmony_ci 14341cb0ef41Sopenharmony_ci// Called by OnFrameReceived when a complete GOAWAY frame has been received. 14351cb0ef41Sopenharmony_civoid Http2Session::HandleGoawayFrame(const nghttp2_frame* frame) { 14361cb0ef41Sopenharmony_ci Isolate* isolate = env()->isolate(); 14371cb0ef41Sopenharmony_ci HandleScope scope(isolate); 14381cb0ef41Sopenharmony_ci Local<Context> context = env()->context(); 14391cb0ef41Sopenharmony_ci Context::Scope context_scope(context); 14401cb0ef41Sopenharmony_ci 14411cb0ef41Sopenharmony_ci nghttp2_goaway goaway_frame = frame->goaway; 14421cb0ef41Sopenharmony_ci Debug(this, "handling goaway frame"); 14431cb0ef41Sopenharmony_ci 14441cb0ef41Sopenharmony_ci Local<Value> argv[3] = { 14451cb0ef41Sopenharmony_ci Integer::NewFromUnsigned(isolate, goaway_frame.error_code), 14461cb0ef41Sopenharmony_ci Integer::New(isolate, goaway_frame.last_stream_id), 14471cb0ef41Sopenharmony_ci Undefined(isolate) 14481cb0ef41Sopenharmony_ci }; 14491cb0ef41Sopenharmony_ci 14501cb0ef41Sopenharmony_ci size_t length = goaway_frame.opaque_data_len; 14511cb0ef41Sopenharmony_ci if (length > 0) { 14521cb0ef41Sopenharmony_ci // If the copy fails for any reason here, we just ignore it. 14531cb0ef41Sopenharmony_ci // The additional goaway data is completely optional and we 14541cb0ef41Sopenharmony_ci // shouldn't fail if we're not able to process it. 14551cb0ef41Sopenharmony_ci argv[2] = Buffer::Copy(isolate, 14561cb0ef41Sopenharmony_ci reinterpret_cast<char*>(goaway_frame.opaque_data), 14571cb0ef41Sopenharmony_ci length).ToLocalChecked(); 14581cb0ef41Sopenharmony_ci } 14591cb0ef41Sopenharmony_ci 14601cb0ef41Sopenharmony_ci MakeCallback(env()->http2session_on_goaway_data_function(), 14611cb0ef41Sopenharmony_ci arraysize(argv), argv); 14621cb0ef41Sopenharmony_ci} 14631cb0ef41Sopenharmony_ci 14641cb0ef41Sopenharmony_ci// Called by OnFrameReceived when a complete ALTSVC frame has been received. 14651cb0ef41Sopenharmony_civoid Http2Session::HandleAltSvcFrame(const nghttp2_frame* frame) { 14661cb0ef41Sopenharmony_ci if (!(js_fields_->bitfield & (1 << kSessionHasAltsvcListeners))) return; 14671cb0ef41Sopenharmony_ci Isolate* isolate = env()->isolate(); 14681cb0ef41Sopenharmony_ci HandleScope scope(isolate); 14691cb0ef41Sopenharmony_ci Local<Context> context = env()->context(); 14701cb0ef41Sopenharmony_ci Context::Scope context_scope(context); 14711cb0ef41Sopenharmony_ci 14721cb0ef41Sopenharmony_ci int32_t id = GetFrameID(frame); 14731cb0ef41Sopenharmony_ci 14741cb0ef41Sopenharmony_ci nghttp2_extension ext = frame->ext; 14751cb0ef41Sopenharmony_ci nghttp2_ext_altsvc* altsvc = static_cast<nghttp2_ext_altsvc*>(ext.payload); 14761cb0ef41Sopenharmony_ci Debug(this, "handling altsvc frame"); 14771cb0ef41Sopenharmony_ci 14781cb0ef41Sopenharmony_ci Local<Value> argv[3] = { 14791cb0ef41Sopenharmony_ci Integer::New(isolate, id), 14801cb0ef41Sopenharmony_ci OneByteString(isolate, altsvc->origin, altsvc->origin_len), 14811cb0ef41Sopenharmony_ci OneByteString(isolate, altsvc->field_value, altsvc->field_value_len) 14821cb0ef41Sopenharmony_ci }; 14831cb0ef41Sopenharmony_ci 14841cb0ef41Sopenharmony_ci MakeCallback(env()->http2session_on_altsvc_function(), 14851cb0ef41Sopenharmony_ci arraysize(argv), argv); 14861cb0ef41Sopenharmony_ci} 14871cb0ef41Sopenharmony_ci 14881cb0ef41Sopenharmony_civoid Http2Session::HandleOriginFrame(const nghttp2_frame* frame) { 14891cb0ef41Sopenharmony_ci Isolate* isolate = env()->isolate(); 14901cb0ef41Sopenharmony_ci HandleScope scope(isolate); 14911cb0ef41Sopenharmony_ci Local<Context> context = env()->context(); 14921cb0ef41Sopenharmony_ci Context::Scope context_scope(context); 14931cb0ef41Sopenharmony_ci 14941cb0ef41Sopenharmony_ci Debug(this, "handling origin frame"); 14951cb0ef41Sopenharmony_ci 14961cb0ef41Sopenharmony_ci nghttp2_extension ext = frame->ext; 14971cb0ef41Sopenharmony_ci nghttp2_ext_origin* origin = static_cast<nghttp2_ext_origin*>(ext.payload); 14981cb0ef41Sopenharmony_ci 14991cb0ef41Sopenharmony_ci size_t nov = origin->nov; 15001cb0ef41Sopenharmony_ci std::vector<Local<Value>> origin_v(nov); 15011cb0ef41Sopenharmony_ci 15021cb0ef41Sopenharmony_ci for (size_t i = 0; i < nov; ++i) { 15031cb0ef41Sopenharmony_ci const nghttp2_origin_entry& entry = origin->ov[i]; 15041cb0ef41Sopenharmony_ci origin_v[i] = OneByteString(isolate, entry.origin, entry.origin_len); 15051cb0ef41Sopenharmony_ci } 15061cb0ef41Sopenharmony_ci Local<Value> holder = Array::New(isolate, origin_v.data(), origin_v.size()); 15071cb0ef41Sopenharmony_ci MakeCallback(env()->http2session_on_origin_function(), 1, &holder); 15081cb0ef41Sopenharmony_ci} 15091cb0ef41Sopenharmony_ci 15101cb0ef41Sopenharmony_ci// Called by OnFrameReceived when a complete PING frame has been received. 15111cb0ef41Sopenharmony_civoid Http2Session::HandlePingFrame(const nghttp2_frame* frame) { 15121cb0ef41Sopenharmony_ci Isolate* isolate = env()->isolate(); 15131cb0ef41Sopenharmony_ci HandleScope scope(isolate); 15141cb0ef41Sopenharmony_ci Local<Context> context = env()->context(); 15151cb0ef41Sopenharmony_ci Context::Scope context_scope(context); 15161cb0ef41Sopenharmony_ci Local<Value> arg; 15171cb0ef41Sopenharmony_ci bool ack = frame->hd.flags & NGHTTP2_FLAG_ACK; 15181cb0ef41Sopenharmony_ci if (ack) { 15191cb0ef41Sopenharmony_ci BaseObjectPtr<Http2Ping> ping = PopPing(); 15201cb0ef41Sopenharmony_ci 15211cb0ef41Sopenharmony_ci if (!ping) { 15221cb0ef41Sopenharmony_ci // PING Ack is unsolicited. Treat as a connection error. The HTTP/2 15231cb0ef41Sopenharmony_ci // spec does not require this, but there is no legitimate reason to 15241cb0ef41Sopenharmony_ci // receive an unsolicited PING ack on a connection. Either the peer 15251cb0ef41Sopenharmony_ci // is buggy or malicious, and we're not going to tolerate such 15261cb0ef41Sopenharmony_ci // nonsense. 15271cb0ef41Sopenharmony_ci arg = Integer::New(isolate, NGHTTP2_ERR_PROTO); 15281cb0ef41Sopenharmony_ci MakeCallback(env()->http2session_on_error_function(), 1, &arg); 15291cb0ef41Sopenharmony_ci return; 15301cb0ef41Sopenharmony_ci } 15311cb0ef41Sopenharmony_ci 15321cb0ef41Sopenharmony_ci ping->Done(true, frame->ping.opaque_data); 15331cb0ef41Sopenharmony_ci return; 15341cb0ef41Sopenharmony_ci } 15351cb0ef41Sopenharmony_ci 15361cb0ef41Sopenharmony_ci if (!(js_fields_->bitfield & (1 << kSessionHasPingListeners))) return; 15371cb0ef41Sopenharmony_ci // Notify the session that a ping occurred 15381cb0ef41Sopenharmony_ci arg = Buffer::Copy( 15391cb0ef41Sopenharmony_ci env(), 15401cb0ef41Sopenharmony_ci reinterpret_cast<const char*>(frame->ping.opaque_data), 15411cb0ef41Sopenharmony_ci 8).ToLocalChecked(); 15421cb0ef41Sopenharmony_ci MakeCallback(env()->http2session_on_ping_function(), 1, &arg); 15431cb0ef41Sopenharmony_ci} 15441cb0ef41Sopenharmony_ci 15451cb0ef41Sopenharmony_ci// Called by OnFrameReceived when a complete SETTINGS frame has been received. 15461cb0ef41Sopenharmony_civoid Http2Session::HandleSettingsFrame(const nghttp2_frame* frame) { 15471cb0ef41Sopenharmony_ci bool ack = frame->hd.flags & NGHTTP2_FLAG_ACK; 15481cb0ef41Sopenharmony_ci if (!ack) { 15491cb0ef41Sopenharmony_ci js_fields_->bitfield &= ~(1 << kSessionRemoteSettingsIsUpToDate); 15501cb0ef41Sopenharmony_ci if (!(js_fields_->bitfield & (1 << kSessionHasRemoteSettingsListeners))) 15511cb0ef41Sopenharmony_ci return; 15521cb0ef41Sopenharmony_ci // This is not a SETTINGS acknowledgement, notify and return 15531cb0ef41Sopenharmony_ci MakeCallback(env()->http2session_on_settings_function(), 0, nullptr); 15541cb0ef41Sopenharmony_ci return; 15551cb0ef41Sopenharmony_ci } 15561cb0ef41Sopenharmony_ci 15571cb0ef41Sopenharmony_ci // If this is an acknowledgement, we should have an Http2Settings 15581cb0ef41Sopenharmony_ci // object for it. 15591cb0ef41Sopenharmony_ci BaseObjectPtr<Http2Settings> settings = PopSettings(); 15601cb0ef41Sopenharmony_ci if (settings) { 15611cb0ef41Sopenharmony_ci settings->Done(true); 15621cb0ef41Sopenharmony_ci return; 15631cb0ef41Sopenharmony_ci } 15641cb0ef41Sopenharmony_ci // SETTINGS Ack is unsolicited. Treat as a connection error. The HTTP/2 15651cb0ef41Sopenharmony_ci // spec does not require this, but there is no legitimate reason to 15661cb0ef41Sopenharmony_ci // receive an unsolicited SETTINGS ack on a connection. Either the peer 15671cb0ef41Sopenharmony_ci // is buggy or malicious, and we're not going to tolerate such 15681cb0ef41Sopenharmony_ci // nonsense. 15691cb0ef41Sopenharmony_ci // Note that nghttp2 currently prevents this from happening for SETTINGS 15701cb0ef41Sopenharmony_ci // frames, so this block is purely defensive just in case that behavior 15711cb0ef41Sopenharmony_ci // changes. Specifically, unlike unsolicited PING acks, unsolicited 15721cb0ef41Sopenharmony_ci // SETTINGS acks should *never* make it this far. 15731cb0ef41Sopenharmony_ci Isolate* isolate = env()->isolate(); 15741cb0ef41Sopenharmony_ci HandleScope scope(isolate); 15751cb0ef41Sopenharmony_ci Local<Context> context = env()->context(); 15761cb0ef41Sopenharmony_ci Context::Scope context_scope(context); 15771cb0ef41Sopenharmony_ci Local<Value> arg = Integer::New(isolate, NGHTTP2_ERR_PROTO); 15781cb0ef41Sopenharmony_ci MakeCallback(env()->http2session_on_error_function(), 1, &arg); 15791cb0ef41Sopenharmony_ci} 15801cb0ef41Sopenharmony_ci 15811cb0ef41Sopenharmony_ci// Callback used when data has been written to the stream. 15821cb0ef41Sopenharmony_civoid Http2Session::OnStreamAfterWrite(WriteWrap* w, int status) { 15831cb0ef41Sopenharmony_ci Debug(this, "write finished with status %d", status); 15841cb0ef41Sopenharmony_ci 15851cb0ef41Sopenharmony_ci CHECK(is_write_in_progress()); 15861cb0ef41Sopenharmony_ci set_write_in_progress(false); 15871cb0ef41Sopenharmony_ci 15881cb0ef41Sopenharmony_ci // Inform all pending writes about their completion. 15891cb0ef41Sopenharmony_ci ClearOutgoing(status); 15901cb0ef41Sopenharmony_ci 15911cb0ef41Sopenharmony_ci if (is_reading_stopped() && 15921cb0ef41Sopenharmony_ci !is_write_in_progress() && 15931cb0ef41Sopenharmony_ci nghttp2_session_want_read(session_.get())) { 15941cb0ef41Sopenharmony_ci set_reading_stopped(false); 15951cb0ef41Sopenharmony_ci stream_->ReadStart(); 15961cb0ef41Sopenharmony_ci } 15971cb0ef41Sopenharmony_ci 15981cb0ef41Sopenharmony_ci if (is_destroyed()) { 15991cb0ef41Sopenharmony_ci HandleScope scope(env()->isolate()); 16001cb0ef41Sopenharmony_ci MakeCallback(env()->ondone_string(), 0, nullptr); 16011cb0ef41Sopenharmony_ci if (stream_ != nullptr) { 16021cb0ef41Sopenharmony_ci // Start reading again to detect the other end finishing. 16031cb0ef41Sopenharmony_ci set_reading_stopped(false); 16041cb0ef41Sopenharmony_ci stream_->ReadStart(); 16051cb0ef41Sopenharmony_ci } 16061cb0ef41Sopenharmony_ci return; 16071cb0ef41Sopenharmony_ci } 16081cb0ef41Sopenharmony_ci 16091cb0ef41Sopenharmony_ci // If there is more incoming data queued up, consume it. 16101cb0ef41Sopenharmony_ci if (stream_buf_offset_ > 0) { 16111cb0ef41Sopenharmony_ci ConsumeHTTP2Data(); 16121cb0ef41Sopenharmony_ci } 16131cb0ef41Sopenharmony_ci 16141cb0ef41Sopenharmony_ci if (!is_write_scheduled() && !is_destroyed()) { 16151cb0ef41Sopenharmony_ci // Schedule a new write if nghttp2 wants to send data. 16161cb0ef41Sopenharmony_ci MaybeScheduleWrite(); 16171cb0ef41Sopenharmony_ci } 16181cb0ef41Sopenharmony_ci} 16191cb0ef41Sopenharmony_ci 16201cb0ef41Sopenharmony_ci// If the underlying nghttp2_session struct has data pending in its outbound 16211cb0ef41Sopenharmony_ci// queue, MaybeScheduleWrite will schedule a SendPendingData() call to occur 16221cb0ef41Sopenharmony_ci// on the next iteration of the Node.js event loop (using the SetImmediate 16231cb0ef41Sopenharmony_ci// queue), but only if a write has not already been scheduled. 16241cb0ef41Sopenharmony_civoid Http2Session::MaybeScheduleWrite() { 16251cb0ef41Sopenharmony_ci CHECK(!is_write_scheduled()); 16261cb0ef41Sopenharmony_ci if (UNLIKELY(!session_)) 16271cb0ef41Sopenharmony_ci return; 16281cb0ef41Sopenharmony_ci 16291cb0ef41Sopenharmony_ci if (nghttp2_session_want_write(session_.get())) { 16301cb0ef41Sopenharmony_ci HandleScope handle_scope(env()->isolate()); 16311cb0ef41Sopenharmony_ci Debug(this, "scheduling write"); 16321cb0ef41Sopenharmony_ci set_write_scheduled(); 16331cb0ef41Sopenharmony_ci BaseObjectPtr<Http2Session> strong_ref{this}; 16341cb0ef41Sopenharmony_ci env()->SetImmediate([this, strong_ref](Environment* env) { 16351cb0ef41Sopenharmony_ci if (!session_ || !is_write_scheduled()) { 16361cb0ef41Sopenharmony_ci // This can happen e.g. when a stream was reset before this turn 16371cb0ef41Sopenharmony_ci // of the event loop, in which case SendPendingData() is called early, 16381cb0ef41Sopenharmony_ci // or the session was destroyed in the meantime. 16391cb0ef41Sopenharmony_ci return; 16401cb0ef41Sopenharmony_ci } 16411cb0ef41Sopenharmony_ci 16421cb0ef41Sopenharmony_ci // Sending data may call arbitrary JS code, so keep track of 16431cb0ef41Sopenharmony_ci // async context. 16441cb0ef41Sopenharmony_ci if (env->can_call_into_js()) { 16451cb0ef41Sopenharmony_ci HandleScope handle_scope(env->isolate()); 16461cb0ef41Sopenharmony_ci InternalCallbackScope callback_scope(this); 16471cb0ef41Sopenharmony_ci SendPendingData(); 16481cb0ef41Sopenharmony_ci } 16491cb0ef41Sopenharmony_ci }); 16501cb0ef41Sopenharmony_ci } 16511cb0ef41Sopenharmony_ci} 16521cb0ef41Sopenharmony_ci 16531cb0ef41Sopenharmony_civoid Http2Session::MaybeStopReading() { 16541cb0ef41Sopenharmony_ci // If the session is already closing we don't want to stop reading as we want 16551cb0ef41Sopenharmony_ci // to detect when the other peer is actually closed. 16561cb0ef41Sopenharmony_ci if (is_reading_stopped() || is_closing()) return; 16571cb0ef41Sopenharmony_ci int want_read = nghttp2_session_want_read(session_.get()); 16581cb0ef41Sopenharmony_ci Debug(this, "wants read? %d", want_read); 16591cb0ef41Sopenharmony_ci if (want_read == 0 || is_write_in_progress()) { 16601cb0ef41Sopenharmony_ci set_reading_stopped(); 16611cb0ef41Sopenharmony_ci stream_->ReadStop(); 16621cb0ef41Sopenharmony_ci } 16631cb0ef41Sopenharmony_ci} 16641cb0ef41Sopenharmony_ci 16651cb0ef41Sopenharmony_ci// Unset the sending state, finish up all current writes, and reset 16661cb0ef41Sopenharmony_ci// storage for data and metadata that was associated with these writes. 16671cb0ef41Sopenharmony_civoid Http2Session::ClearOutgoing(int status) { 16681cb0ef41Sopenharmony_ci CHECK(is_sending()); 16691cb0ef41Sopenharmony_ci 16701cb0ef41Sopenharmony_ci set_sending(false); 16711cb0ef41Sopenharmony_ci 16721cb0ef41Sopenharmony_ci if (!outgoing_buffers_.empty()) { 16731cb0ef41Sopenharmony_ci outgoing_storage_.clear(); 16741cb0ef41Sopenharmony_ci outgoing_length_ = 0; 16751cb0ef41Sopenharmony_ci 16761cb0ef41Sopenharmony_ci std::vector<NgHttp2StreamWrite> current_outgoing_buffers_; 16771cb0ef41Sopenharmony_ci current_outgoing_buffers_.swap(outgoing_buffers_); 16781cb0ef41Sopenharmony_ci for (const NgHttp2StreamWrite& wr : current_outgoing_buffers_) { 16791cb0ef41Sopenharmony_ci BaseObjectPtr<AsyncWrap> wrap = std::move(wr.req_wrap); 16801cb0ef41Sopenharmony_ci if (wrap) { 16811cb0ef41Sopenharmony_ci // TODO(addaleax): Pass `status` instead of 0, so that we actually error 16821cb0ef41Sopenharmony_ci // out with the error from the write to the underlying protocol, 16831cb0ef41Sopenharmony_ci // if one occurred. 16841cb0ef41Sopenharmony_ci WriteWrap::FromObject(wrap)->Done(0); 16851cb0ef41Sopenharmony_ci } 16861cb0ef41Sopenharmony_ci } 16871cb0ef41Sopenharmony_ci } 16881cb0ef41Sopenharmony_ci 16891cb0ef41Sopenharmony_ci // Now that we've finished sending queued data, if there are any pending 16901cb0ef41Sopenharmony_ci // RstStreams we should try sending again and then flush them one by one. 16911cb0ef41Sopenharmony_ci if (!pending_rst_streams_.empty()) { 16921cb0ef41Sopenharmony_ci std::vector<int32_t> current_pending_rst_streams; 16931cb0ef41Sopenharmony_ci pending_rst_streams_.swap(current_pending_rst_streams); 16941cb0ef41Sopenharmony_ci 16951cb0ef41Sopenharmony_ci SendPendingData(); 16961cb0ef41Sopenharmony_ci 16971cb0ef41Sopenharmony_ci for (int32_t stream_id : current_pending_rst_streams) { 16981cb0ef41Sopenharmony_ci BaseObjectPtr<Http2Stream> stream = FindStream(stream_id); 16991cb0ef41Sopenharmony_ci if (LIKELY(stream)) 17001cb0ef41Sopenharmony_ci stream->FlushRstStream(); 17011cb0ef41Sopenharmony_ci } 17021cb0ef41Sopenharmony_ci } 17031cb0ef41Sopenharmony_ci} 17041cb0ef41Sopenharmony_ci 17051cb0ef41Sopenharmony_civoid Http2Session::PushOutgoingBuffer(NgHttp2StreamWrite&& write) { 17061cb0ef41Sopenharmony_ci outgoing_length_ += write.buf.len; 17071cb0ef41Sopenharmony_ci outgoing_buffers_.emplace_back(std::move(write)); 17081cb0ef41Sopenharmony_ci} 17091cb0ef41Sopenharmony_ci 17101cb0ef41Sopenharmony_ci// Queue a given block of data for sending. This always creates a copy, 17111cb0ef41Sopenharmony_ci// so it is used for the cases in which nghttp2 requests sending of a 17121cb0ef41Sopenharmony_ci// small chunk of data. 17131cb0ef41Sopenharmony_civoid Http2Session::CopyDataIntoOutgoing(const uint8_t* src, size_t src_length) { 17141cb0ef41Sopenharmony_ci size_t offset = outgoing_storage_.size(); 17151cb0ef41Sopenharmony_ci outgoing_storage_.resize(offset + src_length); 17161cb0ef41Sopenharmony_ci memcpy(&outgoing_storage_[offset], src, src_length); 17171cb0ef41Sopenharmony_ci 17181cb0ef41Sopenharmony_ci // Store with a base of `nullptr` initially, since future resizes 17191cb0ef41Sopenharmony_ci // of the outgoing_buffers_ vector may invalidate the pointer. 17201cb0ef41Sopenharmony_ci // The correct base pointers will be set later, before writing to the 17211cb0ef41Sopenharmony_ci // underlying socket. 17221cb0ef41Sopenharmony_ci PushOutgoingBuffer(NgHttp2StreamWrite { 17231cb0ef41Sopenharmony_ci uv_buf_init(nullptr, src_length) 17241cb0ef41Sopenharmony_ci }); 17251cb0ef41Sopenharmony_ci} 17261cb0ef41Sopenharmony_ci 17271cb0ef41Sopenharmony_ci// Prompts nghttp2 to begin serializing it's pending data and pushes each 17281cb0ef41Sopenharmony_ci// chunk out to the i/o socket to be sent. This is a particularly hot method 17291cb0ef41Sopenharmony_ci// that will generally be called at least twice be event loop iteration. 17301cb0ef41Sopenharmony_ci// This is a potential performance optimization target later. 17311cb0ef41Sopenharmony_ci// Returns non-zero value if a write is already in progress. 17321cb0ef41Sopenharmony_ciuint8_t Http2Session::SendPendingData() { 17331cb0ef41Sopenharmony_ci Debug(this, "sending pending data"); 17341cb0ef41Sopenharmony_ci // Do not attempt to send data on the socket if the destroying flag has 17351cb0ef41Sopenharmony_ci // been set. That means everything is shutting down and the socket 17361cb0ef41Sopenharmony_ci // will not be usable. 17371cb0ef41Sopenharmony_ci if (is_destroyed()) 17381cb0ef41Sopenharmony_ci return 0; 17391cb0ef41Sopenharmony_ci set_write_scheduled(false); 17401cb0ef41Sopenharmony_ci 17411cb0ef41Sopenharmony_ci // SendPendingData should not be called recursively. 17421cb0ef41Sopenharmony_ci if (is_sending()) 17431cb0ef41Sopenharmony_ci return 1; 17441cb0ef41Sopenharmony_ci // This is cleared by ClearOutgoing(). 17451cb0ef41Sopenharmony_ci set_sending(); 17461cb0ef41Sopenharmony_ci 17471cb0ef41Sopenharmony_ci ssize_t src_length; 17481cb0ef41Sopenharmony_ci const uint8_t* src; 17491cb0ef41Sopenharmony_ci 17501cb0ef41Sopenharmony_ci CHECK(outgoing_buffers_.empty()); 17511cb0ef41Sopenharmony_ci CHECK(outgoing_storage_.empty()); 17521cb0ef41Sopenharmony_ci 17531cb0ef41Sopenharmony_ci // Part One: Gather data from nghttp2 17541cb0ef41Sopenharmony_ci 17551cb0ef41Sopenharmony_ci while ((src_length = nghttp2_session_mem_send(session_.get(), &src)) > 0) { 17561cb0ef41Sopenharmony_ci Debug(this, "nghttp2 has %d bytes to send", src_length); 17571cb0ef41Sopenharmony_ci CopyDataIntoOutgoing(src, src_length); 17581cb0ef41Sopenharmony_ci } 17591cb0ef41Sopenharmony_ci 17601cb0ef41Sopenharmony_ci CHECK_NE(src_length, NGHTTP2_ERR_NOMEM); 17611cb0ef41Sopenharmony_ci 17621cb0ef41Sopenharmony_ci if (stream_ == nullptr) { 17631cb0ef41Sopenharmony_ci // It would seem nice to bail out earlier, but `nghttp2_session_mem_send()` 17641cb0ef41Sopenharmony_ci // does take care of things like closing the individual streams after 17651cb0ef41Sopenharmony_ci // a socket has been torn down, so we still need to call it. 17661cb0ef41Sopenharmony_ci ClearOutgoing(UV_ECANCELED); 17671cb0ef41Sopenharmony_ci return 0; 17681cb0ef41Sopenharmony_ci } 17691cb0ef41Sopenharmony_ci 17701cb0ef41Sopenharmony_ci // Part Two: Pass Data to the underlying stream 17711cb0ef41Sopenharmony_ci 17721cb0ef41Sopenharmony_ci size_t count = outgoing_buffers_.size(); 17731cb0ef41Sopenharmony_ci if (count == 0) { 17741cb0ef41Sopenharmony_ci ClearOutgoing(0); 17751cb0ef41Sopenharmony_ci return 0; 17761cb0ef41Sopenharmony_ci } 17771cb0ef41Sopenharmony_ci MaybeStackBuffer<uv_buf_t, 32> bufs; 17781cb0ef41Sopenharmony_ci bufs.AllocateSufficientStorage(count); 17791cb0ef41Sopenharmony_ci 17801cb0ef41Sopenharmony_ci // Set the buffer base pointers for copied data that ended up in the 17811cb0ef41Sopenharmony_ci // sessions's own storage since it might have shifted around during gathering. 17821cb0ef41Sopenharmony_ci // (Those are marked by having .base == nullptr.) 17831cb0ef41Sopenharmony_ci size_t offset = 0; 17841cb0ef41Sopenharmony_ci size_t i = 0; 17851cb0ef41Sopenharmony_ci for (const NgHttp2StreamWrite& write : outgoing_buffers_) { 17861cb0ef41Sopenharmony_ci statistics_.data_sent += write.buf.len; 17871cb0ef41Sopenharmony_ci if (write.buf.base == nullptr) { 17881cb0ef41Sopenharmony_ci bufs[i++] = uv_buf_init( 17891cb0ef41Sopenharmony_ci reinterpret_cast<char*>(outgoing_storage_.data() + offset), 17901cb0ef41Sopenharmony_ci write.buf.len); 17911cb0ef41Sopenharmony_ci offset += write.buf.len; 17921cb0ef41Sopenharmony_ci } else { 17931cb0ef41Sopenharmony_ci bufs[i++] = write.buf; 17941cb0ef41Sopenharmony_ci } 17951cb0ef41Sopenharmony_ci } 17961cb0ef41Sopenharmony_ci 17971cb0ef41Sopenharmony_ci chunks_sent_since_last_write_++; 17981cb0ef41Sopenharmony_ci 17991cb0ef41Sopenharmony_ci CHECK(!is_write_in_progress()); 18001cb0ef41Sopenharmony_ci set_write_in_progress(); 18011cb0ef41Sopenharmony_ci StreamWriteResult res = underlying_stream()->Write(*bufs, count); 18021cb0ef41Sopenharmony_ci if (!res.async) { 18031cb0ef41Sopenharmony_ci set_write_in_progress(false); 18041cb0ef41Sopenharmony_ci ClearOutgoing(res.err); 18051cb0ef41Sopenharmony_ci } 18061cb0ef41Sopenharmony_ci 18071cb0ef41Sopenharmony_ci MaybeStopReading(); 18081cb0ef41Sopenharmony_ci 18091cb0ef41Sopenharmony_ci return 0; 18101cb0ef41Sopenharmony_ci} 18111cb0ef41Sopenharmony_ci 18121cb0ef41Sopenharmony_ci 18131cb0ef41Sopenharmony_ci// This callback is called from nghttp2 when it wants to send DATA frames for a 18141cb0ef41Sopenharmony_ci// given Http2Stream, when we set the `NGHTTP2_DATA_FLAG_NO_COPY` flag earlier 18151cb0ef41Sopenharmony_ci// in the Http2Stream::Provider::Stream::OnRead callback. 18161cb0ef41Sopenharmony_ci// We take the write information directly out of the stream's data queue. 18171cb0ef41Sopenharmony_ciint Http2Session::OnSendData( 18181cb0ef41Sopenharmony_ci nghttp2_session* session_, 18191cb0ef41Sopenharmony_ci nghttp2_frame* frame, 18201cb0ef41Sopenharmony_ci const uint8_t* framehd, 18211cb0ef41Sopenharmony_ci size_t length, 18221cb0ef41Sopenharmony_ci nghttp2_data_source* source, 18231cb0ef41Sopenharmony_ci void* user_data) { 18241cb0ef41Sopenharmony_ci Http2Session* session = static_cast<Http2Session*>(user_data); 18251cb0ef41Sopenharmony_ci BaseObjectPtr<Http2Stream> stream = session->FindStream(frame->hd.stream_id); 18261cb0ef41Sopenharmony_ci if (!stream) return 0; 18271cb0ef41Sopenharmony_ci 18281cb0ef41Sopenharmony_ci // Send the frame header + a byte that indicates padding length. 18291cb0ef41Sopenharmony_ci session->CopyDataIntoOutgoing(framehd, 9); 18301cb0ef41Sopenharmony_ci if (frame->data.padlen > 0) { 18311cb0ef41Sopenharmony_ci uint8_t padding_byte = frame->data.padlen - 1; 18321cb0ef41Sopenharmony_ci CHECK_EQ(padding_byte, frame->data.padlen - 1); 18331cb0ef41Sopenharmony_ci session->CopyDataIntoOutgoing(&padding_byte, 1); 18341cb0ef41Sopenharmony_ci } 18351cb0ef41Sopenharmony_ci 18361cb0ef41Sopenharmony_ci Debug(session, "nghttp2 has %d bytes to send directly", length); 18371cb0ef41Sopenharmony_ci while (length > 0) { 18381cb0ef41Sopenharmony_ci // nghttp2 thinks that there is data available (length > 0), which means 18391cb0ef41Sopenharmony_ci // we told it so, which means that we *should* have data available. 18401cb0ef41Sopenharmony_ci CHECK(!stream->queue_.empty()); 18411cb0ef41Sopenharmony_ci 18421cb0ef41Sopenharmony_ci NgHttp2StreamWrite& write = stream->queue_.front(); 18431cb0ef41Sopenharmony_ci if (write.buf.len <= length) { 18441cb0ef41Sopenharmony_ci // This write does not suffice by itself, so we can consume it completely. 18451cb0ef41Sopenharmony_ci length -= write.buf.len; 18461cb0ef41Sopenharmony_ci session->PushOutgoingBuffer(std::move(write)); 18471cb0ef41Sopenharmony_ci stream->queue_.pop(); 18481cb0ef41Sopenharmony_ci continue; 18491cb0ef41Sopenharmony_ci } 18501cb0ef41Sopenharmony_ci 18511cb0ef41Sopenharmony_ci // Slice off `length` bytes of the first write in the queue. 18521cb0ef41Sopenharmony_ci session->PushOutgoingBuffer(NgHttp2StreamWrite { 18531cb0ef41Sopenharmony_ci uv_buf_init(write.buf.base, length) 18541cb0ef41Sopenharmony_ci }); 18551cb0ef41Sopenharmony_ci write.buf.base += length; 18561cb0ef41Sopenharmony_ci write.buf.len -= length; 18571cb0ef41Sopenharmony_ci break; 18581cb0ef41Sopenharmony_ci } 18591cb0ef41Sopenharmony_ci 18601cb0ef41Sopenharmony_ci if (frame->data.padlen > 0) { 18611cb0ef41Sopenharmony_ci // Send padding if that was requested. 18621cb0ef41Sopenharmony_ci session->PushOutgoingBuffer(NgHttp2StreamWrite { 18631cb0ef41Sopenharmony_ci uv_buf_init(const_cast<char*>(zero_bytes_256), frame->data.padlen - 1) 18641cb0ef41Sopenharmony_ci }); 18651cb0ef41Sopenharmony_ci } 18661cb0ef41Sopenharmony_ci 18671cb0ef41Sopenharmony_ci return 0; 18681cb0ef41Sopenharmony_ci} 18691cb0ef41Sopenharmony_ci 18701cb0ef41Sopenharmony_ci// Creates a new Http2Stream and submits a new http2 request. 18711cb0ef41Sopenharmony_ciHttp2Stream* Http2Session::SubmitRequest( 18721cb0ef41Sopenharmony_ci const Http2Priority& priority, 18731cb0ef41Sopenharmony_ci const Http2Headers& headers, 18741cb0ef41Sopenharmony_ci int32_t* ret, 18751cb0ef41Sopenharmony_ci int options) { 18761cb0ef41Sopenharmony_ci Debug(this, "submitting request"); 18771cb0ef41Sopenharmony_ci Http2Scope h2scope(this); 18781cb0ef41Sopenharmony_ci Http2Stream* stream = nullptr; 18791cb0ef41Sopenharmony_ci Http2Stream::Provider::Stream prov(options); 18801cb0ef41Sopenharmony_ci *ret = nghttp2_submit_request( 18811cb0ef41Sopenharmony_ci session_.get(), 18821cb0ef41Sopenharmony_ci &priority, 18831cb0ef41Sopenharmony_ci headers.data(), 18841cb0ef41Sopenharmony_ci headers.length(), 18851cb0ef41Sopenharmony_ci *prov, 18861cb0ef41Sopenharmony_ci nullptr); 18871cb0ef41Sopenharmony_ci CHECK_NE(*ret, NGHTTP2_ERR_NOMEM); 18881cb0ef41Sopenharmony_ci if (LIKELY(*ret > 0)) 18891cb0ef41Sopenharmony_ci stream = Http2Stream::New(this, *ret, NGHTTP2_HCAT_HEADERS, options); 18901cb0ef41Sopenharmony_ci return stream; 18911cb0ef41Sopenharmony_ci} 18921cb0ef41Sopenharmony_ci 18931cb0ef41Sopenharmony_ciuv_buf_t Http2Session::OnStreamAlloc(size_t suggested_size) { 18941cb0ef41Sopenharmony_ci return env()->allocate_managed_buffer(suggested_size); 18951cb0ef41Sopenharmony_ci} 18961cb0ef41Sopenharmony_ci 18971cb0ef41Sopenharmony_ci// Callback used to receive inbound data from the i/o stream 18981cb0ef41Sopenharmony_civoid Http2Session::OnStreamRead(ssize_t nread, const uv_buf_t& buf_) { 18991cb0ef41Sopenharmony_ci HandleScope handle_scope(env()->isolate()); 19001cb0ef41Sopenharmony_ci Context::Scope context_scope(env()->context()); 19011cb0ef41Sopenharmony_ci Http2Scope h2scope(this); 19021cb0ef41Sopenharmony_ci CHECK_NOT_NULL(stream_); 19031cb0ef41Sopenharmony_ci Debug(this, "receiving %d bytes, offset %d", nread, stream_buf_offset_); 19041cb0ef41Sopenharmony_ci std::unique_ptr<BackingStore> bs = env()->release_managed_buffer(buf_); 19051cb0ef41Sopenharmony_ci 19061cb0ef41Sopenharmony_ci // Only pass data on if nread > 0 19071cb0ef41Sopenharmony_ci if (nread <= 0) { 19081cb0ef41Sopenharmony_ci if (nread < 0) { 19091cb0ef41Sopenharmony_ci PassReadErrorToPreviousListener(nread); 19101cb0ef41Sopenharmony_ci } 19111cb0ef41Sopenharmony_ci return; 19121cb0ef41Sopenharmony_ci } 19131cb0ef41Sopenharmony_ci 19141cb0ef41Sopenharmony_ci CHECK_LE(static_cast<size_t>(nread), bs->ByteLength()); 19151cb0ef41Sopenharmony_ci 19161cb0ef41Sopenharmony_ci statistics_.data_received += nread; 19171cb0ef41Sopenharmony_ci 19181cb0ef41Sopenharmony_ci if (LIKELY(stream_buf_offset_ == 0)) { 19191cb0ef41Sopenharmony_ci // Shrink to the actual amount of used data. 19201cb0ef41Sopenharmony_ci bs = BackingStore::Reallocate(env()->isolate(), std::move(bs), nread); 19211cb0ef41Sopenharmony_ci } else { 19221cb0ef41Sopenharmony_ci // This is a very unlikely case, and should only happen if the ReadStart() 19231cb0ef41Sopenharmony_ci // call in OnStreamAfterWrite() immediately provides data. If that does 19241cb0ef41Sopenharmony_ci // happen, we concatenate the data we received with the already-stored 19251cb0ef41Sopenharmony_ci // pending input data, slicing off the already processed part. 19261cb0ef41Sopenharmony_ci size_t pending_len = stream_buf_.len - stream_buf_offset_; 19271cb0ef41Sopenharmony_ci std::unique_ptr<BackingStore> new_bs; 19281cb0ef41Sopenharmony_ci { 19291cb0ef41Sopenharmony_ci NoArrayBufferZeroFillScope no_zero_fill_scope(env()->isolate_data()); 19301cb0ef41Sopenharmony_ci new_bs = ArrayBuffer::NewBackingStore(env()->isolate(), 19311cb0ef41Sopenharmony_ci pending_len + nread); 19321cb0ef41Sopenharmony_ci } 19331cb0ef41Sopenharmony_ci memcpy(static_cast<char*>(new_bs->Data()), 19341cb0ef41Sopenharmony_ci stream_buf_.base + stream_buf_offset_, 19351cb0ef41Sopenharmony_ci pending_len); 19361cb0ef41Sopenharmony_ci memcpy(static_cast<char*>(new_bs->Data()) + pending_len, 19371cb0ef41Sopenharmony_ci bs->Data(), 19381cb0ef41Sopenharmony_ci nread); 19391cb0ef41Sopenharmony_ci 19401cb0ef41Sopenharmony_ci bs = std::move(new_bs); 19411cb0ef41Sopenharmony_ci nread = bs->ByteLength(); 19421cb0ef41Sopenharmony_ci stream_buf_offset_ = 0; 19431cb0ef41Sopenharmony_ci stream_buf_ab_.Reset(); 19441cb0ef41Sopenharmony_ci 19451cb0ef41Sopenharmony_ci // We have now fully processed the stream_buf_ input chunk (by moving the 19461cb0ef41Sopenharmony_ci // remaining part into buf, which will be accounted for below). 19471cb0ef41Sopenharmony_ci DecrementCurrentSessionMemory(stream_buf_.len); 19481cb0ef41Sopenharmony_ci } 19491cb0ef41Sopenharmony_ci 19501cb0ef41Sopenharmony_ci IncrementCurrentSessionMemory(nread); 19511cb0ef41Sopenharmony_ci 19521cb0ef41Sopenharmony_ci // Remember the current buffer, so that OnDataChunkReceived knows the 19531cb0ef41Sopenharmony_ci // offset of a DATA frame's data into the socket read buffer. 19541cb0ef41Sopenharmony_ci stream_buf_ = uv_buf_init(static_cast<char*>(bs->Data()), 19551cb0ef41Sopenharmony_ci static_cast<unsigned int>(nread)); 19561cb0ef41Sopenharmony_ci 19571cb0ef41Sopenharmony_ci // Store this so we can create an ArrayBuffer for read data from it. 19581cb0ef41Sopenharmony_ci // DATA frames will be emitted as slices of that ArrayBuffer to avoid having 19591cb0ef41Sopenharmony_ci // to copy memory. 19601cb0ef41Sopenharmony_ci stream_buf_allocation_ = std::move(bs); 19611cb0ef41Sopenharmony_ci 19621cb0ef41Sopenharmony_ci ConsumeHTTP2Data(); 19631cb0ef41Sopenharmony_ci 19641cb0ef41Sopenharmony_ci MaybeStopReading(); 19651cb0ef41Sopenharmony_ci} 19661cb0ef41Sopenharmony_ci 19671cb0ef41Sopenharmony_cibool Http2Session::HasWritesOnSocketForStream(Http2Stream* stream) { 19681cb0ef41Sopenharmony_ci for (const NgHttp2StreamWrite& wr : outgoing_buffers_) { 19691cb0ef41Sopenharmony_ci if (wr.req_wrap && WriteWrap::FromObject(wr.req_wrap)->stream() == stream) 19701cb0ef41Sopenharmony_ci return true; 19711cb0ef41Sopenharmony_ci } 19721cb0ef41Sopenharmony_ci return false; 19731cb0ef41Sopenharmony_ci} 19741cb0ef41Sopenharmony_ci 19751cb0ef41Sopenharmony_ci// Every Http2Session session is tightly bound to a single i/o StreamBase 19761cb0ef41Sopenharmony_ci// (typically a net.Socket or tls.TLSSocket). The lifecycle of the two is 19771cb0ef41Sopenharmony_ci// tightly coupled with all data transfer between the two happening at the 19781cb0ef41Sopenharmony_ci// C++ layer via the StreamBase API. 19791cb0ef41Sopenharmony_civoid Http2Session::Consume(Local<Object> stream_obj) { 19801cb0ef41Sopenharmony_ci StreamBase* stream = StreamBase::FromObject(stream_obj); 19811cb0ef41Sopenharmony_ci stream->PushStreamListener(this); 19821cb0ef41Sopenharmony_ci Debug(this, "i/o stream consumed"); 19831cb0ef41Sopenharmony_ci} 19841cb0ef41Sopenharmony_ci 19851cb0ef41Sopenharmony_ci// Allow injecting of data from JS 19861cb0ef41Sopenharmony_ci// This is used when the socket has already some data received 19871cb0ef41Sopenharmony_ci// before our listener was attached 19881cb0ef41Sopenharmony_ci// https://github.com/nodejs/node/issues/35475 19891cb0ef41Sopenharmony_civoid Http2Session::Receive(const FunctionCallbackInfo<Value>& args) { 19901cb0ef41Sopenharmony_ci Http2Session* session; 19911cb0ef41Sopenharmony_ci ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); 19921cb0ef41Sopenharmony_ci CHECK(args[0]->IsObject()); 19931cb0ef41Sopenharmony_ci 19941cb0ef41Sopenharmony_ci ArrayBufferViewContents<char> buffer(args[0]); 19951cb0ef41Sopenharmony_ci const char* data = buffer.data(); 19961cb0ef41Sopenharmony_ci size_t len = buffer.length(); 19971cb0ef41Sopenharmony_ci Debug(session, "Receiving %zu bytes injected from JS", len); 19981cb0ef41Sopenharmony_ci 19991cb0ef41Sopenharmony_ci // Copy given buffer 20001cb0ef41Sopenharmony_ci while (len > 0) { 20011cb0ef41Sopenharmony_ci uv_buf_t buf = session->OnStreamAlloc(len); 20021cb0ef41Sopenharmony_ci size_t copy = buf.len > len ? len : buf.len; 20031cb0ef41Sopenharmony_ci memcpy(buf.base, data, copy); 20041cb0ef41Sopenharmony_ci buf.len = copy; 20051cb0ef41Sopenharmony_ci session->OnStreamRead(copy, buf); 20061cb0ef41Sopenharmony_ci 20071cb0ef41Sopenharmony_ci data += copy; 20081cb0ef41Sopenharmony_ci len -= copy; 20091cb0ef41Sopenharmony_ci } 20101cb0ef41Sopenharmony_ci} 20111cb0ef41Sopenharmony_ci 20121cb0ef41Sopenharmony_ciHttp2Stream* Http2Stream::New(Http2Session* session, 20131cb0ef41Sopenharmony_ci int32_t id, 20141cb0ef41Sopenharmony_ci nghttp2_headers_category category, 20151cb0ef41Sopenharmony_ci int options) { 20161cb0ef41Sopenharmony_ci Local<Object> obj; 20171cb0ef41Sopenharmony_ci if (!session->env() 20181cb0ef41Sopenharmony_ci ->http2stream_constructor_template() 20191cb0ef41Sopenharmony_ci ->NewInstance(session->env()->context()) 20201cb0ef41Sopenharmony_ci .ToLocal(&obj)) { 20211cb0ef41Sopenharmony_ci return nullptr; 20221cb0ef41Sopenharmony_ci } 20231cb0ef41Sopenharmony_ci return new Http2Stream(session, obj, id, category, options); 20241cb0ef41Sopenharmony_ci} 20251cb0ef41Sopenharmony_ci 20261cb0ef41Sopenharmony_ciHttp2Stream::Http2Stream(Http2Session* session, 20271cb0ef41Sopenharmony_ci Local<Object> obj, 20281cb0ef41Sopenharmony_ci int32_t id, 20291cb0ef41Sopenharmony_ci nghttp2_headers_category category, 20301cb0ef41Sopenharmony_ci int options) 20311cb0ef41Sopenharmony_ci : AsyncWrap(session->env(), obj, AsyncWrap::PROVIDER_HTTP2STREAM), 20321cb0ef41Sopenharmony_ci StreamBase(session->env()), 20331cb0ef41Sopenharmony_ci session_(session), 20341cb0ef41Sopenharmony_ci id_(id), 20351cb0ef41Sopenharmony_ci current_headers_category_(category) { 20361cb0ef41Sopenharmony_ci MakeWeak(); 20371cb0ef41Sopenharmony_ci StreamBase::AttachToObject(GetObject()); 20381cb0ef41Sopenharmony_ci statistics_.id = id; 20391cb0ef41Sopenharmony_ci statistics_.start_time = uv_hrtime(); 20401cb0ef41Sopenharmony_ci 20411cb0ef41Sopenharmony_ci // Limit the number of header pairs 20421cb0ef41Sopenharmony_ci max_header_pairs_ = session->max_header_pairs(); 20431cb0ef41Sopenharmony_ci if (max_header_pairs_ == 0) { 20441cb0ef41Sopenharmony_ci max_header_pairs_ = DEFAULT_MAX_HEADER_LIST_PAIRS; 20451cb0ef41Sopenharmony_ci } 20461cb0ef41Sopenharmony_ci current_headers_.reserve(std::min(max_header_pairs_, 12u)); 20471cb0ef41Sopenharmony_ci 20481cb0ef41Sopenharmony_ci // Limit the number of header octets 20491cb0ef41Sopenharmony_ci max_header_length_ = 20501cb0ef41Sopenharmony_ci std::min( 20511cb0ef41Sopenharmony_ci nghttp2_session_get_local_settings( 20521cb0ef41Sopenharmony_ci session->session(), 20531cb0ef41Sopenharmony_ci NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE), 20541cb0ef41Sopenharmony_ci MAX_MAX_HEADER_LIST_SIZE); 20551cb0ef41Sopenharmony_ci 20561cb0ef41Sopenharmony_ci if (options & STREAM_OPTION_GET_TRAILERS) 20571cb0ef41Sopenharmony_ci set_has_trailers(); 20581cb0ef41Sopenharmony_ci 20591cb0ef41Sopenharmony_ci PushStreamListener(&stream_listener_); 20601cb0ef41Sopenharmony_ci 20611cb0ef41Sopenharmony_ci if (options & STREAM_OPTION_EMPTY_PAYLOAD) 20621cb0ef41Sopenharmony_ci Shutdown(); 20631cb0ef41Sopenharmony_ci session->AddStream(this); 20641cb0ef41Sopenharmony_ci} 20651cb0ef41Sopenharmony_ci 20661cb0ef41Sopenharmony_ciHttp2Stream::~Http2Stream() { 20671cb0ef41Sopenharmony_ci Debug(this, "tearing down stream"); 20681cb0ef41Sopenharmony_ci} 20691cb0ef41Sopenharmony_ci 20701cb0ef41Sopenharmony_civoid Http2Stream::MemoryInfo(MemoryTracker* tracker) const { 20711cb0ef41Sopenharmony_ci tracker->TrackField("current_headers", current_headers_); 20721cb0ef41Sopenharmony_ci tracker->TrackField("queue", queue_); 20731cb0ef41Sopenharmony_ci} 20741cb0ef41Sopenharmony_ci 20751cb0ef41Sopenharmony_cistd::string Http2Stream::diagnostic_name() const { 20761cb0ef41Sopenharmony_ci const Http2Session* sess = session(); 20771cb0ef41Sopenharmony_ci const std::string sname = 20781cb0ef41Sopenharmony_ci sess ? sess->diagnostic_name() : "session already destroyed"; 20791cb0ef41Sopenharmony_ci return "HttpStream " + std::to_string(id()) + " (" + 20801cb0ef41Sopenharmony_ci std::to_string(static_cast<int64_t>(get_async_id())) + ") [" + sname + 20811cb0ef41Sopenharmony_ci "]"; 20821cb0ef41Sopenharmony_ci} 20831cb0ef41Sopenharmony_ci 20841cb0ef41Sopenharmony_ci// Notify the Http2Stream that a new block of HEADERS is being processed. 20851cb0ef41Sopenharmony_civoid Http2Stream::StartHeaders(nghttp2_headers_category category) { 20861cb0ef41Sopenharmony_ci Debug(this, "starting headers, category: %d", category); 20871cb0ef41Sopenharmony_ci CHECK(!this->is_destroyed()); 20881cb0ef41Sopenharmony_ci session_->DecrementCurrentSessionMemory(current_headers_length_); 20891cb0ef41Sopenharmony_ci current_headers_length_ = 0; 20901cb0ef41Sopenharmony_ci current_headers_.clear(); 20911cb0ef41Sopenharmony_ci current_headers_category_ = category; 20921cb0ef41Sopenharmony_ci} 20931cb0ef41Sopenharmony_ci 20941cb0ef41Sopenharmony_ci 20951cb0ef41Sopenharmony_cinghttp2_stream* Http2Stream::operator*() const { return stream(); } 20961cb0ef41Sopenharmony_ci 20971cb0ef41Sopenharmony_cinghttp2_stream* Http2Stream::stream() const { 20981cb0ef41Sopenharmony_ci return nghttp2_session_find_stream(session_->session(), id_); 20991cb0ef41Sopenharmony_ci} 21001cb0ef41Sopenharmony_ci 21011cb0ef41Sopenharmony_civoid Http2Stream::Close(int32_t code) { 21021cb0ef41Sopenharmony_ci CHECK(!this->is_destroyed()); 21031cb0ef41Sopenharmony_ci set_closed(); 21041cb0ef41Sopenharmony_ci code_ = code; 21051cb0ef41Sopenharmony_ci Debug(this, "closed with code %d", code); 21061cb0ef41Sopenharmony_ci} 21071cb0ef41Sopenharmony_ci 21081cb0ef41Sopenharmony_ciShutdownWrap* Http2Stream::CreateShutdownWrap(Local<Object> object) { 21091cb0ef41Sopenharmony_ci // DoShutdown() always finishes synchronously, so there's no need to create 21101cb0ef41Sopenharmony_ci // a structure to store asynchronous context. 21111cb0ef41Sopenharmony_ci return nullptr; 21121cb0ef41Sopenharmony_ci} 21131cb0ef41Sopenharmony_ci 21141cb0ef41Sopenharmony_ciint Http2Stream::DoShutdown(ShutdownWrap* req_wrap) { 21151cb0ef41Sopenharmony_ci if (is_destroyed()) 21161cb0ef41Sopenharmony_ci return UV_EPIPE; 21171cb0ef41Sopenharmony_ci 21181cb0ef41Sopenharmony_ci { 21191cb0ef41Sopenharmony_ci Http2Scope h2scope(this); 21201cb0ef41Sopenharmony_ci set_not_writable(); 21211cb0ef41Sopenharmony_ci CHECK_NE(nghttp2_session_resume_data( 21221cb0ef41Sopenharmony_ci session_->session(), id_), 21231cb0ef41Sopenharmony_ci NGHTTP2_ERR_NOMEM); 21241cb0ef41Sopenharmony_ci Debug(this, "writable side shutdown"); 21251cb0ef41Sopenharmony_ci } 21261cb0ef41Sopenharmony_ci return 1; 21271cb0ef41Sopenharmony_ci} 21281cb0ef41Sopenharmony_ci 21291cb0ef41Sopenharmony_ci// Destroy the Http2Stream and render it unusable. Actual resources for the 21301cb0ef41Sopenharmony_ci// Stream will not be freed until the next tick of the Node.js event loop 21311cb0ef41Sopenharmony_ci// using the SetImmediate queue. 21321cb0ef41Sopenharmony_civoid Http2Stream::Destroy() { 21331cb0ef41Sopenharmony_ci // Do nothing if this stream instance is already destroyed 21341cb0ef41Sopenharmony_ci if (is_destroyed()) 21351cb0ef41Sopenharmony_ci return; 21361cb0ef41Sopenharmony_ci if (session_->has_pending_rststream(id_)) 21371cb0ef41Sopenharmony_ci FlushRstStream(); 21381cb0ef41Sopenharmony_ci set_destroyed(); 21391cb0ef41Sopenharmony_ci 21401cb0ef41Sopenharmony_ci Debug(this, "destroying stream"); 21411cb0ef41Sopenharmony_ci 21421cb0ef41Sopenharmony_ci // Wait until the start of the next loop to delete because there 21431cb0ef41Sopenharmony_ci // may still be some pending operations queued for this stream. 21441cb0ef41Sopenharmony_ci BaseObjectPtr<Http2Stream> strong_ref = session_->RemoveStream(id_); 21451cb0ef41Sopenharmony_ci if (strong_ref) { 21461cb0ef41Sopenharmony_ci env()->SetImmediate([this, strong_ref = std::move(strong_ref)]( 21471cb0ef41Sopenharmony_ci Environment* env) { 21481cb0ef41Sopenharmony_ci // Free any remaining outgoing data chunks here. This should be done 21491cb0ef41Sopenharmony_ci // here because it's possible for destroy to have been called while 21501cb0ef41Sopenharmony_ci // we still have queued outbound writes. 21511cb0ef41Sopenharmony_ci while (!queue_.empty()) { 21521cb0ef41Sopenharmony_ci NgHttp2StreamWrite& head = queue_.front(); 21531cb0ef41Sopenharmony_ci if (head.req_wrap) 21541cb0ef41Sopenharmony_ci WriteWrap::FromObject(head.req_wrap)->Done(UV_ECANCELED); 21551cb0ef41Sopenharmony_ci queue_.pop(); 21561cb0ef41Sopenharmony_ci } 21571cb0ef41Sopenharmony_ci 21581cb0ef41Sopenharmony_ci // We can destroy the stream now if there are no writes for it 21591cb0ef41Sopenharmony_ci // already on the socket. Otherwise, we'll wait for the garbage collector 21601cb0ef41Sopenharmony_ci // to take care of cleaning up. 21611cb0ef41Sopenharmony_ci if (session() == nullptr || 21621cb0ef41Sopenharmony_ci !session()->HasWritesOnSocketForStream(this)) { 21631cb0ef41Sopenharmony_ci // Delete once strong_ref goes out of scope. 21641cb0ef41Sopenharmony_ci Detach(); 21651cb0ef41Sopenharmony_ci } 21661cb0ef41Sopenharmony_ci }); 21671cb0ef41Sopenharmony_ci } 21681cb0ef41Sopenharmony_ci 21691cb0ef41Sopenharmony_ci statistics_.end_time = uv_hrtime(); 21701cb0ef41Sopenharmony_ci session_->statistics_.stream_average_duration = 21711cb0ef41Sopenharmony_ci ((statistics_.end_time - statistics_.start_time) / 21721cb0ef41Sopenharmony_ci session_->statistics_.stream_count) / 1e6; 21731cb0ef41Sopenharmony_ci EmitStatistics(); 21741cb0ef41Sopenharmony_ci} 21751cb0ef41Sopenharmony_ci 21761cb0ef41Sopenharmony_ci 21771cb0ef41Sopenharmony_ci// Initiates a response on the Http2Stream using data provided via the 21781cb0ef41Sopenharmony_ci// StreamBase Streams API. 21791cb0ef41Sopenharmony_ciint Http2Stream::SubmitResponse(const Http2Headers& headers, int options) { 21801cb0ef41Sopenharmony_ci CHECK(!this->is_destroyed()); 21811cb0ef41Sopenharmony_ci Http2Scope h2scope(this); 21821cb0ef41Sopenharmony_ci Debug(this, "submitting response"); 21831cb0ef41Sopenharmony_ci if (options & STREAM_OPTION_GET_TRAILERS) 21841cb0ef41Sopenharmony_ci set_has_trailers(); 21851cb0ef41Sopenharmony_ci 21861cb0ef41Sopenharmony_ci if (!is_writable()) 21871cb0ef41Sopenharmony_ci options |= STREAM_OPTION_EMPTY_PAYLOAD; 21881cb0ef41Sopenharmony_ci 21891cb0ef41Sopenharmony_ci Http2Stream::Provider::Stream prov(this, options); 21901cb0ef41Sopenharmony_ci int ret = nghttp2_submit_response( 21911cb0ef41Sopenharmony_ci session_->session(), 21921cb0ef41Sopenharmony_ci id_, 21931cb0ef41Sopenharmony_ci headers.data(), 21941cb0ef41Sopenharmony_ci headers.length(), 21951cb0ef41Sopenharmony_ci *prov); 21961cb0ef41Sopenharmony_ci CHECK_NE(ret, NGHTTP2_ERR_NOMEM); 21971cb0ef41Sopenharmony_ci return ret; 21981cb0ef41Sopenharmony_ci} 21991cb0ef41Sopenharmony_ci 22001cb0ef41Sopenharmony_ci 22011cb0ef41Sopenharmony_ci// Submit informational headers for a stream. 22021cb0ef41Sopenharmony_ciint Http2Stream::SubmitInfo(const Http2Headers& headers) { 22031cb0ef41Sopenharmony_ci CHECK(!this->is_destroyed()); 22041cb0ef41Sopenharmony_ci Http2Scope h2scope(this); 22051cb0ef41Sopenharmony_ci Debug(this, "sending %d informational headers", headers.length()); 22061cb0ef41Sopenharmony_ci int ret = nghttp2_submit_headers( 22071cb0ef41Sopenharmony_ci session_->session(), 22081cb0ef41Sopenharmony_ci NGHTTP2_FLAG_NONE, 22091cb0ef41Sopenharmony_ci id_, 22101cb0ef41Sopenharmony_ci nullptr, 22111cb0ef41Sopenharmony_ci headers.data(), 22121cb0ef41Sopenharmony_ci headers.length(), 22131cb0ef41Sopenharmony_ci nullptr); 22141cb0ef41Sopenharmony_ci CHECK_NE(ret, NGHTTP2_ERR_NOMEM); 22151cb0ef41Sopenharmony_ci return ret; 22161cb0ef41Sopenharmony_ci} 22171cb0ef41Sopenharmony_ci 22181cb0ef41Sopenharmony_civoid Http2Stream::OnTrailers() { 22191cb0ef41Sopenharmony_ci Debug(this, "let javascript know we are ready for trailers"); 22201cb0ef41Sopenharmony_ci CHECK(!this->is_destroyed()); 22211cb0ef41Sopenharmony_ci Isolate* isolate = env()->isolate(); 22221cb0ef41Sopenharmony_ci HandleScope scope(isolate); 22231cb0ef41Sopenharmony_ci Local<Context> context = env()->context(); 22241cb0ef41Sopenharmony_ci Context::Scope context_scope(context); 22251cb0ef41Sopenharmony_ci set_has_trailers(false); 22261cb0ef41Sopenharmony_ci MakeCallback(env()->http2session_on_stream_trailers_function(), 0, nullptr); 22271cb0ef41Sopenharmony_ci} 22281cb0ef41Sopenharmony_ci 22291cb0ef41Sopenharmony_ci// Submit informational headers for a stream. 22301cb0ef41Sopenharmony_ciint Http2Stream::SubmitTrailers(const Http2Headers& headers) { 22311cb0ef41Sopenharmony_ci CHECK(!this->is_destroyed()); 22321cb0ef41Sopenharmony_ci Http2Scope h2scope(this); 22331cb0ef41Sopenharmony_ci Debug(this, "sending %d trailers", headers.length()); 22341cb0ef41Sopenharmony_ci int ret; 22351cb0ef41Sopenharmony_ci // Sending an empty trailers frame poses problems in Safari, Edge & IE. 22361cb0ef41Sopenharmony_ci // Instead we can just send an empty data frame with NGHTTP2_FLAG_END_STREAM 22371cb0ef41Sopenharmony_ci // to indicate that the stream is ready to be closed. 22381cb0ef41Sopenharmony_ci if (headers.length() == 0) { 22391cb0ef41Sopenharmony_ci Http2Stream::Provider::Stream prov(this, 0); 22401cb0ef41Sopenharmony_ci ret = nghttp2_submit_data( 22411cb0ef41Sopenharmony_ci session_->session(), 22421cb0ef41Sopenharmony_ci NGHTTP2_FLAG_END_STREAM, 22431cb0ef41Sopenharmony_ci id_, 22441cb0ef41Sopenharmony_ci *prov); 22451cb0ef41Sopenharmony_ci } else { 22461cb0ef41Sopenharmony_ci ret = nghttp2_submit_trailer( 22471cb0ef41Sopenharmony_ci session_->session(), 22481cb0ef41Sopenharmony_ci id_, 22491cb0ef41Sopenharmony_ci headers.data(), 22501cb0ef41Sopenharmony_ci headers.length()); 22511cb0ef41Sopenharmony_ci } 22521cb0ef41Sopenharmony_ci CHECK_NE(ret, NGHTTP2_ERR_NOMEM); 22531cb0ef41Sopenharmony_ci return ret; 22541cb0ef41Sopenharmony_ci} 22551cb0ef41Sopenharmony_ci 22561cb0ef41Sopenharmony_ci// Submit a PRIORITY frame to the connected peer. 22571cb0ef41Sopenharmony_ciint Http2Stream::SubmitPriority(const Http2Priority& priority, 22581cb0ef41Sopenharmony_ci bool silent) { 22591cb0ef41Sopenharmony_ci CHECK(!this->is_destroyed()); 22601cb0ef41Sopenharmony_ci Http2Scope h2scope(this); 22611cb0ef41Sopenharmony_ci Debug(this, "sending priority spec"); 22621cb0ef41Sopenharmony_ci int ret = silent ? 22631cb0ef41Sopenharmony_ci nghttp2_session_change_stream_priority( 22641cb0ef41Sopenharmony_ci session_->session(), 22651cb0ef41Sopenharmony_ci id_, 22661cb0ef41Sopenharmony_ci &priority) : 22671cb0ef41Sopenharmony_ci nghttp2_submit_priority( 22681cb0ef41Sopenharmony_ci session_->session(), 22691cb0ef41Sopenharmony_ci NGHTTP2_FLAG_NONE, 22701cb0ef41Sopenharmony_ci id_, &priority); 22711cb0ef41Sopenharmony_ci CHECK_NE(ret, NGHTTP2_ERR_NOMEM); 22721cb0ef41Sopenharmony_ci return ret; 22731cb0ef41Sopenharmony_ci} 22741cb0ef41Sopenharmony_ci 22751cb0ef41Sopenharmony_ci// Closes the Http2Stream by submitting an RST_STREAM frame to the connected 22761cb0ef41Sopenharmony_ci// peer. 22771cb0ef41Sopenharmony_civoid Http2Stream::SubmitRstStream(const uint32_t code) { 22781cb0ef41Sopenharmony_ci CHECK(!this->is_destroyed()); 22791cb0ef41Sopenharmony_ci code_ = code; 22801cb0ef41Sopenharmony_ci 22811cb0ef41Sopenharmony_ci auto is_stream_cancel = [](const uint32_t code) { 22821cb0ef41Sopenharmony_ci return code == NGHTTP2_CANCEL; 22831cb0ef41Sopenharmony_ci }; 22841cb0ef41Sopenharmony_ci 22851cb0ef41Sopenharmony_ci // If RST_STREAM frame is received with error code NGHTTP2_CANCEL, 22861cb0ef41Sopenharmony_ci // add it to the pending list and don't force purge the data. It is 22871cb0ef41Sopenharmony_ci // to avoids the double free error due to unwanted behavior of nghttp2. 22881cb0ef41Sopenharmony_ci 22891cb0ef41Sopenharmony_ci // Add stream to the pending list only if it is received with scope 22901cb0ef41Sopenharmony_ci // below in the stack. The pending list may not get processed 22911cb0ef41Sopenharmony_ci // if RST_STREAM received is not in scope and added to the list 22921cb0ef41Sopenharmony_ci // causing endpoint to hang. 22931cb0ef41Sopenharmony_ci if (session_->is_in_scope() && is_stream_cancel(code)) { 22941cb0ef41Sopenharmony_ci session_->AddPendingRstStream(id_); 22951cb0ef41Sopenharmony_ci return; 22961cb0ef41Sopenharmony_ci } 22971cb0ef41Sopenharmony_ci 22981cb0ef41Sopenharmony_ci 22991cb0ef41Sopenharmony_ci // If possible, force a purge of any currently pending data here to make sure 23001cb0ef41Sopenharmony_ci // it is sent before closing the stream. If it returns non-zero then we need 23011cb0ef41Sopenharmony_ci // to wait until the current write finishes and try again to avoid nghttp2 23021cb0ef41Sopenharmony_ci // behaviour where it prioritizes RstStream over everything else. 23031cb0ef41Sopenharmony_ci if (session_->SendPendingData() != 0) { 23041cb0ef41Sopenharmony_ci session_->AddPendingRstStream(id_); 23051cb0ef41Sopenharmony_ci return; 23061cb0ef41Sopenharmony_ci } 23071cb0ef41Sopenharmony_ci 23081cb0ef41Sopenharmony_ci FlushRstStream(); 23091cb0ef41Sopenharmony_ci} 23101cb0ef41Sopenharmony_ci 23111cb0ef41Sopenharmony_civoid Http2Stream::FlushRstStream() { 23121cb0ef41Sopenharmony_ci if (is_destroyed()) 23131cb0ef41Sopenharmony_ci return; 23141cb0ef41Sopenharmony_ci Http2Scope h2scope(this); 23151cb0ef41Sopenharmony_ci CHECK_EQ(nghttp2_submit_rst_stream( 23161cb0ef41Sopenharmony_ci session_->session(), 23171cb0ef41Sopenharmony_ci NGHTTP2_FLAG_NONE, 23181cb0ef41Sopenharmony_ci id_, 23191cb0ef41Sopenharmony_ci code_), 0); 23201cb0ef41Sopenharmony_ci} 23211cb0ef41Sopenharmony_ci 23221cb0ef41Sopenharmony_ci 23231cb0ef41Sopenharmony_ci// Submit a push promise and create the associated Http2Stream if successful. 23241cb0ef41Sopenharmony_ciHttp2Stream* Http2Stream::SubmitPushPromise(const Http2Headers& headers, 23251cb0ef41Sopenharmony_ci int32_t* ret, 23261cb0ef41Sopenharmony_ci int options) { 23271cb0ef41Sopenharmony_ci CHECK(!this->is_destroyed()); 23281cb0ef41Sopenharmony_ci Http2Scope h2scope(this); 23291cb0ef41Sopenharmony_ci Debug(this, "sending push promise"); 23301cb0ef41Sopenharmony_ci *ret = nghttp2_submit_push_promise( 23311cb0ef41Sopenharmony_ci session_->session(), 23321cb0ef41Sopenharmony_ci NGHTTP2_FLAG_NONE, 23331cb0ef41Sopenharmony_ci id_, 23341cb0ef41Sopenharmony_ci headers.data(), 23351cb0ef41Sopenharmony_ci headers.length(), 23361cb0ef41Sopenharmony_ci nullptr); 23371cb0ef41Sopenharmony_ci CHECK_NE(*ret, NGHTTP2_ERR_NOMEM); 23381cb0ef41Sopenharmony_ci Http2Stream* stream = nullptr; 23391cb0ef41Sopenharmony_ci if (*ret > 0) { 23401cb0ef41Sopenharmony_ci stream = Http2Stream::New( 23411cb0ef41Sopenharmony_ci session_.get(), *ret, NGHTTP2_HCAT_HEADERS, options); 23421cb0ef41Sopenharmony_ci } 23431cb0ef41Sopenharmony_ci 23441cb0ef41Sopenharmony_ci return stream; 23451cb0ef41Sopenharmony_ci} 23461cb0ef41Sopenharmony_ci 23471cb0ef41Sopenharmony_ci// Switch the StreamBase into flowing mode to begin pushing chunks of data 23481cb0ef41Sopenharmony_ci// out to JS land. 23491cb0ef41Sopenharmony_ciint Http2Stream::ReadStart() { 23501cb0ef41Sopenharmony_ci Http2Scope h2scope(this); 23511cb0ef41Sopenharmony_ci CHECK(!this->is_destroyed()); 23521cb0ef41Sopenharmony_ci set_reading(); 23531cb0ef41Sopenharmony_ci 23541cb0ef41Sopenharmony_ci Debug(this, "reading starting"); 23551cb0ef41Sopenharmony_ci 23561cb0ef41Sopenharmony_ci // Tell nghttp2 about our consumption of the data that was handed 23571cb0ef41Sopenharmony_ci // off to JS land. 23581cb0ef41Sopenharmony_ci nghttp2_session_consume_stream( 23591cb0ef41Sopenharmony_ci session_->session(), 23601cb0ef41Sopenharmony_ci id_, 23611cb0ef41Sopenharmony_ci inbound_consumed_data_while_paused_); 23621cb0ef41Sopenharmony_ci inbound_consumed_data_while_paused_ = 0; 23631cb0ef41Sopenharmony_ci 23641cb0ef41Sopenharmony_ci return 0; 23651cb0ef41Sopenharmony_ci} 23661cb0ef41Sopenharmony_ci 23671cb0ef41Sopenharmony_ci// Switch the StreamBase into paused mode. 23681cb0ef41Sopenharmony_ciint Http2Stream::ReadStop() { 23691cb0ef41Sopenharmony_ci CHECK(!this->is_destroyed()); 23701cb0ef41Sopenharmony_ci if (!is_reading()) 23711cb0ef41Sopenharmony_ci return 0; 23721cb0ef41Sopenharmony_ci set_paused(); 23731cb0ef41Sopenharmony_ci Debug(this, "reading stopped"); 23741cb0ef41Sopenharmony_ci return 0; 23751cb0ef41Sopenharmony_ci} 23761cb0ef41Sopenharmony_ci 23771cb0ef41Sopenharmony_ci// The Http2Stream class is a subclass of StreamBase. The DoWrite method 23781cb0ef41Sopenharmony_ci// receives outbound chunks of data to send as outbound DATA frames. These 23791cb0ef41Sopenharmony_ci// are queued in an internal linked list of uv_buf_t structs that are sent 23801cb0ef41Sopenharmony_ci// when nghttp2 is ready to serialize the data frame. 23811cb0ef41Sopenharmony_ci// 23821cb0ef41Sopenharmony_ci// Queue the given set of uv_but_t handles for writing to an 23831cb0ef41Sopenharmony_ci// nghttp2_stream. The WriteWrap's Done callback will be invoked once the 23841cb0ef41Sopenharmony_ci// chunks of data have been flushed to the underlying nghttp2_session. 23851cb0ef41Sopenharmony_ci// Note that this does *not* mean that the data has been flushed 23861cb0ef41Sopenharmony_ci// to the socket yet. 23871cb0ef41Sopenharmony_ciint Http2Stream::DoWrite(WriteWrap* req_wrap, 23881cb0ef41Sopenharmony_ci uv_buf_t* bufs, 23891cb0ef41Sopenharmony_ci size_t nbufs, 23901cb0ef41Sopenharmony_ci uv_stream_t* send_handle) { 23911cb0ef41Sopenharmony_ci CHECK_NULL(send_handle); 23921cb0ef41Sopenharmony_ci Http2Scope h2scope(this); 23931cb0ef41Sopenharmony_ci if (!is_writable() || is_destroyed()) { 23941cb0ef41Sopenharmony_ci return UV_EOF; 23951cb0ef41Sopenharmony_ci } 23961cb0ef41Sopenharmony_ci Debug(this, "queuing %d buffers to send", nbufs); 23971cb0ef41Sopenharmony_ci for (size_t i = 0; i < nbufs; ++i) { 23981cb0ef41Sopenharmony_ci // Store the req_wrap on the last write info in the queue, so that it is 23991cb0ef41Sopenharmony_ci // only marked as finished once all buffers associated with it are finished. 24001cb0ef41Sopenharmony_ci queue_.emplace(NgHttp2StreamWrite { 24011cb0ef41Sopenharmony_ci BaseObjectPtr<AsyncWrap>( 24021cb0ef41Sopenharmony_ci i == nbufs - 1 ? req_wrap->GetAsyncWrap() : nullptr), 24031cb0ef41Sopenharmony_ci bufs[i] 24041cb0ef41Sopenharmony_ci }); 24051cb0ef41Sopenharmony_ci IncrementAvailableOutboundLength(bufs[i].len); 24061cb0ef41Sopenharmony_ci } 24071cb0ef41Sopenharmony_ci CHECK_NE(nghttp2_session_resume_data( 24081cb0ef41Sopenharmony_ci session_->session(), 24091cb0ef41Sopenharmony_ci id_), NGHTTP2_ERR_NOMEM); 24101cb0ef41Sopenharmony_ci return 0; 24111cb0ef41Sopenharmony_ci} 24121cb0ef41Sopenharmony_ci 24131cb0ef41Sopenharmony_ci// Ads a header to the Http2Stream. Note that the header name and value are 24141cb0ef41Sopenharmony_ci// provided using a buffer structure provided by nghttp2 that allows us to 24151cb0ef41Sopenharmony_ci// avoid unnecessary memcpy's. Those buffers are ref counted. The ref count 24161cb0ef41Sopenharmony_ci// is incremented here and are decremented when the header name and values 24171cb0ef41Sopenharmony_ci// are garbage collected later. 24181cb0ef41Sopenharmony_cibool Http2Stream::AddHeader(nghttp2_rcbuf* name, 24191cb0ef41Sopenharmony_ci nghttp2_rcbuf* value, 24201cb0ef41Sopenharmony_ci uint8_t flags) { 24211cb0ef41Sopenharmony_ci CHECK(!this->is_destroyed()); 24221cb0ef41Sopenharmony_ci 24231cb0ef41Sopenharmony_ci if (Http2RcBufferPointer::IsZeroLength(name)) 24241cb0ef41Sopenharmony_ci return true; // Ignore empty headers. 24251cb0ef41Sopenharmony_ci 24261cb0ef41Sopenharmony_ci Http2Header header(env(), name, value, flags); 24271cb0ef41Sopenharmony_ci size_t length = header.length() + 32; 24281cb0ef41Sopenharmony_ci // A header can only be added if we have not exceeded the maximum number 24291cb0ef41Sopenharmony_ci // of headers and the session has memory available for it. 24301cb0ef41Sopenharmony_ci if (!session_->has_available_session_memory(length) || 24311cb0ef41Sopenharmony_ci current_headers_.size() == max_header_pairs_ || 24321cb0ef41Sopenharmony_ci current_headers_length_ + length > max_header_length_) { 24331cb0ef41Sopenharmony_ci return false; 24341cb0ef41Sopenharmony_ci } 24351cb0ef41Sopenharmony_ci 24361cb0ef41Sopenharmony_ci if (statistics_.first_header == 0) 24371cb0ef41Sopenharmony_ci statistics_.first_header = uv_hrtime(); 24381cb0ef41Sopenharmony_ci 24391cb0ef41Sopenharmony_ci current_headers_.push_back(std::move(header)); 24401cb0ef41Sopenharmony_ci 24411cb0ef41Sopenharmony_ci current_headers_length_ += length; 24421cb0ef41Sopenharmony_ci session_->IncrementCurrentSessionMemory(length); 24431cb0ef41Sopenharmony_ci return true; 24441cb0ef41Sopenharmony_ci} 24451cb0ef41Sopenharmony_ci 24461cb0ef41Sopenharmony_ci// A Provider is the thing that provides outbound DATA frame data. 24471cb0ef41Sopenharmony_ciHttp2Stream::Provider::Provider(Http2Stream* stream, int options) { 24481cb0ef41Sopenharmony_ci CHECK(!stream->is_destroyed()); 24491cb0ef41Sopenharmony_ci provider_.source.ptr = stream; 24501cb0ef41Sopenharmony_ci empty_ = options & STREAM_OPTION_EMPTY_PAYLOAD; 24511cb0ef41Sopenharmony_ci} 24521cb0ef41Sopenharmony_ci 24531cb0ef41Sopenharmony_ciHttp2Stream::Provider::Provider(int options) { 24541cb0ef41Sopenharmony_ci provider_.source.ptr = nullptr; 24551cb0ef41Sopenharmony_ci empty_ = options & STREAM_OPTION_EMPTY_PAYLOAD; 24561cb0ef41Sopenharmony_ci} 24571cb0ef41Sopenharmony_ci 24581cb0ef41Sopenharmony_ciHttp2Stream::Provider::~Provider() { 24591cb0ef41Sopenharmony_ci provider_.source.ptr = nullptr; 24601cb0ef41Sopenharmony_ci} 24611cb0ef41Sopenharmony_ci 24621cb0ef41Sopenharmony_ci// The Stream Provider pulls data from a linked list of uv_buf_t structs 24631cb0ef41Sopenharmony_ci// built via the StreamBase API and the Streams js API. 24641cb0ef41Sopenharmony_ciHttp2Stream::Provider::Stream::Stream(int options) 24651cb0ef41Sopenharmony_ci : Http2Stream::Provider(options) { 24661cb0ef41Sopenharmony_ci provider_.read_callback = Http2Stream::Provider::Stream::OnRead; 24671cb0ef41Sopenharmony_ci} 24681cb0ef41Sopenharmony_ci 24691cb0ef41Sopenharmony_ciHttp2Stream::Provider::Stream::Stream(Http2Stream* stream, int options) 24701cb0ef41Sopenharmony_ci : Http2Stream::Provider(stream, options) { 24711cb0ef41Sopenharmony_ci provider_.read_callback = Http2Stream::Provider::Stream::OnRead; 24721cb0ef41Sopenharmony_ci} 24731cb0ef41Sopenharmony_ci 24741cb0ef41Sopenharmony_cissize_t Http2Stream::Provider::Stream::OnRead(nghttp2_session* handle, 24751cb0ef41Sopenharmony_ci int32_t id, 24761cb0ef41Sopenharmony_ci uint8_t* buf, 24771cb0ef41Sopenharmony_ci size_t length, 24781cb0ef41Sopenharmony_ci uint32_t* flags, 24791cb0ef41Sopenharmony_ci nghttp2_data_source* source, 24801cb0ef41Sopenharmony_ci void* user_data) { 24811cb0ef41Sopenharmony_ci Http2Session* session = static_cast<Http2Session*>(user_data); 24821cb0ef41Sopenharmony_ci Debug(session, "reading outbound data for stream %d", id); 24831cb0ef41Sopenharmony_ci BaseObjectPtr<Http2Stream> stream = session->FindStream(id); 24841cb0ef41Sopenharmony_ci if (!stream) return 0; 24851cb0ef41Sopenharmony_ci if (stream->statistics_.first_byte_sent == 0) 24861cb0ef41Sopenharmony_ci stream->statistics_.first_byte_sent = uv_hrtime(); 24871cb0ef41Sopenharmony_ci CHECK_EQ(id, stream->id()); 24881cb0ef41Sopenharmony_ci 24891cb0ef41Sopenharmony_ci size_t amount = 0; // amount of data being sent in this data frame. 24901cb0ef41Sopenharmony_ci 24911cb0ef41Sopenharmony_ci // Remove all empty chunks from the head of the queue. 24921cb0ef41Sopenharmony_ci // This is done here so that .write('', cb) is still a meaningful way to 24931cb0ef41Sopenharmony_ci // find out when the HTTP2 stream wants to consume data, and because the 24941cb0ef41Sopenharmony_ci // StreamBase API allows empty input chunks. 24951cb0ef41Sopenharmony_ci while (!stream->queue_.empty() && stream->queue_.front().buf.len == 0) { 24961cb0ef41Sopenharmony_ci BaseObjectPtr<AsyncWrap> finished = 24971cb0ef41Sopenharmony_ci std::move(stream->queue_.front().req_wrap); 24981cb0ef41Sopenharmony_ci stream->queue_.pop(); 24991cb0ef41Sopenharmony_ci if (finished) 25001cb0ef41Sopenharmony_ci WriteWrap::FromObject(finished)->Done(0); 25011cb0ef41Sopenharmony_ci } 25021cb0ef41Sopenharmony_ci 25031cb0ef41Sopenharmony_ci if (!stream->queue_.empty()) { 25041cb0ef41Sopenharmony_ci Debug(session, "stream %d has pending outbound data", id); 25051cb0ef41Sopenharmony_ci amount = std::min(stream->available_outbound_length_, length); 25061cb0ef41Sopenharmony_ci Debug(session, "sending %d bytes for data frame on stream %d", amount, id); 25071cb0ef41Sopenharmony_ci if (amount > 0) { 25081cb0ef41Sopenharmony_ci // Just return the length, let Http2Session::OnSendData take care of 25091cb0ef41Sopenharmony_ci // actually taking the buffers out of the queue. 25101cb0ef41Sopenharmony_ci *flags |= NGHTTP2_DATA_FLAG_NO_COPY; 25111cb0ef41Sopenharmony_ci stream->DecrementAvailableOutboundLength(amount); 25121cb0ef41Sopenharmony_ci } 25131cb0ef41Sopenharmony_ci } 25141cb0ef41Sopenharmony_ci 25151cb0ef41Sopenharmony_ci if (amount == 0 && stream->is_writable()) { 25161cb0ef41Sopenharmony_ci CHECK(stream->queue_.empty()); 25171cb0ef41Sopenharmony_ci Debug(session, "deferring stream %d", id); 25181cb0ef41Sopenharmony_ci stream->EmitWantsWrite(length); 25191cb0ef41Sopenharmony_ci if (stream->available_outbound_length_ > 0 || !stream->is_writable()) { 25201cb0ef41Sopenharmony_ci // EmitWantsWrite() did something interesting synchronously, restart: 25211cb0ef41Sopenharmony_ci return OnRead(handle, id, buf, length, flags, source, user_data); 25221cb0ef41Sopenharmony_ci } 25231cb0ef41Sopenharmony_ci return NGHTTP2_ERR_DEFERRED; 25241cb0ef41Sopenharmony_ci } 25251cb0ef41Sopenharmony_ci 25261cb0ef41Sopenharmony_ci if (stream->available_outbound_length_ == 0 && !stream->is_writable()) { 25271cb0ef41Sopenharmony_ci Debug(session, "no more data for stream %d", id); 25281cb0ef41Sopenharmony_ci *flags |= NGHTTP2_DATA_FLAG_EOF; 25291cb0ef41Sopenharmony_ci if (stream->has_trailers()) { 25301cb0ef41Sopenharmony_ci *flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM; 25311cb0ef41Sopenharmony_ci stream->OnTrailers(); 25321cb0ef41Sopenharmony_ci } 25331cb0ef41Sopenharmony_ci } 25341cb0ef41Sopenharmony_ci 25351cb0ef41Sopenharmony_ci stream->statistics_.sent_bytes += amount; 25361cb0ef41Sopenharmony_ci return amount; 25371cb0ef41Sopenharmony_ci} 25381cb0ef41Sopenharmony_ci 25391cb0ef41Sopenharmony_civoid Http2Stream::IncrementAvailableOutboundLength(size_t amount) { 25401cb0ef41Sopenharmony_ci available_outbound_length_ += amount; 25411cb0ef41Sopenharmony_ci session_->IncrementCurrentSessionMemory(amount); 25421cb0ef41Sopenharmony_ci} 25431cb0ef41Sopenharmony_ci 25441cb0ef41Sopenharmony_civoid Http2Stream::DecrementAvailableOutboundLength(size_t amount) { 25451cb0ef41Sopenharmony_ci available_outbound_length_ -= amount; 25461cb0ef41Sopenharmony_ci session_->DecrementCurrentSessionMemory(amount); 25471cb0ef41Sopenharmony_ci} 25481cb0ef41Sopenharmony_ci 25491cb0ef41Sopenharmony_ci 25501cb0ef41Sopenharmony_ci// Implementation of the JavaScript API 25511cb0ef41Sopenharmony_ci 25521cb0ef41Sopenharmony_ci// Fetches the string description of a nghttp2 error code and passes that 25531cb0ef41Sopenharmony_ci// back to JS land 25541cb0ef41Sopenharmony_civoid HttpErrorString(const FunctionCallbackInfo<Value>& args) { 25551cb0ef41Sopenharmony_ci Environment* env = Environment::GetCurrent(args); 25561cb0ef41Sopenharmony_ci uint32_t val = args[0]->Uint32Value(env->context()).ToChecked(); 25571cb0ef41Sopenharmony_ci args.GetReturnValue().Set( 25581cb0ef41Sopenharmony_ci OneByteString( 25591cb0ef41Sopenharmony_ci env->isolate(), 25601cb0ef41Sopenharmony_ci reinterpret_cast<const uint8_t*>(nghttp2_strerror(val)))); 25611cb0ef41Sopenharmony_ci} 25621cb0ef41Sopenharmony_ci 25631cb0ef41Sopenharmony_ci 25641cb0ef41Sopenharmony_ci// Serializes the settings object into a Buffer instance that 25651cb0ef41Sopenharmony_ci// would be suitable, for instance, for creating the Base64 25661cb0ef41Sopenharmony_ci// output for an HTTP2-Settings header field. 25671cb0ef41Sopenharmony_civoid PackSettings(const FunctionCallbackInfo<Value>& args) { 25681cb0ef41Sopenharmony_ci Http2State* state = Realm::GetBindingData<Http2State>(args); 25691cb0ef41Sopenharmony_ci args.GetReturnValue().Set(Http2Settings::Pack(state)); 25701cb0ef41Sopenharmony_ci} 25711cb0ef41Sopenharmony_ci 25721cb0ef41Sopenharmony_ci// A TypedArray instance is shared between C++ and JS land to contain the 25731cb0ef41Sopenharmony_ci// default SETTINGS. RefreshDefaultSettings updates that TypedArray with the 25741cb0ef41Sopenharmony_ci// default values. 25751cb0ef41Sopenharmony_civoid RefreshDefaultSettings(const FunctionCallbackInfo<Value>& args) { 25761cb0ef41Sopenharmony_ci Http2State* state = Realm::GetBindingData<Http2State>(args); 25771cb0ef41Sopenharmony_ci Http2Settings::RefreshDefaults(state); 25781cb0ef41Sopenharmony_ci} 25791cb0ef41Sopenharmony_ci 25801cb0ef41Sopenharmony_ci// Sets the next stream ID the Http2Session. If successful, returns true. 25811cb0ef41Sopenharmony_civoid Http2Session::SetNextStreamID(const FunctionCallbackInfo<Value>& args) { 25821cb0ef41Sopenharmony_ci Environment* env = Environment::GetCurrent(args); 25831cb0ef41Sopenharmony_ci Http2Session* session; 25841cb0ef41Sopenharmony_ci ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); 25851cb0ef41Sopenharmony_ci int32_t id = args[0]->Int32Value(env->context()).ToChecked(); 25861cb0ef41Sopenharmony_ci if (nghttp2_session_set_next_stream_id(session->session(), id) < 0) { 25871cb0ef41Sopenharmony_ci Debug(session, "failed to set next stream id to %d", id); 25881cb0ef41Sopenharmony_ci return args.GetReturnValue().Set(false); 25891cb0ef41Sopenharmony_ci } 25901cb0ef41Sopenharmony_ci args.GetReturnValue().Set(true); 25911cb0ef41Sopenharmony_ci Debug(session, "set next stream id to %d", id); 25921cb0ef41Sopenharmony_ci} 25931cb0ef41Sopenharmony_ci 25941cb0ef41Sopenharmony_ci// Set local window size (local endpoints's window size) to the given 25951cb0ef41Sopenharmony_ci// window_size for the stream denoted by 0. 25961cb0ef41Sopenharmony_ci// This function returns 0 if it succeeds, or one of a negative codes 25971cb0ef41Sopenharmony_civoid Http2Session::SetLocalWindowSize( 25981cb0ef41Sopenharmony_ci const FunctionCallbackInfo<Value>& args) { 25991cb0ef41Sopenharmony_ci Environment* env = Environment::GetCurrent(args); 26001cb0ef41Sopenharmony_ci Http2Session* session; 26011cb0ef41Sopenharmony_ci ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); 26021cb0ef41Sopenharmony_ci 26031cb0ef41Sopenharmony_ci int32_t window_size = args[0]->Int32Value(env->context()).ToChecked(); 26041cb0ef41Sopenharmony_ci 26051cb0ef41Sopenharmony_ci int result = nghttp2_session_set_local_window_size( 26061cb0ef41Sopenharmony_ci session->session(), NGHTTP2_FLAG_NONE, 0, window_size); 26071cb0ef41Sopenharmony_ci 26081cb0ef41Sopenharmony_ci args.GetReturnValue().Set(result); 26091cb0ef41Sopenharmony_ci 26101cb0ef41Sopenharmony_ci Debug(session, "set local window size to %d", window_size); 26111cb0ef41Sopenharmony_ci} 26121cb0ef41Sopenharmony_ci 26131cb0ef41Sopenharmony_ci// A TypedArray instance is shared between C++ and JS land to contain the 26141cb0ef41Sopenharmony_ci// SETTINGS (either remote or local). RefreshSettings updates the current 26151cb0ef41Sopenharmony_ci// values established for each of the settings so those can be read in JS land. 26161cb0ef41Sopenharmony_citemplate <get_setting fn> 26171cb0ef41Sopenharmony_civoid Http2Session::RefreshSettings(const FunctionCallbackInfo<Value>& args) { 26181cb0ef41Sopenharmony_ci Http2Session* session; 26191cb0ef41Sopenharmony_ci ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); 26201cb0ef41Sopenharmony_ci Http2Settings::Update(session, fn); 26211cb0ef41Sopenharmony_ci Debug(session, "settings refreshed for session"); 26221cb0ef41Sopenharmony_ci} 26231cb0ef41Sopenharmony_ci 26241cb0ef41Sopenharmony_ci// A TypedArray instance is shared between C++ and JS land to contain state 26251cb0ef41Sopenharmony_ci// information of the current Http2Session. This updates the values in the 26261cb0ef41Sopenharmony_ci// TypedArray so those can be read in JS land. 26271cb0ef41Sopenharmony_civoid Http2Session::RefreshState(const FunctionCallbackInfo<Value>& args) { 26281cb0ef41Sopenharmony_ci Http2Session* session; 26291cb0ef41Sopenharmony_ci ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); 26301cb0ef41Sopenharmony_ci Debug(session, "refreshing state"); 26311cb0ef41Sopenharmony_ci 26321cb0ef41Sopenharmony_ci AliasedFloat64Array& buffer = session->http2_state()->session_state_buffer; 26331cb0ef41Sopenharmony_ci 26341cb0ef41Sopenharmony_ci nghttp2_session* s = session->session(); 26351cb0ef41Sopenharmony_ci 26361cb0ef41Sopenharmony_ci buffer[IDX_SESSION_STATE_EFFECTIVE_LOCAL_WINDOW_SIZE] = 26371cb0ef41Sopenharmony_ci nghttp2_session_get_effective_local_window_size(s); 26381cb0ef41Sopenharmony_ci buffer[IDX_SESSION_STATE_EFFECTIVE_RECV_DATA_LENGTH] = 26391cb0ef41Sopenharmony_ci nghttp2_session_get_effective_recv_data_length(s); 26401cb0ef41Sopenharmony_ci buffer[IDX_SESSION_STATE_NEXT_STREAM_ID] = 26411cb0ef41Sopenharmony_ci nghttp2_session_get_next_stream_id(s); 26421cb0ef41Sopenharmony_ci buffer[IDX_SESSION_STATE_LOCAL_WINDOW_SIZE] = 26431cb0ef41Sopenharmony_ci nghttp2_session_get_local_window_size(s); 26441cb0ef41Sopenharmony_ci buffer[IDX_SESSION_STATE_LAST_PROC_STREAM_ID] = 26451cb0ef41Sopenharmony_ci nghttp2_session_get_last_proc_stream_id(s); 26461cb0ef41Sopenharmony_ci buffer[IDX_SESSION_STATE_REMOTE_WINDOW_SIZE] = 26471cb0ef41Sopenharmony_ci nghttp2_session_get_remote_window_size(s); 26481cb0ef41Sopenharmony_ci buffer[IDX_SESSION_STATE_OUTBOUND_QUEUE_SIZE] = 26491cb0ef41Sopenharmony_ci static_cast<double>(nghttp2_session_get_outbound_queue_size(s)); 26501cb0ef41Sopenharmony_ci buffer[IDX_SESSION_STATE_HD_DEFLATE_DYNAMIC_TABLE_SIZE] = 26511cb0ef41Sopenharmony_ci static_cast<double>(nghttp2_session_get_hd_deflate_dynamic_table_size(s)); 26521cb0ef41Sopenharmony_ci buffer[IDX_SESSION_STATE_HD_INFLATE_DYNAMIC_TABLE_SIZE] = 26531cb0ef41Sopenharmony_ci static_cast<double>(nghttp2_session_get_hd_inflate_dynamic_table_size(s)); 26541cb0ef41Sopenharmony_ci} 26551cb0ef41Sopenharmony_ci 26561cb0ef41Sopenharmony_ci 26571cb0ef41Sopenharmony_ci// Constructor for new Http2Session instances. 26581cb0ef41Sopenharmony_civoid Http2Session::New(const FunctionCallbackInfo<Value>& args) { 26591cb0ef41Sopenharmony_ci Http2State* state = Realm::GetBindingData<Http2State>(args); 26601cb0ef41Sopenharmony_ci Environment* env = state->env(); 26611cb0ef41Sopenharmony_ci CHECK(args.IsConstructCall()); 26621cb0ef41Sopenharmony_ci SessionType type = 26631cb0ef41Sopenharmony_ci static_cast<SessionType>( 26641cb0ef41Sopenharmony_ci args[0]->Int32Value(env->context()).ToChecked()); 26651cb0ef41Sopenharmony_ci Http2Session* session = new Http2Session(state, args.This(), type); 26661cb0ef41Sopenharmony_ci Debug(session, "session created"); 26671cb0ef41Sopenharmony_ci} 26681cb0ef41Sopenharmony_ci 26691cb0ef41Sopenharmony_ci 26701cb0ef41Sopenharmony_ci// Binds the Http2Session with a StreamBase used for i/o 26711cb0ef41Sopenharmony_civoid Http2Session::Consume(const FunctionCallbackInfo<Value>& args) { 26721cb0ef41Sopenharmony_ci Http2Session* session; 26731cb0ef41Sopenharmony_ci ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); 26741cb0ef41Sopenharmony_ci CHECK(args[0]->IsObject()); 26751cb0ef41Sopenharmony_ci session->Consume(args[0].As<Object>()); 26761cb0ef41Sopenharmony_ci} 26771cb0ef41Sopenharmony_ci 26781cb0ef41Sopenharmony_ci// Destroys the Http2Session instance and renders it unusable 26791cb0ef41Sopenharmony_civoid Http2Session::Destroy(const FunctionCallbackInfo<Value>& args) { 26801cb0ef41Sopenharmony_ci Http2Session* session; 26811cb0ef41Sopenharmony_ci ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); 26821cb0ef41Sopenharmony_ci Debug(session, "destroying session"); 26831cb0ef41Sopenharmony_ci Environment* env = Environment::GetCurrent(args); 26841cb0ef41Sopenharmony_ci Local<Context> context = env->context(); 26851cb0ef41Sopenharmony_ci 26861cb0ef41Sopenharmony_ci uint32_t code = args[0]->Uint32Value(context).ToChecked(); 26871cb0ef41Sopenharmony_ci session->Close(code, args[1]->IsTrue()); 26881cb0ef41Sopenharmony_ci} 26891cb0ef41Sopenharmony_ci 26901cb0ef41Sopenharmony_ci// Submits a new request on the Http2Session and returns either an error code 26911cb0ef41Sopenharmony_ci// or the Http2Stream object. 26921cb0ef41Sopenharmony_civoid Http2Session::Request(const FunctionCallbackInfo<Value>& args) { 26931cb0ef41Sopenharmony_ci Http2Session* session; 26941cb0ef41Sopenharmony_ci ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); 26951cb0ef41Sopenharmony_ci Environment* env = session->env(); 26961cb0ef41Sopenharmony_ci 26971cb0ef41Sopenharmony_ci Local<Array> headers = args[0].As<Array>(); 26981cb0ef41Sopenharmony_ci int32_t options = args[1]->Int32Value(env->context()).ToChecked(); 26991cb0ef41Sopenharmony_ci 27001cb0ef41Sopenharmony_ci Debug(session, "request submitted"); 27011cb0ef41Sopenharmony_ci 27021cb0ef41Sopenharmony_ci int32_t ret = 0; 27031cb0ef41Sopenharmony_ci Http2Stream* stream = 27041cb0ef41Sopenharmony_ci session->Http2Session::SubmitRequest( 27051cb0ef41Sopenharmony_ci Http2Priority(env, args[2], args[3], args[4]), 27061cb0ef41Sopenharmony_ci Http2Headers(env, headers), 27071cb0ef41Sopenharmony_ci &ret, 27081cb0ef41Sopenharmony_ci static_cast<int>(options)); 27091cb0ef41Sopenharmony_ci 27101cb0ef41Sopenharmony_ci if (ret <= 0 || stream == nullptr) { 27111cb0ef41Sopenharmony_ci Debug(session, "could not submit request: %s", nghttp2_strerror(ret)); 27121cb0ef41Sopenharmony_ci return args.GetReturnValue().Set(ret); 27131cb0ef41Sopenharmony_ci } 27141cb0ef41Sopenharmony_ci 27151cb0ef41Sopenharmony_ci Debug(session, "request submitted, new stream id %d", stream->id()); 27161cb0ef41Sopenharmony_ci args.GetReturnValue().Set(stream->object()); 27171cb0ef41Sopenharmony_ci} 27181cb0ef41Sopenharmony_ci 27191cb0ef41Sopenharmony_ci// Submits a GOAWAY frame to signal that the Http2Session is in the process 27201cb0ef41Sopenharmony_ci// of shutting down. Note that this function does not actually alter the 27211cb0ef41Sopenharmony_ci// state of the Http2Session, it's simply a notification. 27221cb0ef41Sopenharmony_civoid Http2Session::Goaway(uint32_t code, 27231cb0ef41Sopenharmony_ci int32_t lastStreamID, 27241cb0ef41Sopenharmony_ci const uint8_t* data, 27251cb0ef41Sopenharmony_ci size_t len) { 27261cb0ef41Sopenharmony_ci if (is_destroyed()) 27271cb0ef41Sopenharmony_ci return; 27281cb0ef41Sopenharmony_ci 27291cb0ef41Sopenharmony_ci Http2Scope h2scope(this); 27301cb0ef41Sopenharmony_ci // the last proc stream id is the most recently created Http2Stream. 27311cb0ef41Sopenharmony_ci if (lastStreamID <= 0) 27321cb0ef41Sopenharmony_ci lastStreamID = nghttp2_session_get_last_proc_stream_id(session_.get()); 27331cb0ef41Sopenharmony_ci Debug(this, "submitting goaway"); 27341cb0ef41Sopenharmony_ci nghttp2_submit_goaway(session_.get(), NGHTTP2_FLAG_NONE, 27351cb0ef41Sopenharmony_ci lastStreamID, code, data, len); 27361cb0ef41Sopenharmony_ci} 27371cb0ef41Sopenharmony_ci 27381cb0ef41Sopenharmony_ci// Submits a GOAWAY frame to signal that the Http2Session is in the process 27391cb0ef41Sopenharmony_ci// of shutting down. The opaque data argument is an optional TypedArray that 27401cb0ef41Sopenharmony_ci// can be used to send debugging data to the connected peer. 27411cb0ef41Sopenharmony_civoid Http2Session::Goaway(const FunctionCallbackInfo<Value>& args) { 27421cb0ef41Sopenharmony_ci Environment* env = Environment::GetCurrent(args); 27431cb0ef41Sopenharmony_ci Local<Context> context = env->context(); 27441cb0ef41Sopenharmony_ci Http2Session* session; 27451cb0ef41Sopenharmony_ci ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); 27461cb0ef41Sopenharmony_ci 27471cb0ef41Sopenharmony_ci uint32_t code = args[0]->Uint32Value(context).ToChecked(); 27481cb0ef41Sopenharmony_ci int32_t lastStreamID = args[1]->Int32Value(context).ToChecked(); 27491cb0ef41Sopenharmony_ci ArrayBufferViewContents<uint8_t> opaque_data; 27501cb0ef41Sopenharmony_ci 27511cb0ef41Sopenharmony_ci if (args[2]->IsArrayBufferView()) { 27521cb0ef41Sopenharmony_ci opaque_data.Read(args[2].As<ArrayBufferView>()); 27531cb0ef41Sopenharmony_ci } 27541cb0ef41Sopenharmony_ci 27551cb0ef41Sopenharmony_ci session->Goaway(code, lastStreamID, opaque_data.data(), opaque_data.length()); 27561cb0ef41Sopenharmony_ci} 27571cb0ef41Sopenharmony_ci 27581cb0ef41Sopenharmony_ci// Update accounting of data chunks. This is used primarily to manage timeout 27591cb0ef41Sopenharmony_ci// logic when using the FD Provider. 27601cb0ef41Sopenharmony_civoid Http2Session::UpdateChunksSent(const FunctionCallbackInfo<Value>& args) { 27611cb0ef41Sopenharmony_ci Environment* env = Environment::GetCurrent(args); 27621cb0ef41Sopenharmony_ci Isolate* isolate = env->isolate(); 27631cb0ef41Sopenharmony_ci HandleScope scope(isolate); 27641cb0ef41Sopenharmony_ci Http2Session* session; 27651cb0ef41Sopenharmony_ci ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); 27661cb0ef41Sopenharmony_ci 27671cb0ef41Sopenharmony_ci uint32_t length = session->chunks_sent_since_last_write_; 27681cb0ef41Sopenharmony_ci 27691cb0ef41Sopenharmony_ci session->object()->Set(env->context(), 27701cb0ef41Sopenharmony_ci env->chunks_sent_since_last_write_string(), 27711cb0ef41Sopenharmony_ci Integer::NewFromUnsigned(isolate, length)).Check(); 27721cb0ef41Sopenharmony_ci 27731cb0ef41Sopenharmony_ci args.GetReturnValue().Set(length); 27741cb0ef41Sopenharmony_ci} 27751cb0ef41Sopenharmony_ci 27761cb0ef41Sopenharmony_ci// Submits an RST_STREAM frame effectively closing the Http2Stream. Note that 27771cb0ef41Sopenharmony_ci// this *WILL* alter the state of the stream, causing the OnStreamClose 27781cb0ef41Sopenharmony_ci// callback to the triggered. 27791cb0ef41Sopenharmony_civoid Http2Stream::RstStream(const FunctionCallbackInfo<Value>& args) { 27801cb0ef41Sopenharmony_ci Environment* env = Environment::GetCurrent(args); 27811cb0ef41Sopenharmony_ci Local<Context> context = env->context(); 27821cb0ef41Sopenharmony_ci Http2Stream* stream; 27831cb0ef41Sopenharmony_ci ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder()); 27841cb0ef41Sopenharmony_ci uint32_t code = args[0]->Uint32Value(context).ToChecked(); 27851cb0ef41Sopenharmony_ci Debug(stream, "sending rst_stream with code %d", code); 27861cb0ef41Sopenharmony_ci stream->SubmitRstStream(code); 27871cb0ef41Sopenharmony_ci} 27881cb0ef41Sopenharmony_ci 27891cb0ef41Sopenharmony_ci// Initiates a response on the Http2Stream using the StreamBase API to provide 27901cb0ef41Sopenharmony_ci// outbound DATA frames. 27911cb0ef41Sopenharmony_civoid Http2Stream::Respond(const FunctionCallbackInfo<Value>& args) { 27921cb0ef41Sopenharmony_ci Environment* env = Environment::GetCurrent(args); 27931cb0ef41Sopenharmony_ci Http2Stream* stream; 27941cb0ef41Sopenharmony_ci ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder()); 27951cb0ef41Sopenharmony_ci 27961cb0ef41Sopenharmony_ci Local<Array> headers = args[0].As<Array>(); 27971cb0ef41Sopenharmony_ci int32_t options = args[1]->Int32Value(env->context()).ToChecked(); 27981cb0ef41Sopenharmony_ci 27991cb0ef41Sopenharmony_ci args.GetReturnValue().Set( 28001cb0ef41Sopenharmony_ci stream->SubmitResponse( 28011cb0ef41Sopenharmony_ci Http2Headers(env, headers), 28021cb0ef41Sopenharmony_ci static_cast<int>(options))); 28031cb0ef41Sopenharmony_ci Debug(stream, "response submitted"); 28041cb0ef41Sopenharmony_ci} 28051cb0ef41Sopenharmony_ci 28061cb0ef41Sopenharmony_ci 28071cb0ef41Sopenharmony_ci// Submits informational headers on the Http2Stream 28081cb0ef41Sopenharmony_civoid Http2Stream::Info(const FunctionCallbackInfo<Value>& args) { 28091cb0ef41Sopenharmony_ci Environment* env = Environment::GetCurrent(args); 28101cb0ef41Sopenharmony_ci Http2Stream* stream; 28111cb0ef41Sopenharmony_ci ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder()); 28121cb0ef41Sopenharmony_ci 28131cb0ef41Sopenharmony_ci Local<Array> headers = args[0].As<Array>(); 28141cb0ef41Sopenharmony_ci 28151cb0ef41Sopenharmony_ci args.GetReturnValue().Set(stream->SubmitInfo(Http2Headers(env, headers))); 28161cb0ef41Sopenharmony_ci} 28171cb0ef41Sopenharmony_ci 28181cb0ef41Sopenharmony_ci// Submits trailing headers on the Http2Stream 28191cb0ef41Sopenharmony_civoid Http2Stream::Trailers(const FunctionCallbackInfo<Value>& args) { 28201cb0ef41Sopenharmony_ci Environment* env = Environment::GetCurrent(args); 28211cb0ef41Sopenharmony_ci Http2Stream* stream; 28221cb0ef41Sopenharmony_ci ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder()); 28231cb0ef41Sopenharmony_ci 28241cb0ef41Sopenharmony_ci Local<Array> headers = args[0].As<Array>(); 28251cb0ef41Sopenharmony_ci 28261cb0ef41Sopenharmony_ci args.GetReturnValue().Set( 28271cb0ef41Sopenharmony_ci stream->SubmitTrailers(Http2Headers(env, headers))); 28281cb0ef41Sopenharmony_ci} 28291cb0ef41Sopenharmony_ci 28301cb0ef41Sopenharmony_ci// Grab the numeric id of the Http2Stream 28311cb0ef41Sopenharmony_civoid Http2Stream::GetID(const FunctionCallbackInfo<Value>& args) { 28321cb0ef41Sopenharmony_ci Http2Stream* stream; 28331cb0ef41Sopenharmony_ci ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder()); 28341cb0ef41Sopenharmony_ci args.GetReturnValue().Set(stream->id()); 28351cb0ef41Sopenharmony_ci} 28361cb0ef41Sopenharmony_ci 28371cb0ef41Sopenharmony_ci// Destroy the Http2Stream, rendering it no longer usable 28381cb0ef41Sopenharmony_civoid Http2Stream::Destroy(const FunctionCallbackInfo<Value>& args) { 28391cb0ef41Sopenharmony_ci Http2Stream* stream; 28401cb0ef41Sopenharmony_ci ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder()); 28411cb0ef41Sopenharmony_ci Debug(stream, "destroying stream"); 28421cb0ef41Sopenharmony_ci stream->Destroy(); 28431cb0ef41Sopenharmony_ci} 28441cb0ef41Sopenharmony_ci 28451cb0ef41Sopenharmony_ci// Initiate a Push Promise and create the associated Http2Stream 28461cb0ef41Sopenharmony_civoid Http2Stream::PushPromise(const FunctionCallbackInfo<Value>& args) { 28471cb0ef41Sopenharmony_ci Environment* env = Environment::GetCurrent(args); 28481cb0ef41Sopenharmony_ci Http2Stream* parent; 28491cb0ef41Sopenharmony_ci ASSIGN_OR_RETURN_UNWRAP(&parent, args.Holder()); 28501cb0ef41Sopenharmony_ci 28511cb0ef41Sopenharmony_ci Local<Array> headers = args[0].As<Array>(); 28521cb0ef41Sopenharmony_ci int32_t options = args[1]->Int32Value(env->context()).ToChecked(); 28531cb0ef41Sopenharmony_ci 28541cb0ef41Sopenharmony_ci Debug(parent, "creating push promise"); 28551cb0ef41Sopenharmony_ci 28561cb0ef41Sopenharmony_ci int32_t ret = 0; 28571cb0ef41Sopenharmony_ci Http2Stream* stream = 28581cb0ef41Sopenharmony_ci parent->SubmitPushPromise( 28591cb0ef41Sopenharmony_ci Http2Headers(env, headers), 28601cb0ef41Sopenharmony_ci &ret, 28611cb0ef41Sopenharmony_ci static_cast<int>(options)); 28621cb0ef41Sopenharmony_ci 28631cb0ef41Sopenharmony_ci if (ret <= 0 || stream == nullptr) { 28641cb0ef41Sopenharmony_ci Debug(parent, "failed to create push stream: %d", ret); 28651cb0ef41Sopenharmony_ci return args.GetReturnValue().Set(ret); 28661cb0ef41Sopenharmony_ci } 28671cb0ef41Sopenharmony_ci Debug(parent, "push stream %d created", stream->id()); 28681cb0ef41Sopenharmony_ci args.GetReturnValue().Set(stream->object()); 28691cb0ef41Sopenharmony_ci} 28701cb0ef41Sopenharmony_ci 28711cb0ef41Sopenharmony_ci// Send a PRIORITY frame 28721cb0ef41Sopenharmony_civoid Http2Stream::Priority(const FunctionCallbackInfo<Value>& args) { 28731cb0ef41Sopenharmony_ci Environment* env = Environment::GetCurrent(args); 28741cb0ef41Sopenharmony_ci Http2Stream* stream; 28751cb0ef41Sopenharmony_ci ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder()); 28761cb0ef41Sopenharmony_ci 28771cb0ef41Sopenharmony_ci CHECK_EQ(stream->SubmitPriority( 28781cb0ef41Sopenharmony_ci Http2Priority(env, args[0], args[1], args[2]), 28791cb0ef41Sopenharmony_ci args[3]->IsTrue()), 0); 28801cb0ef41Sopenharmony_ci Debug(stream, "priority submitted"); 28811cb0ef41Sopenharmony_ci} 28821cb0ef41Sopenharmony_ci 28831cb0ef41Sopenharmony_ci// A TypedArray shared by C++ and JS land is used to communicate state 28841cb0ef41Sopenharmony_ci// information about the Http2Stream. This updates the values in that 28851cb0ef41Sopenharmony_ci// TypedArray so that the state can be read by JS. 28861cb0ef41Sopenharmony_civoid Http2Stream::RefreshState(const FunctionCallbackInfo<Value>& args) { 28871cb0ef41Sopenharmony_ci Http2Stream* stream; 28881cb0ef41Sopenharmony_ci ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder()); 28891cb0ef41Sopenharmony_ci 28901cb0ef41Sopenharmony_ci Debug(stream, "refreshing state"); 28911cb0ef41Sopenharmony_ci 28921cb0ef41Sopenharmony_ci CHECK_NOT_NULL(stream->session()); 28931cb0ef41Sopenharmony_ci AliasedFloat64Array& buffer = 28941cb0ef41Sopenharmony_ci stream->session()->http2_state()->stream_state_buffer; 28951cb0ef41Sopenharmony_ci 28961cb0ef41Sopenharmony_ci nghttp2_stream* str = stream->stream(); 28971cb0ef41Sopenharmony_ci nghttp2_session* s = stream->session()->session(); 28981cb0ef41Sopenharmony_ci 28991cb0ef41Sopenharmony_ci if (str == nullptr) { 29001cb0ef41Sopenharmony_ci buffer[IDX_STREAM_STATE] = NGHTTP2_STREAM_STATE_IDLE; 29011cb0ef41Sopenharmony_ci buffer[IDX_STREAM_STATE_WEIGHT] = 29021cb0ef41Sopenharmony_ci buffer[IDX_STREAM_STATE_SUM_DEPENDENCY_WEIGHT] = 29031cb0ef41Sopenharmony_ci buffer[IDX_STREAM_STATE_LOCAL_CLOSE] = 29041cb0ef41Sopenharmony_ci buffer[IDX_STREAM_STATE_REMOTE_CLOSE] = 29051cb0ef41Sopenharmony_ci buffer[IDX_STREAM_STATE_LOCAL_WINDOW_SIZE] = 0; 29061cb0ef41Sopenharmony_ci } else { 29071cb0ef41Sopenharmony_ci buffer[IDX_STREAM_STATE] = 29081cb0ef41Sopenharmony_ci nghttp2_stream_get_state(str); 29091cb0ef41Sopenharmony_ci buffer[IDX_STREAM_STATE_WEIGHT] = 29101cb0ef41Sopenharmony_ci nghttp2_stream_get_weight(str); 29111cb0ef41Sopenharmony_ci buffer[IDX_STREAM_STATE_SUM_DEPENDENCY_WEIGHT] = 29121cb0ef41Sopenharmony_ci nghttp2_stream_get_sum_dependency_weight(str); 29131cb0ef41Sopenharmony_ci buffer[IDX_STREAM_STATE_LOCAL_CLOSE] = 29141cb0ef41Sopenharmony_ci nghttp2_session_get_stream_local_close(s, stream->id()); 29151cb0ef41Sopenharmony_ci buffer[IDX_STREAM_STATE_REMOTE_CLOSE] = 29161cb0ef41Sopenharmony_ci nghttp2_session_get_stream_remote_close(s, stream->id()); 29171cb0ef41Sopenharmony_ci buffer[IDX_STREAM_STATE_LOCAL_WINDOW_SIZE] = 29181cb0ef41Sopenharmony_ci nghttp2_session_get_stream_local_window_size(s, stream->id()); 29191cb0ef41Sopenharmony_ci } 29201cb0ef41Sopenharmony_ci} 29211cb0ef41Sopenharmony_ci 29221cb0ef41Sopenharmony_civoid Http2Session::AltSvc(int32_t id, 29231cb0ef41Sopenharmony_ci uint8_t* origin, 29241cb0ef41Sopenharmony_ci size_t origin_len, 29251cb0ef41Sopenharmony_ci uint8_t* value, 29261cb0ef41Sopenharmony_ci size_t value_len) { 29271cb0ef41Sopenharmony_ci Http2Scope h2scope(this); 29281cb0ef41Sopenharmony_ci CHECK_EQ(nghttp2_submit_altsvc(session_.get(), NGHTTP2_FLAG_NONE, id, 29291cb0ef41Sopenharmony_ci origin, origin_len, value, value_len), 0); 29301cb0ef41Sopenharmony_ci} 29311cb0ef41Sopenharmony_ci 29321cb0ef41Sopenharmony_civoid Http2Session::Origin(const Origins& origins) { 29331cb0ef41Sopenharmony_ci Http2Scope h2scope(this); 29341cb0ef41Sopenharmony_ci CHECK_EQ(nghttp2_submit_origin( 29351cb0ef41Sopenharmony_ci session_.get(), 29361cb0ef41Sopenharmony_ci NGHTTP2_FLAG_NONE, 29371cb0ef41Sopenharmony_ci *origins, 29381cb0ef41Sopenharmony_ci origins.length()), 0); 29391cb0ef41Sopenharmony_ci} 29401cb0ef41Sopenharmony_ci 29411cb0ef41Sopenharmony_ci// Submits an AltSvc frame to be sent to the connected peer. 29421cb0ef41Sopenharmony_civoid Http2Session::AltSvc(const FunctionCallbackInfo<Value>& args) { 29431cb0ef41Sopenharmony_ci Environment* env = Environment::GetCurrent(args); 29441cb0ef41Sopenharmony_ci Http2Session* session; 29451cb0ef41Sopenharmony_ci ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); 29461cb0ef41Sopenharmony_ci 29471cb0ef41Sopenharmony_ci int32_t id = args[0]->Int32Value(env->context()).ToChecked(); 29481cb0ef41Sopenharmony_ci 29491cb0ef41Sopenharmony_ci // origin and value are both required to be ASCII, handle them as such. 29501cb0ef41Sopenharmony_ci Local<String> origin_str = args[1]->ToString(env->context()).ToLocalChecked(); 29511cb0ef41Sopenharmony_ci Local<String> value_str = args[2]->ToString(env->context()).ToLocalChecked(); 29521cb0ef41Sopenharmony_ci 29531cb0ef41Sopenharmony_ci if (origin_str.IsEmpty() || value_str.IsEmpty()) 29541cb0ef41Sopenharmony_ci return; 29551cb0ef41Sopenharmony_ci 29561cb0ef41Sopenharmony_ci size_t origin_len = origin_str->Length(); 29571cb0ef41Sopenharmony_ci size_t value_len = value_str->Length(); 29581cb0ef41Sopenharmony_ci 29591cb0ef41Sopenharmony_ci CHECK_LE(origin_len + value_len, 16382); // Max permitted for ALTSVC 29601cb0ef41Sopenharmony_ci // Verify that origin len != 0 if stream id == 0, or 29611cb0ef41Sopenharmony_ci // that origin len == 0 if stream id != 0 29621cb0ef41Sopenharmony_ci CHECK((origin_len != 0 && id == 0) || (origin_len == 0 && id != 0)); 29631cb0ef41Sopenharmony_ci 29641cb0ef41Sopenharmony_ci MaybeStackBuffer<uint8_t> origin(origin_len); 29651cb0ef41Sopenharmony_ci MaybeStackBuffer<uint8_t> value(value_len); 29661cb0ef41Sopenharmony_ci origin_str->WriteOneByte(env->isolate(), *origin); 29671cb0ef41Sopenharmony_ci value_str->WriteOneByte(env->isolate(), *value); 29681cb0ef41Sopenharmony_ci 29691cb0ef41Sopenharmony_ci session->AltSvc(id, *origin, origin_len, *value, value_len); 29701cb0ef41Sopenharmony_ci} 29711cb0ef41Sopenharmony_ci 29721cb0ef41Sopenharmony_civoid Http2Session::Origin(const FunctionCallbackInfo<Value>& args) { 29731cb0ef41Sopenharmony_ci Environment* env = Environment::GetCurrent(args); 29741cb0ef41Sopenharmony_ci Local<Context> context = env->context(); 29751cb0ef41Sopenharmony_ci Http2Session* session; 29761cb0ef41Sopenharmony_ci ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); 29771cb0ef41Sopenharmony_ci 29781cb0ef41Sopenharmony_ci Local<String> origin_string = args[0].As<String>(); 29791cb0ef41Sopenharmony_ci size_t count = args[1]->Int32Value(context).ToChecked(); 29801cb0ef41Sopenharmony_ci 29811cb0ef41Sopenharmony_ci session->Origin(Origins(env, origin_string, count)); 29821cb0ef41Sopenharmony_ci} 29831cb0ef41Sopenharmony_ci 29841cb0ef41Sopenharmony_ci// Submits a PING frame to be sent to the connected peer. 29851cb0ef41Sopenharmony_civoid Http2Session::Ping(const FunctionCallbackInfo<Value>& args) { 29861cb0ef41Sopenharmony_ci Http2Session* session; 29871cb0ef41Sopenharmony_ci ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); 29881cb0ef41Sopenharmony_ci 29891cb0ef41Sopenharmony_ci // A PING frame may have exactly 8 bytes of payload data. If not provided, 29901cb0ef41Sopenharmony_ci // then the current hrtime will be used as the payload. 29911cb0ef41Sopenharmony_ci ArrayBufferViewContents<uint8_t, 8> payload; 29921cb0ef41Sopenharmony_ci if (args[0]->IsArrayBufferView()) { 29931cb0ef41Sopenharmony_ci payload.Read(args[0].As<ArrayBufferView>()); 29941cb0ef41Sopenharmony_ci CHECK_EQ(payload.length(), 8); 29951cb0ef41Sopenharmony_ci } 29961cb0ef41Sopenharmony_ci 29971cb0ef41Sopenharmony_ci CHECK(args[1]->IsFunction()); 29981cb0ef41Sopenharmony_ci args.GetReturnValue().Set( 29991cb0ef41Sopenharmony_ci session->AddPing(payload.data(), args[1].As<Function>())); 30001cb0ef41Sopenharmony_ci} 30011cb0ef41Sopenharmony_ci 30021cb0ef41Sopenharmony_ci// Submits a SETTINGS frame for the Http2Session 30031cb0ef41Sopenharmony_civoid Http2Session::Settings(const FunctionCallbackInfo<Value>& args) { 30041cb0ef41Sopenharmony_ci Http2Session* session; 30051cb0ef41Sopenharmony_ci ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); 30061cb0ef41Sopenharmony_ci CHECK(args[0]->IsFunction()); 30071cb0ef41Sopenharmony_ci args.GetReturnValue().Set(session->AddSettings(args[0].As<Function>())); 30081cb0ef41Sopenharmony_ci} 30091cb0ef41Sopenharmony_ci 30101cb0ef41Sopenharmony_ciBaseObjectPtr<Http2Ping> Http2Session::PopPing() { 30111cb0ef41Sopenharmony_ci BaseObjectPtr<Http2Ping> ping; 30121cb0ef41Sopenharmony_ci if (!outstanding_pings_.empty()) { 30131cb0ef41Sopenharmony_ci ping = std::move(outstanding_pings_.front()); 30141cb0ef41Sopenharmony_ci outstanding_pings_.pop(); 30151cb0ef41Sopenharmony_ci DecrementCurrentSessionMemory(sizeof(*ping)); 30161cb0ef41Sopenharmony_ci } 30171cb0ef41Sopenharmony_ci return ping; 30181cb0ef41Sopenharmony_ci} 30191cb0ef41Sopenharmony_ci 30201cb0ef41Sopenharmony_cibool Http2Session::AddPing(const uint8_t* payload, Local<Function> callback) { 30211cb0ef41Sopenharmony_ci Local<Object> obj; 30221cb0ef41Sopenharmony_ci if (!env()->http2ping_constructor_template() 30231cb0ef41Sopenharmony_ci ->NewInstance(env()->context()) 30241cb0ef41Sopenharmony_ci .ToLocal(&obj)) { 30251cb0ef41Sopenharmony_ci return false; 30261cb0ef41Sopenharmony_ci } 30271cb0ef41Sopenharmony_ci 30281cb0ef41Sopenharmony_ci BaseObjectPtr<Http2Ping> ping = 30291cb0ef41Sopenharmony_ci MakeDetachedBaseObject<Http2Ping>(this, obj, callback); 30301cb0ef41Sopenharmony_ci if (!ping) 30311cb0ef41Sopenharmony_ci return false; 30321cb0ef41Sopenharmony_ci 30331cb0ef41Sopenharmony_ci if (outstanding_pings_.size() == max_outstanding_pings_) { 30341cb0ef41Sopenharmony_ci ping->Done(false); 30351cb0ef41Sopenharmony_ci return false; 30361cb0ef41Sopenharmony_ci } 30371cb0ef41Sopenharmony_ci 30381cb0ef41Sopenharmony_ci IncrementCurrentSessionMemory(sizeof(*ping)); 30391cb0ef41Sopenharmony_ci // The Ping itself is an Async resource. When the acknowledgement is received, 30401cb0ef41Sopenharmony_ci // the callback will be invoked and a notification sent out to JS land. The 30411cb0ef41Sopenharmony_ci // notification will include the duration of the ping, allowing the round 30421cb0ef41Sopenharmony_ci // trip to be measured. 30431cb0ef41Sopenharmony_ci ping->Send(payload); 30441cb0ef41Sopenharmony_ci 30451cb0ef41Sopenharmony_ci outstanding_pings_.emplace(std::move(ping)); 30461cb0ef41Sopenharmony_ci return true; 30471cb0ef41Sopenharmony_ci} 30481cb0ef41Sopenharmony_ci 30491cb0ef41Sopenharmony_ciBaseObjectPtr<Http2Settings> Http2Session::PopSettings() { 30501cb0ef41Sopenharmony_ci BaseObjectPtr<Http2Settings> settings; 30511cb0ef41Sopenharmony_ci if (!outstanding_settings_.empty()) { 30521cb0ef41Sopenharmony_ci settings = std::move(outstanding_settings_.front()); 30531cb0ef41Sopenharmony_ci outstanding_settings_.pop(); 30541cb0ef41Sopenharmony_ci DecrementCurrentSessionMemory(sizeof(*settings)); 30551cb0ef41Sopenharmony_ci } 30561cb0ef41Sopenharmony_ci return settings; 30571cb0ef41Sopenharmony_ci} 30581cb0ef41Sopenharmony_ci 30591cb0ef41Sopenharmony_cibool Http2Session::AddSettings(Local<Function> callback) { 30601cb0ef41Sopenharmony_ci Local<Object> obj; 30611cb0ef41Sopenharmony_ci if (!env()->http2settings_constructor_template() 30621cb0ef41Sopenharmony_ci ->NewInstance(env()->context()) 30631cb0ef41Sopenharmony_ci .ToLocal(&obj)) { 30641cb0ef41Sopenharmony_ci return false; 30651cb0ef41Sopenharmony_ci } 30661cb0ef41Sopenharmony_ci 30671cb0ef41Sopenharmony_ci BaseObjectPtr<Http2Settings> settings = 30681cb0ef41Sopenharmony_ci MakeDetachedBaseObject<Http2Settings>(this, obj, callback, 0); 30691cb0ef41Sopenharmony_ci if (!settings) 30701cb0ef41Sopenharmony_ci return false; 30711cb0ef41Sopenharmony_ci 30721cb0ef41Sopenharmony_ci if (outstanding_settings_.size() == max_outstanding_settings_) { 30731cb0ef41Sopenharmony_ci settings->Done(false); 30741cb0ef41Sopenharmony_ci return false; 30751cb0ef41Sopenharmony_ci } 30761cb0ef41Sopenharmony_ci 30771cb0ef41Sopenharmony_ci IncrementCurrentSessionMemory(sizeof(*settings)); 30781cb0ef41Sopenharmony_ci settings->Send(); 30791cb0ef41Sopenharmony_ci outstanding_settings_.emplace(std::move(settings)); 30801cb0ef41Sopenharmony_ci return true; 30811cb0ef41Sopenharmony_ci} 30821cb0ef41Sopenharmony_ci 30831cb0ef41Sopenharmony_ciHttp2Ping::Http2Ping( 30841cb0ef41Sopenharmony_ci Http2Session* session, 30851cb0ef41Sopenharmony_ci Local<Object> obj, 30861cb0ef41Sopenharmony_ci Local<Function> callback) 30871cb0ef41Sopenharmony_ci : AsyncWrap(session->env(), obj, AsyncWrap::PROVIDER_HTTP2PING), 30881cb0ef41Sopenharmony_ci session_(session), 30891cb0ef41Sopenharmony_ci startTime_(uv_hrtime()) { 30901cb0ef41Sopenharmony_ci callback_.Reset(env()->isolate(), callback); 30911cb0ef41Sopenharmony_ci} 30921cb0ef41Sopenharmony_ci 30931cb0ef41Sopenharmony_civoid Http2Ping::MemoryInfo(MemoryTracker* tracker) const { 30941cb0ef41Sopenharmony_ci tracker->TrackField("callback", callback_); 30951cb0ef41Sopenharmony_ci} 30961cb0ef41Sopenharmony_ci 30971cb0ef41Sopenharmony_ciLocal<Function> Http2Ping::callback() const { 30981cb0ef41Sopenharmony_ci return callback_.Get(env()->isolate()); 30991cb0ef41Sopenharmony_ci} 31001cb0ef41Sopenharmony_ci 31011cb0ef41Sopenharmony_civoid Http2Ping::Send(const uint8_t* payload) { 31021cb0ef41Sopenharmony_ci CHECK(session_); 31031cb0ef41Sopenharmony_ci uint8_t data[8]; 31041cb0ef41Sopenharmony_ci if (payload == nullptr) { 31051cb0ef41Sopenharmony_ci memcpy(&data, &startTime_, arraysize(data)); 31061cb0ef41Sopenharmony_ci payload = data; 31071cb0ef41Sopenharmony_ci } 31081cb0ef41Sopenharmony_ci Http2Scope h2scope(session_.get()); 31091cb0ef41Sopenharmony_ci CHECK_EQ(nghttp2_submit_ping( 31101cb0ef41Sopenharmony_ci session_->session(), 31111cb0ef41Sopenharmony_ci NGHTTP2_FLAG_NONE, 31121cb0ef41Sopenharmony_ci payload), 0); 31131cb0ef41Sopenharmony_ci} 31141cb0ef41Sopenharmony_ci 31151cb0ef41Sopenharmony_civoid Http2Ping::Done(bool ack, const uint8_t* payload) { 31161cb0ef41Sopenharmony_ci uint64_t duration_ns = uv_hrtime() - startTime_; 31171cb0ef41Sopenharmony_ci double duration_ms = duration_ns / 1e6; 31181cb0ef41Sopenharmony_ci if (session_) session_->statistics_.ping_rtt = duration_ns; 31191cb0ef41Sopenharmony_ci 31201cb0ef41Sopenharmony_ci Isolate* isolate = env()->isolate(); 31211cb0ef41Sopenharmony_ci HandleScope handle_scope(isolate); 31221cb0ef41Sopenharmony_ci Context::Scope context_scope(env()->context()); 31231cb0ef41Sopenharmony_ci 31241cb0ef41Sopenharmony_ci Local<Value> buf = Undefined(isolate); 31251cb0ef41Sopenharmony_ci if (payload != nullptr) { 31261cb0ef41Sopenharmony_ci buf = Buffer::Copy(isolate, 31271cb0ef41Sopenharmony_ci reinterpret_cast<const char*>(payload), 31281cb0ef41Sopenharmony_ci 8).ToLocalChecked(); 31291cb0ef41Sopenharmony_ci } 31301cb0ef41Sopenharmony_ci 31311cb0ef41Sopenharmony_ci Local<Value> argv[] = { 31321cb0ef41Sopenharmony_ci Boolean::New(isolate, ack), Number::New(isolate, duration_ms), buf}; 31331cb0ef41Sopenharmony_ci MakeCallback(callback(), arraysize(argv), argv); 31341cb0ef41Sopenharmony_ci} 31351cb0ef41Sopenharmony_ci 31361cb0ef41Sopenharmony_civoid Http2Ping::DetachFromSession() { 31371cb0ef41Sopenharmony_ci session_.reset(); 31381cb0ef41Sopenharmony_ci} 31391cb0ef41Sopenharmony_ci 31401cb0ef41Sopenharmony_civoid NgHttp2StreamWrite::MemoryInfo(MemoryTracker* tracker) const { 31411cb0ef41Sopenharmony_ci if (req_wrap) 31421cb0ef41Sopenharmony_ci tracker->TrackField("req_wrap", req_wrap); 31431cb0ef41Sopenharmony_ci tracker->TrackField("buf", buf); 31441cb0ef41Sopenharmony_ci} 31451cb0ef41Sopenharmony_ci 31461cb0ef41Sopenharmony_civoid SetCallbackFunctions(const FunctionCallbackInfo<Value>& args) { 31471cb0ef41Sopenharmony_ci Environment* env = Environment::GetCurrent(args); 31481cb0ef41Sopenharmony_ci CHECK_EQ(args.Length(), 11); 31491cb0ef41Sopenharmony_ci 31501cb0ef41Sopenharmony_ci#define SET_FUNCTION(arg, name) \ 31511cb0ef41Sopenharmony_ci CHECK(args[arg]->IsFunction()); \ 31521cb0ef41Sopenharmony_ci env->set_http2session_on_ ## name ## _function(args[arg].As<Function>()); 31531cb0ef41Sopenharmony_ci 31541cb0ef41Sopenharmony_ci SET_FUNCTION(0, error) 31551cb0ef41Sopenharmony_ci SET_FUNCTION(1, priority) 31561cb0ef41Sopenharmony_ci SET_FUNCTION(2, settings) 31571cb0ef41Sopenharmony_ci SET_FUNCTION(3, ping) 31581cb0ef41Sopenharmony_ci SET_FUNCTION(4, headers) 31591cb0ef41Sopenharmony_ci SET_FUNCTION(5, frame_error) 31601cb0ef41Sopenharmony_ci SET_FUNCTION(6, goaway_data) 31611cb0ef41Sopenharmony_ci SET_FUNCTION(7, altsvc) 31621cb0ef41Sopenharmony_ci SET_FUNCTION(8, origin) 31631cb0ef41Sopenharmony_ci SET_FUNCTION(9, stream_trailers) 31641cb0ef41Sopenharmony_ci SET_FUNCTION(10, stream_close) 31651cb0ef41Sopenharmony_ci 31661cb0ef41Sopenharmony_ci#undef SET_FUNCTION 31671cb0ef41Sopenharmony_ci} 31681cb0ef41Sopenharmony_ci 31691cb0ef41Sopenharmony_ci#ifdef NODE_DEBUG_NGHTTP2 31701cb0ef41Sopenharmony_civoid NgHttp2Debug(const char* format, va_list args) { 31711cb0ef41Sopenharmony_ci vfprintf(stderr, format, args); 31721cb0ef41Sopenharmony_ci} 31731cb0ef41Sopenharmony_ci#endif 31741cb0ef41Sopenharmony_ci 31751cb0ef41Sopenharmony_civoid Http2State::MemoryInfo(MemoryTracker* tracker) const { 31761cb0ef41Sopenharmony_ci tracker->TrackField("root_buffer", root_buffer); 31771cb0ef41Sopenharmony_ci} 31781cb0ef41Sopenharmony_ci 31791cb0ef41Sopenharmony_ci// Set up the process.binding('http2') binding. 31801cb0ef41Sopenharmony_civoid Initialize(Local<Object> target, 31811cb0ef41Sopenharmony_ci Local<Value> unused, 31821cb0ef41Sopenharmony_ci Local<Context> context, 31831cb0ef41Sopenharmony_ci void* priv) { 31841cb0ef41Sopenharmony_ci Realm* realm = Realm::GetCurrent(context); 31851cb0ef41Sopenharmony_ci Environment* env = realm->env(); 31861cb0ef41Sopenharmony_ci Isolate* isolate = env->isolate(); 31871cb0ef41Sopenharmony_ci HandleScope handle_scope(isolate); 31881cb0ef41Sopenharmony_ci 31891cb0ef41Sopenharmony_ci Http2State* const state = realm->AddBindingData<Http2State>(context, target); 31901cb0ef41Sopenharmony_ci if (state == nullptr) return; 31911cb0ef41Sopenharmony_ci 31921cb0ef41Sopenharmony_ci#define SET_STATE_TYPEDARRAY(name, field) \ 31931cb0ef41Sopenharmony_ci target->Set(context, \ 31941cb0ef41Sopenharmony_ci FIXED_ONE_BYTE_STRING(isolate, (name)), \ 31951cb0ef41Sopenharmony_ci (field)).FromJust() 31961cb0ef41Sopenharmony_ci 31971cb0ef41Sopenharmony_ci // Initialize the buffer used to store the session state 31981cb0ef41Sopenharmony_ci SET_STATE_TYPEDARRAY( 31991cb0ef41Sopenharmony_ci "sessionState", state->session_state_buffer.GetJSArray()); 32001cb0ef41Sopenharmony_ci // Initialize the buffer used to store the stream state 32011cb0ef41Sopenharmony_ci SET_STATE_TYPEDARRAY( 32021cb0ef41Sopenharmony_ci "streamState", state->stream_state_buffer.GetJSArray()); 32031cb0ef41Sopenharmony_ci SET_STATE_TYPEDARRAY( 32041cb0ef41Sopenharmony_ci "settingsBuffer", state->settings_buffer.GetJSArray()); 32051cb0ef41Sopenharmony_ci SET_STATE_TYPEDARRAY( 32061cb0ef41Sopenharmony_ci "optionsBuffer", state->options_buffer.GetJSArray()); 32071cb0ef41Sopenharmony_ci SET_STATE_TYPEDARRAY( 32081cb0ef41Sopenharmony_ci "streamStats", state->stream_stats_buffer.GetJSArray()); 32091cb0ef41Sopenharmony_ci SET_STATE_TYPEDARRAY( 32101cb0ef41Sopenharmony_ci "sessionStats", state->session_stats_buffer.GetJSArray()); 32111cb0ef41Sopenharmony_ci#undef SET_STATE_TYPEDARRAY 32121cb0ef41Sopenharmony_ci 32131cb0ef41Sopenharmony_ci NODE_DEFINE_CONSTANT(target, kBitfield); 32141cb0ef41Sopenharmony_ci NODE_DEFINE_CONSTANT(target, kSessionPriorityListenerCount); 32151cb0ef41Sopenharmony_ci NODE_DEFINE_CONSTANT(target, kSessionFrameErrorListenerCount); 32161cb0ef41Sopenharmony_ci NODE_DEFINE_CONSTANT(target, kSessionMaxInvalidFrames); 32171cb0ef41Sopenharmony_ci NODE_DEFINE_CONSTANT(target, kSessionMaxRejectedStreams); 32181cb0ef41Sopenharmony_ci NODE_DEFINE_CONSTANT(target, kSessionUint8FieldCount); 32191cb0ef41Sopenharmony_ci 32201cb0ef41Sopenharmony_ci NODE_DEFINE_CONSTANT(target, kSessionHasRemoteSettingsListeners); 32211cb0ef41Sopenharmony_ci NODE_DEFINE_CONSTANT(target, kSessionRemoteSettingsIsUpToDate); 32221cb0ef41Sopenharmony_ci NODE_DEFINE_CONSTANT(target, kSessionHasPingListeners); 32231cb0ef41Sopenharmony_ci NODE_DEFINE_CONSTANT(target, kSessionHasAltsvcListeners); 32241cb0ef41Sopenharmony_ci 32251cb0ef41Sopenharmony_ci // Method to fetch the nghttp2 string description of an nghttp2 error code 32261cb0ef41Sopenharmony_ci SetMethod(context, target, "nghttp2ErrorString", HttpErrorString); 32271cb0ef41Sopenharmony_ci SetMethod(context, target, "refreshDefaultSettings", RefreshDefaultSettings); 32281cb0ef41Sopenharmony_ci SetMethod(context, target, "packSettings", PackSettings); 32291cb0ef41Sopenharmony_ci SetMethod(context, target, "setCallbackFunctions", SetCallbackFunctions); 32301cb0ef41Sopenharmony_ci 32311cb0ef41Sopenharmony_ci Local<FunctionTemplate> ping = FunctionTemplate::New(env->isolate()); 32321cb0ef41Sopenharmony_ci ping->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "Http2Ping")); 32331cb0ef41Sopenharmony_ci ping->Inherit(AsyncWrap::GetConstructorTemplate(env)); 32341cb0ef41Sopenharmony_ci Local<ObjectTemplate> pingt = ping->InstanceTemplate(); 32351cb0ef41Sopenharmony_ci pingt->SetInternalFieldCount(Http2Ping::kInternalFieldCount); 32361cb0ef41Sopenharmony_ci env->set_http2ping_constructor_template(pingt); 32371cb0ef41Sopenharmony_ci 32381cb0ef41Sopenharmony_ci Local<FunctionTemplate> setting = FunctionTemplate::New(env->isolate()); 32391cb0ef41Sopenharmony_ci setting->Inherit(AsyncWrap::GetConstructorTemplate(env)); 32401cb0ef41Sopenharmony_ci Local<ObjectTemplate> settingt = setting->InstanceTemplate(); 32411cb0ef41Sopenharmony_ci settingt->SetInternalFieldCount(AsyncWrap::kInternalFieldCount); 32421cb0ef41Sopenharmony_ci env->set_http2settings_constructor_template(settingt); 32431cb0ef41Sopenharmony_ci 32441cb0ef41Sopenharmony_ci Local<FunctionTemplate> stream = FunctionTemplate::New(env->isolate()); 32451cb0ef41Sopenharmony_ci SetProtoMethod(isolate, stream, "id", Http2Stream::GetID); 32461cb0ef41Sopenharmony_ci SetProtoMethod(isolate, stream, "destroy", Http2Stream::Destroy); 32471cb0ef41Sopenharmony_ci SetProtoMethod(isolate, stream, "priority", Http2Stream::Priority); 32481cb0ef41Sopenharmony_ci SetProtoMethod(isolate, stream, "pushPromise", Http2Stream::PushPromise); 32491cb0ef41Sopenharmony_ci SetProtoMethod(isolate, stream, "info", Http2Stream::Info); 32501cb0ef41Sopenharmony_ci SetProtoMethod(isolate, stream, "trailers", Http2Stream::Trailers); 32511cb0ef41Sopenharmony_ci SetProtoMethod(isolate, stream, "respond", Http2Stream::Respond); 32521cb0ef41Sopenharmony_ci SetProtoMethod(isolate, stream, "rstStream", Http2Stream::RstStream); 32531cb0ef41Sopenharmony_ci SetProtoMethod(isolate, stream, "refreshState", Http2Stream::RefreshState); 32541cb0ef41Sopenharmony_ci stream->Inherit(AsyncWrap::GetConstructorTemplate(env)); 32551cb0ef41Sopenharmony_ci StreamBase::AddMethods(env, stream); 32561cb0ef41Sopenharmony_ci Local<ObjectTemplate> streamt = stream->InstanceTemplate(); 32571cb0ef41Sopenharmony_ci streamt->SetInternalFieldCount(StreamBase::kInternalFieldCount); 32581cb0ef41Sopenharmony_ci env->set_http2stream_constructor_template(streamt); 32591cb0ef41Sopenharmony_ci SetConstructorFunction(context, target, "Http2Stream", stream); 32601cb0ef41Sopenharmony_ci 32611cb0ef41Sopenharmony_ci Local<FunctionTemplate> session = 32621cb0ef41Sopenharmony_ci NewFunctionTemplate(isolate, Http2Session::New); 32631cb0ef41Sopenharmony_ci session->InstanceTemplate()->SetInternalFieldCount( 32641cb0ef41Sopenharmony_ci Http2Session::kInternalFieldCount); 32651cb0ef41Sopenharmony_ci session->Inherit(AsyncWrap::GetConstructorTemplate(env)); 32661cb0ef41Sopenharmony_ci SetProtoMethod(isolate, session, "origin", Http2Session::Origin); 32671cb0ef41Sopenharmony_ci SetProtoMethod(isolate, session, "altsvc", Http2Session::AltSvc); 32681cb0ef41Sopenharmony_ci SetProtoMethod(isolate, session, "ping", Http2Session::Ping); 32691cb0ef41Sopenharmony_ci SetProtoMethod(isolate, session, "consume", Http2Session::Consume); 32701cb0ef41Sopenharmony_ci SetProtoMethod(isolate, session, "receive", Http2Session::Receive); 32711cb0ef41Sopenharmony_ci SetProtoMethod(isolate, session, "destroy", Http2Session::Destroy); 32721cb0ef41Sopenharmony_ci SetProtoMethod(isolate, session, "goaway", Http2Session::Goaway); 32731cb0ef41Sopenharmony_ci SetProtoMethod(isolate, session, "settings", Http2Session::Settings); 32741cb0ef41Sopenharmony_ci SetProtoMethod(isolate, session, "request", Http2Session::Request); 32751cb0ef41Sopenharmony_ci SetProtoMethod( 32761cb0ef41Sopenharmony_ci isolate, session, "setNextStreamID", Http2Session::SetNextStreamID); 32771cb0ef41Sopenharmony_ci SetProtoMethod( 32781cb0ef41Sopenharmony_ci isolate, session, "setLocalWindowSize", Http2Session::SetLocalWindowSize); 32791cb0ef41Sopenharmony_ci SetProtoMethod( 32801cb0ef41Sopenharmony_ci isolate, session, "updateChunksSent", Http2Session::UpdateChunksSent); 32811cb0ef41Sopenharmony_ci SetProtoMethod(isolate, session, "refreshState", Http2Session::RefreshState); 32821cb0ef41Sopenharmony_ci SetProtoMethod( 32831cb0ef41Sopenharmony_ci isolate, 32841cb0ef41Sopenharmony_ci session, 32851cb0ef41Sopenharmony_ci "localSettings", 32861cb0ef41Sopenharmony_ci Http2Session::RefreshSettings<nghttp2_session_get_local_settings>); 32871cb0ef41Sopenharmony_ci SetProtoMethod( 32881cb0ef41Sopenharmony_ci isolate, 32891cb0ef41Sopenharmony_ci session, 32901cb0ef41Sopenharmony_ci "remoteSettings", 32911cb0ef41Sopenharmony_ci Http2Session::RefreshSettings<nghttp2_session_get_remote_settings>); 32921cb0ef41Sopenharmony_ci SetConstructorFunction(context, target, "Http2Session", session); 32931cb0ef41Sopenharmony_ci 32941cb0ef41Sopenharmony_ci Local<Object> constants = Object::New(isolate); 32951cb0ef41Sopenharmony_ci 32961cb0ef41Sopenharmony_ci // This does allocate one more slot than needed but it's not used. 32971cb0ef41Sopenharmony_ci#define V(name) FIXED_ONE_BYTE_STRING(isolate, #name), 32981cb0ef41Sopenharmony_ci Local<Value> error_code_names[] = { 32991cb0ef41Sopenharmony_ci HTTP2_ERROR_CODES(V) 33001cb0ef41Sopenharmony_ci }; 33011cb0ef41Sopenharmony_ci#undef V 33021cb0ef41Sopenharmony_ci 33031cb0ef41Sopenharmony_ci Local<Array> name_for_error_code = 33041cb0ef41Sopenharmony_ci Array::New( 33051cb0ef41Sopenharmony_ci isolate, 33061cb0ef41Sopenharmony_ci error_code_names, 33071cb0ef41Sopenharmony_ci arraysize(error_code_names)); 33081cb0ef41Sopenharmony_ci 33091cb0ef41Sopenharmony_ci target->Set(context, 33101cb0ef41Sopenharmony_ci FIXED_ONE_BYTE_STRING(isolate, "nameForErrorCode"), 33111cb0ef41Sopenharmony_ci name_for_error_code).Check(); 33121cb0ef41Sopenharmony_ci 33131cb0ef41Sopenharmony_ci#define V(constant) NODE_DEFINE_HIDDEN_CONSTANT(constants, constant); 33141cb0ef41Sopenharmony_ci HTTP2_HIDDEN_CONSTANTS(V) 33151cb0ef41Sopenharmony_ci#undef V 33161cb0ef41Sopenharmony_ci 33171cb0ef41Sopenharmony_ci#define V(constant) NODE_DEFINE_CONSTANT(constants, constant); 33181cb0ef41Sopenharmony_ci HTTP2_CONSTANTS(V) 33191cb0ef41Sopenharmony_ci#undef V 33201cb0ef41Sopenharmony_ci 33211cb0ef41Sopenharmony_ci // NGHTTP2_DEFAULT_WEIGHT is a macro and not a regular define 33221cb0ef41Sopenharmony_ci // it won't be set properly on the constants object if included 33231cb0ef41Sopenharmony_ci // in the HTTP2_CONSTANTS macro. 33241cb0ef41Sopenharmony_ci NODE_DEFINE_CONSTANT(constants, NGHTTP2_DEFAULT_WEIGHT); 33251cb0ef41Sopenharmony_ci 33261cb0ef41Sopenharmony_ci#define V(NAME, VALUE) \ 33271cb0ef41Sopenharmony_ci NODE_DEFINE_STRING_CONSTANT(constants, "HTTP2_HEADER_" # NAME, VALUE); 33281cb0ef41Sopenharmony_ci HTTP_KNOWN_HEADERS(V) 33291cb0ef41Sopenharmony_ci#undef V 33301cb0ef41Sopenharmony_ci 33311cb0ef41Sopenharmony_ci#define V(NAME, VALUE) \ 33321cb0ef41Sopenharmony_ci NODE_DEFINE_STRING_CONSTANT(constants, "HTTP2_METHOD_" # NAME, VALUE); 33331cb0ef41Sopenharmony_ci HTTP_KNOWN_METHODS(V) 33341cb0ef41Sopenharmony_ci#undef V 33351cb0ef41Sopenharmony_ci 33361cb0ef41Sopenharmony_ci#define V(name, _) NODE_DEFINE_CONSTANT(constants, HTTP_STATUS_##name); 33371cb0ef41Sopenharmony_ci HTTP_STATUS_CODES(V) 33381cb0ef41Sopenharmony_ci#undef V 33391cb0ef41Sopenharmony_ci 33401cb0ef41Sopenharmony_ci target->Set(context, env->constants_string(), constants).Check(); 33411cb0ef41Sopenharmony_ci 33421cb0ef41Sopenharmony_ci#ifdef NODE_DEBUG_NGHTTP2 33431cb0ef41Sopenharmony_ci nghttp2_set_debug_vprintf_callback(NgHttp2Debug); 33441cb0ef41Sopenharmony_ci#endif 33451cb0ef41Sopenharmony_ci} 33461cb0ef41Sopenharmony_ci} // namespace http2 33471cb0ef41Sopenharmony_ci} // namespace node 33481cb0ef41Sopenharmony_ci 33491cb0ef41Sopenharmony_ciNODE_BINDING_CONTEXT_AWARE_INTERNAL(http2, node::http2::Initialize) 3350