11cb0ef41Sopenharmony_ci'use strict'; 21cb0ef41Sopenharmony_ci 31cb0ef41Sopenharmony_ciconst { 41cb0ef41Sopenharmony_ci ArrayFrom, 51cb0ef41Sopenharmony_ci ArrayIsArray, 61cb0ef41Sopenharmony_ci ArrayPrototypeForEach, 71cb0ef41Sopenharmony_ci ArrayPrototypePush, 81cb0ef41Sopenharmony_ci ArrayPrototypeUnshift, 91cb0ef41Sopenharmony_ci FunctionPrototypeBind, 101cb0ef41Sopenharmony_ci FunctionPrototypeCall, 111cb0ef41Sopenharmony_ci MathMin, 121cb0ef41Sopenharmony_ci ObjectAssign, 131cb0ef41Sopenharmony_ci ObjectCreate, 141cb0ef41Sopenharmony_ci ObjectKeys, 151cb0ef41Sopenharmony_ci ObjectDefineProperty, 161cb0ef41Sopenharmony_ci ObjectPrototypeHasOwnProperty, 171cb0ef41Sopenharmony_ci Promise, 181cb0ef41Sopenharmony_ci PromisePrototypeThen, 191cb0ef41Sopenharmony_ci Proxy, 201cb0ef41Sopenharmony_ci ReflectApply, 211cb0ef41Sopenharmony_ci ReflectGet, 221cb0ef41Sopenharmony_ci ReflectGetPrototypeOf, 231cb0ef41Sopenharmony_ci ReflectSet, 241cb0ef41Sopenharmony_ci RegExpPrototypeExec, 251cb0ef41Sopenharmony_ci SafeArrayIterator, 261cb0ef41Sopenharmony_ci SafeMap, 271cb0ef41Sopenharmony_ci SafeSet, 281cb0ef41Sopenharmony_ci StringPrototypeSlice, 291cb0ef41Sopenharmony_ci Symbol, 301cb0ef41Sopenharmony_ci SymbolDispose, 311cb0ef41Sopenharmony_ci TypedArrayPrototypeGetLength, 321cb0ef41Sopenharmony_ci Uint32Array, 331cb0ef41Sopenharmony_ci Uint8Array, 341cb0ef41Sopenharmony_ci} = primordials; 351cb0ef41Sopenharmony_ci 361cb0ef41Sopenharmony_ciconst { 371cb0ef41Sopenharmony_ci assertCrypto, 381cb0ef41Sopenharmony_ci customInspectSymbol: kInspect, 391cb0ef41Sopenharmony_ci kEmptyObject, 401cb0ef41Sopenharmony_ci promisify, 411cb0ef41Sopenharmony_ci} = require('internal/util'); 421cb0ef41Sopenharmony_ci 431cb0ef41Sopenharmony_ciassertCrypto(); 441cb0ef41Sopenharmony_ci 451cb0ef41Sopenharmony_ciconst assert = require('assert'); 461cb0ef41Sopenharmony_ciconst EventEmitter = require('events'); 471cb0ef41Sopenharmony_ciconst fs = require('fs'); 481cb0ef41Sopenharmony_ciconst http = require('http'); 491cb0ef41Sopenharmony_ciconst { readUInt16BE, readUInt32BE } = require('internal/buffer'); 501cb0ef41Sopenharmony_ciconst { URL } = require('internal/url'); 511cb0ef41Sopenharmony_ciconst net = require('net'); 521cb0ef41Sopenharmony_ciconst { Duplex } = require('stream'); 531cb0ef41Sopenharmony_ciconst tls = require('tls'); 541cb0ef41Sopenharmony_ciconst { setImmediate, setTimeout, clearTimeout } = require('timers'); 551cb0ef41Sopenharmony_ci 561cb0ef41Sopenharmony_ciconst { 571cb0ef41Sopenharmony_ci kIncomingMessage, 581cb0ef41Sopenharmony_ci _checkIsHttpToken: checkIsHttpToken, 591cb0ef41Sopenharmony_ci} = require('_http_common'); 601cb0ef41Sopenharmony_ciconst { kServerResponse } = require('_http_server'); 611cb0ef41Sopenharmony_ciconst JSStreamSocket = require('internal/js_stream_socket'); 621cb0ef41Sopenharmony_ci 631cb0ef41Sopenharmony_ciconst { 641cb0ef41Sopenharmony_ci defaultTriggerAsyncIdScope, 651cb0ef41Sopenharmony_ci symbols: { 661cb0ef41Sopenharmony_ci async_id_symbol, 671cb0ef41Sopenharmony_ci owner_symbol, 681cb0ef41Sopenharmony_ci }, 691cb0ef41Sopenharmony_ci} = require('internal/async_hooks'); 701cb0ef41Sopenharmony_ciconst { 711cb0ef41Sopenharmony_ci aggregateTwoErrors, 721cb0ef41Sopenharmony_ci codes: { 731cb0ef41Sopenharmony_ci ERR_HTTP2_ALTSVC_INVALID_ORIGIN, 741cb0ef41Sopenharmony_ci ERR_HTTP2_ALTSVC_LENGTH, 751cb0ef41Sopenharmony_ci ERR_HTTP2_CONNECT_AUTHORITY, 761cb0ef41Sopenharmony_ci ERR_HTTP2_CONNECT_PATH, 771cb0ef41Sopenharmony_ci ERR_HTTP2_CONNECT_SCHEME, 781cb0ef41Sopenharmony_ci ERR_HTTP2_GOAWAY_SESSION, 791cb0ef41Sopenharmony_ci ERR_HTTP2_HEADERS_AFTER_RESPOND, 801cb0ef41Sopenharmony_ci ERR_HTTP2_HEADERS_SENT, 811cb0ef41Sopenharmony_ci ERR_HTTP2_INVALID_INFO_STATUS, 821cb0ef41Sopenharmony_ci ERR_HTTP2_INVALID_ORIGIN, 831cb0ef41Sopenharmony_ci ERR_HTTP2_INVALID_PACKED_SETTINGS_LENGTH, 841cb0ef41Sopenharmony_ci ERR_HTTP2_INVALID_SESSION, 851cb0ef41Sopenharmony_ci ERR_HTTP2_INVALID_SETTING_VALUE, 861cb0ef41Sopenharmony_ci ERR_HTTP2_INVALID_STREAM, 871cb0ef41Sopenharmony_ci ERR_HTTP2_MAX_PENDING_SETTINGS_ACK, 881cb0ef41Sopenharmony_ci ERR_HTTP2_NESTED_PUSH, 891cb0ef41Sopenharmony_ci ERR_HTTP2_NO_MEM, 901cb0ef41Sopenharmony_ci ERR_HTTP2_NO_SOCKET_MANIPULATION, 911cb0ef41Sopenharmony_ci ERR_HTTP2_ORIGIN_LENGTH, 921cb0ef41Sopenharmony_ci ERR_HTTP2_OUT_OF_STREAMS, 931cb0ef41Sopenharmony_ci ERR_HTTP2_PAYLOAD_FORBIDDEN, 941cb0ef41Sopenharmony_ci ERR_HTTP2_PING_CANCEL, 951cb0ef41Sopenharmony_ci ERR_HTTP2_PING_LENGTH, 961cb0ef41Sopenharmony_ci ERR_HTTP2_PUSH_DISABLED, 971cb0ef41Sopenharmony_ci ERR_HTTP2_SEND_FILE, 981cb0ef41Sopenharmony_ci ERR_HTTP2_SEND_FILE_NOSEEK, 991cb0ef41Sopenharmony_ci ERR_HTTP2_SESSION_ERROR, 1001cb0ef41Sopenharmony_ci ERR_HTTP2_SETTINGS_CANCEL, 1011cb0ef41Sopenharmony_ci ERR_HTTP2_SOCKET_BOUND, 1021cb0ef41Sopenharmony_ci ERR_HTTP2_SOCKET_UNBOUND, 1031cb0ef41Sopenharmony_ci ERR_HTTP2_STATUS_101, 1041cb0ef41Sopenharmony_ci ERR_HTTP2_STATUS_INVALID, 1051cb0ef41Sopenharmony_ci ERR_HTTP2_STREAM_CANCEL, 1061cb0ef41Sopenharmony_ci ERR_HTTP2_STREAM_ERROR, 1071cb0ef41Sopenharmony_ci ERR_HTTP2_STREAM_SELF_DEPENDENCY, 1081cb0ef41Sopenharmony_ci ERR_HTTP2_TRAILERS_ALREADY_SENT, 1091cb0ef41Sopenharmony_ci ERR_HTTP2_TRAILERS_NOT_READY, 1101cb0ef41Sopenharmony_ci ERR_HTTP2_UNSUPPORTED_PROTOCOL, 1111cb0ef41Sopenharmony_ci ERR_INVALID_ARG_TYPE, 1121cb0ef41Sopenharmony_ci ERR_INVALID_ARG_VALUE, 1131cb0ef41Sopenharmony_ci ERR_INVALID_CHAR, 1141cb0ef41Sopenharmony_ci ERR_INVALID_HTTP_TOKEN, 1151cb0ef41Sopenharmony_ci ERR_OUT_OF_RANGE, 1161cb0ef41Sopenharmony_ci ERR_SOCKET_CLOSED, 1171cb0ef41Sopenharmony_ci }, 1181cb0ef41Sopenharmony_ci hideStackFrames, 1191cb0ef41Sopenharmony_ci AbortError, 1201cb0ef41Sopenharmony_ci} = require('internal/errors'); 1211cb0ef41Sopenharmony_ciconst { 1221cb0ef41Sopenharmony_ci isUint32, 1231cb0ef41Sopenharmony_ci validateAbortSignal, 1241cb0ef41Sopenharmony_ci validateBuffer, 1251cb0ef41Sopenharmony_ci validateFunction, 1261cb0ef41Sopenharmony_ci validateInt32, 1271cb0ef41Sopenharmony_ci validateInteger, 1281cb0ef41Sopenharmony_ci validateNumber, 1291cb0ef41Sopenharmony_ci validateString, 1301cb0ef41Sopenharmony_ci validateUint32, 1311cb0ef41Sopenharmony_ci} = require('internal/validators'); 1321cb0ef41Sopenharmony_ciconst fsPromisesInternal = require('internal/fs/promises'); 1331cb0ef41Sopenharmony_ciconst { utcDate } = require('internal/http'); 1341cb0ef41Sopenharmony_ciconst { 1351cb0ef41Sopenharmony_ci Http2ServerRequest, 1361cb0ef41Sopenharmony_ci Http2ServerResponse, 1371cb0ef41Sopenharmony_ci onServerStream, 1381cb0ef41Sopenharmony_ci} = require('internal/http2/compat'); 1391cb0ef41Sopenharmony_ci 1401cb0ef41Sopenharmony_ciconst { 1411cb0ef41Sopenharmony_ci assertIsObject, 1421cb0ef41Sopenharmony_ci assertValidPseudoHeader, 1431cb0ef41Sopenharmony_ci assertValidPseudoHeaderResponse, 1441cb0ef41Sopenharmony_ci assertValidPseudoHeaderTrailer, 1451cb0ef41Sopenharmony_ci assertWithinRange, 1461cb0ef41Sopenharmony_ci getAuthority, 1471cb0ef41Sopenharmony_ci getDefaultSettings, 1481cb0ef41Sopenharmony_ci getSessionState, 1491cb0ef41Sopenharmony_ci getSettings, 1501cb0ef41Sopenharmony_ci getStreamState, 1511cb0ef41Sopenharmony_ci isPayloadMeaningless, 1521cb0ef41Sopenharmony_ci kSensitiveHeaders, 1531cb0ef41Sopenharmony_ci kSocket, 1541cb0ef41Sopenharmony_ci kRequest, 1551cb0ef41Sopenharmony_ci kProxySocket, 1561cb0ef41Sopenharmony_ci mapToHeaders, 1571cb0ef41Sopenharmony_ci NghttpError, 1581cb0ef41Sopenharmony_ci sessionName, 1591cb0ef41Sopenharmony_ci toHeaderObject, 1601cb0ef41Sopenharmony_ci updateOptionsBuffer, 1611cb0ef41Sopenharmony_ci updateSettingsBuffer, 1621cb0ef41Sopenharmony_ci} = require('internal/http2/util'); 1631cb0ef41Sopenharmony_ciconst { 1641cb0ef41Sopenharmony_ci writeGeneric, 1651cb0ef41Sopenharmony_ci writevGeneric, 1661cb0ef41Sopenharmony_ci onStreamRead, 1671cb0ef41Sopenharmony_ci kAfterAsyncWrite, 1681cb0ef41Sopenharmony_ci kMaybeDestroy, 1691cb0ef41Sopenharmony_ci kUpdateTimer, 1701cb0ef41Sopenharmony_ci kHandle, 1711cb0ef41Sopenharmony_ci kSession, 1721cb0ef41Sopenharmony_ci setStreamTimeout, 1731cb0ef41Sopenharmony_ci} = require('internal/stream_base_commons'); 1741cb0ef41Sopenharmony_ciconst { kTimeout } = require('internal/timers'); 1751cb0ef41Sopenharmony_ciconst { isArrayBufferView } = require('internal/util/types'); 1761cb0ef41Sopenharmony_ciconst { format } = require('internal/util/inspect'); 1771cb0ef41Sopenharmony_ci 1781cb0ef41Sopenharmony_ciconst { FileHandle } = internalBinding('fs'); 1791cb0ef41Sopenharmony_ciconst binding = internalBinding('http2'); 1801cb0ef41Sopenharmony_ciconst { 1811cb0ef41Sopenharmony_ci ShutdownWrap, 1821cb0ef41Sopenharmony_ci kReadBytesOrError, 1831cb0ef41Sopenharmony_ci streamBaseState, 1841cb0ef41Sopenharmony_ci} = internalBinding('stream_wrap'); 1851cb0ef41Sopenharmony_ciconst { UV_EOF } = internalBinding('uv'); 1861cb0ef41Sopenharmony_ci 1871cb0ef41Sopenharmony_ciconst { StreamPipe } = internalBinding('stream_pipe'); 1881cb0ef41Sopenharmony_ciconst { _connectionListener: httpConnectionListener } = http; 1891cb0ef41Sopenharmony_cilet debug = require('internal/util/debuglog').debuglog('http2', (fn) => { 1901cb0ef41Sopenharmony_ci debug = fn; 1911cb0ef41Sopenharmony_ci}); 1921cb0ef41Sopenharmony_ciconst debugEnabled = debug.enabled; 1931cb0ef41Sopenharmony_ci 1941cb0ef41Sopenharmony_cifunction debugStream(id, sessionType, message, ...args) { 1951cb0ef41Sopenharmony_ci if (!debugEnabled) { 1961cb0ef41Sopenharmony_ci return; 1971cb0ef41Sopenharmony_ci } 1981cb0ef41Sopenharmony_ci debug('Http2Stream %s [Http2Session %s]: ' + message, 1991cb0ef41Sopenharmony_ci id, sessionName(sessionType), ...new SafeArrayIterator(args)); 2001cb0ef41Sopenharmony_ci} 2011cb0ef41Sopenharmony_ci 2021cb0ef41Sopenharmony_cifunction debugStreamObj(stream, message, ...args) { 2031cb0ef41Sopenharmony_ci const session = stream[kSession]; 2041cb0ef41Sopenharmony_ci const type = session ? session[kType] : undefined; 2051cb0ef41Sopenharmony_ci debugStream(stream[kID], type, message, ...new SafeArrayIterator(args)); 2061cb0ef41Sopenharmony_ci} 2071cb0ef41Sopenharmony_ci 2081cb0ef41Sopenharmony_cifunction debugSession(sessionType, message, ...args) { 2091cb0ef41Sopenharmony_ci debug('Http2Session %s: ' + message, sessionName(sessionType), 2101cb0ef41Sopenharmony_ci ...new SafeArrayIterator(args)); 2111cb0ef41Sopenharmony_ci} 2121cb0ef41Sopenharmony_ci 2131cb0ef41Sopenharmony_cifunction debugSessionObj(session, message, ...args) { 2141cb0ef41Sopenharmony_ci debugSession(session[kType], message, ...new SafeArrayIterator(args)); 2151cb0ef41Sopenharmony_ci} 2161cb0ef41Sopenharmony_ci 2171cb0ef41Sopenharmony_ciconst kMaxFrameSize = (2 ** 24) - 1; 2181cb0ef41Sopenharmony_ciconst kMaxInt = (2 ** 32) - 1; 2191cb0ef41Sopenharmony_ciconst kMaxStreams = (2 ** 32) - 1; 2201cb0ef41Sopenharmony_ciconst kMaxALTSVC = (2 ** 14) - 2; 2211cb0ef41Sopenharmony_ci 2221cb0ef41Sopenharmony_ci// eslint-disable-next-line no-control-regex 2231cb0ef41Sopenharmony_ciconst kQuotedString = /^[\x09\x20-\x5b\x5d-\x7e\x80-\xff]*$/; 2241cb0ef41Sopenharmony_ci 2251cb0ef41Sopenharmony_ciconst { constants, nameForErrorCode } = binding; 2261cb0ef41Sopenharmony_ci 2271cb0ef41Sopenharmony_ciconst NETServer = net.Server; 2281cb0ef41Sopenharmony_ciconst TLSServer = tls.Server; 2291cb0ef41Sopenharmony_ci 2301cb0ef41Sopenharmony_ciconst kAlpnProtocol = Symbol('alpnProtocol'); 2311cb0ef41Sopenharmony_ciconst kAuthority = Symbol('authority'); 2321cb0ef41Sopenharmony_ciconst kEncrypted = Symbol('encrypted'); 2331cb0ef41Sopenharmony_ciconst kID = Symbol('id'); 2341cb0ef41Sopenharmony_ciconst kInit = Symbol('init'); 2351cb0ef41Sopenharmony_ciconst kInfoHeaders = Symbol('sent-info-headers'); 2361cb0ef41Sopenharmony_ciconst kLocalSettings = Symbol('local-settings'); 2371cb0ef41Sopenharmony_ciconst kNativeFields = Symbol('kNativeFields'); 2381cb0ef41Sopenharmony_ciconst kOptions = Symbol('options'); 2391cb0ef41Sopenharmony_ciconst kOwner = owner_symbol; 2401cb0ef41Sopenharmony_ciconst kOrigin = Symbol('origin'); 2411cb0ef41Sopenharmony_ciconst kPendingRequestCalls = Symbol('kPendingRequestCalls'); 2421cb0ef41Sopenharmony_ciconst kProceed = Symbol('proceed'); 2431cb0ef41Sopenharmony_ciconst kProtocol = Symbol('protocol'); 2441cb0ef41Sopenharmony_ciconst kRemoteSettings = Symbol('remote-settings'); 2451cb0ef41Sopenharmony_ciconst kSelectPadding = Symbol('select-padding'); 2461cb0ef41Sopenharmony_ciconst kSentHeaders = Symbol('sent-headers'); 2471cb0ef41Sopenharmony_ciconst kSentTrailers = Symbol('sent-trailers'); 2481cb0ef41Sopenharmony_ciconst kServer = Symbol('server'); 2491cb0ef41Sopenharmony_ciconst kState = Symbol('state'); 2501cb0ef41Sopenharmony_ciconst kType = Symbol('type'); 2511cb0ef41Sopenharmony_ciconst kWriteGeneric = Symbol('write-generic'); 2521cb0ef41Sopenharmony_ci 2531cb0ef41Sopenharmony_ciconst { 2541cb0ef41Sopenharmony_ci kBitfield, 2551cb0ef41Sopenharmony_ci kSessionPriorityListenerCount, 2561cb0ef41Sopenharmony_ci kSessionFrameErrorListenerCount, 2571cb0ef41Sopenharmony_ci kSessionMaxInvalidFrames, 2581cb0ef41Sopenharmony_ci kSessionMaxRejectedStreams, 2591cb0ef41Sopenharmony_ci kSessionUint8FieldCount, 2601cb0ef41Sopenharmony_ci kSessionHasRemoteSettingsListeners, 2611cb0ef41Sopenharmony_ci kSessionRemoteSettingsIsUpToDate, 2621cb0ef41Sopenharmony_ci kSessionHasPingListeners, 2631cb0ef41Sopenharmony_ci kSessionHasAltsvcListeners, 2641cb0ef41Sopenharmony_ci} = binding; 2651cb0ef41Sopenharmony_ci 2661cb0ef41Sopenharmony_ciconst { 2671cb0ef41Sopenharmony_ci NGHTTP2_CANCEL, 2681cb0ef41Sopenharmony_ci NGHTTP2_REFUSED_STREAM, 2691cb0ef41Sopenharmony_ci NGHTTP2_DEFAULT_WEIGHT, 2701cb0ef41Sopenharmony_ci NGHTTP2_FLAG_END_STREAM, 2711cb0ef41Sopenharmony_ci NGHTTP2_HCAT_PUSH_RESPONSE, 2721cb0ef41Sopenharmony_ci NGHTTP2_HCAT_RESPONSE, 2731cb0ef41Sopenharmony_ci NGHTTP2_INTERNAL_ERROR, 2741cb0ef41Sopenharmony_ci NGHTTP2_NO_ERROR, 2751cb0ef41Sopenharmony_ci NGHTTP2_SESSION_CLIENT, 2761cb0ef41Sopenharmony_ci NGHTTP2_SESSION_SERVER, 2771cb0ef41Sopenharmony_ci NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE, 2781cb0ef41Sopenharmony_ci NGHTTP2_ERR_INVALID_ARGUMENT, 2791cb0ef41Sopenharmony_ci NGHTTP2_ERR_STREAM_CLOSED, 2801cb0ef41Sopenharmony_ci NGHTTP2_ERR_NOMEM, 2811cb0ef41Sopenharmony_ci 2821cb0ef41Sopenharmony_ci HTTP2_HEADER_AUTHORITY, 2831cb0ef41Sopenharmony_ci HTTP2_HEADER_DATE, 2841cb0ef41Sopenharmony_ci HTTP2_HEADER_METHOD, 2851cb0ef41Sopenharmony_ci HTTP2_HEADER_PATH, 2861cb0ef41Sopenharmony_ci HTTP2_HEADER_PROTOCOL, 2871cb0ef41Sopenharmony_ci HTTP2_HEADER_SCHEME, 2881cb0ef41Sopenharmony_ci HTTP2_HEADER_STATUS, 2891cb0ef41Sopenharmony_ci HTTP2_HEADER_CONTENT_LENGTH, 2901cb0ef41Sopenharmony_ci 2911cb0ef41Sopenharmony_ci NGHTTP2_SETTINGS_HEADER_TABLE_SIZE, 2921cb0ef41Sopenharmony_ci NGHTTP2_SETTINGS_ENABLE_PUSH, 2931cb0ef41Sopenharmony_ci NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 2941cb0ef41Sopenharmony_ci NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, 2951cb0ef41Sopenharmony_ci NGHTTP2_SETTINGS_MAX_FRAME_SIZE, 2961cb0ef41Sopenharmony_ci NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE, 2971cb0ef41Sopenharmony_ci NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL, 2981cb0ef41Sopenharmony_ci 2991cb0ef41Sopenharmony_ci HTTP2_METHOD_GET, 3001cb0ef41Sopenharmony_ci HTTP2_METHOD_HEAD, 3011cb0ef41Sopenharmony_ci HTTP2_METHOD_CONNECT, 3021cb0ef41Sopenharmony_ci 3031cb0ef41Sopenharmony_ci HTTP_STATUS_CONTINUE, 3041cb0ef41Sopenharmony_ci HTTP_STATUS_RESET_CONTENT, 3051cb0ef41Sopenharmony_ci HTTP_STATUS_OK, 3061cb0ef41Sopenharmony_ci HTTP_STATUS_NO_CONTENT, 3071cb0ef41Sopenharmony_ci HTTP_STATUS_NOT_MODIFIED, 3081cb0ef41Sopenharmony_ci HTTP_STATUS_SWITCHING_PROTOCOLS, 3091cb0ef41Sopenharmony_ci HTTP_STATUS_MISDIRECTED_REQUEST, 3101cb0ef41Sopenharmony_ci 3111cb0ef41Sopenharmony_ci STREAM_OPTION_EMPTY_PAYLOAD, 3121cb0ef41Sopenharmony_ci STREAM_OPTION_GET_TRAILERS, 3131cb0ef41Sopenharmony_ci} = constants; 3141cb0ef41Sopenharmony_ci 3151cb0ef41Sopenharmony_ciconst STREAM_FLAGS_PENDING = 0x0; 3161cb0ef41Sopenharmony_ciconst STREAM_FLAGS_READY = 0x1; 3171cb0ef41Sopenharmony_ciconst STREAM_FLAGS_CLOSED = 0x2; 3181cb0ef41Sopenharmony_ciconst STREAM_FLAGS_HEADERS_SENT = 0x4; 3191cb0ef41Sopenharmony_ciconst STREAM_FLAGS_HEAD_REQUEST = 0x8; 3201cb0ef41Sopenharmony_ciconst STREAM_FLAGS_ABORTED = 0x10; 3211cb0ef41Sopenharmony_ciconst STREAM_FLAGS_HAS_TRAILERS = 0x20; 3221cb0ef41Sopenharmony_ci 3231cb0ef41Sopenharmony_ciconst SESSION_FLAGS_PENDING = 0x0; 3241cb0ef41Sopenharmony_ciconst SESSION_FLAGS_READY = 0x1; 3251cb0ef41Sopenharmony_ciconst SESSION_FLAGS_CLOSED = 0x2; 3261cb0ef41Sopenharmony_ciconst SESSION_FLAGS_DESTROYED = 0x4; 3271cb0ef41Sopenharmony_ci 3281cb0ef41Sopenharmony_ci// Top level to avoid creating a closure 3291cb0ef41Sopenharmony_cifunction emit(self, ...args) { 3301cb0ef41Sopenharmony_ci ReflectApply(self.emit, self, args); 3311cb0ef41Sopenharmony_ci} 3321cb0ef41Sopenharmony_ci 3331cb0ef41Sopenharmony_ci// Called when a new block of headers has been received for a given 3341cb0ef41Sopenharmony_ci// stream. The stream may or may not be new. If the stream is new, 3351cb0ef41Sopenharmony_ci// create the associated Http2Stream instance and emit the 'stream' 3361cb0ef41Sopenharmony_ci// event. If the stream is not new, emit the 'headers' event to pass 3371cb0ef41Sopenharmony_ci// the block of headers on. 3381cb0ef41Sopenharmony_cifunction onSessionHeaders(handle, id, cat, flags, headers, sensitiveHeaders) { 3391cb0ef41Sopenharmony_ci const session = this[kOwner]; 3401cb0ef41Sopenharmony_ci if (session.destroyed) 3411cb0ef41Sopenharmony_ci return; 3421cb0ef41Sopenharmony_ci 3431cb0ef41Sopenharmony_ci const type = session[kType]; 3441cb0ef41Sopenharmony_ci session[kUpdateTimer](); 3451cb0ef41Sopenharmony_ci debugStream(id, type, 'headers received'); 3461cb0ef41Sopenharmony_ci const streams = session[kState].streams; 3471cb0ef41Sopenharmony_ci 3481cb0ef41Sopenharmony_ci const endOfStream = !!(flags & NGHTTP2_FLAG_END_STREAM); 3491cb0ef41Sopenharmony_ci let stream = streams.get(id); 3501cb0ef41Sopenharmony_ci 3511cb0ef41Sopenharmony_ci // Convert the array of header name value pairs into an object 3521cb0ef41Sopenharmony_ci const obj = toHeaderObject(headers, sensitiveHeaders); 3531cb0ef41Sopenharmony_ci 3541cb0ef41Sopenharmony_ci if (stream === undefined) { 3551cb0ef41Sopenharmony_ci if (session.closed) { 3561cb0ef41Sopenharmony_ci // We are not accepting any new streams at this point. This callback 3571cb0ef41Sopenharmony_ci // should not be invoked at this point in time, but just in case it is, 3581cb0ef41Sopenharmony_ci // refuse the stream using an RST_STREAM and destroy the handle. 3591cb0ef41Sopenharmony_ci handle.rstStream(NGHTTP2_REFUSED_STREAM); 3601cb0ef41Sopenharmony_ci handle.destroy(); 3611cb0ef41Sopenharmony_ci return; 3621cb0ef41Sopenharmony_ci } 3631cb0ef41Sopenharmony_ci // session[kType] can be only one of two possible values 3641cb0ef41Sopenharmony_ci if (type === NGHTTP2_SESSION_SERVER) { 3651cb0ef41Sopenharmony_ci // eslint-disable-next-line no-use-before-define 3661cb0ef41Sopenharmony_ci stream = new ServerHttp2Stream(session, handle, id, {}, obj); 3671cb0ef41Sopenharmony_ci if (endOfStream) { 3681cb0ef41Sopenharmony_ci stream.push(null); 3691cb0ef41Sopenharmony_ci } 3701cb0ef41Sopenharmony_ci if (obj[HTTP2_HEADER_METHOD] === HTTP2_METHOD_HEAD) { 3711cb0ef41Sopenharmony_ci // For head requests, there must not be a body... 3721cb0ef41Sopenharmony_ci // end the writable side immediately. 3731cb0ef41Sopenharmony_ci stream.end(); 3741cb0ef41Sopenharmony_ci stream[kState].flags |= STREAM_FLAGS_HEAD_REQUEST; 3751cb0ef41Sopenharmony_ci } 3761cb0ef41Sopenharmony_ci } else { 3771cb0ef41Sopenharmony_ci // eslint-disable-next-line no-use-before-define 3781cb0ef41Sopenharmony_ci stream = new ClientHttp2Stream(session, handle, id, {}); 3791cb0ef41Sopenharmony_ci if (endOfStream) { 3801cb0ef41Sopenharmony_ci stream.push(null); 3811cb0ef41Sopenharmony_ci } 3821cb0ef41Sopenharmony_ci stream.end(); 3831cb0ef41Sopenharmony_ci } 3841cb0ef41Sopenharmony_ci if (endOfStream) 3851cb0ef41Sopenharmony_ci stream[kState].endAfterHeaders = true; 3861cb0ef41Sopenharmony_ci process.nextTick(emit, session, 'stream', stream, obj, flags, headers); 3871cb0ef41Sopenharmony_ci } else { 3881cb0ef41Sopenharmony_ci let event; 3891cb0ef41Sopenharmony_ci const status = obj[HTTP2_HEADER_STATUS]; 3901cb0ef41Sopenharmony_ci if (cat === NGHTTP2_HCAT_RESPONSE) { 3911cb0ef41Sopenharmony_ci if (!endOfStream && 3921cb0ef41Sopenharmony_ci status !== undefined && 3931cb0ef41Sopenharmony_ci status >= 100 && 3941cb0ef41Sopenharmony_ci status < 200) { 3951cb0ef41Sopenharmony_ci event = 'headers'; 3961cb0ef41Sopenharmony_ci } else { 3971cb0ef41Sopenharmony_ci event = 'response'; 3981cb0ef41Sopenharmony_ci } 3991cb0ef41Sopenharmony_ci } else if (cat === NGHTTP2_HCAT_PUSH_RESPONSE) { 4001cb0ef41Sopenharmony_ci event = 'push'; 4011cb0ef41Sopenharmony_ci } else if (status !== undefined && status >= 200) { 4021cb0ef41Sopenharmony_ci event = 'response'; 4031cb0ef41Sopenharmony_ci } else { 4041cb0ef41Sopenharmony_ci event = endOfStream ? 'trailers' : 'headers'; 4051cb0ef41Sopenharmony_ci } 4061cb0ef41Sopenharmony_ci const session = stream.session; 4071cb0ef41Sopenharmony_ci if (status === HTTP_STATUS_MISDIRECTED_REQUEST) { 4081cb0ef41Sopenharmony_ci const originSet = session[kState].originSet = initOriginSet(session); 4091cb0ef41Sopenharmony_ci originSet.delete(stream[kOrigin]); 4101cb0ef41Sopenharmony_ci } 4111cb0ef41Sopenharmony_ci debugStream(id, type, "emitting stream '%s' event", event); 4121cb0ef41Sopenharmony_ci process.nextTick(emit, stream, event, obj, flags, headers); 4131cb0ef41Sopenharmony_ci } 4141cb0ef41Sopenharmony_ci if (endOfStream) { 4151cb0ef41Sopenharmony_ci stream.push(null); 4161cb0ef41Sopenharmony_ci } 4171cb0ef41Sopenharmony_ci} 4181cb0ef41Sopenharmony_ci 4191cb0ef41Sopenharmony_cifunction tryClose(fd) { 4201cb0ef41Sopenharmony_ci // Try to close the file descriptor. If closing fails, assert because 4211cb0ef41Sopenharmony_ci // an error really should not happen at this point. 4221cb0ef41Sopenharmony_ci fs.close(fd, assert.ifError); 4231cb0ef41Sopenharmony_ci} 4241cb0ef41Sopenharmony_ci 4251cb0ef41Sopenharmony_ci// Called when the Http2Stream has finished sending data and is ready for 4261cb0ef41Sopenharmony_ci// trailers to be sent. This will only be called if the { hasOptions: true } 4271cb0ef41Sopenharmony_ci// option is set. 4281cb0ef41Sopenharmony_cifunction onStreamTrailers() { 4291cb0ef41Sopenharmony_ci const stream = this[kOwner]; 4301cb0ef41Sopenharmony_ci stream[kState].trailersReady = true; 4311cb0ef41Sopenharmony_ci if (stream.destroyed || stream.closed) 4321cb0ef41Sopenharmony_ci return; 4331cb0ef41Sopenharmony_ci if (!stream.emit('wantTrailers')) { 4341cb0ef41Sopenharmony_ci // There are no listeners, send empty trailing HEADERS frame and close. 4351cb0ef41Sopenharmony_ci stream.sendTrailers({}); 4361cb0ef41Sopenharmony_ci } 4371cb0ef41Sopenharmony_ci} 4381cb0ef41Sopenharmony_ci 4391cb0ef41Sopenharmony_ci// Submit an RST-STREAM frame to be sent to the remote peer. 4401cb0ef41Sopenharmony_ci// This will cause the Http2Stream to be closed. 4411cb0ef41Sopenharmony_cifunction submitRstStream(code) { 4421cb0ef41Sopenharmony_ci if (this[kHandle] !== undefined) { 4431cb0ef41Sopenharmony_ci this[kHandle].rstStream(code); 4441cb0ef41Sopenharmony_ci } 4451cb0ef41Sopenharmony_ci} 4461cb0ef41Sopenharmony_ci 4471cb0ef41Sopenharmony_ci// Keep track of the number/presence of JS event listeners. Knowing that there 4481cb0ef41Sopenharmony_ci// are no listeners allows the C++ code to skip calling into JS for an event. 4491cb0ef41Sopenharmony_cifunction sessionListenerAdded(name) { 4501cb0ef41Sopenharmony_ci switch (name) { 4511cb0ef41Sopenharmony_ci case 'ping': 4521cb0ef41Sopenharmony_ci this[kNativeFields][kBitfield] |= 1 << kSessionHasPingListeners; 4531cb0ef41Sopenharmony_ci break; 4541cb0ef41Sopenharmony_ci case 'altsvc': 4551cb0ef41Sopenharmony_ci this[kNativeFields][kBitfield] |= 1 << kSessionHasAltsvcListeners; 4561cb0ef41Sopenharmony_ci break; 4571cb0ef41Sopenharmony_ci case 'remoteSettings': 4581cb0ef41Sopenharmony_ci this[kNativeFields][kBitfield] |= 1 << kSessionHasRemoteSettingsListeners; 4591cb0ef41Sopenharmony_ci break; 4601cb0ef41Sopenharmony_ci case 'priority': 4611cb0ef41Sopenharmony_ci this[kNativeFields][kSessionPriorityListenerCount]++; 4621cb0ef41Sopenharmony_ci break; 4631cb0ef41Sopenharmony_ci case 'frameError': 4641cb0ef41Sopenharmony_ci this[kNativeFields][kSessionFrameErrorListenerCount]++; 4651cb0ef41Sopenharmony_ci break; 4661cb0ef41Sopenharmony_ci } 4671cb0ef41Sopenharmony_ci} 4681cb0ef41Sopenharmony_ci 4691cb0ef41Sopenharmony_cifunction sessionListenerRemoved(name) { 4701cb0ef41Sopenharmony_ci switch (name) { 4711cb0ef41Sopenharmony_ci case 'ping': 4721cb0ef41Sopenharmony_ci if (this.listenerCount(name) > 0) return; 4731cb0ef41Sopenharmony_ci this[kNativeFields][kBitfield] &= ~(1 << kSessionHasPingListeners); 4741cb0ef41Sopenharmony_ci break; 4751cb0ef41Sopenharmony_ci case 'altsvc': 4761cb0ef41Sopenharmony_ci if (this.listenerCount(name) > 0) return; 4771cb0ef41Sopenharmony_ci this[kNativeFields][kBitfield] &= ~(1 << kSessionHasAltsvcListeners); 4781cb0ef41Sopenharmony_ci break; 4791cb0ef41Sopenharmony_ci case 'remoteSettings': 4801cb0ef41Sopenharmony_ci if (this.listenerCount(name) > 0) return; 4811cb0ef41Sopenharmony_ci this[kNativeFields][kBitfield] &= 4821cb0ef41Sopenharmony_ci ~(1 << kSessionHasRemoteSettingsListeners); 4831cb0ef41Sopenharmony_ci break; 4841cb0ef41Sopenharmony_ci case 'priority': 4851cb0ef41Sopenharmony_ci this[kNativeFields][kSessionPriorityListenerCount]--; 4861cb0ef41Sopenharmony_ci break; 4871cb0ef41Sopenharmony_ci case 'frameError': 4881cb0ef41Sopenharmony_ci this[kNativeFields][kSessionFrameErrorListenerCount]--; 4891cb0ef41Sopenharmony_ci break; 4901cb0ef41Sopenharmony_ci } 4911cb0ef41Sopenharmony_ci} 4921cb0ef41Sopenharmony_ci 4931cb0ef41Sopenharmony_ci// Also keep track of listeners for the Http2Stream instances, as some events 4941cb0ef41Sopenharmony_ci// are emitted on those objects. 4951cb0ef41Sopenharmony_cifunction streamListenerAdded(name) { 4961cb0ef41Sopenharmony_ci const session = this[kSession]; 4971cb0ef41Sopenharmony_ci if (!session) return; 4981cb0ef41Sopenharmony_ci switch (name) { 4991cb0ef41Sopenharmony_ci case 'priority': 5001cb0ef41Sopenharmony_ci session[kNativeFields][kSessionPriorityListenerCount]++; 5011cb0ef41Sopenharmony_ci break; 5021cb0ef41Sopenharmony_ci case 'frameError': 5031cb0ef41Sopenharmony_ci session[kNativeFields][kSessionFrameErrorListenerCount]++; 5041cb0ef41Sopenharmony_ci break; 5051cb0ef41Sopenharmony_ci } 5061cb0ef41Sopenharmony_ci} 5071cb0ef41Sopenharmony_ci 5081cb0ef41Sopenharmony_cifunction streamListenerRemoved(name) { 5091cb0ef41Sopenharmony_ci const session = this[kSession]; 5101cb0ef41Sopenharmony_ci if (!session) return; 5111cb0ef41Sopenharmony_ci switch (name) { 5121cb0ef41Sopenharmony_ci case 'priority': 5131cb0ef41Sopenharmony_ci session[kNativeFields][kSessionPriorityListenerCount]--; 5141cb0ef41Sopenharmony_ci break; 5151cb0ef41Sopenharmony_ci case 'frameError': 5161cb0ef41Sopenharmony_ci session[kNativeFields][kSessionFrameErrorListenerCount]--; 5171cb0ef41Sopenharmony_ci break; 5181cb0ef41Sopenharmony_ci } 5191cb0ef41Sopenharmony_ci} 5201cb0ef41Sopenharmony_ci 5211cb0ef41Sopenharmony_cifunction onPing(payload) { 5221cb0ef41Sopenharmony_ci const session = this[kOwner]; 5231cb0ef41Sopenharmony_ci if (session.destroyed) 5241cb0ef41Sopenharmony_ci return; 5251cb0ef41Sopenharmony_ci session[kUpdateTimer](); 5261cb0ef41Sopenharmony_ci debugSessionObj(session, 'new ping received'); 5271cb0ef41Sopenharmony_ci session.emit('ping', payload); 5281cb0ef41Sopenharmony_ci} 5291cb0ef41Sopenharmony_ci 5301cb0ef41Sopenharmony_ci// Called when the stream is closed either by sending or receiving an 5311cb0ef41Sopenharmony_ci// RST_STREAM frame, or through a natural end-of-stream. 5321cb0ef41Sopenharmony_ci// If the writable and readable sides of the stream are still open at this 5331cb0ef41Sopenharmony_ci// point, close them. If there is an open fd for file send, close that also. 5341cb0ef41Sopenharmony_ci// At this point the underlying node::http2:Http2Stream handle is no 5351cb0ef41Sopenharmony_ci// longer usable so destroy it also. 5361cb0ef41Sopenharmony_cifunction onStreamClose(code) { 5371cb0ef41Sopenharmony_ci const stream = this[kOwner]; 5381cb0ef41Sopenharmony_ci if (!stream || stream.destroyed) 5391cb0ef41Sopenharmony_ci return false; 5401cb0ef41Sopenharmony_ci 5411cb0ef41Sopenharmony_ci debugStreamObj( 5421cb0ef41Sopenharmony_ci stream, 'closed with code %d, closed %s, readable %s', 5431cb0ef41Sopenharmony_ci code, stream.closed, stream.readable, 5441cb0ef41Sopenharmony_ci ); 5451cb0ef41Sopenharmony_ci 5461cb0ef41Sopenharmony_ci if (!stream.closed) 5471cb0ef41Sopenharmony_ci closeStream(stream, code, kNoRstStream); 5481cb0ef41Sopenharmony_ci 5491cb0ef41Sopenharmony_ci stream[kState].fd = -1; 5501cb0ef41Sopenharmony_ci // Defer destroy we actually emit end. 5511cb0ef41Sopenharmony_ci if (!stream.readable || code !== NGHTTP2_NO_ERROR) { 5521cb0ef41Sopenharmony_ci // If errored or ended, we can destroy immediately. 5531cb0ef41Sopenharmony_ci stream.destroy(); 5541cb0ef41Sopenharmony_ci } else { 5551cb0ef41Sopenharmony_ci // Wait for end to destroy. 5561cb0ef41Sopenharmony_ci stream.on('end', stream[kMaybeDestroy]); 5571cb0ef41Sopenharmony_ci // Push a null so the stream can end whenever the client consumes 5581cb0ef41Sopenharmony_ci // it completely. 5591cb0ef41Sopenharmony_ci stream.push(null); 5601cb0ef41Sopenharmony_ci 5611cb0ef41Sopenharmony_ci // If the user hasn't tried to consume the stream (and this is a server 5621cb0ef41Sopenharmony_ci // session) then just dump the incoming data so that the stream can 5631cb0ef41Sopenharmony_ci // be destroyed. 5641cb0ef41Sopenharmony_ci if (stream[kSession][kType] === NGHTTP2_SESSION_SERVER && 5651cb0ef41Sopenharmony_ci !stream[kState].didRead && 5661cb0ef41Sopenharmony_ci stream.readableFlowing === null) 5671cb0ef41Sopenharmony_ci stream.resume(); 5681cb0ef41Sopenharmony_ci else 5691cb0ef41Sopenharmony_ci stream.read(0); 5701cb0ef41Sopenharmony_ci } 5711cb0ef41Sopenharmony_ci return true; 5721cb0ef41Sopenharmony_ci} 5731cb0ef41Sopenharmony_ci 5741cb0ef41Sopenharmony_ci// Called when the remote peer settings have been updated. 5751cb0ef41Sopenharmony_ci// Resets the cached settings. 5761cb0ef41Sopenharmony_cifunction onSettings() { 5771cb0ef41Sopenharmony_ci const session = this[kOwner]; 5781cb0ef41Sopenharmony_ci if (session.destroyed) 5791cb0ef41Sopenharmony_ci return; 5801cb0ef41Sopenharmony_ci session[kUpdateTimer](); 5811cb0ef41Sopenharmony_ci debugSessionObj(session, 'new settings received'); 5821cb0ef41Sopenharmony_ci session[kRemoteSettings] = undefined; 5831cb0ef41Sopenharmony_ci session.emit('remoteSettings', session.remoteSettings); 5841cb0ef41Sopenharmony_ci} 5851cb0ef41Sopenharmony_ci 5861cb0ef41Sopenharmony_ci// If the stream exists, an attempt will be made to emit an event 5871cb0ef41Sopenharmony_ci// on the stream object itself. Otherwise, forward it on to the 5881cb0ef41Sopenharmony_ci// session (which may, in turn, forward it on to the server) 5891cb0ef41Sopenharmony_cifunction onPriority(id, parent, weight, exclusive) { 5901cb0ef41Sopenharmony_ci const session = this[kOwner]; 5911cb0ef41Sopenharmony_ci if (session.destroyed) 5921cb0ef41Sopenharmony_ci return; 5931cb0ef41Sopenharmony_ci debugStream(id, session[kType], 5941cb0ef41Sopenharmony_ci 'priority [parent: %d, weight: %d, exclusive: %s]', 5951cb0ef41Sopenharmony_ci parent, weight, exclusive); 5961cb0ef41Sopenharmony_ci const emitter = session[kState].streams.get(id) || session; 5971cb0ef41Sopenharmony_ci if (!emitter.destroyed) { 5981cb0ef41Sopenharmony_ci emitter[kUpdateTimer](); 5991cb0ef41Sopenharmony_ci emitter.emit('priority', id, parent, weight, exclusive); 6001cb0ef41Sopenharmony_ci } 6011cb0ef41Sopenharmony_ci} 6021cb0ef41Sopenharmony_ci 6031cb0ef41Sopenharmony_ci// Called by the native layer when an error has occurred sending a 6041cb0ef41Sopenharmony_ci// frame. This should be exceedingly rare. 6051cb0ef41Sopenharmony_cifunction onFrameError(id, type, code) { 6061cb0ef41Sopenharmony_ci const session = this[kOwner]; 6071cb0ef41Sopenharmony_ci if (session.destroyed) 6081cb0ef41Sopenharmony_ci return; 6091cb0ef41Sopenharmony_ci debugSessionObj(session, 'error sending frame type %d on stream %d, code: %d', 6101cb0ef41Sopenharmony_ci type, id, code); 6111cb0ef41Sopenharmony_ci const emitter = session[kState].streams.get(id) || session; 6121cb0ef41Sopenharmony_ci emitter[kUpdateTimer](); 6131cb0ef41Sopenharmony_ci emitter.emit('frameError', type, code, id); 6141cb0ef41Sopenharmony_ci session[kState].streams.get(id).close(code); 6151cb0ef41Sopenharmony_ci session.close(); 6161cb0ef41Sopenharmony_ci} 6171cb0ef41Sopenharmony_ci 6181cb0ef41Sopenharmony_cifunction onAltSvc(stream, origin, alt) { 6191cb0ef41Sopenharmony_ci const session = this[kOwner]; 6201cb0ef41Sopenharmony_ci if (session.destroyed) 6211cb0ef41Sopenharmony_ci return; 6221cb0ef41Sopenharmony_ci debugSessionObj(session, 'altsvc received: stream: %d, origin: %s, alt: %s', 6231cb0ef41Sopenharmony_ci stream, origin, alt); 6241cb0ef41Sopenharmony_ci session[kUpdateTimer](); 6251cb0ef41Sopenharmony_ci session.emit('altsvc', alt, origin, stream); 6261cb0ef41Sopenharmony_ci} 6271cb0ef41Sopenharmony_ci 6281cb0ef41Sopenharmony_cifunction initOriginSet(session) { 6291cb0ef41Sopenharmony_ci let originSet = session[kState].originSet; 6301cb0ef41Sopenharmony_ci if (originSet === undefined) { 6311cb0ef41Sopenharmony_ci const socket = session[kSocket]; 6321cb0ef41Sopenharmony_ci session[kState].originSet = originSet = new SafeSet(); 6331cb0ef41Sopenharmony_ci if (socket.servername != null) { 6341cb0ef41Sopenharmony_ci let originString = `https://${socket.servername}`; 6351cb0ef41Sopenharmony_ci if (socket.remotePort != null) 6361cb0ef41Sopenharmony_ci originString += `:${socket.remotePort}`; 6371cb0ef41Sopenharmony_ci // We have to ensure that it is a properly serialized 6381cb0ef41Sopenharmony_ci // ASCII origin string. The socket.servername might not 6391cb0ef41Sopenharmony_ci // be properly ASCII encoded. 6401cb0ef41Sopenharmony_ci originSet.add((new URL(originString)).origin); 6411cb0ef41Sopenharmony_ci } 6421cb0ef41Sopenharmony_ci } 6431cb0ef41Sopenharmony_ci return originSet; 6441cb0ef41Sopenharmony_ci} 6451cb0ef41Sopenharmony_ci 6461cb0ef41Sopenharmony_cifunction onOrigin(origins) { 6471cb0ef41Sopenharmony_ci const session = this[kOwner]; 6481cb0ef41Sopenharmony_ci if (session.destroyed) 6491cb0ef41Sopenharmony_ci return; 6501cb0ef41Sopenharmony_ci debugSessionObj(session, 'origin received: %j', origins); 6511cb0ef41Sopenharmony_ci session[kUpdateTimer](); 6521cb0ef41Sopenharmony_ci if (!session.encrypted || session.destroyed) 6531cb0ef41Sopenharmony_ci return undefined; 6541cb0ef41Sopenharmony_ci const originSet = initOriginSet(session); 6551cb0ef41Sopenharmony_ci for (let n = 0; n < origins.length; n++) 6561cb0ef41Sopenharmony_ci originSet.add(origins[n]); 6571cb0ef41Sopenharmony_ci session.emit('origin', origins); 6581cb0ef41Sopenharmony_ci} 6591cb0ef41Sopenharmony_ci 6601cb0ef41Sopenharmony_ci// Receiving a GOAWAY frame from the connected peer is a signal that no 6611cb0ef41Sopenharmony_ci// new streams should be created. If the code === NGHTTP2_NO_ERROR, we 6621cb0ef41Sopenharmony_ci// are going to send our close, but allow existing frames to close 6631cb0ef41Sopenharmony_ci// normally. If code !== NGHTTP2_NO_ERROR, we are going to send our own 6641cb0ef41Sopenharmony_ci// close using the same code then destroy the session with an error. 6651cb0ef41Sopenharmony_ci// The goaway event will be emitted on next tick. 6661cb0ef41Sopenharmony_cifunction onGoawayData(code, lastStreamID, buf) { 6671cb0ef41Sopenharmony_ci const session = this[kOwner]; 6681cb0ef41Sopenharmony_ci if (session.destroyed) 6691cb0ef41Sopenharmony_ci return; 6701cb0ef41Sopenharmony_ci debugSessionObj(session, 'goaway %d received [last stream id: %d]', 6711cb0ef41Sopenharmony_ci code, lastStreamID); 6721cb0ef41Sopenharmony_ci 6731cb0ef41Sopenharmony_ci const state = session[kState]; 6741cb0ef41Sopenharmony_ci state.goawayCode = code; 6751cb0ef41Sopenharmony_ci state.goawayLastStreamID = lastStreamID; 6761cb0ef41Sopenharmony_ci 6771cb0ef41Sopenharmony_ci session.emit('goaway', code, lastStreamID, buf); 6781cb0ef41Sopenharmony_ci if (code === NGHTTP2_NO_ERROR) { 6791cb0ef41Sopenharmony_ci // If this is a no error goaway, begin shutting down. 6801cb0ef41Sopenharmony_ci // No new streams permitted, but existing streams may 6811cb0ef41Sopenharmony_ci // close naturally on their own. 6821cb0ef41Sopenharmony_ci session.close(); 6831cb0ef41Sopenharmony_ci } else { 6841cb0ef41Sopenharmony_ci // However, if the code is not NGHTTP_NO_ERROR, destroy the 6851cb0ef41Sopenharmony_ci // session immediately. We destroy with an error but send a 6861cb0ef41Sopenharmony_ci // goaway using NGHTTP2_NO_ERROR because there was no error 6871cb0ef41Sopenharmony_ci // condition on this side of the session that caused the 6881cb0ef41Sopenharmony_ci // shutdown. 6891cb0ef41Sopenharmony_ci session.destroy(new ERR_HTTP2_SESSION_ERROR(code), NGHTTP2_NO_ERROR); 6901cb0ef41Sopenharmony_ci } 6911cb0ef41Sopenharmony_ci} 6921cb0ef41Sopenharmony_ci 6931cb0ef41Sopenharmony_ci// When a ClientHttp2Session is first created, the socket may not yet be 6941cb0ef41Sopenharmony_ci// connected. If request() is called during this time, the actual request 6951cb0ef41Sopenharmony_ci// will be deferred until the socket is ready to go. 6961cb0ef41Sopenharmony_cifunction requestOnConnect(headers, options) { 6971cb0ef41Sopenharmony_ci const session = this[kSession]; 6981cb0ef41Sopenharmony_ci 6991cb0ef41Sopenharmony_ci // At this point, the stream should have already been destroyed during 7001cb0ef41Sopenharmony_ci // the session.destroy() method. Do nothing else. 7011cb0ef41Sopenharmony_ci if (session === undefined || session.destroyed) 7021cb0ef41Sopenharmony_ci return; 7031cb0ef41Sopenharmony_ci 7041cb0ef41Sopenharmony_ci // If the session was closed while waiting for the connect, destroy 7051cb0ef41Sopenharmony_ci // the stream and do not continue with the request. 7061cb0ef41Sopenharmony_ci if (session.closed) { 7071cb0ef41Sopenharmony_ci const err = new ERR_HTTP2_GOAWAY_SESSION(); 7081cb0ef41Sopenharmony_ci this.destroy(err); 7091cb0ef41Sopenharmony_ci return; 7101cb0ef41Sopenharmony_ci } 7111cb0ef41Sopenharmony_ci 7121cb0ef41Sopenharmony_ci debugSessionObj(session, 'connected, initializing request'); 7131cb0ef41Sopenharmony_ci 7141cb0ef41Sopenharmony_ci let streamOptions = 0; 7151cb0ef41Sopenharmony_ci if (options.endStream) 7161cb0ef41Sopenharmony_ci streamOptions |= STREAM_OPTION_EMPTY_PAYLOAD; 7171cb0ef41Sopenharmony_ci 7181cb0ef41Sopenharmony_ci if (options.waitForTrailers) 7191cb0ef41Sopenharmony_ci streamOptions |= STREAM_OPTION_GET_TRAILERS; 7201cb0ef41Sopenharmony_ci 7211cb0ef41Sopenharmony_ci // `ret` will be either the reserved stream ID (if positive) 7221cb0ef41Sopenharmony_ci // or an error code (if negative) 7231cb0ef41Sopenharmony_ci const ret = session[kHandle].request(headers, 7241cb0ef41Sopenharmony_ci streamOptions, 7251cb0ef41Sopenharmony_ci options.parent | 0, 7261cb0ef41Sopenharmony_ci options.weight | 0, 7271cb0ef41Sopenharmony_ci !!options.exclusive); 7281cb0ef41Sopenharmony_ci 7291cb0ef41Sopenharmony_ci // In an error condition, one of three possible response codes will be 7301cb0ef41Sopenharmony_ci // possible: 7311cb0ef41Sopenharmony_ci // * NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE - Maximum stream ID is reached, this 7321cb0ef41Sopenharmony_ci // is fatal for the session 7331cb0ef41Sopenharmony_ci // * NGHTTP2_ERR_INVALID_ARGUMENT - Stream was made dependent on itself, this 7341cb0ef41Sopenharmony_ci // impacts on this stream. 7351cb0ef41Sopenharmony_ci // For the first two, emit the error on the session, 7361cb0ef41Sopenharmony_ci // For the third, emit the error on the stream, it will bubble up to the 7371cb0ef41Sopenharmony_ci // session if not handled. 7381cb0ef41Sopenharmony_ci if (typeof ret === 'number') { 7391cb0ef41Sopenharmony_ci let err; 7401cb0ef41Sopenharmony_ci switch (ret) { 7411cb0ef41Sopenharmony_ci case NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE: 7421cb0ef41Sopenharmony_ci err = new ERR_HTTP2_OUT_OF_STREAMS(); 7431cb0ef41Sopenharmony_ci this.destroy(err); 7441cb0ef41Sopenharmony_ci break; 7451cb0ef41Sopenharmony_ci case NGHTTP2_ERR_INVALID_ARGUMENT: 7461cb0ef41Sopenharmony_ci err = new ERR_HTTP2_STREAM_SELF_DEPENDENCY(); 7471cb0ef41Sopenharmony_ci this.destroy(err); 7481cb0ef41Sopenharmony_ci break; 7491cb0ef41Sopenharmony_ci default: 7501cb0ef41Sopenharmony_ci session.destroy(new NghttpError(ret)); 7511cb0ef41Sopenharmony_ci } 7521cb0ef41Sopenharmony_ci return; 7531cb0ef41Sopenharmony_ci } 7541cb0ef41Sopenharmony_ci this[kInit](ret.id(), ret); 7551cb0ef41Sopenharmony_ci} 7561cb0ef41Sopenharmony_ci 7571cb0ef41Sopenharmony_ci// Validates that priority options are correct, specifically: 7581cb0ef41Sopenharmony_ci// 1. options.weight must be a number 7591cb0ef41Sopenharmony_ci// 2. options.parent must be a positive number 7601cb0ef41Sopenharmony_ci// 3. options.exclusive must be a boolean 7611cb0ef41Sopenharmony_ci// 4. if specified, options.silent must be a boolean 7621cb0ef41Sopenharmony_ci// 7631cb0ef41Sopenharmony_ci// Also sets the default priority options if they are not set. 7641cb0ef41Sopenharmony_ciconst setAndValidatePriorityOptions = hideStackFrames((options) => { 7651cb0ef41Sopenharmony_ci if (options.weight === undefined) { 7661cb0ef41Sopenharmony_ci options.weight = NGHTTP2_DEFAULT_WEIGHT; 7671cb0ef41Sopenharmony_ci } else if (typeof options.weight !== 'number') { 7681cb0ef41Sopenharmony_ci throw new ERR_INVALID_ARG_VALUE('options.weight', options.weight); 7691cb0ef41Sopenharmony_ci } 7701cb0ef41Sopenharmony_ci 7711cb0ef41Sopenharmony_ci if (options.parent === undefined) { 7721cb0ef41Sopenharmony_ci options.parent = 0; 7731cb0ef41Sopenharmony_ci } else if (typeof options.parent !== 'number' || options.parent < 0) { 7741cb0ef41Sopenharmony_ci throw new ERR_INVALID_ARG_VALUE('options.parent', options.parent); 7751cb0ef41Sopenharmony_ci } 7761cb0ef41Sopenharmony_ci 7771cb0ef41Sopenharmony_ci if (options.exclusive === undefined) { 7781cb0ef41Sopenharmony_ci options.exclusive = false; 7791cb0ef41Sopenharmony_ci } else if (typeof options.exclusive !== 'boolean') { 7801cb0ef41Sopenharmony_ci throw new ERR_INVALID_ARG_VALUE('options.exclusive', options.exclusive); 7811cb0ef41Sopenharmony_ci } 7821cb0ef41Sopenharmony_ci 7831cb0ef41Sopenharmony_ci if (options.silent === undefined) { 7841cb0ef41Sopenharmony_ci options.silent = false; 7851cb0ef41Sopenharmony_ci } else if (typeof options.silent !== 'boolean') { 7861cb0ef41Sopenharmony_ci throw new ERR_INVALID_ARG_VALUE('options.silent', options.silent); 7871cb0ef41Sopenharmony_ci } 7881cb0ef41Sopenharmony_ci}); 7891cb0ef41Sopenharmony_ci 7901cb0ef41Sopenharmony_ci// When an error occurs internally at the binding level, immediately 7911cb0ef41Sopenharmony_ci// destroy the session. 7921cb0ef41Sopenharmony_cifunction onSessionInternalError(integerCode, customErrorCode) { 7931cb0ef41Sopenharmony_ci if (this[kOwner] !== undefined) 7941cb0ef41Sopenharmony_ci this[kOwner].destroy(new NghttpError(integerCode, customErrorCode)); 7951cb0ef41Sopenharmony_ci} 7961cb0ef41Sopenharmony_ci 7971cb0ef41Sopenharmony_cifunction settingsCallback(cb, ack, duration) { 7981cb0ef41Sopenharmony_ci this[kState].pendingAck--; 7991cb0ef41Sopenharmony_ci this[kLocalSettings] = undefined; 8001cb0ef41Sopenharmony_ci if (ack) { 8011cb0ef41Sopenharmony_ci debugSessionObj(this, 'settings received'); 8021cb0ef41Sopenharmony_ci const settings = this.localSettings; 8031cb0ef41Sopenharmony_ci if (typeof cb === 'function') 8041cb0ef41Sopenharmony_ci cb(null, settings, duration); 8051cb0ef41Sopenharmony_ci this.emit('localSettings', settings); 8061cb0ef41Sopenharmony_ci } else { 8071cb0ef41Sopenharmony_ci debugSessionObj(this, 'settings canceled'); 8081cb0ef41Sopenharmony_ci if (typeof cb === 'function') 8091cb0ef41Sopenharmony_ci cb(new ERR_HTTP2_SETTINGS_CANCEL()); 8101cb0ef41Sopenharmony_ci } 8111cb0ef41Sopenharmony_ci} 8121cb0ef41Sopenharmony_ci 8131cb0ef41Sopenharmony_ci// Submits a SETTINGS frame to be sent to the remote peer. 8141cb0ef41Sopenharmony_cifunction submitSettings(settings, callback) { 8151cb0ef41Sopenharmony_ci if (this.destroyed) 8161cb0ef41Sopenharmony_ci return; 8171cb0ef41Sopenharmony_ci debugSessionObj(this, 'submitting settings'); 8181cb0ef41Sopenharmony_ci this[kUpdateTimer](); 8191cb0ef41Sopenharmony_ci updateSettingsBuffer(settings); 8201cb0ef41Sopenharmony_ci if (!this[kHandle].settings(FunctionPrototypeBind(settingsCallback, 8211cb0ef41Sopenharmony_ci this, callback))) { 8221cb0ef41Sopenharmony_ci this.destroy(new ERR_HTTP2_MAX_PENDING_SETTINGS_ACK()); 8231cb0ef41Sopenharmony_ci } 8241cb0ef41Sopenharmony_ci} 8251cb0ef41Sopenharmony_ci 8261cb0ef41Sopenharmony_ci// Submits a PRIORITY frame to be sent to the remote peer 8271cb0ef41Sopenharmony_ci// Note: If the silent option is true, the change will be made 8281cb0ef41Sopenharmony_ci// locally with no PRIORITY frame sent. 8291cb0ef41Sopenharmony_cifunction submitPriority(options) { 8301cb0ef41Sopenharmony_ci if (this.destroyed) 8311cb0ef41Sopenharmony_ci return; 8321cb0ef41Sopenharmony_ci this[kUpdateTimer](); 8331cb0ef41Sopenharmony_ci 8341cb0ef41Sopenharmony_ci // If the parent is the id, do nothing because a 8351cb0ef41Sopenharmony_ci // stream cannot be made to depend on itself. 8361cb0ef41Sopenharmony_ci if (options.parent === this[kID]) 8371cb0ef41Sopenharmony_ci return; 8381cb0ef41Sopenharmony_ci 8391cb0ef41Sopenharmony_ci this[kHandle].priority(options.parent | 0, 8401cb0ef41Sopenharmony_ci options.weight | 0, 8411cb0ef41Sopenharmony_ci !!options.exclusive, 8421cb0ef41Sopenharmony_ci !!options.silent); 8431cb0ef41Sopenharmony_ci} 8441cb0ef41Sopenharmony_ci 8451cb0ef41Sopenharmony_ci// Submit a GOAWAY frame to be sent to the remote peer. 8461cb0ef41Sopenharmony_ci// If the lastStreamID is set to <= 0, then the lastProcStreamID will 8471cb0ef41Sopenharmony_ci// be used. The opaqueData must either be a typed array or undefined 8481cb0ef41Sopenharmony_ci// (which will be checked elsewhere). 8491cb0ef41Sopenharmony_cifunction submitGoaway(code, lastStreamID, opaqueData) { 8501cb0ef41Sopenharmony_ci if (this.destroyed) 8511cb0ef41Sopenharmony_ci return; 8521cb0ef41Sopenharmony_ci debugSessionObj(this, 'submitting goaway'); 8531cb0ef41Sopenharmony_ci this[kUpdateTimer](); 8541cb0ef41Sopenharmony_ci this[kHandle].goaway(code, lastStreamID, opaqueData); 8551cb0ef41Sopenharmony_ci} 8561cb0ef41Sopenharmony_ci 8571cb0ef41Sopenharmony_ciconst proxySocketHandler = { 8581cb0ef41Sopenharmony_ci get(session, prop) { 8591cb0ef41Sopenharmony_ci switch (prop) { 8601cb0ef41Sopenharmony_ci case 'setTimeout': 8611cb0ef41Sopenharmony_ci case 'ref': 8621cb0ef41Sopenharmony_ci case 'unref': 8631cb0ef41Sopenharmony_ci return FunctionPrototypeBind(session[prop], session); 8641cb0ef41Sopenharmony_ci case 'destroy': 8651cb0ef41Sopenharmony_ci case 'emit': 8661cb0ef41Sopenharmony_ci case 'end': 8671cb0ef41Sopenharmony_ci case 'pause': 8681cb0ef41Sopenharmony_ci case 'read': 8691cb0ef41Sopenharmony_ci case 'resume': 8701cb0ef41Sopenharmony_ci case 'write': 8711cb0ef41Sopenharmony_ci case 'setEncoding': 8721cb0ef41Sopenharmony_ci case 'setKeepAlive': 8731cb0ef41Sopenharmony_ci case 'setNoDelay': 8741cb0ef41Sopenharmony_ci throw new ERR_HTTP2_NO_SOCKET_MANIPULATION(); 8751cb0ef41Sopenharmony_ci default: { 8761cb0ef41Sopenharmony_ci const socket = session[kSocket]; 8771cb0ef41Sopenharmony_ci if (socket === undefined) 8781cb0ef41Sopenharmony_ci throw new ERR_HTTP2_SOCKET_UNBOUND(); 8791cb0ef41Sopenharmony_ci const value = socket[prop]; 8801cb0ef41Sopenharmony_ci return typeof value === 'function' ? 8811cb0ef41Sopenharmony_ci FunctionPrototypeBind(value, socket) : 8821cb0ef41Sopenharmony_ci value; 8831cb0ef41Sopenharmony_ci } 8841cb0ef41Sopenharmony_ci } 8851cb0ef41Sopenharmony_ci }, 8861cb0ef41Sopenharmony_ci getPrototypeOf(session) { 8871cb0ef41Sopenharmony_ci const socket = session[kSocket]; 8881cb0ef41Sopenharmony_ci if (socket === undefined) 8891cb0ef41Sopenharmony_ci throw new ERR_HTTP2_SOCKET_UNBOUND(); 8901cb0ef41Sopenharmony_ci return ReflectGetPrototypeOf(socket); 8911cb0ef41Sopenharmony_ci }, 8921cb0ef41Sopenharmony_ci set(session, prop, value) { 8931cb0ef41Sopenharmony_ci switch (prop) { 8941cb0ef41Sopenharmony_ci case 'setTimeout': 8951cb0ef41Sopenharmony_ci case 'ref': 8961cb0ef41Sopenharmony_ci case 'unref': 8971cb0ef41Sopenharmony_ci session[prop] = value; 8981cb0ef41Sopenharmony_ci return true; 8991cb0ef41Sopenharmony_ci case 'destroy': 9001cb0ef41Sopenharmony_ci case 'emit': 9011cb0ef41Sopenharmony_ci case 'end': 9021cb0ef41Sopenharmony_ci case 'pause': 9031cb0ef41Sopenharmony_ci case 'read': 9041cb0ef41Sopenharmony_ci case 'resume': 9051cb0ef41Sopenharmony_ci case 'write': 9061cb0ef41Sopenharmony_ci case 'setEncoding': 9071cb0ef41Sopenharmony_ci case 'setKeepAlive': 9081cb0ef41Sopenharmony_ci case 'setNoDelay': 9091cb0ef41Sopenharmony_ci throw new ERR_HTTP2_NO_SOCKET_MANIPULATION(); 9101cb0ef41Sopenharmony_ci default: { 9111cb0ef41Sopenharmony_ci const socket = session[kSocket]; 9121cb0ef41Sopenharmony_ci if (socket === undefined) 9131cb0ef41Sopenharmony_ci throw new ERR_HTTP2_SOCKET_UNBOUND(); 9141cb0ef41Sopenharmony_ci socket[prop] = value; 9151cb0ef41Sopenharmony_ci return true; 9161cb0ef41Sopenharmony_ci } 9171cb0ef41Sopenharmony_ci } 9181cb0ef41Sopenharmony_ci }, 9191cb0ef41Sopenharmony_ci}; 9201cb0ef41Sopenharmony_ci 9211cb0ef41Sopenharmony_ci// pingCallback() returns a function that is invoked when an HTTP2 PING 9221cb0ef41Sopenharmony_ci// frame acknowledgement is received. The ack is either true or false to 9231cb0ef41Sopenharmony_ci// indicate if the ping was successful or not. The duration indicates the 9241cb0ef41Sopenharmony_ci// number of milliseconds elapsed since the ping was sent and the ack 9251cb0ef41Sopenharmony_ci// received. The payload is a Buffer containing the 8 bytes of payload 9261cb0ef41Sopenharmony_ci// data received on the PING acknowledgement. 9271cb0ef41Sopenharmony_cifunction pingCallback(cb) { 9281cb0ef41Sopenharmony_ci return function pingCallback(ack, duration, payload) { 9291cb0ef41Sopenharmony_ci if (ack) { 9301cb0ef41Sopenharmony_ci cb(null, duration, payload); 9311cb0ef41Sopenharmony_ci } else { 9321cb0ef41Sopenharmony_ci cb(new ERR_HTTP2_PING_CANCEL()); 9331cb0ef41Sopenharmony_ci } 9341cb0ef41Sopenharmony_ci }; 9351cb0ef41Sopenharmony_ci} 9361cb0ef41Sopenharmony_ci 9371cb0ef41Sopenharmony_ci// Validates the values in a settings object. Specifically: 9381cb0ef41Sopenharmony_ci// 1. headerTableSize must be a number in the range 0 <= n <= kMaxInt 9391cb0ef41Sopenharmony_ci// 2. initialWindowSize must be a number in the range 0 <= n <= kMaxInt 9401cb0ef41Sopenharmony_ci// 3. maxFrameSize must be a number in the range 16384 <= n <= kMaxFrameSize 9411cb0ef41Sopenharmony_ci// 4. maxConcurrentStreams must be a number in the range 0 <= n <= kMaxStreams 9421cb0ef41Sopenharmony_ci// 5. maxHeaderListSize must be a number in the range 0 <= n <= kMaxInt 9431cb0ef41Sopenharmony_ci// 6. enablePush must be a boolean 9441cb0ef41Sopenharmony_ci// 7. enableConnectProtocol must be a boolean 9451cb0ef41Sopenharmony_ci// All settings are optional and may be left undefined 9461cb0ef41Sopenharmony_ciconst validateSettings = hideStackFrames((settings) => { 9471cb0ef41Sopenharmony_ci if (settings === undefined) return; 9481cb0ef41Sopenharmony_ci assertWithinRange('headerTableSize', 9491cb0ef41Sopenharmony_ci settings.headerTableSize, 9501cb0ef41Sopenharmony_ci 0, kMaxInt); 9511cb0ef41Sopenharmony_ci assertWithinRange('initialWindowSize', 9521cb0ef41Sopenharmony_ci settings.initialWindowSize, 9531cb0ef41Sopenharmony_ci 0, kMaxInt); 9541cb0ef41Sopenharmony_ci assertWithinRange('maxFrameSize', 9551cb0ef41Sopenharmony_ci settings.maxFrameSize, 9561cb0ef41Sopenharmony_ci 16384, kMaxFrameSize); 9571cb0ef41Sopenharmony_ci assertWithinRange('maxConcurrentStreams', 9581cb0ef41Sopenharmony_ci settings.maxConcurrentStreams, 9591cb0ef41Sopenharmony_ci 0, kMaxStreams); 9601cb0ef41Sopenharmony_ci assertWithinRange('maxHeaderListSize', 9611cb0ef41Sopenharmony_ci settings.maxHeaderListSize, 9621cb0ef41Sopenharmony_ci 0, kMaxInt); 9631cb0ef41Sopenharmony_ci assertWithinRange('maxHeaderSize', 9641cb0ef41Sopenharmony_ci settings.maxHeaderSize, 9651cb0ef41Sopenharmony_ci 0, kMaxInt); 9661cb0ef41Sopenharmony_ci if (settings.enablePush !== undefined && 9671cb0ef41Sopenharmony_ci typeof settings.enablePush !== 'boolean') { 9681cb0ef41Sopenharmony_ci throw new ERR_HTTP2_INVALID_SETTING_VALUE('enablePush', 9691cb0ef41Sopenharmony_ci settings.enablePush); 9701cb0ef41Sopenharmony_ci } 9711cb0ef41Sopenharmony_ci if (settings.enableConnectProtocol !== undefined && 9721cb0ef41Sopenharmony_ci typeof settings.enableConnectProtocol !== 'boolean') { 9731cb0ef41Sopenharmony_ci throw new ERR_HTTP2_INVALID_SETTING_VALUE('enableConnectProtocol', 9741cb0ef41Sopenharmony_ci settings.enableConnectProtocol); 9751cb0ef41Sopenharmony_ci } 9761cb0ef41Sopenharmony_ci}); 9771cb0ef41Sopenharmony_ci 9781cb0ef41Sopenharmony_ci// Wrap a typed array in a proxy, and allow selectively copying the entries 9791cb0ef41Sopenharmony_ci// that have explicitly been set to another typed array. 9801cb0ef41Sopenharmony_cifunction trackAssignmentsTypedArray(typedArray) { 9811cb0ef41Sopenharmony_ci const typedArrayLength = TypedArrayPrototypeGetLength(typedArray); 9821cb0ef41Sopenharmony_ci const modifiedEntries = new Uint8Array(typedArrayLength); 9831cb0ef41Sopenharmony_ci 9841cb0ef41Sopenharmony_ci function copyAssigned(target) { 9851cb0ef41Sopenharmony_ci for (let i = 0; i < typedArrayLength; i++) { 9861cb0ef41Sopenharmony_ci if (modifiedEntries[i]) { 9871cb0ef41Sopenharmony_ci target[i] = typedArray[i]; 9881cb0ef41Sopenharmony_ci } 9891cb0ef41Sopenharmony_ci } 9901cb0ef41Sopenharmony_ci } 9911cb0ef41Sopenharmony_ci 9921cb0ef41Sopenharmony_ci return new Proxy(typedArray, { 9931cb0ef41Sopenharmony_ci __proto__: null, 9941cb0ef41Sopenharmony_ci get(obj, prop, receiver) { 9951cb0ef41Sopenharmony_ci if (prop === 'copyAssigned') { 9961cb0ef41Sopenharmony_ci return copyAssigned; 9971cb0ef41Sopenharmony_ci } 9981cb0ef41Sopenharmony_ci return ReflectGet(obj, prop, receiver); 9991cb0ef41Sopenharmony_ci }, 10001cb0ef41Sopenharmony_ci set(obj, prop, value) { 10011cb0ef41Sopenharmony_ci if (`${+prop}` === prop) { 10021cb0ef41Sopenharmony_ci modifiedEntries[prop] = 1; 10031cb0ef41Sopenharmony_ci } 10041cb0ef41Sopenharmony_ci return ReflectSet(obj, prop, value); 10051cb0ef41Sopenharmony_ci }, 10061cb0ef41Sopenharmony_ci }); 10071cb0ef41Sopenharmony_ci} 10081cb0ef41Sopenharmony_ci 10091cb0ef41Sopenharmony_ci// Creates the internal binding.Http2Session handle for an Http2Session 10101cb0ef41Sopenharmony_ci// instance. This occurs only after the socket connection has been 10111cb0ef41Sopenharmony_ci// established. Note: the binding.Http2Session will take over ownership 10121cb0ef41Sopenharmony_ci// of the socket. No other code should read from or write to the socket. 10131cb0ef41Sopenharmony_cifunction setupHandle(socket, type, options) { 10141cb0ef41Sopenharmony_ci // If the session has been destroyed, go ahead and emit 'connect', 10151cb0ef41Sopenharmony_ci // but do nothing else. The various on('connect') handlers set by 10161cb0ef41Sopenharmony_ci // core will check for session.destroyed before progressing, this 10171cb0ef41Sopenharmony_ci // ensures that those at l`east get cleared out. 10181cb0ef41Sopenharmony_ci if (this.destroyed) { 10191cb0ef41Sopenharmony_ci process.nextTick(emit, this, 'connect', this, socket); 10201cb0ef41Sopenharmony_ci return; 10211cb0ef41Sopenharmony_ci } 10221cb0ef41Sopenharmony_ci 10231cb0ef41Sopenharmony_ci assert(socket._handle !== undefined, 10241cb0ef41Sopenharmony_ci 'Internal HTTP/2 Failure. The socket is not connected. Please ' + 10251cb0ef41Sopenharmony_ci 'report this as a bug in Node.js'); 10261cb0ef41Sopenharmony_ci 10271cb0ef41Sopenharmony_ci debugSession(type, 'setting up session handle'); 10281cb0ef41Sopenharmony_ci this[kState].flags |= SESSION_FLAGS_READY; 10291cb0ef41Sopenharmony_ci 10301cb0ef41Sopenharmony_ci updateOptionsBuffer(options); 10311cb0ef41Sopenharmony_ci const handle = new binding.Http2Session(type); 10321cb0ef41Sopenharmony_ci handle[kOwner] = this; 10331cb0ef41Sopenharmony_ci 10341cb0ef41Sopenharmony_ci if (typeof options.selectPadding === 'function') 10351cb0ef41Sopenharmony_ci this[kSelectPadding] = options.selectPadding; 10361cb0ef41Sopenharmony_ci handle.consume(socket._handle); 10371cb0ef41Sopenharmony_ci 10381cb0ef41Sopenharmony_ci this[kHandle] = handle; 10391cb0ef41Sopenharmony_ci if (this[kNativeFields]) { 10401cb0ef41Sopenharmony_ci // If some options have already been set before the handle existed, copy 10411cb0ef41Sopenharmony_ci // those (and only those) that have manually been set over. 10421cb0ef41Sopenharmony_ci this[kNativeFields].copyAssigned(handle.fields); 10431cb0ef41Sopenharmony_ci } 10441cb0ef41Sopenharmony_ci 10451cb0ef41Sopenharmony_ci this[kNativeFields] = handle.fields; 10461cb0ef41Sopenharmony_ci 10471cb0ef41Sopenharmony_ci if (socket.encrypted) { 10481cb0ef41Sopenharmony_ci this[kAlpnProtocol] = socket.alpnProtocol; 10491cb0ef41Sopenharmony_ci this[kEncrypted] = true; 10501cb0ef41Sopenharmony_ci } else { 10511cb0ef41Sopenharmony_ci // 'h2c' is the protocol identifier for HTTP/2 over plain-text. We use 10521cb0ef41Sopenharmony_ci // it here to identify any session that is not explicitly using an 10531cb0ef41Sopenharmony_ci // encrypted socket. 10541cb0ef41Sopenharmony_ci this[kAlpnProtocol] = 'h2c'; 10551cb0ef41Sopenharmony_ci this[kEncrypted] = false; 10561cb0ef41Sopenharmony_ci } 10571cb0ef41Sopenharmony_ci 10581cb0ef41Sopenharmony_ci if (isUint32(options.maxSessionInvalidFrames)) { 10591cb0ef41Sopenharmony_ci const uint32 = new Uint32Array( 10601cb0ef41Sopenharmony_ci this[kNativeFields].buffer, kSessionMaxInvalidFrames, 1); 10611cb0ef41Sopenharmony_ci uint32[0] = options.maxSessionInvalidFrames; 10621cb0ef41Sopenharmony_ci } 10631cb0ef41Sopenharmony_ci 10641cb0ef41Sopenharmony_ci if (isUint32(options.maxSessionRejectedStreams)) { 10651cb0ef41Sopenharmony_ci const uint32 = new Uint32Array( 10661cb0ef41Sopenharmony_ci this[kNativeFields].buffer, kSessionMaxRejectedStreams, 1); 10671cb0ef41Sopenharmony_ci uint32[0] = options.maxSessionRejectedStreams; 10681cb0ef41Sopenharmony_ci } 10691cb0ef41Sopenharmony_ci 10701cb0ef41Sopenharmony_ci const settings = typeof options.settings === 'object' ? 10711cb0ef41Sopenharmony_ci options.settings : {}; 10721cb0ef41Sopenharmony_ci 10731cb0ef41Sopenharmony_ci this.settings(settings); 10741cb0ef41Sopenharmony_ci 10751cb0ef41Sopenharmony_ci if (type === NGHTTP2_SESSION_SERVER && 10761cb0ef41Sopenharmony_ci ArrayIsArray(options.origins)) { 10771cb0ef41Sopenharmony_ci ReflectApply(this.origin, this, options.origins); 10781cb0ef41Sopenharmony_ci } 10791cb0ef41Sopenharmony_ci 10801cb0ef41Sopenharmony_ci process.nextTick(emit, this, 'connect', this, socket); 10811cb0ef41Sopenharmony_ci} 10821cb0ef41Sopenharmony_ci 10831cb0ef41Sopenharmony_ci// Emits a close event followed by an error event if err is truthy. Used 10841cb0ef41Sopenharmony_ci// by Http2Session.prototype.destroy() 10851cb0ef41Sopenharmony_cifunction emitClose(self, error) { 10861cb0ef41Sopenharmony_ci if (error) 10871cb0ef41Sopenharmony_ci self.emit('error', error); 10881cb0ef41Sopenharmony_ci self.emit('close'); 10891cb0ef41Sopenharmony_ci} 10901cb0ef41Sopenharmony_ci 10911cb0ef41Sopenharmony_cifunction cleanupSession(session) { 10921cb0ef41Sopenharmony_ci const socket = session[kSocket]; 10931cb0ef41Sopenharmony_ci const handle = session[kHandle]; 10941cb0ef41Sopenharmony_ci session[kProxySocket] = undefined; 10951cb0ef41Sopenharmony_ci session[kSocket] = undefined; 10961cb0ef41Sopenharmony_ci session[kHandle] = undefined; 10971cb0ef41Sopenharmony_ci session[kNativeFields] = trackAssignmentsTypedArray( 10981cb0ef41Sopenharmony_ci new Uint8Array(kSessionUint8FieldCount)); 10991cb0ef41Sopenharmony_ci if (handle) 11001cb0ef41Sopenharmony_ci handle.ondone = null; 11011cb0ef41Sopenharmony_ci if (socket) { 11021cb0ef41Sopenharmony_ci socket[kSession] = undefined; 11031cb0ef41Sopenharmony_ci socket[kServer] = undefined; 11041cb0ef41Sopenharmony_ci } 11051cb0ef41Sopenharmony_ci} 11061cb0ef41Sopenharmony_ci 11071cb0ef41Sopenharmony_cifunction finishSessionClose(session, error) { 11081cb0ef41Sopenharmony_ci debugSessionObj(session, 'finishSessionClose'); 11091cb0ef41Sopenharmony_ci 11101cb0ef41Sopenharmony_ci const socket = session[kSocket]; 11111cb0ef41Sopenharmony_ci cleanupSession(session); 11121cb0ef41Sopenharmony_ci 11131cb0ef41Sopenharmony_ci if (socket && !socket.destroyed) { 11141cb0ef41Sopenharmony_ci socket.on('close', () => { 11151cb0ef41Sopenharmony_ci emitClose(session, error); 11161cb0ef41Sopenharmony_ci }); 11171cb0ef41Sopenharmony_ci if (session.closed) { 11181cb0ef41Sopenharmony_ci // If we're gracefully closing the socket, call resume() so we can detect 11191cb0ef41Sopenharmony_ci // the peer closing in case binding.Http2Session is already gone. 11201cb0ef41Sopenharmony_ci socket.resume(); 11211cb0ef41Sopenharmony_ci } 11221cb0ef41Sopenharmony_ci 11231cb0ef41Sopenharmony_ci // Always wait for writable side to finish. 11241cb0ef41Sopenharmony_ci socket.end((err) => { 11251cb0ef41Sopenharmony_ci debugSessionObj(session, 'finishSessionClose socket end', err, error); 11261cb0ef41Sopenharmony_ci // If session.destroy() was called, destroy the underlying socket. Delay 11271cb0ef41Sopenharmony_ci // it a bit to try to avoid ECONNRESET on Windows. 11281cb0ef41Sopenharmony_ci if (!session.closed) { 11291cb0ef41Sopenharmony_ci setImmediate(() => { 11301cb0ef41Sopenharmony_ci socket.destroy(error); 11311cb0ef41Sopenharmony_ci }); 11321cb0ef41Sopenharmony_ci } 11331cb0ef41Sopenharmony_ci }); 11341cb0ef41Sopenharmony_ci } else { 11351cb0ef41Sopenharmony_ci process.nextTick(emitClose, session, error); 11361cb0ef41Sopenharmony_ci } 11371cb0ef41Sopenharmony_ci} 11381cb0ef41Sopenharmony_ci 11391cb0ef41Sopenharmony_cifunction closeSession(session, code, error) { 11401cb0ef41Sopenharmony_ci debugSessionObj(session, 'start closing/destroying', error); 11411cb0ef41Sopenharmony_ci 11421cb0ef41Sopenharmony_ci const state = session[kState]; 11431cb0ef41Sopenharmony_ci state.flags |= SESSION_FLAGS_DESTROYED; 11441cb0ef41Sopenharmony_ci state.destroyCode = code; 11451cb0ef41Sopenharmony_ci 11461cb0ef41Sopenharmony_ci // Clear timeout and remove timeout listeners. 11471cb0ef41Sopenharmony_ci session.setTimeout(0); 11481cb0ef41Sopenharmony_ci session.removeAllListeners('timeout'); 11491cb0ef41Sopenharmony_ci 11501cb0ef41Sopenharmony_ci // Destroy any pending and open streams 11511cb0ef41Sopenharmony_ci if (state.pendingStreams.size > 0 || state.streams.size > 0) { 11521cb0ef41Sopenharmony_ci const cancel = new ERR_HTTP2_STREAM_CANCEL(error); 11531cb0ef41Sopenharmony_ci state.pendingStreams.forEach((stream) => stream.destroy(cancel)); 11541cb0ef41Sopenharmony_ci state.streams.forEach((stream) => stream.destroy(error)); 11551cb0ef41Sopenharmony_ci } 11561cb0ef41Sopenharmony_ci 11571cb0ef41Sopenharmony_ci // Disassociate from the socket and server. 11581cb0ef41Sopenharmony_ci const socket = session[kSocket]; 11591cb0ef41Sopenharmony_ci const handle = session[kHandle]; 11601cb0ef41Sopenharmony_ci 11611cb0ef41Sopenharmony_ci // Destroy the handle if it exists at this point. 11621cb0ef41Sopenharmony_ci if (handle !== undefined) { 11631cb0ef41Sopenharmony_ci handle.ondone = FunctionPrototypeBind(finishSessionClose, 11641cb0ef41Sopenharmony_ci null, session, error); 11651cb0ef41Sopenharmony_ci handle.destroy(code, socket.destroyed); 11661cb0ef41Sopenharmony_ci } else { 11671cb0ef41Sopenharmony_ci finishSessionClose(session, error); 11681cb0ef41Sopenharmony_ci } 11691cb0ef41Sopenharmony_ci} 11701cb0ef41Sopenharmony_ci 11711cb0ef41Sopenharmony_ci// Upon creation, the Http2Session takes ownership of the socket. The session 11721cb0ef41Sopenharmony_ci// may not be ready to use immediately if the socket is not yet fully connected. 11731cb0ef41Sopenharmony_ci// In that case, the Http2Session will wait for the socket to connect. Once 11741cb0ef41Sopenharmony_ci// the Http2Session is ready, it will emit its own 'connect' event. 11751cb0ef41Sopenharmony_ci// 11761cb0ef41Sopenharmony_ci// The Http2Session.goaway() method will send a GOAWAY frame, signalling 11771cb0ef41Sopenharmony_ci// to the connected peer that a shutdown is in progress. Sending a goaway 11781cb0ef41Sopenharmony_ci// frame has no other effect, however. 11791cb0ef41Sopenharmony_ci// 11801cb0ef41Sopenharmony_ci// Receiving a GOAWAY frame will cause the Http2Session to first emit a 'goaway' 11811cb0ef41Sopenharmony_ci// event notifying the user that a shutdown is in progress. If the goaway 11821cb0ef41Sopenharmony_ci// error code equals 0 (NGHTTP2_NO_ERROR), session.close() will be called, 11831cb0ef41Sopenharmony_ci// causing the Http2Session to send its own GOAWAY frame and switch itself 11841cb0ef41Sopenharmony_ci// into a graceful closing state. In this state, new inbound or outbound 11851cb0ef41Sopenharmony_ci// Http2Streams will be rejected. Existing *pending* streams (those created 11861cb0ef41Sopenharmony_ci// but without an assigned stream ID or handle) will be destroyed with a 11871cb0ef41Sopenharmony_ci// cancel error. Existing open streams will be permitted to complete on their 11881cb0ef41Sopenharmony_ci// own. Once all existing streams close, session.destroy() will be called 11891cb0ef41Sopenharmony_ci// automatically. 11901cb0ef41Sopenharmony_ci// 11911cb0ef41Sopenharmony_ci// Calling session.destroy() will tear down the Http2Session immediately, 11921cb0ef41Sopenharmony_ci// making it no longer usable. Pending and existing streams will be destroyed. 11931cb0ef41Sopenharmony_ci// The bound socket will be destroyed. Once all resources have been freed up, 11941cb0ef41Sopenharmony_ci// the 'close' event will be emitted. Note that pending streams will be 11951cb0ef41Sopenharmony_ci// destroyed using a specific "ERR_HTTP2_STREAM_CANCEL" error. Existing open 11961cb0ef41Sopenharmony_ci// streams will be destroyed using the same error passed to session.destroy() 11971cb0ef41Sopenharmony_ci// 11981cb0ef41Sopenharmony_ci// If destroy is called with an error, an 'error' event will be emitted 11991cb0ef41Sopenharmony_ci// immediately following the 'close' event. 12001cb0ef41Sopenharmony_ci// 12011cb0ef41Sopenharmony_ci// The socket and Http2Session lifecycles are tightly bound. Once one is 12021cb0ef41Sopenharmony_ci// destroyed, the other should also be destroyed. When the socket is destroyed 12031cb0ef41Sopenharmony_ci// with an error, session.destroy() will be called with that same error. 12041cb0ef41Sopenharmony_ci// Likewise, when session.destroy() is called with an error, the same error 12051cb0ef41Sopenharmony_ci// will be sent to the socket. 12061cb0ef41Sopenharmony_ciclass Http2Session extends EventEmitter { 12071cb0ef41Sopenharmony_ci constructor(type, options, socket) { 12081cb0ef41Sopenharmony_ci super(); 12091cb0ef41Sopenharmony_ci 12101cb0ef41Sopenharmony_ci if (!socket._handle || !socket._handle.isStreamBase) { 12111cb0ef41Sopenharmony_ci socket = new JSStreamSocket(socket); 12121cb0ef41Sopenharmony_ci } 12131cb0ef41Sopenharmony_ci socket.on('error', socketOnError); 12141cb0ef41Sopenharmony_ci socket.on('close', socketOnClose); 12151cb0ef41Sopenharmony_ci 12161cb0ef41Sopenharmony_ci // No validation is performed on the input parameters because this 12171cb0ef41Sopenharmony_ci // constructor is not exported directly for users. 12181cb0ef41Sopenharmony_ci 12191cb0ef41Sopenharmony_ci // If the session property already exists on the socket, 12201cb0ef41Sopenharmony_ci // then it has already been bound to an Http2Session instance 12211cb0ef41Sopenharmony_ci // and cannot be attached again. 12221cb0ef41Sopenharmony_ci if (socket[kSession] !== undefined) 12231cb0ef41Sopenharmony_ci throw new ERR_HTTP2_SOCKET_BOUND(); 12241cb0ef41Sopenharmony_ci 12251cb0ef41Sopenharmony_ci socket[kSession] = this; 12261cb0ef41Sopenharmony_ci 12271cb0ef41Sopenharmony_ci this[kState] = { 12281cb0ef41Sopenharmony_ci destroyCode: NGHTTP2_NO_ERROR, 12291cb0ef41Sopenharmony_ci flags: SESSION_FLAGS_PENDING, 12301cb0ef41Sopenharmony_ci goawayCode: null, 12311cb0ef41Sopenharmony_ci goawayLastStreamID: null, 12321cb0ef41Sopenharmony_ci streams: new SafeMap(), 12331cb0ef41Sopenharmony_ci pendingStreams: new SafeSet(), 12341cb0ef41Sopenharmony_ci pendingAck: 0, 12351cb0ef41Sopenharmony_ci shutdownWritableCalled: false, 12361cb0ef41Sopenharmony_ci writeQueueSize: 0, 12371cb0ef41Sopenharmony_ci originSet: undefined, 12381cb0ef41Sopenharmony_ci }; 12391cb0ef41Sopenharmony_ci 12401cb0ef41Sopenharmony_ci this[kEncrypted] = undefined; 12411cb0ef41Sopenharmony_ci this[kAlpnProtocol] = undefined; 12421cb0ef41Sopenharmony_ci this[kType] = type; 12431cb0ef41Sopenharmony_ci this[kProxySocket] = null; 12441cb0ef41Sopenharmony_ci this[kSocket] = socket; 12451cb0ef41Sopenharmony_ci this[kTimeout] = null; 12461cb0ef41Sopenharmony_ci this[kHandle] = undefined; 12471cb0ef41Sopenharmony_ci 12481cb0ef41Sopenharmony_ci // Do not use nagle's algorithm 12491cb0ef41Sopenharmony_ci if (typeof socket.setNoDelay === 'function') 12501cb0ef41Sopenharmony_ci socket.setNoDelay(); 12511cb0ef41Sopenharmony_ci 12521cb0ef41Sopenharmony_ci // Disable TLS renegotiation on the socket 12531cb0ef41Sopenharmony_ci if (typeof socket.disableRenegotiation === 'function') 12541cb0ef41Sopenharmony_ci socket.disableRenegotiation(); 12551cb0ef41Sopenharmony_ci 12561cb0ef41Sopenharmony_ci const setupFn = FunctionPrototypeBind(setupHandle, this, 12571cb0ef41Sopenharmony_ci socket, type, options); 12581cb0ef41Sopenharmony_ci if (socket.connecting || socket.secureConnecting) { 12591cb0ef41Sopenharmony_ci const connectEvent = 12601cb0ef41Sopenharmony_ci socket instanceof tls.TLSSocket ? 'secureConnect' : 'connect'; 12611cb0ef41Sopenharmony_ci socket.once(connectEvent, () => { 12621cb0ef41Sopenharmony_ci try { 12631cb0ef41Sopenharmony_ci setupFn(); 12641cb0ef41Sopenharmony_ci } catch (error) { 12651cb0ef41Sopenharmony_ci socket.destroy(error); 12661cb0ef41Sopenharmony_ci } 12671cb0ef41Sopenharmony_ci }); 12681cb0ef41Sopenharmony_ci } else { 12691cb0ef41Sopenharmony_ci setupFn(); 12701cb0ef41Sopenharmony_ci } 12711cb0ef41Sopenharmony_ci 12721cb0ef41Sopenharmony_ci if (!this[kNativeFields]) { 12731cb0ef41Sopenharmony_ci this[kNativeFields] = trackAssignmentsTypedArray( 12741cb0ef41Sopenharmony_ci new Uint8Array(kSessionUint8FieldCount)); 12751cb0ef41Sopenharmony_ci } 12761cb0ef41Sopenharmony_ci this.on('newListener', sessionListenerAdded); 12771cb0ef41Sopenharmony_ci this.on('removeListener', sessionListenerRemoved); 12781cb0ef41Sopenharmony_ci 12791cb0ef41Sopenharmony_ci // Process data on the next tick - a remoteSettings handler may be attached. 12801cb0ef41Sopenharmony_ci // https://github.com/nodejs/node/issues/35981 12811cb0ef41Sopenharmony_ci process.nextTick(() => { 12821cb0ef41Sopenharmony_ci // Socket already has some buffered data - emulate receiving it 12831cb0ef41Sopenharmony_ci // https://github.com/nodejs/node/issues/35475 12841cb0ef41Sopenharmony_ci // https://github.com/nodejs/node/issues/34532 12851cb0ef41Sopenharmony_ci if (socket.readableLength) { 12861cb0ef41Sopenharmony_ci let buf; 12871cb0ef41Sopenharmony_ci while ((buf = socket.read()) !== null) { 12881cb0ef41Sopenharmony_ci debugSession(type, `${buf.length} bytes already in buffer`); 12891cb0ef41Sopenharmony_ci this[kHandle].receive(buf); 12901cb0ef41Sopenharmony_ci } 12911cb0ef41Sopenharmony_ci } 12921cb0ef41Sopenharmony_ci }); 12931cb0ef41Sopenharmony_ci 12941cb0ef41Sopenharmony_ci debugSession(type, 'created'); 12951cb0ef41Sopenharmony_ci } 12961cb0ef41Sopenharmony_ci 12971cb0ef41Sopenharmony_ci // Returns undefined if the socket is not yet connected, true if the 12981cb0ef41Sopenharmony_ci // socket is a TLSSocket, and false if it is not. 12991cb0ef41Sopenharmony_ci get encrypted() { 13001cb0ef41Sopenharmony_ci return this[kEncrypted]; 13011cb0ef41Sopenharmony_ci } 13021cb0ef41Sopenharmony_ci 13031cb0ef41Sopenharmony_ci // Returns undefined if the socket is not yet connected, `h2` if the 13041cb0ef41Sopenharmony_ci // socket is a TLSSocket and the alpnProtocol is `h2`, or `h2c` if the 13051cb0ef41Sopenharmony_ci // socket is not a TLSSocket. 13061cb0ef41Sopenharmony_ci get alpnProtocol() { 13071cb0ef41Sopenharmony_ci return this[kAlpnProtocol]; 13081cb0ef41Sopenharmony_ci } 13091cb0ef41Sopenharmony_ci 13101cb0ef41Sopenharmony_ci // TODO(jasnell): originSet is being added in preparation for ORIGIN frame 13111cb0ef41Sopenharmony_ci // support. At the current time, the ORIGIN frame specification is awaiting 13121cb0ef41Sopenharmony_ci // publication as an RFC and is awaiting implementation in nghttp2. Once 13131cb0ef41Sopenharmony_ci // added, an ORIGIN frame will add to the origins included in the origin 13141cb0ef41Sopenharmony_ci // set. 421 responses will remove origins from the set. 13151cb0ef41Sopenharmony_ci get originSet() { 13161cb0ef41Sopenharmony_ci if (!this.encrypted || this.destroyed) 13171cb0ef41Sopenharmony_ci return undefined; 13181cb0ef41Sopenharmony_ci return ArrayFrom(initOriginSet(this)); 13191cb0ef41Sopenharmony_ci } 13201cb0ef41Sopenharmony_ci 13211cb0ef41Sopenharmony_ci // True if the Http2Session is still waiting for the socket to connect 13221cb0ef41Sopenharmony_ci get connecting() { 13231cb0ef41Sopenharmony_ci return (this[kState].flags & SESSION_FLAGS_READY) === 0; 13241cb0ef41Sopenharmony_ci } 13251cb0ef41Sopenharmony_ci 13261cb0ef41Sopenharmony_ci // True if Http2Session.prototype.close() has been called 13271cb0ef41Sopenharmony_ci get closed() { 13281cb0ef41Sopenharmony_ci return !!(this[kState].flags & SESSION_FLAGS_CLOSED); 13291cb0ef41Sopenharmony_ci } 13301cb0ef41Sopenharmony_ci 13311cb0ef41Sopenharmony_ci // True if Http2Session.prototype.destroy() has been called 13321cb0ef41Sopenharmony_ci get destroyed() { 13331cb0ef41Sopenharmony_ci return !!(this[kState].flags & SESSION_FLAGS_DESTROYED); 13341cb0ef41Sopenharmony_ci } 13351cb0ef41Sopenharmony_ci 13361cb0ef41Sopenharmony_ci // Resets the timeout counter 13371cb0ef41Sopenharmony_ci [kUpdateTimer]() { 13381cb0ef41Sopenharmony_ci if (this.destroyed) 13391cb0ef41Sopenharmony_ci return; 13401cb0ef41Sopenharmony_ci if (this[kTimeout]) this[kTimeout].refresh(); 13411cb0ef41Sopenharmony_ci } 13421cb0ef41Sopenharmony_ci 13431cb0ef41Sopenharmony_ci // Sets the id of the next stream to be created by this Http2Session. 13441cb0ef41Sopenharmony_ci // The value must be a number in the range 0 <= n <= kMaxStreams. The 13451cb0ef41Sopenharmony_ci // value also needs to be larger than the current next stream ID. 13461cb0ef41Sopenharmony_ci setNextStreamID(id) { 13471cb0ef41Sopenharmony_ci if (this.destroyed) 13481cb0ef41Sopenharmony_ci throw new ERR_HTTP2_INVALID_SESSION(); 13491cb0ef41Sopenharmony_ci 13501cb0ef41Sopenharmony_ci validateNumber(id, 'id'); 13511cb0ef41Sopenharmony_ci if (id <= 0 || id > kMaxStreams) 13521cb0ef41Sopenharmony_ci throw new ERR_OUT_OF_RANGE('id', `> 0 and <= ${kMaxStreams}`, id); 13531cb0ef41Sopenharmony_ci this[kHandle].setNextStreamID(id); 13541cb0ef41Sopenharmony_ci } 13551cb0ef41Sopenharmony_ci 13561cb0ef41Sopenharmony_ci // Sets the local window size (local endpoints's window size) 13571cb0ef41Sopenharmony_ci // Returns 0 if success or throw an exception if NGHTTP2_ERR_NOMEM 13581cb0ef41Sopenharmony_ci // if the window allocation fails 13591cb0ef41Sopenharmony_ci setLocalWindowSize(windowSize) { 13601cb0ef41Sopenharmony_ci if (this.destroyed) 13611cb0ef41Sopenharmony_ci throw new ERR_HTTP2_INVALID_SESSION(); 13621cb0ef41Sopenharmony_ci 13631cb0ef41Sopenharmony_ci validateInt32(windowSize, 'windowSize', 0); 13641cb0ef41Sopenharmony_ci const ret = this[kHandle].setLocalWindowSize(windowSize); 13651cb0ef41Sopenharmony_ci 13661cb0ef41Sopenharmony_ci if (ret === NGHTTP2_ERR_NOMEM) { 13671cb0ef41Sopenharmony_ci this.destroy(new ERR_HTTP2_NO_MEM()); 13681cb0ef41Sopenharmony_ci } 13691cb0ef41Sopenharmony_ci } 13701cb0ef41Sopenharmony_ci 13711cb0ef41Sopenharmony_ci // If ping is called while we are still connecting, or after close() has 13721cb0ef41Sopenharmony_ci // been called, the ping callback will be invoked immediately with a ping 13731cb0ef41Sopenharmony_ci // cancelled error and a duration of 0.0. 13741cb0ef41Sopenharmony_ci ping(payload, callback) { 13751cb0ef41Sopenharmony_ci if (this.destroyed) 13761cb0ef41Sopenharmony_ci throw new ERR_HTTP2_INVALID_SESSION(); 13771cb0ef41Sopenharmony_ci 13781cb0ef41Sopenharmony_ci if (typeof payload === 'function') { 13791cb0ef41Sopenharmony_ci callback = payload; 13801cb0ef41Sopenharmony_ci payload = undefined; 13811cb0ef41Sopenharmony_ci } 13821cb0ef41Sopenharmony_ci if (payload) { 13831cb0ef41Sopenharmony_ci validateBuffer(payload, 'payload'); 13841cb0ef41Sopenharmony_ci } 13851cb0ef41Sopenharmony_ci if (payload && payload.length !== 8) { 13861cb0ef41Sopenharmony_ci throw new ERR_HTTP2_PING_LENGTH(); 13871cb0ef41Sopenharmony_ci } 13881cb0ef41Sopenharmony_ci validateFunction(callback, 'callback'); 13891cb0ef41Sopenharmony_ci 13901cb0ef41Sopenharmony_ci const cb = pingCallback(callback); 13911cb0ef41Sopenharmony_ci if (this.connecting || this.closed) { 13921cb0ef41Sopenharmony_ci process.nextTick(cb, false, 0.0, payload); 13931cb0ef41Sopenharmony_ci return; 13941cb0ef41Sopenharmony_ci } 13951cb0ef41Sopenharmony_ci 13961cb0ef41Sopenharmony_ci return this[kHandle].ping(payload, cb); 13971cb0ef41Sopenharmony_ci } 13981cb0ef41Sopenharmony_ci 13991cb0ef41Sopenharmony_ci [kInspect](depth, opts) { 14001cb0ef41Sopenharmony_ci if (typeof depth === 'number' && depth < 0) 14011cb0ef41Sopenharmony_ci return this; 14021cb0ef41Sopenharmony_ci 14031cb0ef41Sopenharmony_ci const obj = { 14041cb0ef41Sopenharmony_ci type: this[kType], 14051cb0ef41Sopenharmony_ci closed: this.closed, 14061cb0ef41Sopenharmony_ci destroyed: this.destroyed, 14071cb0ef41Sopenharmony_ci state: this.state, 14081cb0ef41Sopenharmony_ci localSettings: this.localSettings, 14091cb0ef41Sopenharmony_ci remoteSettings: this.remoteSettings, 14101cb0ef41Sopenharmony_ci }; 14111cb0ef41Sopenharmony_ci return `Http2Session ${format(obj)}`; 14121cb0ef41Sopenharmony_ci } 14131cb0ef41Sopenharmony_ci 14141cb0ef41Sopenharmony_ci // The socket owned by this session 14151cb0ef41Sopenharmony_ci get socket() { 14161cb0ef41Sopenharmony_ci const proxySocket = this[kProxySocket]; 14171cb0ef41Sopenharmony_ci if (proxySocket === null) 14181cb0ef41Sopenharmony_ci return this[kProxySocket] = new Proxy(this, proxySocketHandler); 14191cb0ef41Sopenharmony_ci return proxySocket; 14201cb0ef41Sopenharmony_ci } 14211cb0ef41Sopenharmony_ci 14221cb0ef41Sopenharmony_ci // The session type 14231cb0ef41Sopenharmony_ci get type() { 14241cb0ef41Sopenharmony_ci return this[kType]; 14251cb0ef41Sopenharmony_ci } 14261cb0ef41Sopenharmony_ci 14271cb0ef41Sopenharmony_ci // If a GOAWAY frame has been received, gives the error code specified 14281cb0ef41Sopenharmony_ci get goawayCode() { 14291cb0ef41Sopenharmony_ci return this[kState].goawayCode || NGHTTP2_NO_ERROR; 14301cb0ef41Sopenharmony_ci } 14311cb0ef41Sopenharmony_ci 14321cb0ef41Sopenharmony_ci // If a GOAWAY frame has been received, gives the last stream ID reported 14331cb0ef41Sopenharmony_ci get goawayLastStreamID() { 14341cb0ef41Sopenharmony_ci return this[kState].goawayLastStreamID || 0; 14351cb0ef41Sopenharmony_ci } 14361cb0ef41Sopenharmony_ci 14371cb0ef41Sopenharmony_ci // True if the Http2Session is waiting for a settings acknowledgement 14381cb0ef41Sopenharmony_ci get pendingSettingsAck() { 14391cb0ef41Sopenharmony_ci return this[kState].pendingAck > 0; 14401cb0ef41Sopenharmony_ci } 14411cb0ef41Sopenharmony_ci 14421cb0ef41Sopenharmony_ci // Retrieves state information for the Http2Session 14431cb0ef41Sopenharmony_ci get state() { 14441cb0ef41Sopenharmony_ci return this.connecting || this.destroyed ? 14451cb0ef41Sopenharmony_ci {} : getSessionState(this[kHandle]); 14461cb0ef41Sopenharmony_ci } 14471cb0ef41Sopenharmony_ci 14481cb0ef41Sopenharmony_ci // The settings currently in effect for the local peer. These will 14491cb0ef41Sopenharmony_ci // be updated only when a settings acknowledgement has been received. 14501cb0ef41Sopenharmony_ci get localSettings() { 14511cb0ef41Sopenharmony_ci const settings = this[kLocalSettings]; 14521cb0ef41Sopenharmony_ci if (settings !== undefined) 14531cb0ef41Sopenharmony_ci return settings; 14541cb0ef41Sopenharmony_ci 14551cb0ef41Sopenharmony_ci if (this.destroyed || this.connecting) 14561cb0ef41Sopenharmony_ci return {}; 14571cb0ef41Sopenharmony_ci 14581cb0ef41Sopenharmony_ci return this[kLocalSettings] = getSettings(this[kHandle], false); // Local 14591cb0ef41Sopenharmony_ci } 14601cb0ef41Sopenharmony_ci 14611cb0ef41Sopenharmony_ci // The settings currently in effect for the remote peer. 14621cb0ef41Sopenharmony_ci get remoteSettings() { 14631cb0ef41Sopenharmony_ci if (this[kNativeFields][kBitfield] & 14641cb0ef41Sopenharmony_ci (1 << kSessionRemoteSettingsIsUpToDate)) { 14651cb0ef41Sopenharmony_ci const settings = this[kRemoteSettings]; 14661cb0ef41Sopenharmony_ci if (settings !== undefined) { 14671cb0ef41Sopenharmony_ci return settings; 14681cb0ef41Sopenharmony_ci } 14691cb0ef41Sopenharmony_ci } 14701cb0ef41Sopenharmony_ci 14711cb0ef41Sopenharmony_ci if (this.destroyed || this.connecting) 14721cb0ef41Sopenharmony_ci return {}; 14731cb0ef41Sopenharmony_ci 14741cb0ef41Sopenharmony_ci this[kNativeFields][kBitfield] |= (1 << kSessionRemoteSettingsIsUpToDate); 14751cb0ef41Sopenharmony_ci return this[kRemoteSettings] = getSettings(this[kHandle], true); // Remote 14761cb0ef41Sopenharmony_ci } 14771cb0ef41Sopenharmony_ci 14781cb0ef41Sopenharmony_ci // Submits a SETTINGS frame to be sent to the remote peer. 14791cb0ef41Sopenharmony_ci settings(settings, callback) { 14801cb0ef41Sopenharmony_ci if (this.destroyed) 14811cb0ef41Sopenharmony_ci throw new ERR_HTTP2_INVALID_SESSION(); 14821cb0ef41Sopenharmony_ci assertIsObject(settings, 'settings'); 14831cb0ef41Sopenharmony_ci validateSettings(settings); 14841cb0ef41Sopenharmony_ci 14851cb0ef41Sopenharmony_ci if (callback) { 14861cb0ef41Sopenharmony_ci validateFunction(callback, 'callback'); 14871cb0ef41Sopenharmony_ci } 14881cb0ef41Sopenharmony_ci debugSessionObj(this, 'sending settings'); 14891cb0ef41Sopenharmony_ci 14901cb0ef41Sopenharmony_ci this[kState].pendingAck++; 14911cb0ef41Sopenharmony_ci 14921cb0ef41Sopenharmony_ci const settingsFn = FunctionPrototypeBind(submitSettings, this, 14931cb0ef41Sopenharmony_ci { ...settings }, callback); 14941cb0ef41Sopenharmony_ci if (this.connecting) { 14951cb0ef41Sopenharmony_ci this.once('connect', settingsFn); 14961cb0ef41Sopenharmony_ci return; 14971cb0ef41Sopenharmony_ci } 14981cb0ef41Sopenharmony_ci settingsFn(); 14991cb0ef41Sopenharmony_ci } 15001cb0ef41Sopenharmony_ci 15011cb0ef41Sopenharmony_ci // Submits a GOAWAY frame to be sent to the remote peer. Note that this 15021cb0ef41Sopenharmony_ci // is only a notification, and does not affect the usable state of the 15031cb0ef41Sopenharmony_ci // session with the notable exception that new incoming streams will 15041cb0ef41Sopenharmony_ci // be rejected automatically. 15051cb0ef41Sopenharmony_ci goaway(code = NGHTTP2_NO_ERROR, lastStreamID = 0, opaqueData) { 15061cb0ef41Sopenharmony_ci if (this.destroyed) 15071cb0ef41Sopenharmony_ci throw new ERR_HTTP2_INVALID_SESSION(); 15081cb0ef41Sopenharmony_ci 15091cb0ef41Sopenharmony_ci if (opaqueData !== undefined) { 15101cb0ef41Sopenharmony_ci validateBuffer(opaqueData, 'opaqueData'); 15111cb0ef41Sopenharmony_ci } 15121cb0ef41Sopenharmony_ci validateNumber(code, 'code'); 15131cb0ef41Sopenharmony_ci validateNumber(lastStreamID, 'lastStreamID'); 15141cb0ef41Sopenharmony_ci 15151cb0ef41Sopenharmony_ci const goawayFn = FunctionPrototypeBind(submitGoaway, 15161cb0ef41Sopenharmony_ci this, 15171cb0ef41Sopenharmony_ci code, lastStreamID, opaqueData); 15181cb0ef41Sopenharmony_ci if (this.connecting) { 15191cb0ef41Sopenharmony_ci this.once('connect', goawayFn); 15201cb0ef41Sopenharmony_ci return; 15211cb0ef41Sopenharmony_ci } 15221cb0ef41Sopenharmony_ci goawayFn(); 15231cb0ef41Sopenharmony_ci } 15241cb0ef41Sopenharmony_ci 15251cb0ef41Sopenharmony_ci // Destroy the Http2Session, making it no longer usable and cancelling 15261cb0ef41Sopenharmony_ci // any pending activity. 15271cb0ef41Sopenharmony_ci destroy(error = NGHTTP2_NO_ERROR, code) { 15281cb0ef41Sopenharmony_ci if (this.destroyed) 15291cb0ef41Sopenharmony_ci return; 15301cb0ef41Sopenharmony_ci 15311cb0ef41Sopenharmony_ci debugSessionObj(this, 'destroying'); 15321cb0ef41Sopenharmony_ci 15331cb0ef41Sopenharmony_ci if (typeof error === 'number') { 15341cb0ef41Sopenharmony_ci code = error; 15351cb0ef41Sopenharmony_ci error = 15361cb0ef41Sopenharmony_ci code !== NGHTTP2_NO_ERROR ? 15371cb0ef41Sopenharmony_ci new ERR_HTTP2_SESSION_ERROR(code) : undefined; 15381cb0ef41Sopenharmony_ci } 15391cb0ef41Sopenharmony_ci if (code === undefined && error != null) 15401cb0ef41Sopenharmony_ci code = NGHTTP2_INTERNAL_ERROR; 15411cb0ef41Sopenharmony_ci 15421cb0ef41Sopenharmony_ci closeSession(this, code, error); 15431cb0ef41Sopenharmony_ci } 15441cb0ef41Sopenharmony_ci 15451cb0ef41Sopenharmony_ci // Closing the session will: 15461cb0ef41Sopenharmony_ci // 1. Send a goaway frame 15471cb0ef41Sopenharmony_ci // 2. Mark the session as closed 15481cb0ef41Sopenharmony_ci // 3. Prevent new inbound or outbound streams from being opened 15491cb0ef41Sopenharmony_ci // 4. Optionally register a 'close' event handler 15501cb0ef41Sopenharmony_ci // 5. Will cause the session to automatically destroy after the 15511cb0ef41Sopenharmony_ci // last currently open Http2Stream closes. 15521cb0ef41Sopenharmony_ci // 15531cb0ef41Sopenharmony_ci // Close always assumes a good, non-error shutdown (NGHTTP_NO_ERROR) 15541cb0ef41Sopenharmony_ci // 15551cb0ef41Sopenharmony_ci // If the session has not connected yet, the closed flag will still be 15561cb0ef41Sopenharmony_ci // set but the goaway will not be sent until after the connect event 15571cb0ef41Sopenharmony_ci // is emitted. 15581cb0ef41Sopenharmony_ci close(callback) { 15591cb0ef41Sopenharmony_ci if (this.closed || this.destroyed) 15601cb0ef41Sopenharmony_ci return; 15611cb0ef41Sopenharmony_ci debugSessionObj(this, 'marking session closed'); 15621cb0ef41Sopenharmony_ci this[kState].flags |= SESSION_FLAGS_CLOSED; 15631cb0ef41Sopenharmony_ci if (typeof callback === 'function') 15641cb0ef41Sopenharmony_ci this.once('close', callback); 15651cb0ef41Sopenharmony_ci this.goaway(); 15661cb0ef41Sopenharmony_ci this[kMaybeDestroy](); 15671cb0ef41Sopenharmony_ci } 15681cb0ef41Sopenharmony_ci 15691cb0ef41Sopenharmony_ci [EventEmitter.captureRejectionSymbol](err, event, ...args) { 15701cb0ef41Sopenharmony_ci switch (event) { 15711cb0ef41Sopenharmony_ci case 'stream': { 15721cb0ef41Sopenharmony_ci const stream = args[0]; 15731cb0ef41Sopenharmony_ci stream.destroy(err); 15741cb0ef41Sopenharmony_ci break; 15751cb0ef41Sopenharmony_ci } 15761cb0ef41Sopenharmony_ci default: 15771cb0ef41Sopenharmony_ci this.destroy(err); 15781cb0ef41Sopenharmony_ci } 15791cb0ef41Sopenharmony_ci } 15801cb0ef41Sopenharmony_ci 15811cb0ef41Sopenharmony_ci // Destroy the session if: 15821cb0ef41Sopenharmony_ci // * error is not undefined/null 15831cb0ef41Sopenharmony_ci // * session is closed and there are no more pending or open streams 15841cb0ef41Sopenharmony_ci [kMaybeDestroy](error) { 15851cb0ef41Sopenharmony_ci if (error == null) { 15861cb0ef41Sopenharmony_ci const state = this[kState]; 15871cb0ef41Sopenharmony_ci // Do not destroy if we're not closed and there are pending/open streams 15881cb0ef41Sopenharmony_ci if (!this.closed || 15891cb0ef41Sopenharmony_ci state.streams.size > 0 || 15901cb0ef41Sopenharmony_ci state.pendingStreams.size > 0) { 15911cb0ef41Sopenharmony_ci return; 15921cb0ef41Sopenharmony_ci } 15931cb0ef41Sopenharmony_ci } 15941cb0ef41Sopenharmony_ci this.destroy(error); 15951cb0ef41Sopenharmony_ci } 15961cb0ef41Sopenharmony_ci 15971cb0ef41Sopenharmony_ci _onTimeout() { 15981cb0ef41Sopenharmony_ci callTimeout(this); 15991cb0ef41Sopenharmony_ci } 16001cb0ef41Sopenharmony_ci 16011cb0ef41Sopenharmony_ci ref() { 16021cb0ef41Sopenharmony_ci if (this[kSocket]) { 16031cb0ef41Sopenharmony_ci this[kSocket].ref(); 16041cb0ef41Sopenharmony_ci } 16051cb0ef41Sopenharmony_ci } 16061cb0ef41Sopenharmony_ci 16071cb0ef41Sopenharmony_ci unref() { 16081cb0ef41Sopenharmony_ci if (this[kSocket]) { 16091cb0ef41Sopenharmony_ci this[kSocket].unref(); 16101cb0ef41Sopenharmony_ci } 16111cb0ef41Sopenharmony_ci } 16121cb0ef41Sopenharmony_ci} 16131cb0ef41Sopenharmony_ci 16141cb0ef41Sopenharmony_ci// ServerHttp2Session instances should never have to wait for the socket 16151cb0ef41Sopenharmony_ci// to connect as they are always created after the socket has already been 16161cb0ef41Sopenharmony_ci// established. 16171cb0ef41Sopenharmony_ciclass ServerHttp2Session extends Http2Session { 16181cb0ef41Sopenharmony_ci constructor(options, socket, server) { 16191cb0ef41Sopenharmony_ci super(NGHTTP2_SESSION_SERVER, options, socket); 16201cb0ef41Sopenharmony_ci this[kServer] = server; 16211cb0ef41Sopenharmony_ci // This is a bit inaccurate because it does not reflect changes to 16221cb0ef41Sopenharmony_ci // number of listeners made after the session was created. This should 16231cb0ef41Sopenharmony_ci // not be an issue in practice. Additionally, the 'priority' event on 16241cb0ef41Sopenharmony_ci // server instances (or any other object) is fully undocumented. 16251cb0ef41Sopenharmony_ci this[kNativeFields][kSessionPriorityListenerCount] = 16261cb0ef41Sopenharmony_ci server.listenerCount('priority'); 16271cb0ef41Sopenharmony_ci } 16281cb0ef41Sopenharmony_ci 16291cb0ef41Sopenharmony_ci get server() { 16301cb0ef41Sopenharmony_ci return this[kServer]; 16311cb0ef41Sopenharmony_ci } 16321cb0ef41Sopenharmony_ci 16331cb0ef41Sopenharmony_ci // Submits an altsvc frame to be sent to the client. `stream` is a 16341cb0ef41Sopenharmony_ci // numeric Stream ID. origin is a URL string that will be used to get 16351cb0ef41Sopenharmony_ci // the origin. alt is a string containing the altsvc details. No fancy 16361cb0ef41Sopenharmony_ci // API is provided for that. 16371cb0ef41Sopenharmony_ci altsvc(alt, originOrStream) { 16381cb0ef41Sopenharmony_ci if (this.destroyed) 16391cb0ef41Sopenharmony_ci throw new ERR_HTTP2_INVALID_SESSION(); 16401cb0ef41Sopenharmony_ci 16411cb0ef41Sopenharmony_ci let stream = 0; 16421cb0ef41Sopenharmony_ci let origin; 16431cb0ef41Sopenharmony_ci 16441cb0ef41Sopenharmony_ci if (typeof originOrStream === 'string') { 16451cb0ef41Sopenharmony_ci origin = (new URL(originOrStream)).origin; 16461cb0ef41Sopenharmony_ci if (origin === 'null') 16471cb0ef41Sopenharmony_ci throw new ERR_HTTP2_ALTSVC_INVALID_ORIGIN(); 16481cb0ef41Sopenharmony_ci } else if (typeof originOrStream === 'number') { 16491cb0ef41Sopenharmony_ci if (originOrStream >>> 0 !== originOrStream || originOrStream === 0) { 16501cb0ef41Sopenharmony_ci throw new ERR_OUT_OF_RANGE('originOrStream', 16511cb0ef41Sopenharmony_ci `> 0 && < ${2 ** 32}`, originOrStream); 16521cb0ef41Sopenharmony_ci } 16531cb0ef41Sopenharmony_ci stream = originOrStream; 16541cb0ef41Sopenharmony_ci } else if (originOrStream !== undefined) { 16551cb0ef41Sopenharmony_ci // Allow origin to be passed a URL or object with origin property 16561cb0ef41Sopenharmony_ci if (originOrStream !== null && typeof originOrStream === 'object') 16571cb0ef41Sopenharmony_ci origin = originOrStream.origin; 16581cb0ef41Sopenharmony_ci // Note: if originOrStream is an object with an origin property other 16591cb0ef41Sopenharmony_ci // than a URL, then it is possible that origin will be malformed. 16601cb0ef41Sopenharmony_ci // We do not verify that here. Users who go that route need to 16611cb0ef41Sopenharmony_ci // ensure they are doing the right thing or the payload data will 16621cb0ef41Sopenharmony_ci // be invalid. 16631cb0ef41Sopenharmony_ci if (typeof origin !== 'string') { 16641cb0ef41Sopenharmony_ci throw new ERR_INVALID_ARG_TYPE('originOrStream', 16651cb0ef41Sopenharmony_ci ['string', 'number', 'URL', 'object'], 16661cb0ef41Sopenharmony_ci originOrStream); 16671cb0ef41Sopenharmony_ci } else if (origin === 'null' || origin.length === 0) { 16681cb0ef41Sopenharmony_ci throw new ERR_HTTP2_ALTSVC_INVALID_ORIGIN(); 16691cb0ef41Sopenharmony_ci } 16701cb0ef41Sopenharmony_ci } 16711cb0ef41Sopenharmony_ci 16721cb0ef41Sopenharmony_ci validateString(alt, 'alt'); 16731cb0ef41Sopenharmony_ci if (RegExpPrototypeExec(kQuotedString, alt) === null) 16741cb0ef41Sopenharmony_ci throw new ERR_INVALID_CHAR('alt'); 16751cb0ef41Sopenharmony_ci 16761cb0ef41Sopenharmony_ci // Max length permitted for ALTSVC 16771cb0ef41Sopenharmony_ci if ((alt.length + (origin !== undefined ? origin.length : 0)) > kMaxALTSVC) 16781cb0ef41Sopenharmony_ci throw new ERR_HTTP2_ALTSVC_LENGTH(); 16791cb0ef41Sopenharmony_ci 16801cb0ef41Sopenharmony_ci this[kHandle].altsvc(stream, origin || '', alt); 16811cb0ef41Sopenharmony_ci } 16821cb0ef41Sopenharmony_ci 16831cb0ef41Sopenharmony_ci // Submits an origin frame to be sent. 16841cb0ef41Sopenharmony_ci origin(...origins) { 16851cb0ef41Sopenharmony_ci if (this.destroyed) 16861cb0ef41Sopenharmony_ci throw new ERR_HTTP2_INVALID_SESSION(); 16871cb0ef41Sopenharmony_ci 16881cb0ef41Sopenharmony_ci if (origins.length === 0) 16891cb0ef41Sopenharmony_ci return; 16901cb0ef41Sopenharmony_ci 16911cb0ef41Sopenharmony_ci let arr = ''; 16921cb0ef41Sopenharmony_ci let len = 0; 16931cb0ef41Sopenharmony_ci const count = origins.length; 16941cb0ef41Sopenharmony_ci for (let i = 0; i < count; i++) { 16951cb0ef41Sopenharmony_ci let origin = origins[i]; 16961cb0ef41Sopenharmony_ci if (typeof origin === 'string') { 16971cb0ef41Sopenharmony_ci origin = (new URL(origin)).origin; 16981cb0ef41Sopenharmony_ci } else if (origin != null && typeof origin === 'object') { 16991cb0ef41Sopenharmony_ci origin = origin.origin; 17001cb0ef41Sopenharmony_ci } 17011cb0ef41Sopenharmony_ci validateString(origin, 'origin'); 17021cb0ef41Sopenharmony_ci if (origin === 'null') 17031cb0ef41Sopenharmony_ci throw new ERR_HTTP2_INVALID_ORIGIN(); 17041cb0ef41Sopenharmony_ci 17051cb0ef41Sopenharmony_ci arr += `${origin}\0`; 17061cb0ef41Sopenharmony_ci len += origin.length; 17071cb0ef41Sopenharmony_ci } 17081cb0ef41Sopenharmony_ci 17091cb0ef41Sopenharmony_ci if (len > kMaxALTSVC) 17101cb0ef41Sopenharmony_ci throw new ERR_HTTP2_ORIGIN_LENGTH(); 17111cb0ef41Sopenharmony_ci 17121cb0ef41Sopenharmony_ci this[kHandle].origin(arr, count); 17131cb0ef41Sopenharmony_ci } 17141cb0ef41Sopenharmony_ci 17151cb0ef41Sopenharmony_ci} 17161cb0ef41Sopenharmony_ci 17171cb0ef41Sopenharmony_ci// ClientHttp2Session instances have to wait for the socket to connect after 17181cb0ef41Sopenharmony_ci// they have been created. Various operations such as request() may be used, 17191cb0ef41Sopenharmony_ci// but the actual protocol communication will only occur after the socket 17201cb0ef41Sopenharmony_ci// has been connected. 17211cb0ef41Sopenharmony_ciclass ClientHttp2Session extends Http2Session { 17221cb0ef41Sopenharmony_ci constructor(options, socket) { 17231cb0ef41Sopenharmony_ci super(NGHTTP2_SESSION_CLIENT, options, socket); 17241cb0ef41Sopenharmony_ci this[kPendingRequestCalls] = null; 17251cb0ef41Sopenharmony_ci } 17261cb0ef41Sopenharmony_ci 17271cb0ef41Sopenharmony_ci // Submits a new HTTP2 request to the connected peer. Returns the 17281cb0ef41Sopenharmony_ci // associated Http2Stream instance. 17291cb0ef41Sopenharmony_ci request(headers, options) { 17301cb0ef41Sopenharmony_ci debugSessionObj(this, 'initiating request'); 17311cb0ef41Sopenharmony_ci 17321cb0ef41Sopenharmony_ci if (this.destroyed) 17331cb0ef41Sopenharmony_ci throw new ERR_HTTP2_INVALID_SESSION(); 17341cb0ef41Sopenharmony_ci 17351cb0ef41Sopenharmony_ci if (this.closed) 17361cb0ef41Sopenharmony_ci throw new ERR_HTTP2_GOAWAY_SESSION(); 17371cb0ef41Sopenharmony_ci 17381cb0ef41Sopenharmony_ci this[kUpdateTimer](); 17391cb0ef41Sopenharmony_ci 17401cb0ef41Sopenharmony_ci if (headers !== null && headers !== undefined) { 17411cb0ef41Sopenharmony_ci const keys = ObjectKeys(headers); 17421cb0ef41Sopenharmony_ci for (let i = 0; i < keys.length; i++) { 17431cb0ef41Sopenharmony_ci const header = keys[i]; 17441cb0ef41Sopenharmony_ci if (header[0] === ':') { 17451cb0ef41Sopenharmony_ci assertValidPseudoHeader(header); 17461cb0ef41Sopenharmony_ci } else if (header && !checkIsHttpToken(header)) 17471cb0ef41Sopenharmony_ci this.destroy(new ERR_INVALID_HTTP_TOKEN('Header name', header)); 17481cb0ef41Sopenharmony_ci } 17491cb0ef41Sopenharmony_ci } 17501cb0ef41Sopenharmony_ci 17511cb0ef41Sopenharmony_ci assertIsObject(headers, 'headers'); 17521cb0ef41Sopenharmony_ci assertIsObject(options, 'options'); 17531cb0ef41Sopenharmony_ci 17541cb0ef41Sopenharmony_ci headers = ObjectAssign(ObjectCreate(null), headers); 17551cb0ef41Sopenharmony_ci options = { ...options }; 17561cb0ef41Sopenharmony_ci 17571cb0ef41Sopenharmony_ci if (headers[HTTP2_HEADER_METHOD] === undefined) 17581cb0ef41Sopenharmony_ci headers[HTTP2_HEADER_METHOD] = HTTP2_METHOD_GET; 17591cb0ef41Sopenharmony_ci 17601cb0ef41Sopenharmony_ci const connect = headers[HTTP2_HEADER_METHOD] === HTTP2_METHOD_CONNECT; 17611cb0ef41Sopenharmony_ci 17621cb0ef41Sopenharmony_ci if (!connect || headers[HTTP2_HEADER_PROTOCOL] !== undefined) { 17631cb0ef41Sopenharmony_ci if (getAuthority(headers) === undefined) 17641cb0ef41Sopenharmony_ci headers[HTTP2_HEADER_AUTHORITY] = this[kAuthority]; 17651cb0ef41Sopenharmony_ci if (headers[HTTP2_HEADER_SCHEME] === undefined) 17661cb0ef41Sopenharmony_ci headers[HTTP2_HEADER_SCHEME] = StringPrototypeSlice(this[kProtocol], 17671cb0ef41Sopenharmony_ci 0, -1); 17681cb0ef41Sopenharmony_ci if (headers[HTTP2_HEADER_PATH] === undefined) 17691cb0ef41Sopenharmony_ci headers[HTTP2_HEADER_PATH] = '/'; 17701cb0ef41Sopenharmony_ci } else { 17711cb0ef41Sopenharmony_ci if (headers[HTTP2_HEADER_AUTHORITY] === undefined) 17721cb0ef41Sopenharmony_ci throw new ERR_HTTP2_CONNECT_AUTHORITY(); 17731cb0ef41Sopenharmony_ci if (headers[HTTP2_HEADER_SCHEME] !== undefined) 17741cb0ef41Sopenharmony_ci throw new ERR_HTTP2_CONNECT_SCHEME(); 17751cb0ef41Sopenharmony_ci if (headers[HTTP2_HEADER_PATH] !== undefined) 17761cb0ef41Sopenharmony_ci throw new ERR_HTTP2_CONNECT_PATH(); 17771cb0ef41Sopenharmony_ci } 17781cb0ef41Sopenharmony_ci 17791cb0ef41Sopenharmony_ci setAndValidatePriorityOptions(options); 17801cb0ef41Sopenharmony_ci 17811cb0ef41Sopenharmony_ci if (options.endStream === undefined) { 17821cb0ef41Sopenharmony_ci // For some methods, we know that a payload is meaningless, so end the 17831cb0ef41Sopenharmony_ci // stream by default if the user has not specifically indicated a 17841cb0ef41Sopenharmony_ci // preference. 17851cb0ef41Sopenharmony_ci options.endStream = isPayloadMeaningless(headers[HTTP2_HEADER_METHOD]); 17861cb0ef41Sopenharmony_ci } else if (typeof options.endStream !== 'boolean') { 17871cb0ef41Sopenharmony_ci throw new ERR_INVALID_ARG_VALUE('options.endStream', options.endStream); 17881cb0ef41Sopenharmony_ci } 17891cb0ef41Sopenharmony_ci 17901cb0ef41Sopenharmony_ci const headersList = mapToHeaders(headers); 17911cb0ef41Sopenharmony_ci 17921cb0ef41Sopenharmony_ci // eslint-disable-next-line no-use-before-define 17931cb0ef41Sopenharmony_ci const stream = new ClientHttp2Stream(this, undefined, undefined, {}); 17941cb0ef41Sopenharmony_ci stream[kSentHeaders] = headers; 17951cb0ef41Sopenharmony_ci stream[kOrigin] = `${headers[HTTP2_HEADER_SCHEME]}://` + 17961cb0ef41Sopenharmony_ci `${getAuthority(headers)}`; 17971cb0ef41Sopenharmony_ci 17981cb0ef41Sopenharmony_ci // Close the writable side of the stream if options.endStream is set. 17991cb0ef41Sopenharmony_ci if (options.endStream) 18001cb0ef41Sopenharmony_ci stream.end(); 18011cb0ef41Sopenharmony_ci 18021cb0ef41Sopenharmony_ci if (options.waitForTrailers) 18031cb0ef41Sopenharmony_ci stream[kState].flags |= STREAM_FLAGS_HAS_TRAILERS; 18041cb0ef41Sopenharmony_ci 18051cb0ef41Sopenharmony_ci const { signal } = options; 18061cb0ef41Sopenharmony_ci if (signal) { 18071cb0ef41Sopenharmony_ci validateAbortSignal(signal, 'options.signal'); 18081cb0ef41Sopenharmony_ci const aborter = () => { 18091cb0ef41Sopenharmony_ci stream.destroy(new AbortError(undefined, { cause: signal.reason })); 18101cb0ef41Sopenharmony_ci }; 18111cb0ef41Sopenharmony_ci if (signal.aborted) { 18121cb0ef41Sopenharmony_ci aborter(); 18131cb0ef41Sopenharmony_ci } else { 18141cb0ef41Sopenharmony_ci const disposable = EventEmitter.addAbortListener(signal, aborter); 18151cb0ef41Sopenharmony_ci stream.once('close', disposable[SymbolDispose]); 18161cb0ef41Sopenharmony_ci } 18171cb0ef41Sopenharmony_ci } 18181cb0ef41Sopenharmony_ci 18191cb0ef41Sopenharmony_ci const onConnect = FunctionPrototypeBind(requestOnConnect, 18201cb0ef41Sopenharmony_ci stream, headersList, options); 18211cb0ef41Sopenharmony_ci if (this.connecting) { 18221cb0ef41Sopenharmony_ci if (this[kPendingRequestCalls] !== null) { 18231cb0ef41Sopenharmony_ci ArrayPrototypePush(this[kPendingRequestCalls], onConnect); 18241cb0ef41Sopenharmony_ci } else { 18251cb0ef41Sopenharmony_ci this[kPendingRequestCalls] = [onConnect]; 18261cb0ef41Sopenharmony_ci this.once('connect', () => { 18271cb0ef41Sopenharmony_ci ArrayPrototypeForEach(this[kPendingRequestCalls], (f) => f()); 18281cb0ef41Sopenharmony_ci this[kPendingRequestCalls] = null; 18291cb0ef41Sopenharmony_ci }); 18301cb0ef41Sopenharmony_ci } 18311cb0ef41Sopenharmony_ci } else { 18321cb0ef41Sopenharmony_ci onConnect(); 18331cb0ef41Sopenharmony_ci } 18341cb0ef41Sopenharmony_ci return stream; 18351cb0ef41Sopenharmony_ci } 18361cb0ef41Sopenharmony_ci} 18371cb0ef41Sopenharmony_ci 18381cb0ef41Sopenharmony_cifunction trackWriteState(stream, bytes) { 18391cb0ef41Sopenharmony_ci const session = stream[kSession]; 18401cb0ef41Sopenharmony_ci stream[kState].writeQueueSize += bytes; 18411cb0ef41Sopenharmony_ci session[kState].writeQueueSize += bytes; 18421cb0ef41Sopenharmony_ci session[kHandle].chunksSentSinceLastWrite = 0; 18431cb0ef41Sopenharmony_ci} 18441cb0ef41Sopenharmony_ci 18451cb0ef41Sopenharmony_cifunction streamOnResume() { 18461cb0ef41Sopenharmony_ci if (!this.destroyed) 18471cb0ef41Sopenharmony_ci this[kHandle].readStart(); 18481cb0ef41Sopenharmony_ci} 18491cb0ef41Sopenharmony_ci 18501cb0ef41Sopenharmony_cifunction streamOnPause() { 18511cb0ef41Sopenharmony_ci if (!this.destroyed && !this.pending) 18521cb0ef41Sopenharmony_ci this[kHandle].readStop(); 18531cb0ef41Sopenharmony_ci} 18541cb0ef41Sopenharmony_ci 18551cb0ef41Sopenharmony_cifunction afterShutdown(status) { 18561cb0ef41Sopenharmony_ci const stream = this.handle[kOwner]; 18571cb0ef41Sopenharmony_ci if (stream) { 18581cb0ef41Sopenharmony_ci stream.on('finish', () => { 18591cb0ef41Sopenharmony_ci stream[kMaybeDestroy](); 18601cb0ef41Sopenharmony_ci }); 18611cb0ef41Sopenharmony_ci } 18621cb0ef41Sopenharmony_ci // Currently this status value is unused 18631cb0ef41Sopenharmony_ci this.callback(); 18641cb0ef41Sopenharmony_ci} 18651cb0ef41Sopenharmony_ci 18661cb0ef41Sopenharmony_cifunction shutdownWritable(callback) { 18671cb0ef41Sopenharmony_ci const handle = this[kHandle]; 18681cb0ef41Sopenharmony_ci if (!handle) return callback(); 18691cb0ef41Sopenharmony_ci const state = this[kState]; 18701cb0ef41Sopenharmony_ci if (state.shutdownWritableCalled) { 18711cb0ef41Sopenharmony_ci debugStreamObj(this, 'shutdownWritable() already called'); 18721cb0ef41Sopenharmony_ci return callback(); 18731cb0ef41Sopenharmony_ci } 18741cb0ef41Sopenharmony_ci state.shutdownWritableCalled = true; 18751cb0ef41Sopenharmony_ci 18761cb0ef41Sopenharmony_ci const req = new ShutdownWrap(); 18771cb0ef41Sopenharmony_ci req.oncomplete = afterShutdown; 18781cb0ef41Sopenharmony_ci req.callback = callback; 18791cb0ef41Sopenharmony_ci req.handle = handle; 18801cb0ef41Sopenharmony_ci const err = handle.shutdown(req); 18811cb0ef41Sopenharmony_ci if (err === 1) // synchronous finish 18821cb0ef41Sopenharmony_ci return ReflectApply(afterShutdown, req, [0]); 18831cb0ef41Sopenharmony_ci} 18841cb0ef41Sopenharmony_ci 18851cb0ef41Sopenharmony_cifunction finishSendTrailers(stream, headersList) { 18861cb0ef41Sopenharmony_ci // The stream might be destroyed and in that case 18871cb0ef41Sopenharmony_ci // there is nothing to do. 18881cb0ef41Sopenharmony_ci // This can happen because finishSendTrailers is 18891cb0ef41Sopenharmony_ci // scheduled via setImmediate. 18901cb0ef41Sopenharmony_ci if (stream.destroyed) { 18911cb0ef41Sopenharmony_ci return; 18921cb0ef41Sopenharmony_ci } 18931cb0ef41Sopenharmony_ci 18941cb0ef41Sopenharmony_ci stream[kState].flags &= ~STREAM_FLAGS_HAS_TRAILERS; 18951cb0ef41Sopenharmony_ci 18961cb0ef41Sopenharmony_ci const ret = stream[kHandle].trailers(headersList); 18971cb0ef41Sopenharmony_ci if (ret < 0) 18981cb0ef41Sopenharmony_ci stream.destroy(new NghttpError(ret)); 18991cb0ef41Sopenharmony_ci else 19001cb0ef41Sopenharmony_ci stream[kMaybeDestroy](); 19011cb0ef41Sopenharmony_ci} 19021cb0ef41Sopenharmony_ci 19031cb0ef41Sopenharmony_ciconst kNoRstStream = 0; 19041cb0ef41Sopenharmony_ciconst kSubmitRstStream = 1; 19051cb0ef41Sopenharmony_ciconst kForceRstStream = 2; 19061cb0ef41Sopenharmony_ci 19071cb0ef41Sopenharmony_cifunction closeStream(stream, code, rstStreamStatus = kSubmitRstStream) { 19081cb0ef41Sopenharmony_ci const state = stream[kState]; 19091cb0ef41Sopenharmony_ci state.flags |= STREAM_FLAGS_CLOSED; 19101cb0ef41Sopenharmony_ci state.rstCode = code; 19111cb0ef41Sopenharmony_ci 19121cb0ef41Sopenharmony_ci // Clear timeout and remove timeout listeners 19131cb0ef41Sopenharmony_ci stream.setTimeout(0); 19141cb0ef41Sopenharmony_ci stream.removeAllListeners('timeout'); 19151cb0ef41Sopenharmony_ci 19161cb0ef41Sopenharmony_ci const { ending } = stream._writableState; 19171cb0ef41Sopenharmony_ci 19181cb0ef41Sopenharmony_ci if (!ending) { 19191cb0ef41Sopenharmony_ci // If the writable side of the Http2Stream is still open, emit the 19201cb0ef41Sopenharmony_ci // 'aborted' event and set the aborted flag. 19211cb0ef41Sopenharmony_ci if (!stream.aborted) { 19221cb0ef41Sopenharmony_ci state.flags |= STREAM_FLAGS_ABORTED; 19231cb0ef41Sopenharmony_ci stream.emit('aborted'); 19241cb0ef41Sopenharmony_ci } 19251cb0ef41Sopenharmony_ci 19261cb0ef41Sopenharmony_ci // Close the writable side. 19271cb0ef41Sopenharmony_ci stream.end(); 19281cb0ef41Sopenharmony_ci } 19291cb0ef41Sopenharmony_ci 19301cb0ef41Sopenharmony_ci if (rstStreamStatus !== kNoRstStream) { 19311cb0ef41Sopenharmony_ci const finishFn = FunctionPrototypeBind(finishCloseStream, stream, code); 19321cb0ef41Sopenharmony_ci if (!ending || stream.writableFinished || code !== NGHTTP2_NO_ERROR || 19331cb0ef41Sopenharmony_ci rstStreamStatus === kForceRstStream) 19341cb0ef41Sopenharmony_ci finishFn(); 19351cb0ef41Sopenharmony_ci else 19361cb0ef41Sopenharmony_ci stream.once('finish', finishFn); 19371cb0ef41Sopenharmony_ci } 19381cb0ef41Sopenharmony_ci} 19391cb0ef41Sopenharmony_ci 19401cb0ef41Sopenharmony_cifunction finishCloseStream(code) { 19411cb0ef41Sopenharmony_ci const rstStreamFn = FunctionPrototypeBind(submitRstStream, this, code); 19421cb0ef41Sopenharmony_ci // If the handle has not yet been assigned, queue up the request to 19431cb0ef41Sopenharmony_ci // ensure that the RST_STREAM frame is sent after the stream ID has 19441cb0ef41Sopenharmony_ci // been determined. 19451cb0ef41Sopenharmony_ci if (this.pending) { 19461cb0ef41Sopenharmony_ci this.push(null); 19471cb0ef41Sopenharmony_ci this.once('ready', rstStreamFn); 19481cb0ef41Sopenharmony_ci return; 19491cb0ef41Sopenharmony_ci } 19501cb0ef41Sopenharmony_ci rstStreamFn(); 19511cb0ef41Sopenharmony_ci} 19521cb0ef41Sopenharmony_ci 19531cb0ef41Sopenharmony_ci// An Http2Stream is a Duplex stream that is backed by a 19541cb0ef41Sopenharmony_ci// node::http2::Http2Stream handle implementing StreamBase. 19551cb0ef41Sopenharmony_ciclass Http2Stream extends Duplex { 19561cb0ef41Sopenharmony_ci constructor(session, options) { 19571cb0ef41Sopenharmony_ci options.allowHalfOpen = true; 19581cb0ef41Sopenharmony_ci options.decodeStrings = false; 19591cb0ef41Sopenharmony_ci options.autoDestroy = false; 19601cb0ef41Sopenharmony_ci super(options); 19611cb0ef41Sopenharmony_ci this[async_id_symbol] = -1; 19621cb0ef41Sopenharmony_ci 19631cb0ef41Sopenharmony_ci // Corking the stream automatically allows writes to happen 19641cb0ef41Sopenharmony_ci // but ensures that those are buffered until the handle has 19651cb0ef41Sopenharmony_ci // been assigned. 19661cb0ef41Sopenharmony_ci this.cork(); 19671cb0ef41Sopenharmony_ci this[kSession] = session; 19681cb0ef41Sopenharmony_ci session[kState].pendingStreams.add(this); 19691cb0ef41Sopenharmony_ci 19701cb0ef41Sopenharmony_ci // Allow our logic for determining whether any reads have happened to 19711cb0ef41Sopenharmony_ci // work in all situations. This is similar to what we do in _http_incoming. 19721cb0ef41Sopenharmony_ci this._readableState.readingMore = true; 19731cb0ef41Sopenharmony_ci 19741cb0ef41Sopenharmony_ci this[kTimeout] = null; 19751cb0ef41Sopenharmony_ci 19761cb0ef41Sopenharmony_ci this[kState] = { 19771cb0ef41Sopenharmony_ci didRead: false, 19781cb0ef41Sopenharmony_ci flags: STREAM_FLAGS_PENDING, 19791cb0ef41Sopenharmony_ci rstCode: NGHTTP2_NO_ERROR, 19801cb0ef41Sopenharmony_ci writeQueueSize: 0, 19811cb0ef41Sopenharmony_ci trailersReady: false, 19821cb0ef41Sopenharmony_ci endAfterHeaders: false, 19831cb0ef41Sopenharmony_ci }; 19841cb0ef41Sopenharmony_ci 19851cb0ef41Sopenharmony_ci // Fields used by the compat API to avoid megamorphisms. 19861cb0ef41Sopenharmony_ci this[kRequest] = null; 19871cb0ef41Sopenharmony_ci this[kProxySocket] = null; 19881cb0ef41Sopenharmony_ci 19891cb0ef41Sopenharmony_ci this.on('pause', streamOnPause); 19901cb0ef41Sopenharmony_ci 19911cb0ef41Sopenharmony_ci this.on('newListener', streamListenerAdded); 19921cb0ef41Sopenharmony_ci this.on('removeListener', streamListenerRemoved); 19931cb0ef41Sopenharmony_ci } 19941cb0ef41Sopenharmony_ci 19951cb0ef41Sopenharmony_ci [kUpdateTimer]() { 19961cb0ef41Sopenharmony_ci if (this.destroyed) 19971cb0ef41Sopenharmony_ci return; 19981cb0ef41Sopenharmony_ci if (this[kTimeout]) 19991cb0ef41Sopenharmony_ci this[kTimeout].refresh(); 20001cb0ef41Sopenharmony_ci if (this[kSession]) 20011cb0ef41Sopenharmony_ci this[kSession][kUpdateTimer](); 20021cb0ef41Sopenharmony_ci } 20031cb0ef41Sopenharmony_ci 20041cb0ef41Sopenharmony_ci [kInit](id, handle) { 20051cb0ef41Sopenharmony_ci const state = this[kState]; 20061cb0ef41Sopenharmony_ci state.flags |= STREAM_FLAGS_READY; 20071cb0ef41Sopenharmony_ci 20081cb0ef41Sopenharmony_ci const session = this[kSession]; 20091cb0ef41Sopenharmony_ci session[kState].pendingStreams.delete(this); 20101cb0ef41Sopenharmony_ci session[kState].streams.set(id, this); 20111cb0ef41Sopenharmony_ci 20121cb0ef41Sopenharmony_ci this[kID] = id; 20131cb0ef41Sopenharmony_ci this[async_id_symbol] = handle.getAsyncId(); 20141cb0ef41Sopenharmony_ci handle[kOwner] = this; 20151cb0ef41Sopenharmony_ci this[kHandle] = handle; 20161cb0ef41Sopenharmony_ci handle.onread = onStreamRead; 20171cb0ef41Sopenharmony_ci this.uncork(); 20181cb0ef41Sopenharmony_ci this.emit('ready'); 20191cb0ef41Sopenharmony_ci } 20201cb0ef41Sopenharmony_ci 20211cb0ef41Sopenharmony_ci [kInspect](depth, opts) { 20221cb0ef41Sopenharmony_ci if (typeof depth === 'number' && depth < 0) 20231cb0ef41Sopenharmony_ci return this; 20241cb0ef41Sopenharmony_ci 20251cb0ef41Sopenharmony_ci const obj = { 20261cb0ef41Sopenharmony_ci id: this[kID] || '<pending>', 20271cb0ef41Sopenharmony_ci closed: this.closed, 20281cb0ef41Sopenharmony_ci destroyed: this.destroyed, 20291cb0ef41Sopenharmony_ci state: this.state, 20301cb0ef41Sopenharmony_ci readableState: this._readableState, 20311cb0ef41Sopenharmony_ci writableState: this._writableState, 20321cb0ef41Sopenharmony_ci }; 20331cb0ef41Sopenharmony_ci return `Http2Stream ${format(obj)}`; 20341cb0ef41Sopenharmony_ci } 20351cb0ef41Sopenharmony_ci 20361cb0ef41Sopenharmony_ci get bufferSize() { 20371cb0ef41Sopenharmony_ci // `bufferSize` properties of `net.Socket` are `undefined` when 20381cb0ef41Sopenharmony_ci // their `_handle` are falsy. Here we avoid the behavior. 20391cb0ef41Sopenharmony_ci return this[kState].writeQueueSize + this.writableLength; 20401cb0ef41Sopenharmony_ci } 20411cb0ef41Sopenharmony_ci 20421cb0ef41Sopenharmony_ci get endAfterHeaders() { 20431cb0ef41Sopenharmony_ci return this[kState].endAfterHeaders; 20441cb0ef41Sopenharmony_ci } 20451cb0ef41Sopenharmony_ci 20461cb0ef41Sopenharmony_ci get sentHeaders() { 20471cb0ef41Sopenharmony_ci return this[kSentHeaders]; 20481cb0ef41Sopenharmony_ci } 20491cb0ef41Sopenharmony_ci 20501cb0ef41Sopenharmony_ci get sentTrailers() { 20511cb0ef41Sopenharmony_ci return this[kSentTrailers]; 20521cb0ef41Sopenharmony_ci } 20531cb0ef41Sopenharmony_ci 20541cb0ef41Sopenharmony_ci get sentInfoHeaders() { 20551cb0ef41Sopenharmony_ci return this[kInfoHeaders]; 20561cb0ef41Sopenharmony_ci } 20571cb0ef41Sopenharmony_ci 20581cb0ef41Sopenharmony_ci get pending() { 20591cb0ef41Sopenharmony_ci return this[kID] === undefined; 20601cb0ef41Sopenharmony_ci } 20611cb0ef41Sopenharmony_ci 20621cb0ef41Sopenharmony_ci // The id of the Http2Stream, will be undefined if the socket is not 20631cb0ef41Sopenharmony_ci // yet connected. 20641cb0ef41Sopenharmony_ci get id() { 20651cb0ef41Sopenharmony_ci return this[kID]; 20661cb0ef41Sopenharmony_ci } 20671cb0ef41Sopenharmony_ci 20681cb0ef41Sopenharmony_ci // The Http2Session that owns this Http2Stream. 20691cb0ef41Sopenharmony_ci get session() { 20701cb0ef41Sopenharmony_ci return this[kSession]; 20711cb0ef41Sopenharmony_ci } 20721cb0ef41Sopenharmony_ci 20731cb0ef41Sopenharmony_ci _onTimeout() { 20741cb0ef41Sopenharmony_ci callTimeout(this, kSession); 20751cb0ef41Sopenharmony_ci } 20761cb0ef41Sopenharmony_ci 20771cb0ef41Sopenharmony_ci // True if the HEADERS frame has been sent 20781cb0ef41Sopenharmony_ci get headersSent() { 20791cb0ef41Sopenharmony_ci return !!(this[kState].flags & STREAM_FLAGS_HEADERS_SENT); 20801cb0ef41Sopenharmony_ci } 20811cb0ef41Sopenharmony_ci 20821cb0ef41Sopenharmony_ci // True if the Http2Stream was aborted abnormally. 20831cb0ef41Sopenharmony_ci get aborted() { 20841cb0ef41Sopenharmony_ci return !!(this[kState].flags & STREAM_FLAGS_ABORTED); 20851cb0ef41Sopenharmony_ci } 20861cb0ef41Sopenharmony_ci 20871cb0ef41Sopenharmony_ci // True if dealing with a HEAD request 20881cb0ef41Sopenharmony_ci get headRequest() { 20891cb0ef41Sopenharmony_ci return !!(this[kState].flags & STREAM_FLAGS_HEAD_REQUEST); 20901cb0ef41Sopenharmony_ci } 20911cb0ef41Sopenharmony_ci 20921cb0ef41Sopenharmony_ci // The error code reported when this Http2Stream was closed. 20931cb0ef41Sopenharmony_ci get rstCode() { 20941cb0ef41Sopenharmony_ci return this[kState].rstCode; 20951cb0ef41Sopenharmony_ci } 20961cb0ef41Sopenharmony_ci 20971cb0ef41Sopenharmony_ci // State information for the Http2Stream 20981cb0ef41Sopenharmony_ci get state() { 20991cb0ef41Sopenharmony_ci const id = this[kID]; 21001cb0ef41Sopenharmony_ci if (this.destroyed || id === undefined) 21011cb0ef41Sopenharmony_ci return {}; 21021cb0ef41Sopenharmony_ci return getStreamState(this[kHandle], id); 21031cb0ef41Sopenharmony_ci } 21041cb0ef41Sopenharmony_ci 21051cb0ef41Sopenharmony_ci [kProceed]() { 21061cb0ef41Sopenharmony_ci assert.fail('Implementors MUST implement this. Please report this as a ' + 21071cb0ef41Sopenharmony_ci 'bug in Node.js'); 21081cb0ef41Sopenharmony_ci } 21091cb0ef41Sopenharmony_ci 21101cb0ef41Sopenharmony_ci [kAfterAsyncWrite]({ bytes }) { 21111cb0ef41Sopenharmony_ci this[kState].writeQueueSize -= bytes; 21121cb0ef41Sopenharmony_ci 21131cb0ef41Sopenharmony_ci if (this.session !== undefined) 21141cb0ef41Sopenharmony_ci this.session[kState].writeQueueSize -= bytes; 21151cb0ef41Sopenharmony_ci } 21161cb0ef41Sopenharmony_ci 21171cb0ef41Sopenharmony_ci [kWriteGeneric](writev, data, encoding, cb) { 21181cb0ef41Sopenharmony_ci // When the Http2Stream is first created, it is corked until the 21191cb0ef41Sopenharmony_ci // handle and the stream ID is assigned. However, if the user calls 21201cb0ef41Sopenharmony_ci // uncork() before that happens, the Duplex will attempt to pass 21211cb0ef41Sopenharmony_ci // writes through. Those need to be queued up here. 21221cb0ef41Sopenharmony_ci if (this.pending) { 21231cb0ef41Sopenharmony_ci this.once( 21241cb0ef41Sopenharmony_ci 'ready', 21251cb0ef41Sopenharmony_ci FunctionPrototypeBind(this[kWriteGeneric], 21261cb0ef41Sopenharmony_ci this, writev, data, encoding, cb), 21271cb0ef41Sopenharmony_ci ); 21281cb0ef41Sopenharmony_ci return; 21291cb0ef41Sopenharmony_ci } 21301cb0ef41Sopenharmony_ci 21311cb0ef41Sopenharmony_ci // If the stream has been destroyed, there's nothing else we can do 21321cb0ef41Sopenharmony_ci // because the handle has been destroyed. This should only be an 21331cb0ef41Sopenharmony_ci // issue if a write occurs before the 'ready' event in the case where 21341cb0ef41Sopenharmony_ci // the duplex is uncorked before the stream is ready to go. In that 21351cb0ef41Sopenharmony_ci // case, drop the data on the floor. An error should have already been 21361cb0ef41Sopenharmony_ci // emitted. 21371cb0ef41Sopenharmony_ci if (this.destroyed) 21381cb0ef41Sopenharmony_ci return; 21391cb0ef41Sopenharmony_ci 21401cb0ef41Sopenharmony_ci this[kUpdateTimer](); 21411cb0ef41Sopenharmony_ci if (!this.headersSent) 21421cb0ef41Sopenharmony_ci this[kProceed](); 21431cb0ef41Sopenharmony_ci 21441cb0ef41Sopenharmony_ci let req; 21451cb0ef41Sopenharmony_ci 21461cb0ef41Sopenharmony_ci let waitingForWriteCallback = true; 21471cb0ef41Sopenharmony_ci let waitingForEndCheck = true; 21481cb0ef41Sopenharmony_ci let writeCallbackErr; 21491cb0ef41Sopenharmony_ci let endCheckCallbackErr; 21501cb0ef41Sopenharmony_ci const done = () => { 21511cb0ef41Sopenharmony_ci if (waitingForEndCheck || waitingForWriteCallback) return; 21521cb0ef41Sopenharmony_ci const err = aggregateTwoErrors(endCheckCallbackErr, writeCallbackErr); 21531cb0ef41Sopenharmony_ci // writeGeneric does not destroy on error and 21541cb0ef41Sopenharmony_ci // we cannot enable autoDestroy, 21551cb0ef41Sopenharmony_ci // so make sure to destroy on error. 21561cb0ef41Sopenharmony_ci if (err) { 21571cb0ef41Sopenharmony_ci this.destroy(err); 21581cb0ef41Sopenharmony_ci } 21591cb0ef41Sopenharmony_ci cb(err); 21601cb0ef41Sopenharmony_ci }; 21611cb0ef41Sopenharmony_ci const writeCallback = (err) => { 21621cb0ef41Sopenharmony_ci waitingForWriteCallback = false; 21631cb0ef41Sopenharmony_ci writeCallbackErr = err; 21641cb0ef41Sopenharmony_ci done(); 21651cb0ef41Sopenharmony_ci }; 21661cb0ef41Sopenharmony_ci const endCheckCallback = (err) => { 21671cb0ef41Sopenharmony_ci waitingForEndCheck = false; 21681cb0ef41Sopenharmony_ci endCheckCallbackErr = err; 21691cb0ef41Sopenharmony_ci done(); 21701cb0ef41Sopenharmony_ci }; 21711cb0ef41Sopenharmony_ci // Shutdown write stream right after last chunk is sent 21721cb0ef41Sopenharmony_ci // so final DATA frame can include END_STREAM flag 21731cb0ef41Sopenharmony_ci process.nextTick(() => { 21741cb0ef41Sopenharmony_ci if (writeCallbackErr || 21751cb0ef41Sopenharmony_ci !this._writableState.ending || 21761cb0ef41Sopenharmony_ci this._writableState.buffered.length || 21771cb0ef41Sopenharmony_ci (this[kState].flags & STREAM_FLAGS_HAS_TRAILERS)) 21781cb0ef41Sopenharmony_ci return endCheckCallback(); 21791cb0ef41Sopenharmony_ci debugStreamObj(this, 'shutting down writable on last write'); 21801cb0ef41Sopenharmony_ci shutdownWritable.call(this, endCheckCallback); 21811cb0ef41Sopenharmony_ci }); 21821cb0ef41Sopenharmony_ci 21831cb0ef41Sopenharmony_ci if (writev) 21841cb0ef41Sopenharmony_ci req = writevGeneric(this, data, writeCallback); 21851cb0ef41Sopenharmony_ci else 21861cb0ef41Sopenharmony_ci req = writeGeneric(this, data, encoding, writeCallback); 21871cb0ef41Sopenharmony_ci 21881cb0ef41Sopenharmony_ci trackWriteState(this, req.bytes); 21891cb0ef41Sopenharmony_ci } 21901cb0ef41Sopenharmony_ci 21911cb0ef41Sopenharmony_ci _write(data, encoding, cb) { 21921cb0ef41Sopenharmony_ci this[kWriteGeneric](false, data, encoding, cb); 21931cb0ef41Sopenharmony_ci } 21941cb0ef41Sopenharmony_ci 21951cb0ef41Sopenharmony_ci _writev(data, cb) { 21961cb0ef41Sopenharmony_ci this[kWriteGeneric](true, data, '', cb); 21971cb0ef41Sopenharmony_ci } 21981cb0ef41Sopenharmony_ci 21991cb0ef41Sopenharmony_ci _final(cb) { 22001cb0ef41Sopenharmony_ci if (this.pending) { 22011cb0ef41Sopenharmony_ci this.once('ready', () => this._final(cb)); 22021cb0ef41Sopenharmony_ci return; 22031cb0ef41Sopenharmony_ci } 22041cb0ef41Sopenharmony_ci debugStreamObj(this, 'shutting down writable on _final'); 22051cb0ef41Sopenharmony_ci ReflectApply(shutdownWritable, this, [cb]); 22061cb0ef41Sopenharmony_ci } 22071cb0ef41Sopenharmony_ci 22081cb0ef41Sopenharmony_ci _read(nread) { 22091cb0ef41Sopenharmony_ci if (this.destroyed) { 22101cb0ef41Sopenharmony_ci this.push(null); 22111cb0ef41Sopenharmony_ci return; 22121cb0ef41Sopenharmony_ci } 22131cb0ef41Sopenharmony_ci if (!this[kState].didRead) { 22141cb0ef41Sopenharmony_ci this._readableState.readingMore = false; 22151cb0ef41Sopenharmony_ci this[kState].didRead = true; 22161cb0ef41Sopenharmony_ci } 22171cb0ef41Sopenharmony_ci if (!this.pending) { 22181cb0ef41Sopenharmony_ci FunctionPrototypeCall(streamOnResume, this); 22191cb0ef41Sopenharmony_ci } else { 22201cb0ef41Sopenharmony_ci this.once('ready', streamOnResume); 22211cb0ef41Sopenharmony_ci } 22221cb0ef41Sopenharmony_ci } 22231cb0ef41Sopenharmony_ci 22241cb0ef41Sopenharmony_ci priority(options) { 22251cb0ef41Sopenharmony_ci if (this.destroyed) 22261cb0ef41Sopenharmony_ci throw new ERR_HTTP2_INVALID_STREAM(); 22271cb0ef41Sopenharmony_ci 22281cb0ef41Sopenharmony_ci assertIsObject(options, 'options'); 22291cb0ef41Sopenharmony_ci options = { ...options }; 22301cb0ef41Sopenharmony_ci setAndValidatePriorityOptions(options); 22311cb0ef41Sopenharmony_ci 22321cb0ef41Sopenharmony_ci const priorityFn = FunctionPrototypeBind(submitPriority, this, options); 22331cb0ef41Sopenharmony_ci 22341cb0ef41Sopenharmony_ci // If the handle has not yet been assigned, queue up the priority 22351cb0ef41Sopenharmony_ci // frame to be sent as soon as the ready event is emitted. 22361cb0ef41Sopenharmony_ci if (this.pending) { 22371cb0ef41Sopenharmony_ci this.once('ready', priorityFn); 22381cb0ef41Sopenharmony_ci return; 22391cb0ef41Sopenharmony_ci } 22401cb0ef41Sopenharmony_ci priorityFn(); 22411cb0ef41Sopenharmony_ci } 22421cb0ef41Sopenharmony_ci 22431cb0ef41Sopenharmony_ci sendTrailers(headers) { 22441cb0ef41Sopenharmony_ci if (this.destroyed || this.closed) 22451cb0ef41Sopenharmony_ci throw new ERR_HTTP2_INVALID_STREAM(); 22461cb0ef41Sopenharmony_ci if (this[kSentTrailers]) 22471cb0ef41Sopenharmony_ci throw new ERR_HTTP2_TRAILERS_ALREADY_SENT(); 22481cb0ef41Sopenharmony_ci if (!this[kState].trailersReady) 22491cb0ef41Sopenharmony_ci throw new ERR_HTTP2_TRAILERS_NOT_READY(); 22501cb0ef41Sopenharmony_ci 22511cb0ef41Sopenharmony_ci assertIsObject(headers, 'headers'); 22521cb0ef41Sopenharmony_ci headers = ObjectAssign(ObjectCreate(null), headers); 22531cb0ef41Sopenharmony_ci 22541cb0ef41Sopenharmony_ci debugStreamObj(this, 'sending trailers'); 22551cb0ef41Sopenharmony_ci 22561cb0ef41Sopenharmony_ci this[kUpdateTimer](); 22571cb0ef41Sopenharmony_ci 22581cb0ef41Sopenharmony_ci const headersList = mapToHeaders(headers, assertValidPseudoHeaderTrailer); 22591cb0ef41Sopenharmony_ci this[kSentTrailers] = headers; 22601cb0ef41Sopenharmony_ci 22611cb0ef41Sopenharmony_ci // Send the trailers in setImmediate so we don't do it on nghttp2 stack. 22621cb0ef41Sopenharmony_ci setImmediate(finishSendTrailers, this, headersList); 22631cb0ef41Sopenharmony_ci } 22641cb0ef41Sopenharmony_ci 22651cb0ef41Sopenharmony_ci get closed() { 22661cb0ef41Sopenharmony_ci return !!(this[kState].flags & STREAM_FLAGS_CLOSED); 22671cb0ef41Sopenharmony_ci } 22681cb0ef41Sopenharmony_ci 22691cb0ef41Sopenharmony_ci // Close initiates closing the Http2Stream instance by sending an RST_STREAM 22701cb0ef41Sopenharmony_ci // frame to the connected peer. The readable and writable sides of the 22711cb0ef41Sopenharmony_ci // Http2Stream duplex are closed and the timeout timer is cleared. If 22721cb0ef41Sopenharmony_ci // a callback is passed, it is registered to listen for the 'close' event. 22731cb0ef41Sopenharmony_ci // 22741cb0ef41Sopenharmony_ci // If the handle and stream ID have not been assigned yet, the close 22751cb0ef41Sopenharmony_ci // will be queued up to wait for the ready event. As soon as the stream ID 22761cb0ef41Sopenharmony_ci // is determined, the close will proceed. 22771cb0ef41Sopenharmony_ci // 22781cb0ef41Sopenharmony_ci // Submitting the RST_STREAM frame to the underlying handle will cause 22791cb0ef41Sopenharmony_ci // the Http2Stream to be closed and ultimately destroyed. After calling 22801cb0ef41Sopenharmony_ci // close, it is still possible to queue up PRIORITY and RST_STREAM frames, 22811cb0ef41Sopenharmony_ci // but no DATA and HEADERS frames may be sent. 22821cb0ef41Sopenharmony_ci close(code = NGHTTP2_NO_ERROR, callback) { 22831cb0ef41Sopenharmony_ci validateInteger(code, 'code', 0, kMaxInt); 22841cb0ef41Sopenharmony_ci 22851cb0ef41Sopenharmony_ci if (callback !== undefined) { 22861cb0ef41Sopenharmony_ci validateFunction(callback, 'callback'); 22871cb0ef41Sopenharmony_ci } 22881cb0ef41Sopenharmony_ci 22891cb0ef41Sopenharmony_ci if (this.closed) 22901cb0ef41Sopenharmony_ci return; 22911cb0ef41Sopenharmony_ci 22921cb0ef41Sopenharmony_ci if (callback !== undefined) 22931cb0ef41Sopenharmony_ci this.once('close', callback); 22941cb0ef41Sopenharmony_ci 22951cb0ef41Sopenharmony_ci closeStream(this, code); 22961cb0ef41Sopenharmony_ci } 22971cb0ef41Sopenharmony_ci 22981cb0ef41Sopenharmony_ci // Called by this.destroy(). 22991cb0ef41Sopenharmony_ci // * Will submit an RST stream to shutdown the stream if necessary. 23001cb0ef41Sopenharmony_ci // This will cause the internal resources to be released. 23011cb0ef41Sopenharmony_ci // * Then cleans up the resources on the js side 23021cb0ef41Sopenharmony_ci _destroy(err, callback) { 23031cb0ef41Sopenharmony_ci const session = this[kSession]; 23041cb0ef41Sopenharmony_ci const handle = this[kHandle]; 23051cb0ef41Sopenharmony_ci const id = this[kID]; 23061cb0ef41Sopenharmony_ci 23071cb0ef41Sopenharmony_ci debugStream(this[kID] || 'pending', session[kType], 'destroying stream'); 23081cb0ef41Sopenharmony_ci 23091cb0ef41Sopenharmony_ci const state = this[kState]; 23101cb0ef41Sopenharmony_ci const sessionState = session[kState]; 23111cb0ef41Sopenharmony_ci const sessionCode = sessionState.goawayCode || sessionState.destroyCode; 23121cb0ef41Sopenharmony_ci 23131cb0ef41Sopenharmony_ci // If a stream has already closed successfully, there is no error 23141cb0ef41Sopenharmony_ci // to report from this stream, even if the session has errored. 23151cb0ef41Sopenharmony_ci // This can happen if the stream was already in process of destroying 23161cb0ef41Sopenharmony_ci // after a successful close, but the session had a error between 23171cb0ef41Sopenharmony_ci // this stream's close and destroy operations. 23181cb0ef41Sopenharmony_ci // Previously, this always overrode a successful close operation code 23191cb0ef41Sopenharmony_ci // NGHTTP2_NO_ERROR (0) with sessionCode because the use of the || operator. 23201cb0ef41Sopenharmony_ci let code = this.closed ? this.rstCode : sessionCode; 23211cb0ef41Sopenharmony_ci if (err != null) { 23221cb0ef41Sopenharmony_ci if (sessionCode) { 23231cb0ef41Sopenharmony_ci code = sessionCode; 23241cb0ef41Sopenharmony_ci } else if (err instanceof AbortError) { 23251cb0ef41Sopenharmony_ci // Enables using AbortController to cancel requests with RST code 8. 23261cb0ef41Sopenharmony_ci code = NGHTTP2_CANCEL; 23271cb0ef41Sopenharmony_ci } else { 23281cb0ef41Sopenharmony_ci code = NGHTTP2_INTERNAL_ERROR; 23291cb0ef41Sopenharmony_ci } 23301cb0ef41Sopenharmony_ci } 23311cb0ef41Sopenharmony_ci const hasHandle = handle !== undefined; 23321cb0ef41Sopenharmony_ci 23331cb0ef41Sopenharmony_ci if (!this.closed) 23341cb0ef41Sopenharmony_ci closeStream(this, code, hasHandle ? kForceRstStream : kNoRstStream); 23351cb0ef41Sopenharmony_ci this.push(null); 23361cb0ef41Sopenharmony_ci 23371cb0ef41Sopenharmony_ci if (hasHandle) { 23381cb0ef41Sopenharmony_ci handle.destroy(); 23391cb0ef41Sopenharmony_ci sessionState.streams.delete(id); 23401cb0ef41Sopenharmony_ci } else { 23411cb0ef41Sopenharmony_ci sessionState.pendingStreams.delete(this); 23421cb0ef41Sopenharmony_ci } 23431cb0ef41Sopenharmony_ci 23441cb0ef41Sopenharmony_ci // Adjust the write queue size for accounting 23451cb0ef41Sopenharmony_ci sessionState.writeQueueSize -= state.writeQueueSize; 23461cb0ef41Sopenharmony_ci state.writeQueueSize = 0; 23471cb0ef41Sopenharmony_ci 23481cb0ef41Sopenharmony_ci // RST code 8 not emitted as an error as its used by clients to signify 23491cb0ef41Sopenharmony_ci // abort and is already covered by aborted event, also allows more 23501cb0ef41Sopenharmony_ci // seamless compatibility with http1 23511cb0ef41Sopenharmony_ci if (err == null && code !== NGHTTP2_NO_ERROR && code !== NGHTTP2_CANCEL) 23521cb0ef41Sopenharmony_ci err = new ERR_HTTP2_STREAM_ERROR(nameForErrorCode[code] || code); 23531cb0ef41Sopenharmony_ci 23541cb0ef41Sopenharmony_ci this[kSession] = undefined; 23551cb0ef41Sopenharmony_ci this[kHandle] = undefined; 23561cb0ef41Sopenharmony_ci 23571cb0ef41Sopenharmony_ci // This notifies the session that this stream has been destroyed and 23581cb0ef41Sopenharmony_ci // gives the session the opportunity to clean itself up. The session 23591cb0ef41Sopenharmony_ci // will destroy if it has been closed and there are no other open or 23601cb0ef41Sopenharmony_ci // pending streams. 23611cb0ef41Sopenharmony_ci session[kMaybeDestroy](); 23621cb0ef41Sopenharmony_ci callback(err); 23631cb0ef41Sopenharmony_ci } 23641cb0ef41Sopenharmony_ci // The Http2Stream can be destroyed if it has closed and if the readable 23651cb0ef41Sopenharmony_ci // side has received the final chunk. 23661cb0ef41Sopenharmony_ci [kMaybeDestroy](code = NGHTTP2_NO_ERROR) { 23671cb0ef41Sopenharmony_ci if (code !== NGHTTP2_NO_ERROR) { 23681cb0ef41Sopenharmony_ci this.destroy(); 23691cb0ef41Sopenharmony_ci return; 23701cb0ef41Sopenharmony_ci } 23711cb0ef41Sopenharmony_ci 23721cb0ef41Sopenharmony_ci if (this.writableFinished) { 23731cb0ef41Sopenharmony_ci if (!this.readable && this.closed) { 23741cb0ef41Sopenharmony_ci this.destroy(); 23751cb0ef41Sopenharmony_ci return; 23761cb0ef41Sopenharmony_ci } 23771cb0ef41Sopenharmony_ci 23781cb0ef41Sopenharmony_ci // We've submitted a response from our server session, have not attempted 23791cb0ef41Sopenharmony_ci // to process any incoming data, and have no trailers. This means we can 23801cb0ef41Sopenharmony_ci // attempt to gracefully close the session. 23811cb0ef41Sopenharmony_ci const state = this[kState]; 23821cb0ef41Sopenharmony_ci if (this.headersSent && 23831cb0ef41Sopenharmony_ci this[kSession] && 23841cb0ef41Sopenharmony_ci this[kSession][kType] === NGHTTP2_SESSION_SERVER && 23851cb0ef41Sopenharmony_ci !(state.flags & STREAM_FLAGS_HAS_TRAILERS) && 23861cb0ef41Sopenharmony_ci !state.didRead && 23871cb0ef41Sopenharmony_ci this.readableFlowing === null) { 23881cb0ef41Sopenharmony_ci // By using setImmediate we allow pushStreams to make it through 23891cb0ef41Sopenharmony_ci // before the stream is officially closed. This prevents a bug 23901cb0ef41Sopenharmony_ci // in most browsers where those pushStreams would be rejected. 23911cb0ef41Sopenharmony_ci setImmediate(callStreamClose, this); 23921cb0ef41Sopenharmony_ci } 23931cb0ef41Sopenharmony_ci } 23941cb0ef41Sopenharmony_ci } 23951cb0ef41Sopenharmony_ci} 23961cb0ef41Sopenharmony_ci 23971cb0ef41Sopenharmony_cifunction callTimeout(self, kSession) { 23981cb0ef41Sopenharmony_ci // If the session is destroyed, this should never actually be invoked, 23991cb0ef41Sopenharmony_ci // but just in case... 24001cb0ef41Sopenharmony_ci if (self.destroyed) 24011cb0ef41Sopenharmony_ci return; 24021cb0ef41Sopenharmony_ci // This checks whether a write is currently in progress and also whether 24031cb0ef41Sopenharmony_ci // that write is actually sending data across the write. The kHandle 24041cb0ef41Sopenharmony_ci // stored `chunksSentSinceLastWrite` is only updated when a timeout event 24051cb0ef41Sopenharmony_ci // happens, meaning that if a write is ongoing it should never equal the 24061cb0ef41Sopenharmony_ci // newly fetched, updated value. 24071cb0ef41Sopenharmony_ci if (self[kState].writeQueueSize > 0) { 24081cb0ef41Sopenharmony_ci const handle = kSession ? self[kSession][kHandle] : self[kHandle]; 24091cb0ef41Sopenharmony_ci const chunksSentSinceLastWrite = handle !== undefined ? 24101cb0ef41Sopenharmony_ci handle.chunksSentSinceLastWrite : null; 24111cb0ef41Sopenharmony_ci if (chunksSentSinceLastWrite !== null && 24121cb0ef41Sopenharmony_ci chunksSentSinceLastWrite !== handle.updateChunksSent()) { 24131cb0ef41Sopenharmony_ci self[kUpdateTimer](); 24141cb0ef41Sopenharmony_ci return; 24151cb0ef41Sopenharmony_ci } 24161cb0ef41Sopenharmony_ci } 24171cb0ef41Sopenharmony_ci 24181cb0ef41Sopenharmony_ci self.emit('timeout'); 24191cb0ef41Sopenharmony_ci} 24201cb0ef41Sopenharmony_ci 24211cb0ef41Sopenharmony_cifunction callStreamClose(stream) { 24221cb0ef41Sopenharmony_ci stream.close(); 24231cb0ef41Sopenharmony_ci} 24241cb0ef41Sopenharmony_ci 24251cb0ef41Sopenharmony_cifunction processHeaders(oldHeaders, options) { 24261cb0ef41Sopenharmony_ci assertIsObject(oldHeaders, 'headers'); 24271cb0ef41Sopenharmony_ci const headers = ObjectCreate(null); 24281cb0ef41Sopenharmony_ci 24291cb0ef41Sopenharmony_ci if (oldHeaders !== null && oldHeaders !== undefined) { 24301cb0ef41Sopenharmony_ci // This loop is here for performance reason. Do not change. 24311cb0ef41Sopenharmony_ci for (const key in oldHeaders) { 24321cb0ef41Sopenharmony_ci if (ObjectPrototypeHasOwnProperty(oldHeaders, key)) { 24331cb0ef41Sopenharmony_ci headers[key] = oldHeaders[key]; 24341cb0ef41Sopenharmony_ci } 24351cb0ef41Sopenharmony_ci } 24361cb0ef41Sopenharmony_ci headers[kSensitiveHeaders] = oldHeaders[kSensitiveHeaders]; 24371cb0ef41Sopenharmony_ci } 24381cb0ef41Sopenharmony_ci 24391cb0ef41Sopenharmony_ci const statusCode = 24401cb0ef41Sopenharmony_ci headers[HTTP2_HEADER_STATUS] = 24411cb0ef41Sopenharmony_ci headers[HTTP2_HEADER_STATUS] | 0 || HTTP_STATUS_OK; 24421cb0ef41Sopenharmony_ci 24431cb0ef41Sopenharmony_ci if (options.sendDate == null || options.sendDate) { 24441cb0ef41Sopenharmony_ci if (headers[HTTP2_HEADER_DATE] === null || 24451cb0ef41Sopenharmony_ci headers[HTTP2_HEADER_DATE] === undefined) { 24461cb0ef41Sopenharmony_ci headers[HTTP2_HEADER_DATE] = utcDate(); 24471cb0ef41Sopenharmony_ci } 24481cb0ef41Sopenharmony_ci } 24491cb0ef41Sopenharmony_ci 24501cb0ef41Sopenharmony_ci // This is intentionally stricter than the HTTP/1 implementation, which 24511cb0ef41Sopenharmony_ci // allows values between 100 and 999 (inclusive) in order to allow for 24521cb0ef41Sopenharmony_ci // backwards compatibility with non-spec compliant code. With HTTP/2, 24531cb0ef41Sopenharmony_ci // we have the opportunity to start fresh with stricter spec compliance. 24541cb0ef41Sopenharmony_ci // This will have an impact on the compatibility layer for anyone using 24551cb0ef41Sopenharmony_ci // non-standard, non-compliant status codes. 24561cb0ef41Sopenharmony_ci if (statusCode < 200 || statusCode > 599) 24571cb0ef41Sopenharmony_ci throw new ERR_HTTP2_STATUS_INVALID(headers[HTTP2_HEADER_STATUS]); 24581cb0ef41Sopenharmony_ci 24591cb0ef41Sopenharmony_ci const neverIndex = headers[kSensitiveHeaders]; 24601cb0ef41Sopenharmony_ci if (neverIndex !== undefined && !ArrayIsArray(neverIndex)) 24611cb0ef41Sopenharmony_ci throw new ERR_INVALID_ARG_VALUE('headers[http2.neverIndex]', neverIndex); 24621cb0ef41Sopenharmony_ci 24631cb0ef41Sopenharmony_ci return headers; 24641cb0ef41Sopenharmony_ci} 24651cb0ef41Sopenharmony_ci 24661cb0ef41Sopenharmony_ci 24671cb0ef41Sopenharmony_cifunction onFileUnpipe() { 24681cb0ef41Sopenharmony_ci const stream = this.sink[kOwner]; 24691cb0ef41Sopenharmony_ci if (stream.ownsFd) 24701cb0ef41Sopenharmony_ci PromisePrototypeThen(this.source.close(), undefined, 24711cb0ef41Sopenharmony_ci FunctionPrototypeBind(stream.destroy, stream)); 24721cb0ef41Sopenharmony_ci else 24731cb0ef41Sopenharmony_ci this.source.releaseFD(); 24741cb0ef41Sopenharmony_ci} 24751cb0ef41Sopenharmony_ci 24761cb0ef41Sopenharmony_ci// This is only called once the pipe has returned back control, so 24771cb0ef41Sopenharmony_ci// it only has to handle errors and End-of-File. 24781cb0ef41Sopenharmony_cifunction onPipedFileHandleRead() { 24791cb0ef41Sopenharmony_ci const err = streamBaseState[kReadBytesOrError]; 24801cb0ef41Sopenharmony_ci if (err < 0 && err !== UV_EOF) { 24811cb0ef41Sopenharmony_ci this.stream.close(NGHTTP2_INTERNAL_ERROR); 24821cb0ef41Sopenharmony_ci } 24831cb0ef41Sopenharmony_ci} 24841cb0ef41Sopenharmony_ci 24851cb0ef41Sopenharmony_cifunction processRespondWithFD(self, fd, headers, offset = 0, length = -1, 24861cb0ef41Sopenharmony_ci streamOptions = 0) { 24871cb0ef41Sopenharmony_ci const state = self[kState]; 24881cb0ef41Sopenharmony_ci state.flags |= STREAM_FLAGS_HEADERS_SENT; 24891cb0ef41Sopenharmony_ci 24901cb0ef41Sopenharmony_ci let headersList; 24911cb0ef41Sopenharmony_ci try { 24921cb0ef41Sopenharmony_ci headersList = mapToHeaders(headers, assertValidPseudoHeaderResponse); 24931cb0ef41Sopenharmony_ci } catch (err) { 24941cb0ef41Sopenharmony_ci self.destroy(err); 24951cb0ef41Sopenharmony_ci return; 24961cb0ef41Sopenharmony_ci } 24971cb0ef41Sopenharmony_ci self[kSentHeaders] = headers; 24981cb0ef41Sopenharmony_ci 24991cb0ef41Sopenharmony_ci // Close the writable side of the stream, but only as far as the writable 25001cb0ef41Sopenharmony_ci // stream implementation is concerned. 25011cb0ef41Sopenharmony_ci self._final = null; 25021cb0ef41Sopenharmony_ci self.end(); 25031cb0ef41Sopenharmony_ci 25041cb0ef41Sopenharmony_ci const ret = self[kHandle].respond(headersList, streamOptions); 25051cb0ef41Sopenharmony_ci 25061cb0ef41Sopenharmony_ci if (ret < 0) { 25071cb0ef41Sopenharmony_ci self.destroy(new NghttpError(ret)); 25081cb0ef41Sopenharmony_ci return; 25091cb0ef41Sopenharmony_ci } 25101cb0ef41Sopenharmony_ci 25111cb0ef41Sopenharmony_ci defaultTriggerAsyncIdScope(self[async_id_symbol], startFilePipe, 25121cb0ef41Sopenharmony_ci self, fd, offset, length); 25131cb0ef41Sopenharmony_ci} 25141cb0ef41Sopenharmony_ci 25151cb0ef41Sopenharmony_cifunction startFilePipe(self, fd, offset, length) { 25161cb0ef41Sopenharmony_ci const handle = new FileHandle(fd, offset, length); 25171cb0ef41Sopenharmony_ci handle.onread = onPipedFileHandleRead; 25181cb0ef41Sopenharmony_ci handle.stream = self; 25191cb0ef41Sopenharmony_ci 25201cb0ef41Sopenharmony_ci const pipe = new StreamPipe(handle, self[kHandle]); 25211cb0ef41Sopenharmony_ci pipe.onunpipe = onFileUnpipe; 25221cb0ef41Sopenharmony_ci pipe.start(); 25231cb0ef41Sopenharmony_ci 25241cb0ef41Sopenharmony_ci // Exact length of the file doesn't matter here, since the 25251cb0ef41Sopenharmony_ci // stream is closing anyway - just use 1 to signify that 25261cb0ef41Sopenharmony_ci // a write does exist 25271cb0ef41Sopenharmony_ci trackWriteState(self, 1); 25281cb0ef41Sopenharmony_ci} 25291cb0ef41Sopenharmony_ci 25301cb0ef41Sopenharmony_cifunction doSendFD(session, options, fd, headers, streamOptions, err, stat) { 25311cb0ef41Sopenharmony_ci if (err) { 25321cb0ef41Sopenharmony_ci this.destroy(err); 25331cb0ef41Sopenharmony_ci return; 25341cb0ef41Sopenharmony_ci } 25351cb0ef41Sopenharmony_ci 25361cb0ef41Sopenharmony_ci // This can happen if the stream is destroyed or closed while we are waiting 25371cb0ef41Sopenharmony_ci // for the file descriptor to be opened or the stat call to be completed. 25381cb0ef41Sopenharmony_ci // In either case, we do not want to continue because the we are shutting 25391cb0ef41Sopenharmony_ci // down and should not attempt to send any data. 25401cb0ef41Sopenharmony_ci if (this.destroyed || this.closed) { 25411cb0ef41Sopenharmony_ci this.destroy(new ERR_HTTP2_INVALID_STREAM()); 25421cb0ef41Sopenharmony_ci return; 25431cb0ef41Sopenharmony_ci } 25441cb0ef41Sopenharmony_ci 25451cb0ef41Sopenharmony_ci const statOptions = { 25461cb0ef41Sopenharmony_ci offset: options.offset !== undefined ? options.offset : 0, 25471cb0ef41Sopenharmony_ci length: options.length !== undefined ? options.length : -1, 25481cb0ef41Sopenharmony_ci }; 25491cb0ef41Sopenharmony_ci 25501cb0ef41Sopenharmony_ci // options.statCheck is a user-provided function that can be used to 25511cb0ef41Sopenharmony_ci // verify stat values, override or set headers, or even cancel the 25521cb0ef41Sopenharmony_ci // response operation. If statCheck explicitly returns false, the 25531cb0ef41Sopenharmony_ci // response is canceled. The user code may also send a separate type 25541cb0ef41Sopenharmony_ci // of response so check again for the HEADERS_SENT flag 25551cb0ef41Sopenharmony_ci if ((typeof options.statCheck === 'function' && 25561cb0ef41Sopenharmony_ci ReflectApply(options.statCheck, this, 25571cb0ef41Sopenharmony_ci [stat, headers, statOptions]) === false) || 25581cb0ef41Sopenharmony_ci (this[kState].flags & STREAM_FLAGS_HEADERS_SENT)) { 25591cb0ef41Sopenharmony_ci return; 25601cb0ef41Sopenharmony_ci } 25611cb0ef41Sopenharmony_ci 25621cb0ef41Sopenharmony_ci processRespondWithFD(this, fd, headers, 25631cb0ef41Sopenharmony_ci statOptions.offset | 0, 25641cb0ef41Sopenharmony_ci statOptions.length | 0, 25651cb0ef41Sopenharmony_ci streamOptions); 25661cb0ef41Sopenharmony_ci} 25671cb0ef41Sopenharmony_ci 25681cb0ef41Sopenharmony_cifunction doSendFileFD(session, options, fd, headers, streamOptions, err, stat) { 25691cb0ef41Sopenharmony_ci const onError = options.onError; 25701cb0ef41Sopenharmony_ci 25711cb0ef41Sopenharmony_ci if (err) { 25721cb0ef41Sopenharmony_ci tryClose(fd); 25731cb0ef41Sopenharmony_ci if (onError) 25741cb0ef41Sopenharmony_ci onError(err); 25751cb0ef41Sopenharmony_ci else 25761cb0ef41Sopenharmony_ci this.destroy(err); 25771cb0ef41Sopenharmony_ci return; 25781cb0ef41Sopenharmony_ci } 25791cb0ef41Sopenharmony_ci 25801cb0ef41Sopenharmony_ci if (!stat.isFile()) { 25811cb0ef41Sopenharmony_ci const isDirectory = stat.isDirectory(); 25821cb0ef41Sopenharmony_ci if (options.offset !== undefined || options.offset > 0 || 25831cb0ef41Sopenharmony_ci options.length !== undefined || options.length >= 0 || 25841cb0ef41Sopenharmony_ci isDirectory) { 25851cb0ef41Sopenharmony_ci const err = isDirectory ? 25861cb0ef41Sopenharmony_ci new ERR_HTTP2_SEND_FILE() : new ERR_HTTP2_SEND_FILE_NOSEEK(); 25871cb0ef41Sopenharmony_ci tryClose(fd); 25881cb0ef41Sopenharmony_ci if (onError) 25891cb0ef41Sopenharmony_ci onError(err); 25901cb0ef41Sopenharmony_ci else 25911cb0ef41Sopenharmony_ci this.destroy(err); 25921cb0ef41Sopenharmony_ci return; 25931cb0ef41Sopenharmony_ci } 25941cb0ef41Sopenharmony_ci 25951cb0ef41Sopenharmony_ci options.offset = -1; 25961cb0ef41Sopenharmony_ci options.length = -1; 25971cb0ef41Sopenharmony_ci } 25981cb0ef41Sopenharmony_ci 25991cb0ef41Sopenharmony_ci if (this.destroyed || this.closed) { 26001cb0ef41Sopenharmony_ci tryClose(fd); 26011cb0ef41Sopenharmony_ci this.destroy(new ERR_HTTP2_INVALID_STREAM()); 26021cb0ef41Sopenharmony_ci return; 26031cb0ef41Sopenharmony_ci } 26041cb0ef41Sopenharmony_ci 26051cb0ef41Sopenharmony_ci const statOptions = { 26061cb0ef41Sopenharmony_ci offset: options.offset !== undefined ? options.offset : 0, 26071cb0ef41Sopenharmony_ci length: options.length !== undefined ? options.length : -1, 26081cb0ef41Sopenharmony_ci }; 26091cb0ef41Sopenharmony_ci 26101cb0ef41Sopenharmony_ci // options.statCheck is a user-provided function that can be used to 26111cb0ef41Sopenharmony_ci // verify stat values, override or set headers, or even cancel the 26121cb0ef41Sopenharmony_ci // response operation. If statCheck explicitly returns false, the 26131cb0ef41Sopenharmony_ci // response is canceled. The user code may also send a separate type 26141cb0ef41Sopenharmony_ci // of response so check again for the HEADERS_SENT flag 26151cb0ef41Sopenharmony_ci if ((typeof options.statCheck === 'function' && 26161cb0ef41Sopenharmony_ci ReflectApply(options.statCheck, this, [stat, headers]) === false) || 26171cb0ef41Sopenharmony_ci (this[kState].flags & STREAM_FLAGS_HEADERS_SENT)) { 26181cb0ef41Sopenharmony_ci tryClose(fd); 26191cb0ef41Sopenharmony_ci return; 26201cb0ef41Sopenharmony_ci } 26211cb0ef41Sopenharmony_ci 26221cb0ef41Sopenharmony_ci if (stat.isFile()) { 26231cb0ef41Sopenharmony_ci statOptions.length = 26241cb0ef41Sopenharmony_ci statOptions.length < 0 ? stat.size - (+statOptions.offset) : 26251cb0ef41Sopenharmony_ci MathMin(stat.size - (+statOptions.offset), 26261cb0ef41Sopenharmony_ci statOptions.length); 26271cb0ef41Sopenharmony_ci 26281cb0ef41Sopenharmony_ci headers[HTTP2_HEADER_CONTENT_LENGTH] = statOptions.length; 26291cb0ef41Sopenharmony_ci } 26301cb0ef41Sopenharmony_ci 26311cb0ef41Sopenharmony_ci processRespondWithFD(this, fd, headers, 26321cb0ef41Sopenharmony_ci options.offset | 0, 26331cb0ef41Sopenharmony_ci statOptions.length | 0, 26341cb0ef41Sopenharmony_ci streamOptions); 26351cb0ef41Sopenharmony_ci} 26361cb0ef41Sopenharmony_ci 26371cb0ef41Sopenharmony_cifunction afterOpen(session, options, headers, streamOptions, err, fd) { 26381cb0ef41Sopenharmony_ci const state = this[kState]; 26391cb0ef41Sopenharmony_ci const onError = options.onError; 26401cb0ef41Sopenharmony_ci if (err) { 26411cb0ef41Sopenharmony_ci if (onError) 26421cb0ef41Sopenharmony_ci onError(err); 26431cb0ef41Sopenharmony_ci else 26441cb0ef41Sopenharmony_ci this.destroy(err); 26451cb0ef41Sopenharmony_ci return; 26461cb0ef41Sopenharmony_ci } 26471cb0ef41Sopenharmony_ci if (this.destroyed || this.closed) { 26481cb0ef41Sopenharmony_ci tryClose(fd); 26491cb0ef41Sopenharmony_ci return; 26501cb0ef41Sopenharmony_ci } 26511cb0ef41Sopenharmony_ci state.fd = fd; 26521cb0ef41Sopenharmony_ci 26531cb0ef41Sopenharmony_ci fs.fstat(fd, 26541cb0ef41Sopenharmony_ci FunctionPrototypeBind(doSendFileFD, this, 26551cb0ef41Sopenharmony_ci session, options, fd, 26561cb0ef41Sopenharmony_ci headers, streamOptions)); 26571cb0ef41Sopenharmony_ci} 26581cb0ef41Sopenharmony_ci 26591cb0ef41Sopenharmony_ciclass ServerHttp2Stream extends Http2Stream { 26601cb0ef41Sopenharmony_ci constructor(session, handle, id, options, headers) { 26611cb0ef41Sopenharmony_ci super(session, options); 26621cb0ef41Sopenharmony_ci handle.owner = this; 26631cb0ef41Sopenharmony_ci this[kInit](id, handle); 26641cb0ef41Sopenharmony_ci this[kProtocol] = headers[HTTP2_HEADER_SCHEME]; 26651cb0ef41Sopenharmony_ci this[kAuthority] = getAuthority(headers); 26661cb0ef41Sopenharmony_ci } 26671cb0ef41Sopenharmony_ci 26681cb0ef41Sopenharmony_ci // True if the remote peer accepts push streams 26691cb0ef41Sopenharmony_ci get pushAllowed() { 26701cb0ef41Sopenharmony_ci return !this.destroyed && 26711cb0ef41Sopenharmony_ci !this.closed && 26721cb0ef41Sopenharmony_ci !this.session.closed && 26731cb0ef41Sopenharmony_ci !this.session.destroyed && 26741cb0ef41Sopenharmony_ci this[kSession].remoteSettings.enablePush; 26751cb0ef41Sopenharmony_ci } 26761cb0ef41Sopenharmony_ci 26771cb0ef41Sopenharmony_ci // Create a push stream, call the given callback with the created 26781cb0ef41Sopenharmony_ci // Http2Stream for the push stream. 26791cb0ef41Sopenharmony_ci pushStream(headers, options, callback) { 26801cb0ef41Sopenharmony_ci if (!this.pushAllowed) 26811cb0ef41Sopenharmony_ci throw new ERR_HTTP2_PUSH_DISABLED(); 26821cb0ef41Sopenharmony_ci if (this[kID] % 2 === 0) 26831cb0ef41Sopenharmony_ci throw new ERR_HTTP2_NESTED_PUSH(); 26841cb0ef41Sopenharmony_ci 26851cb0ef41Sopenharmony_ci const session = this[kSession]; 26861cb0ef41Sopenharmony_ci 26871cb0ef41Sopenharmony_ci debugStreamObj(this, 'initiating push stream'); 26881cb0ef41Sopenharmony_ci 26891cb0ef41Sopenharmony_ci this[kUpdateTimer](); 26901cb0ef41Sopenharmony_ci 26911cb0ef41Sopenharmony_ci if (typeof options === 'function') { 26921cb0ef41Sopenharmony_ci callback = options; 26931cb0ef41Sopenharmony_ci options = undefined; 26941cb0ef41Sopenharmony_ci } 26951cb0ef41Sopenharmony_ci 26961cb0ef41Sopenharmony_ci validateFunction(callback, 'callback'); 26971cb0ef41Sopenharmony_ci 26981cb0ef41Sopenharmony_ci assertIsObject(options, 'options'); 26991cb0ef41Sopenharmony_ci options = { ...options }; 27001cb0ef41Sopenharmony_ci options.endStream = !!options.endStream; 27011cb0ef41Sopenharmony_ci 27021cb0ef41Sopenharmony_ci assertIsObject(headers, 'headers'); 27031cb0ef41Sopenharmony_ci headers = ObjectAssign(ObjectCreate(null), headers); 27041cb0ef41Sopenharmony_ci 27051cb0ef41Sopenharmony_ci if (headers[HTTP2_HEADER_METHOD] === undefined) 27061cb0ef41Sopenharmony_ci headers[HTTP2_HEADER_METHOD] = HTTP2_METHOD_GET; 27071cb0ef41Sopenharmony_ci if (getAuthority(headers) === undefined) 27081cb0ef41Sopenharmony_ci headers[HTTP2_HEADER_AUTHORITY] = this[kAuthority]; 27091cb0ef41Sopenharmony_ci if (headers[HTTP2_HEADER_SCHEME] === undefined) 27101cb0ef41Sopenharmony_ci headers[HTTP2_HEADER_SCHEME] = this[kProtocol]; 27111cb0ef41Sopenharmony_ci if (headers[HTTP2_HEADER_PATH] === undefined) 27121cb0ef41Sopenharmony_ci headers[HTTP2_HEADER_PATH] = '/'; 27131cb0ef41Sopenharmony_ci 27141cb0ef41Sopenharmony_ci let headRequest = false; 27151cb0ef41Sopenharmony_ci if (headers[HTTP2_HEADER_METHOD] === HTTP2_METHOD_HEAD) 27161cb0ef41Sopenharmony_ci headRequest = options.endStream = true; 27171cb0ef41Sopenharmony_ci 27181cb0ef41Sopenharmony_ci const headersList = mapToHeaders(headers); 27191cb0ef41Sopenharmony_ci 27201cb0ef41Sopenharmony_ci const streamOptions = options.endStream ? STREAM_OPTION_EMPTY_PAYLOAD : 0; 27211cb0ef41Sopenharmony_ci 27221cb0ef41Sopenharmony_ci const ret = this[kHandle].pushPromise(headersList, streamOptions); 27231cb0ef41Sopenharmony_ci let err; 27241cb0ef41Sopenharmony_ci if (typeof ret === 'number') { 27251cb0ef41Sopenharmony_ci switch (ret) { 27261cb0ef41Sopenharmony_ci case NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE: 27271cb0ef41Sopenharmony_ci err = new ERR_HTTP2_OUT_OF_STREAMS(); 27281cb0ef41Sopenharmony_ci break; 27291cb0ef41Sopenharmony_ci case NGHTTP2_ERR_STREAM_CLOSED: 27301cb0ef41Sopenharmony_ci err = new ERR_HTTP2_INVALID_STREAM(); 27311cb0ef41Sopenharmony_ci break; 27321cb0ef41Sopenharmony_ci default: 27331cb0ef41Sopenharmony_ci err = new NghttpError(ret); 27341cb0ef41Sopenharmony_ci break; 27351cb0ef41Sopenharmony_ci } 27361cb0ef41Sopenharmony_ci process.nextTick(callback, err); 27371cb0ef41Sopenharmony_ci return; 27381cb0ef41Sopenharmony_ci } 27391cb0ef41Sopenharmony_ci 27401cb0ef41Sopenharmony_ci const id = ret.id(); 27411cb0ef41Sopenharmony_ci const stream = new ServerHttp2Stream(session, ret, id, options, headers); 27421cb0ef41Sopenharmony_ci stream[kSentHeaders] = headers; 27431cb0ef41Sopenharmony_ci 27441cb0ef41Sopenharmony_ci stream.push(null); 27451cb0ef41Sopenharmony_ci 27461cb0ef41Sopenharmony_ci if (options.endStream) 27471cb0ef41Sopenharmony_ci stream.end(); 27481cb0ef41Sopenharmony_ci 27491cb0ef41Sopenharmony_ci if (headRequest) 27501cb0ef41Sopenharmony_ci stream[kState].flags |= STREAM_FLAGS_HEAD_REQUEST; 27511cb0ef41Sopenharmony_ci 27521cb0ef41Sopenharmony_ci process.nextTick(callback, null, stream, headers, 0); 27531cb0ef41Sopenharmony_ci } 27541cb0ef41Sopenharmony_ci 27551cb0ef41Sopenharmony_ci // Initiate a response on this Http2Stream 27561cb0ef41Sopenharmony_ci respond(headers, options) { 27571cb0ef41Sopenharmony_ci if (this.destroyed || this.closed) 27581cb0ef41Sopenharmony_ci throw new ERR_HTTP2_INVALID_STREAM(); 27591cb0ef41Sopenharmony_ci if (this.headersSent) 27601cb0ef41Sopenharmony_ci throw new ERR_HTTP2_HEADERS_SENT(); 27611cb0ef41Sopenharmony_ci 27621cb0ef41Sopenharmony_ci const state = this[kState]; 27631cb0ef41Sopenharmony_ci 27641cb0ef41Sopenharmony_ci assertIsObject(options, 'options'); 27651cb0ef41Sopenharmony_ci options = { ...options }; 27661cb0ef41Sopenharmony_ci 27671cb0ef41Sopenharmony_ci debugStreamObj(this, 'initiating response'); 27681cb0ef41Sopenharmony_ci this[kUpdateTimer](); 27691cb0ef41Sopenharmony_ci 27701cb0ef41Sopenharmony_ci options.endStream = !!options.endStream; 27711cb0ef41Sopenharmony_ci 27721cb0ef41Sopenharmony_ci let streamOptions = 0; 27731cb0ef41Sopenharmony_ci if (options.endStream) 27741cb0ef41Sopenharmony_ci streamOptions |= STREAM_OPTION_EMPTY_PAYLOAD; 27751cb0ef41Sopenharmony_ci 27761cb0ef41Sopenharmony_ci if (options.waitForTrailers) { 27771cb0ef41Sopenharmony_ci streamOptions |= STREAM_OPTION_GET_TRAILERS; 27781cb0ef41Sopenharmony_ci state.flags |= STREAM_FLAGS_HAS_TRAILERS; 27791cb0ef41Sopenharmony_ci } 27801cb0ef41Sopenharmony_ci 27811cb0ef41Sopenharmony_ci headers = processHeaders(headers, options); 27821cb0ef41Sopenharmony_ci const headersList = mapToHeaders(headers, assertValidPseudoHeaderResponse); 27831cb0ef41Sopenharmony_ci this[kSentHeaders] = headers; 27841cb0ef41Sopenharmony_ci 27851cb0ef41Sopenharmony_ci state.flags |= STREAM_FLAGS_HEADERS_SENT; 27861cb0ef41Sopenharmony_ci 27871cb0ef41Sopenharmony_ci // Close the writable side if the endStream option is set or status 27881cb0ef41Sopenharmony_ci // is one of known codes with no payload, or it's a head request 27891cb0ef41Sopenharmony_ci const statusCode = headers[HTTP2_HEADER_STATUS] | 0; 27901cb0ef41Sopenharmony_ci if (!!options.endStream || 27911cb0ef41Sopenharmony_ci statusCode === HTTP_STATUS_NO_CONTENT || 27921cb0ef41Sopenharmony_ci statusCode === HTTP_STATUS_RESET_CONTENT || 27931cb0ef41Sopenharmony_ci statusCode === HTTP_STATUS_NOT_MODIFIED || 27941cb0ef41Sopenharmony_ci this.headRequest === true) { 27951cb0ef41Sopenharmony_ci options.endStream = true; 27961cb0ef41Sopenharmony_ci this.end(); 27971cb0ef41Sopenharmony_ci } 27981cb0ef41Sopenharmony_ci 27991cb0ef41Sopenharmony_ci const ret = this[kHandle].respond(headersList, streamOptions); 28001cb0ef41Sopenharmony_ci if (ret < 0) 28011cb0ef41Sopenharmony_ci this.destroy(new NghttpError(ret)); 28021cb0ef41Sopenharmony_ci } 28031cb0ef41Sopenharmony_ci 28041cb0ef41Sopenharmony_ci // Initiate a response using an open FD. Note that there are fewer 28051cb0ef41Sopenharmony_ci // protections with this approach. For one, the fd is not validated by 28061cb0ef41Sopenharmony_ci // default. In respondWithFile, the file is checked to make sure it is a 28071cb0ef41Sopenharmony_ci // regular file, here the fd is passed directly. If the underlying 28081cb0ef41Sopenharmony_ci // mechanism is not able to read from the fd, then the stream will be 28091cb0ef41Sopenharmony_ci // reset with an error code. 28101cb0ef41Sopenharmony_ci respondWithFD(fd, headers, options) { 28111cb0ef41Sopenharmony_ci if (this.destroyed || this.closed) 28121cb0ef41Sopenharmony_ci throw new ERR_HTTP2_INVALID_STREAM(); 28131cb0ef41Sopenharmony_ci if (this.headersSent) 28141cb0ef41Sopenharmony_ci throw new ERR_HTTP2_HEADERS_SENT(); 28151cb0ef41Sopenharmony_ci 28161cb0ef41Sopenharmony_ci const session = this[kSession]; 28171cb0ef41Sopenharmony_ci 28181cb0ef41Sopenharmony_ci assertIsObject(options, 'options'); 28191cb0ef41Sopenharmony_ci options = { ...options }; 28201cb0ef41Sopenharmony_ci 28211cb0ef41Sopenharmony_ci if (options.offset !== undefined && typeof options.offset !== 'number') 28221cb0ef41Sopenharmony_ci throw new ERR_INVALID_ARG_VALUE('options.offset', options.offset); 28231cb0ef41Sopenharmony_ci 28241cb0ef41Sopenharmony_ci if (options.length !== undefined && typeof options.length !== 'number') 28251cb0ef41Sopenharmony_ci throw new ERR_INVALID_ARG_VALUE('options.length', options.length); 28261cb0ef41Sopenharmony_ci 28271cb0ef41Sopenharmony_ci if (options.statCheck !== undefined && 28281cb0ef41Sopenharmony_ci typeof options.statCheck !== 'function') { 28291cb0ef41Sopenharmony_ci throw new ERR_INVALID_ARG_VALUE('options.statCheck', options.statCheck); 28301cb0ef41Sopenharmony_ci } 28311cb0ef41Sopenharmony_ci 28321cb0ef41Sopenharmony_ci let streamOptions = 0; 28331cb0ef41Sopenharmony_ci if (options.waitForTrailers) { 28341cb0ef41Sopenharmony_ci streamOptions |= STREAM_OPTION_GET_TRAILERS; 28351cb0ef41Sopenharmony_ci this[kState].flags |= STREAM_FLAGS_HAS_TRAILERS; 28361cb0ef41Sopenharmony_ci } 28371cb0ef41Sopenharmony_ci 28381cb0ef41Sopenharmony_ci if (fd instanceof fsPromisesInternal.FileHandle) 28391cb0ef41Sopenharmony_ci fd = fd.fd; 28401cb0ef41Sopenharmony_ci else if (typeof fd !== 'number') 28411cb0ef41Sopenharmony_ci throw new ERR_INVALID_ARG_TYPE('fd', ['number', 'FileHandle'], fd); 28421cb0ef41Sopenharmony_ci 28431cb0ef41Sopenharmony_ci debugStreamObj(this, 'initiating response from fd'); 28441cb0ef41Sopenharmony_ci this[kUpdateTimer](); 28451cb0ef41Sopenharmony_ci this.ownsFd = false; 28461cb0ef41Sopenharmony_ci 28471cb0ef41Sopenharmony_ci headers = processHeaders(headers, options); 28481cb0ef41Sopenharmony_ci const statusCode = headers[HTTP2_HEADER_STATUS] |= 0; 28491cb0ef41Sopenharmony_ci // Payload/DATA frames are not permitted in these cases 28501cb0ef41Sopenharmony_ci if (statusCode === HTTP_STATUS_NO_CONTENT || 28511cb0ef41Sopenharmony_ci statusCode === HTTP_STATUS_RESET_CONTENT || 28521cb0ef41Sopenharmony_ci statusCode === HTTP_STATUS_NOT_MODIFIED || 28531cb0ef41Sopenharmony_ci this.headRequest) { 28541cb0ef41Sopenharmony_ci throw new ERR_HTTP2_PAYLOAD_FORBIDDEN(statusCode); 28551cb0ef41Sopenharmony_ci } 28561cb0ef41Sopenharmony_ci 28571cb0ef41Sopenharmony_ci if (options.statCheck !== undefined) { 28581cb0ef41Sopenharmony_ci fs.fstat(fd, 28591cb0ef41Sopenharmony_ci FunctionPrototypeBind(doSendFD, this, 28601cb0ef41Sopenharmony_ci session, options, fd, 28611cb0ef41Sopenharmony_ci headers, streamOptions)); 28621cb0ef41Sopenharmony_ci return; 28631cb0ef41Sopenharmony_ci } 28641cb0ef41Sopenharmony_ci 28651cb0ef41Sopenharmony_ci processRespondWithFD(this, fd, headers, 28661cb0ef41Sopenharmony_ci options.offset, 28671cb0ef41Sopenharmony_ci options.length, 28681cb0ef41Sopenharmony_ci streamOptions); 28691cb0ef41Sopenharmony_ci } 28701cb0ef41Sopenharmony_ci 28711cb0ef41Sopenharmony_ci // Initiate a file response on this Http2Stream. The path is passed to 28721cb0ef41Sopenharmony_ci // fs.open() to acquire the fd with mode 'r', then the fd is passed to 28731cb0ef41Sopenharmony_ci // fs.fstat(). Assuming fstat is successful, a check is made to ensure 28741cb0ef41Sopenharmony_ci // that the file is a regular file, then options.statCheck is called, 28751cb0ef41Sopenharmony_ci // giving the user an opportunity to verify the details and set additional 28761cb0ef41Sopenharmony_ci // headers. If statCheck returns false, the operation is aborted and no 28771cb0ef41Sopenharmony_ci // file details are sent. 28781cb0ef41Sopenharmony_ci respondWithFile(path, headers, options) { 28791cb0ef41Sopenharmony_ci if (this.destroyed || this.closed) 28801cb0ef41Sopenharmony_ci throw new ERR_HTTP2_INVALID_STREAM(); 28811cb0ef41Sopenharmony_ci if (this.headersSent) 28821cb0ef41Sopenharmony_ci throw new ERR_HTTP2_HEADERS_SENT(); 28831cb0ef41Sopenharmony_ci 28841cb0ef41Sopenharmony_ci assertIsObject(options, 'options'); 28851cb0ef41Sopenharmony_ci options = { ...options }; 28861cb0ef41Sopenharmony_ci 28871cb0ef41Sopenharmony_ci if (options.offset !== undefined && typeof options.offset !== 'number') 28881cb0ef41Sopenharmony_ci throw new ERR_INVALID_ARG_VALUE('options.offset', options.offset); 28891cb0ef41Sopenharmony_ci 28901cb0ef41Sopenharmony_ci if (options.length !== undefined && typeof options.length !== 'number') 28911cb0ef41Sopenharmony_ci throw new ERR_INVALID_ARG_VALUE('options.length', options.length); 28921cb0ef41Sopenharmony_ci 28931cb0ef41Sopenharmony_ci if (options.statCheck !== undefined && 28941cb0ef41Sopenharmony_ci typeof options.statCheck !== 'function') { 28951cb0ef41Sopenharmony_ci throw new ERR_INVALID_ARG_VALUE('options.statCheck', options.statCheck); 28961cb0ef41Sopenharmony_ci } 28971cb0ef41Sopenharmony_ci 28981cb0ef41Sopenharmony_ci let streamOptions = 0; 28991cb0ef41Sopenharmony_ci if (options.waitForTrailers) { 29001cb0ef41Sopenharmony_ci streamOptions |= STREAM_OPTION_GET_TRAILERS; 29011cb0ef41Sopenharmony_ci this[kState].flags |= STREAM_FLAGS_HAS_TRAILERS; 29021cb0ef41Sopenharmony_ci } 29031cb0ef41Sopenharmony_ci 29041cb0ef41Sopenharmony_ci const session = this[kSession]; 29051cb0ef41Sopenharmony_ci debugStreamObj(this, 'initiating response from file'); 29061cb0ef41Sopenharmony_ci this[kUpdateTimer](); 29071cb0ef41Sopenharmony_ci this.ownsFd = true; 29081cb0ef41Sopenharmony_ci 29091cb0ef41Sopenharmony_ci headers = processHeaders(headers, options); 29101cb0ef41Sopenharmony_ci const statusCode = headers[HTTP2_HEADER_STATUS] |= 0; 29111cb0ef41Sopenharmony_ci // Payload/DATA frames are not permitted in these cases 29121cb0ef41Sopenharmony_ci if (statusCode === HTTP_STATUS_NO_CONTENT || 29131cb0ef41Sopenharmony_ci statusCode === HTTP_STATUS_RESET_CONTENT || 29141cb0ef41Sopenharmony_ci statusCode === HTTP_STATUS_NOT_MODIFIED || 29151cb0ef41Sopenharmony_ci this.headRequest) { 29161cb0ef41Sopenharmony_ci throw new ERR_HTTP2_PAYLOAD_FORBIDDEN(statusCode); 29171cb0ef41Sopenharmony_ci } 29181cb0ef41Sopenharmony_ci 29191cb0ef41Sopenharmony_ci fs.open(path, 'r', 29201cb0ef41Sopenharmony_ci FunctionPrototypeBind(afterOpen, this, 29211cb0ef41Sopenharmony_ci session, options, headers, streamOptions)); 29221cb0ef41Sopenharmony_ci } 29231cb0ef41Sopenharmony_ci 29241cb0ef41Sopenharmony_ci // Sends a block of informational headers. In theory, the HTTP/2 spec 29251cb0ef41Sopenharmony_ci // allows sending a HEADER block at any time during a streams lifecycle, 29261cb0ef41Sopenharmony_ci // but the HTTP request/response semantics defined in HTTP/2 places limits 29271cb0ef41Sopenharmony_ci // such that HEADERS may only be sent *before* or *after* DATA frames. 29281cb0ef41Sopenharmony_ci // If the block of headers being sent includes a status code, it MUST be 29291cb0ef41Sopenharmony_ci // a 1xx informational code and it MUST be sent before the request/response 29301cb0ef41Sopenharmony_ci // headers are sent, or an error will be thrown. 29311cb0ef41Sopenharmony_ci additionalHeaders(headers) { 29321cb0ef41Sopenharmony_ci if (this.destroyed || this.closed) 29331cb0ef41Sopenharmony_ci throw new ERR_HTTP2_INVALID_STREAM(); 29341cb0ef41Sopenharmony_ci if (this.headersSent) 29351cb0ef41Sopenharmony_ci throw new ERR_HTTP2_HEADERS_AFTER_RESPOND(); 29361cb0ef41Sopenharmony_ci 29371cb0ef41Sopenharmony_ci assertIsObject(headers, 'headers'); 29381cb0ef41Sopenharmony_ci headers = ObjectAssign(ObjectCreate(null), headers); 29391cb0ef41Sopenharmony_ci 29401cb0ef41Sopenharmony_ci debugStreamObj(this, 'sending additional headers'); 29411cb0ef41Sopenharmony_ci 29421cb0ef41Sopenharmony_ci if (headers[HTTP2_HEADER_STATUS] != null) { 29431cb0ef41Sopenharmony_ci const statusCode = headers[HTTP2_HEADER_STATUS] |= 0; 29441cb0ef41Sopenharmony_ci if (statusCode === HTTP_STATUS_SWITCHING_PROTOCOLS) 29451cb0ef41Sopenharmony_ci throw new ERR_HTTP2_STATUS_101(); 29461cb0ef41Sopenharmony_ci if (statusCode < 100 || statusCode >= 200) { 29471cb0ef41Sopenharmony_ci throw new ERR_HTTP2_INVALID_INFO_STATUS(headers[HTTP2_HEADER_STATUS]); 29481cb0ef41Sopenharmony_ci } 29491cb0ef41Sopenharmony_ci } 29501cb0ef41Sopenharmony_ci 29511cb0ef41Sopenharmony_ci this[kUpdateTimer](); 29521cb0ef41Sopenharmony_ci 29531cb0ef41Sopenharmony_ci const headersList = mapToHeaders(headers, assertValidPseudoHeaderResponse); 29541cb0ef41Sopenharmony_ci if (!this[kInfoHeaders]) 29551cb0ef41Sopenharmony_ci this[kInfoHeaders] = [headers]; 29561cb0ef41Sopenharmony_ci else 29571cb0ef41Sopenharmony_ci ArrayPrototypePush(this[kInfoHeaders], headers); 29581cb0ef41Sopenharmony_ci 29591cb0ef41Sopenharmony_ci const ret = this[kHandle].info(headersList); 29601cb0ef41Sopenharmony_ci if (ret < 0) 29611cb0ef41Sopenharmony_ci this.destroy(new NghttpError(ret)); 29621cb0ef41Sopenharmony_ci } 29631cb0ef41Sopenharmony_ci} 29641cb0ef41Sopenharmony_ci 29651cb0ef41Sopenharmony_ciServerHttp2Stream.prototype[kProceed] = ServerHttp2Stream.prototype.respond; 29661cb0ef41Sopenharmony_ci 29671cb0ef41Sopenharmony_ciclass ClientHttp2Stream extends Http2Stream { 29681cb0ef41Sopenharmony_ci constructor(session, handle, id, options) { 29691cb0ef41Sopenharmony_ci super(session, options); 29701cb0ef41Sopenharmony_ci this[kState].flags |= STREAM_FLAGS_HEADERS_SENT; 29711cb0ef41Sopenharmony_ci if (id !== undefined) 29721cb0ef41Sopenharmony_ci this[kInit](id, handle); 29731cb0ef41Sopenharmony_ci this.on('headers', handleHeaderContinue); 29741cb0ef41Sopenharmony_ci } 29751cb0ef41Sopenharmony_ci} 29761cb0ef41Sopenharmony_ci 29771cb0ef41Sopenharmony_cifunction handleHeaderContinue(headers) { 29781cb0ef41Sopenharmony_ci if (headers[HTTP2_HEADER_STATUS] === HTTP_STATUS_CONTINUE) 29791cb0ef41Sopenharmony_ci this.emit('continue'); 29801cb0ef41Sopenharmony_ci} 29811cb0ef41Sopenharmony_ci 29821cb0ef41Sopenharmony_ciconst setTimeoutValue = { 29831cb0ef41Sopenharmony_ci configurable: true, 29841cb0ef41Sopenharmony_ci enumerable: true, 29851cb0ef41Sopenharmony_ci writable: true, 29861cb0ef41Sopenharmony_ci value: setStreamTimeout, 29871cb0ef41Sopenharmony_ci}; 29881cb0ef41Sopenharmony_ciObjectDefineProperty(Http2Stream.prototype, 'setTimeout', setTimeoutValue); 29891cb0ef41Sopenharmony_ciObjectDefineProperty(Http2Session.prototype, 'setTimeout', setTimeoutValue); 29901cb0ef41Sopenharmony_ci 29911cb0ef41Sopenharmony_ci 29921cb0ef41Sopenharmony_ci// When the socket emits an error, destroy the associated Http2Session and 29931cb0ef41Sopenharmony_ci// forward it the same error. 29941cb0ef41Sopenharmony_cifunction socketOnError(error) { 29951cb0ef41Sopenharmony_ci const session = this[kSession]; 29961cb0ef41Sopenharmony_ci if (session !== undefined) { 29971cb0ef41Sopenharmony_ci // We can ignore ECONNRESET after GOAWAY was received as there's nothing 29981cb0ef41Sopenharmony_ci // we can do and the other side is fully within its rights to do so. 29991cb0ef41Sopenharmony_ci if (error.code === 'ECONNRESET' && session[kState].goawayCode !== null) 30001cb0ef41Sopenharmony_ci return session.destroy(); 30011cb0ef41Sopenharmony_ci debugSessionObj(this, 'socket error [%s]', error.message); 30021cb0ef41Sopenharmony_ci session.destroy(error); 30031cb0ef41Sopenharmony_ci } 30041cb0ef41Sopenharmony_ci} 30051cb0ef41Sopenharmony_ci 30061cb0ef41Sopenharmony_ci// Handles the on('stream') event for a session and forwards 30071cb0ef41Sopenharmony_ci// it on to the server object. 30081cb0ef41Sopenharmony_cifunction sessionOnStream(stream, headers, flags, rawHeaders) { 30091cb0ef41Sopenharmony_ci if (this[kServer] !== undefined) 30101cb0ef41Sopenharmony_ci this[kServer].emit('stream', stream, headers, flags, rawHeaders); 30111cb0ef41Sopenharmony_ci} 30121cb0ef41Sopenharmony_ci 30131cb0ef41Sopenharmony_cifunction sessionOnPriority(stream, parent, weight, exclusive) { 30141cb0ef41Sopenharmony_ci if (this[kServer] !== undefined) 30151cb0ef41Sopenharmony_ci this[kServer].emit('priority', stream, parent, weight, exclusive); 30161cb0ef41Sopenharmony_ci} 30171cb0ef41Sopenharmony_ci 30181cb0ef41Sopenharmony_cifunction sessionOnError(error) { 30191cb0ef41Sopenharmony_ci if (this[kServer] !== undefined) 30201cb0ef41Sopenharmony_ci this[kServer].emit('sessionError', error, this); 30211cb0ef41Sopenharmony_ci} 30221cb0ef41Sopenharmony_ci 30231cb0ef41Sopenharmony_ci// When the session times out on the server, try emitting a timeout event. 30241cb0ef41Sopenharmony_ci// If no handler is registered, destroy the session. 30251cb0ef41Sopenharmony_cifunction sessionOnTimeout() { 30261cb0ef41Sopenharmony_ci // If destroyed or closed already, do nothing 30271cb0ef41Sopenharmony_ci if (this.destroyed || this.closed) 30281cb0ef41Sopenharmony_ci return; 30291cb0ef41Sopenharmony_ci const server = this[kServer]; 30301cb0ef41Sopenharmony_ci if (!server.emit('timeout', this)) 30311cb0ef41Sopenharmony_ci this.destroy(); // No error code, just things down. 30321cb0ef41Sopenharmony_ci} 30331cb0ef41Sopenharmony_ci 30341cb0ef41Sopenharmony_cifunction connectionListener(socket) { 30351cb0ef41Sopenharmony_ci debug('Http2Session server: received a connection'); 30361cb0ef41Sopenharmony_ci const options = this[kOptions] || {}; 30371cb0ef41Sopenharmony_ci 30381cb0ef41Sopenharmony_ci if (socket.alpnProtocol === false || socket.alpnProtocol === 'http/1.1') { 30391cb0ef41Sopenharmony_ci // Fallback to HTTP/1.1 30401cb0ef41Sopenharmony_ci if (options.allowHTTP1 === true) { 30411cb0ef41Sopenharmony_ci socket.server[kIncomingMessage] = options.Http1IncomingMessage; 30421cb0ef41Sopenharmony_ci socket.server[kServerResponse] = options.Http1ServerResponse; 30431cb0ef41Sopenharmony_ci return FunctionPrototypeCall(httpConnectionListener, this, socket); 30441cb0ef41Sopenharmony_ci } 30451cb0ef41Sopenharmony_ci // Let event handler deal with the socket 30461cb0ef41Sopenharmony_ci debug('Unknown protocol from %s:%s', 30471cb0ef41Sopenharmony_ci socket.remoteAddress, socket.remotePort); 30481cb0ef41Sopenharmony_ci if (!this.emit('unknownProtocol', socket)) { 30491cb0ef41Sopenharmony_ci debug('Unknown protocol timeout: %s', options.unknownProtocolTimeout); 30501cb0ef41Sopenharmony_ci // Install a timeout if the socket was not successfully closed, then 30511cb0ef41Sopenharmony_ci // destroy the socket to ensure that the underlying resources are 30521cb0ef41Sopenharmony_ci // released. 30531cb0ef41Sopenharmony_ci const timer = setTimeout(() => { 30541cb0ef41Sopenharmony_ci if (!socket.destroyed) { 30551cb0ef41Sopenharmony_ci debug('UnknownProtocol socket timeout, destroy socket'); 30561cb0ef41Sopenharmony_ci socket.destroy(); 30571cb0ef41Sopenharmony_ci } 30581cb0ef41Sopenharmony_ci }, options.unknownProtocolTimeout); 30591cb0ef41Sopenharmony_ci // Un-reference the timer to avoid blocking of application shutdown and 30601cb0ef41Sopenharmony_ci // clear the timeout if the socket was successfully closed. 30611cb0ef41Sopenharmony_ci timer.unref(); 30621cb0ef41Sopenharmony_ci 30631cb0ef41Sopenharmony_ci socket.once('close', () => clearTimeout(timer)); 30641cb0ef41Sopenharmony_ci 30651cb0ef41Sopenharmony_ci // We don't know what to do, so let's just tell the other side what's 30661cb0ef41Sopenharmony_ci // going on in a format that they *might* understand. 30671cb0ef41Sopenharmony_ci socket.end('HTTP/1.0 403 Forbidden\r\n' + 30681cb0ef41Sopenharmony_ci 'Content-Type: text/plain\r\n\r\n' + 30691cb0ef41Sopenharmony_ci 'Unknown ALPN Protocol, expected `h2` to be available.\n' + 30701cb0ef41Sopenharmony_ci 'If this is a HTTP request: The server was not ' + 30711cb0ef41Sopenharmony_ci 'configured with the `allowHTTP1` option or a ' + 30721cb0ef41Sopenharmony_ci 'listener for the `unknownProtocol` event.\n'); 30731cb0ef41Sopenharmony_ci } 30741cb0ef41Sopenharmony_ci return; 30751cb0ef41Sopenharmony_ci } 30761cb0ef41Sopenharmony_ci 30771cb0ef41Sopenharmony_ci // Set up the Session 30781cb0ef41Sopenharmony_ci const session = new ServerHttp2Session(options, socket, this); 30791cb0ef41Sopenharmony_ci 30801cb0ef41Sopenharmony_ci session.on('stream', sessionOnStream); 30811cb0ef41Sopenharmony_ci session.on('error', sessionOnError); 30821cb0ef41Sopenharmony_ci // Don't count our own internal listener. 30831cb0ef41Sopenharmony_ci session.on('priority', sessionOnPriority); 30841cb0ef41Sopenharmony_ci session[kNativeFields][kSessionPriorityListenerCount]--; 30851cb0ef41Sopenharmony_ci 30861cb0ef41Sopenharmony_ci if (this.timeout) 30871cb0ef41Sopenharmony_ci session.setTimeout(this.timeout, sessionOnTimeout); 30881cb0ef41Sopenharmony_ci 30891cb0ef41Sopenharmony_ci socket[kServer] = this; 30901cb0ef41Sopenharmony_ci 30911cb0ef41Sopenharmony_ci this.emit('session', session); 30921cb0ef41Sopenharmony_ci} 30931cb0ef41Sopenharmony_ci 30941cb0ef41Sopenharmony_cifunction initializeOptions(options) { 30951cb0ef41Sopenharmony_ci assertIsObject(options, 'options'); 30961cb0ef41Sopenharmony_ci options = { ...options }; 30971cb0ef41Sopenharmony_ci assertIsObject(options.settings, 'options.settings'); 30981cb0ef41Sopenharmony_ci options.settings = { ...options.settings }; 30991cb0ef41Sopenharmony_ci 31001cb0ef41Sopenharmony_ci if (options.maxSessionInvalidFrames !== undefined) 31011cb0ef41Sopenharmony_ci validateUint32(options.maxSessionInvalidFrames, 'maxSessionInvalidFrames'); 31021cb0ef41Sopenharmony_ci 31031cb0ef41Sopenharmony_ci if (options.maxSessionRejectedStreams !== undefined) { 31041cb0ef41Sopenharmony_ci validateUint32( 31051cb0ef41Sopenharmony_ci options.maxSessionRejectedStreams, 31061cb0ef41Sopenharmony_ci 'maxSessionRejectedStreams', 31071cb0ef41Sopenharmony_ci ); 31081cb0ef41Sopenharmony_ci } 31091cb0ef41Sopenharmony_ci 31101cb0ef41Sopenharmony_ci if (options.unknownProtocolTimeout !== undefined) 31111cb0ef41Sopenharmony_ci validateUint32(options.unknownProtocolTimeout, 'unknownProtocolTimeout'); 31121cb0ef41Sopenharmony_ci else 31131cb0ef41Sopenharmony_ci // TODO(danbev): is this a good default value? 31141cb0ef41Sopenharmony_ci options.unknownProtocolTimeout = 10000; 31151cb0ef41Sopenharmony_ci 31161cb0ef41Sopenharmony_ci 31171cb0ef41Sopenharmony_ci // Used only with allowHTTP1 31181cb0ef41Sopenharmony_ci options.Http1IncomingMessage = options.Http1IncomingMessage || 31191cb0ef41Sopenharmony_ci http.IncomingMessage; 31201cb0ef41Sopenharmony_ci options.Http1ServerResponse = options.Http1ServerResponse || 31211cb0ef41Sopenharmony_ci http.ServerResponse; 31221cb0ef41Sopenharmony_ci 31231cb0ef41Sopenharmony_ci options.Http2ServerRequest = options.Http2ServerRequest || 31241cb0ef41Sopenharmony_ci Http2ServerRequest; 31251cb0ef41Sopenharmony_ci options.Http2ServerResponse = options.Http2ServerResponse || 31261cb0ef41Sopenharmony_ci Http2ServerResponse; 31271cb0ef41Sopenharmony_ci return options; 31281cb0ef41Sopenharmony_ci} 31291cb0ef41Sopenharmony_ci 31301cb0ef41Sopenharmony_cifunction initializeTLSOptions(options, servername) { 31311cb0ef41Sopenharmony_ci options = initializeOptions(options); 31321cb0ef41Sopenharmony_ci options.ALPNProtocols = ['h2']; 31331cb0ef41Sopenharmony_ci if (options.allowHTTP1 === true) 31341cb0ef41Sopenharmony_ci ArrayPrototypePush(options.ALPNProtocols, 'http/1.1'); 31351cb0ef41Sopenharmony_ci if (servername !== undefined && !options.servername) 31361cb0ef41Sopenharmony_ci options.servername = servername; 31371cb0ef41Sopenharmony_ci return options; 31381cb0ef41Sopenharmony_ci} 31391cb0ef41Sopenharmony_ci 31401cb0ef41Sopenharmony_cifunction onErrorSecureServerSession(err, socket) { 31411cb0ef41Sopenharmony_ci if (!this.emit('clientError', err, socket)) 31421cb0ef41Sopenharmony_ci socket.destroy(err); 31431cb0ef41Sopenharmony_ci} 31441cb0ef41Sopenharmony_ci 31451cb0ef41Sopenharmony_ciclass Http2SecureServer extends TLSServer { 31461cb0ef41Sopenharmony_ci constructor(options, requestListener) { 31471cb0ef41Sopenharmony_ci options = initializeTLSOptions(options); 31481cb0ef41Sopenharmony_ci super(options, connectionListener); 31491cb0ef41Sopenharmony_ci this[kOptions] = options; 31501cb0ef41Sopenharmony_ci this.timeout = 0; 31511cb0ef41Sopenharmony_ci this.on('newListener', setupCompat); 31521cb0ef41Sopenharmony_ci if (typeof requestListener === 'function') 31531cb0ef41Sopenharmony_ci this.on('request', requestListener); 31541cb0ef41Sopenharmony_ci this.on('tlsClientError', onErrorSecureServerSession); 31551cb0ef41Sopenharmony_ci } 31561cb0ef41Sopenharmony_ci 31571cb0ef41Sopenharmony_ci setTimeout(msecs, callback) { 31581cb0ef41Sopenharmony_ci this.timeout = msecs; 31591cb0ef41Sopenharmony_ci if (callback !== undefined) { 31601cb0ef41Sopenharmony_ci validateFunction(callback, 'callback'); 31611cb0ef41Sopenharmony_ci this.on('timeout', callback); 31621cb0ef41Sopenharmony_ci } 31631cb0ef41Sopenharmony_ci return this; 31641cb0ef41Sopenharmony_ci } 31651cb0ef41Sopenharmony_ci 31661cb0ef41Sopenharmony_ci updateSettings(settings) { 31671cb0ef41Sopenharmony_ci assertIsObject(settings, 'settings'); 31681cb0ef41Sopenharmony_ci validateSettings(settings); 31691cb0ef41Sopenharmony_ci this[kOptions].settings = { ...this[kOptions].settings, ...settings }; 31701cb0ef41Sopenharmony_ci } 31711cb0ef41Sopenharmony_ci} 31721cb0ef41Sopenharmony_ci 31731cb0ef41Sopenharmony_ciclass Http2Server extends NETServer { 31741cb0ef41Sopenharmony_ci constructor(options, requestListener) { 31751cb0ef41Sopenharmony_ci options = initializeOptions(options); 31761cb0ef41Sopenharmony_ci super(options, connectionListener); 31771cb0ef41Sopenharmony_ci this[kOptions] = options; 31781cb0ef41Sopenharmony_ci this.timeout = 0; 31791cb0ef41Sopenharmony_ci this.on('newListener', setupCompat); 31801cb0ef41Sopenharmony_ci if (typeof requestListener === 'function') 31811cb0ef41Sopenharmony_ci this.on('request', requestListener); 31821cb0ef41Sopenharmony_ci } 31831cb0ef41Sopenharmony_ci 31841cb0ef41Sopenharmony_ci setTimeout(msecs, callback) { 31851cb0ef41Sopenharmony_ci this.timeout = msecs; 31861cb0ef41Sopenharmony_ci if (callback !== undefined) { 31871cb0ef41Sopenharmony_ci validateFunction(callback, 'callback'); 31881cb0ef41Sopenharmony_ci this.on('timeout', callback); 31891cb0ef41Sopenharmony_ci } 31901cb0ef41Sopenharmony_ci return this; 31911cb0ef41Sopenharmony_ci } 31921cb0ef41Sopenharmony_ci 31931cb0ef41Sopenharmony_ci updateSettings(settings) { 31941cb0ef41Sopenharmony_ci assertIsObject(settings, 'settings'); 31951cb0ef41Sopenharmony_ci validateSettings(settings); 31961cb0ef41Sopenharmony_ci this[kOptions].settings = { ...this[kOptions].settings, ...settings }; 31971cb0ef41Sopenharmony_ci } 31981cb0ef41Sopenharmony_ci} 31991cb0ef41Sopenharmony_ci 32001cb0ef41Sopenharmony_ciHttp2Server.prototype[EventEmitter.captureRejectionSymbol] = function( 32011cb0ef41Sopenharmony_ci err, event, ...args) { 32021cb0ef41Sopenharmony_ci 32031cb0ef41Sopenharmony_ci switch (event) { 32041cb0ef41Sopenharmony_ci case 'stream': { 32051cb0ef41Sopenharmony_ci // TODO(mcollina): we might want to match this with what we do on 32061cb0ef41Sopenharmony_ci // the compat side. 32071cb0ef41Sopenharmony_ci const { 0: stream } = args; 32081cb0ef41Sopenharmony_ci if (stream.sentHeaders) { 32091cb0ef41Sopenharmony_ci stream.destroy(err); 32101cb0ef41Sopenharmony_ci } else { 32111cb0ef41Sopenharmony_ci stream.respond({ [HTTP2_HEADER_STATUS]: 500 }); 32121cb0ef41Sopenharmony_ci stream.end(); 32131cb0ef41Sopenharmony_ci } 32141cb0ef41Sopenharmony_ci break; 32151cb0ef41Sopenharmony_ci } 32161cb0ef41Sopenharmony_ci case 'request': { 32171cb0ef41Sopenharmony_ci const { 1: res } = args; 32181cb0ef41Sopenharmony_ci if (!res.headersSent && !res.finished) { 32191cb0ef41Sopenharmony_ci // Don't leak headers. 32201cb0ef41Sopenharmony_ci for (const name of res.getHeaderNames()) { 32211cb0ef41Sopenharmony_ci res.removeHeader(name); 32221cb0ef41Sopenharmony_ci } 32231cb0ef41Sopenharmony_ci res.statusCode = 500; 32241cb0ef41Sopenharmony_ci res.end(http.STATUS_CODES[500]); 32251cb0ef41Sopenharmony_ci } else { 32261cb0ef41Sopenharmony_ci res.destroy(); 32271cb0ef41Sopenharmony_ci } 32281cb0ef41Sopenharmony_ci break; 32291cb0ef41Sopenharmony_ci } 32301cb0ef41Sopenharmony_ci default: 32311cb0ef41Sopenharmony_ci ArrayPrototypeUnshift(args, err, event); 32321cb0ef41Sopenharmony_ci ReflectApply(net.Server.prototype[EventEmitter.captureRejectionSymbol], 32331cb0ef41Sopenharmony_ci this, args); 32341cb0ef41Sopenharmony_ci } 32351cb0ef41Sopenharmony_ci}; 32361cb0ef41Sopenharmony_ci 32371cb0ef41Sopenharmony_cifunction setupCompat(ev) { 32381cb0ef41Sopenharmony_ci if (ev === 'request') { 32391cb0ef41Sopenharmony_ci this.removeListener('newListener', setupCompat); 32401cb0ef41Sopenharmony_ci this.on('stream', FunctionPrototypeBind(onServerStream, 32411cb0ef41Sopenharmony_ci this, 32421cb0ef41Sopenharmony_ci this[kOptions].Http2ServerRequest, 32431cb0ef41Sopenharmony_ci this[kOptions].Http2ServerResponse), 32441cb0ef41Sopenharmony_ci ); 32451cb0ef41Sopenharmony_ci } 32461cb0ef41Sopenharmony_ci} 32471cb0ef41Sopenharmony_ci 32481cb0ef41Sopenharmony_cifunction socketOnClose() { 32491cb0ef41Sopenharmony_ci const session = this[kSession]; 32501cb0ef41Sopenharmony_ci if (session !== undefined) { 32511cb0ef41Sopenharmony_ci debugSessionObj(session, 'socket closed'); 32521cb0ef41Sopenharmony_ci const err = session.connecting ? new ERR_SOCKET_CLOSED() : null; 32531cb0ef41Sopenharmony_ci const state = session[kState]; 32541cb0ef41Sopenharmony_ci state.streams.forEach((stream) => stream.close(NGHTTP2_CANCEL)); 32551cb0ef41Sopenharmony_ci state.pendingStreams.forEach((stream) => stream.close(NGHTTP2_CANCEL)); 32561cb0ef41Sopenharmony_ci session.close(); 32571cb0ef41Sopenharmony_ci session[kMaybeDestroy](err); 32581cb0ef41Sopenharmony_ci } 32591cb0ef41Sopenharmony_ci} 32601cb0ef41Sopenharmony_ci 32611cb0ef41Sopenharmony_cifunction connect(authority, options, listener) { 32621cb0ef41Sopenharmony_ci if (typeof options === 'function') { 32631cb0ef41Sopenharmony_ci listener = options; 32641cb0ef41Sopenharmony_ci options = undefined; 32651cb0ef41Sopenharmony_ci } 32661cb0ef41Sopenharmony_ci 32671cb0ef41Sopenharmony_ci assertIsObject(options, 'options'); 32681cb0ef41Sopenharmony_ci options = { ...options }; 32691cb0ef41Sopenharmony_ci 32701cb0ef41Sopenharmony_ci if (typeof authority === 'string') 32711cb0ef41Sopenharmony_ci authority = new URL(authority); 32721cb0ef41Sopenharmony_ci 32731cb0ef41Sopenharmony_ci assertIsObject(authority, 'authority', ['string', 'Object', 'URL']); 32741cb0ef41Sopenharmony_ci 32751cb0ef41Sopenharmony_ci const protocol = authority.protocol || options.protocol || 'https:'; 32761cb0ef41Sopenharmony_ci const port = '' + (authority.port !== '' ? 32771cb0ef41Sopenharmony_ci authority.port : (authority.protocol === 'http:' ? 80 : 443)); 32781cb0ef41Sopenharmony_ci let host = 'localhost'; 32791cb0ef41Sopenharmony_ci 32801cb0ef41Sopenharmony_ci if (authority.hostname) { 32811cb0ef41Sopenharmony_ci host = authority.hostname; 32821cb0ef41Sopenharmony_ci 32831cb0ef41Sopenharmony_ci if (host[0] === '[') 32841cb0ef41Sopenharmony_ci host = StringPrototypeSlice(host, 1, -1); 32851cb0ef41Sopenharmony_ci } else if (authority.host) { 32861cb0ef41Sopenharmony_ci host = authority.host; 32871cb0ef41Sopenharmony_ci } 32881cb0ef41Sopenharmony_ci 32891cb0ef41Sopenharmony_ci let socket; 32901cb0ef41Sopenharmony_ci if (typeof options.createConnection === 'function') { 32911cb0ef41Sopenharmony_ci socket = options.createConnection(authority, options); 32921cb0ef41Sopenharmony_ci } else { 32931cb0ef41Sopenharmony_ci switch (protocol) { 32941cb0ef41Sopenharmony_ci case 'http:': 32951cb0ef41Sopenharmony_ci socket = net.connect({ port, host, ...options }); 32961cb0ef41Sopenharmony_ci break; 32971cb0ef41Sopenharmony_ci case 'https:': 32981cb0ef41Sopenharmony_ci socket = tls.connect(port, host, initializeTLSOptions(options, host)); 32991cb0ef41Sopenharmony_ci break; 33001cb0ef41Sopenharmony_ci default: 33011cb0ef41Sopenharmony_ci throw new ERR_HTTP2_UNSUPPORTED_PROTOCOL(protocol); 33021cb0ef41Sopenharmony_ci } 33031cb0ef41Sopenharmony_ci } 33041cb0ef41Sopenharmony_ci 33051cb0ef41Sopenharmony_ci const session = new ClientHttp2Session(options, socket); 33061cb0ef41Sopenharmony_ci 33071cb0ef41Sopenharmony_ci session[kAuthority] = `${options.servername || host}:${port}`; 33081cb0ef41Sopenharmony_ci session[kProtocol] = protocol; 33091cb0ef41Sopenharmony_ci 33101cb0ef41Sopenharmony_ci if (typeof listener === 'function') 33111cb0ef41Sopenharmony_ci session.once('connect', listener); 33121cb0ef41Sopenharmony_ci 33131cb0ef41Sopenharmony_ci return session; 33141cb0ef41Sopenharmony_ci} 33151cb0ef41Sopenharmony_ci 33161cb0ef41Sopenharmony_ci// Support util.promisify 33171cb0ef41Sopenharmony_ciObjectDefineProperty(connect, promisify.custom, { 33181cb0ef41Sopenharmony_ci __proto__: null, 33191cb0ef41Sopenharmony_ci value: (authority, options) => { 33201cb0ef41Sopenharmony_ci return new Promise((resolve) => { 33211cb0ef41Sopenharmony_ci const server = connect(authority, options, () => resolve(server)); 33221cb0ef41Sopenharmony_ci }); 33231cb0ef41Sopenharmony_ci }, 33241cb0ef41Sopenharmony_ci}); 33251cb0ef41Sopenharmony_ci 33261cb0ef41Sopenharmony_cifunction createSecureServer(options, handler) { 33271cb0ef41Sopenharmony_ci return new Http2SecureServer(options, handler); 33281cb0ef41Sopenharmony_ci} 33291cb0ef41Sopenharmony_ci 33301cb0ef41Sopenharmony_cifunction createServer(options, handler) { 33311cb0ef41Sopenharmony_ci if (typeof options === 'function') { 33321cb0ef41Sopenharmony_ci handler = options; 33331cb0ef41Sopenharmony_ci options = kEmptyObject; 33341cb0ef41Sopenharmony_ci } 33351cb0ef41Sopenharmony_ci return new Http2Server(options, handler); 33361cb0ef41Sopenharmony_ci} 33371cb0ef41Sopenharmony_ci 33381cb0ef41Sopenharmony_ci// Returns a Base64 encoded settings frame payload from the given 33391cb0ef41Sopenharmony_ci// object. The value is suitable for passing as the value of the 33401cb0ef41Sopenharmony_ci// HTTP2-Settings header frame. 33411cb0ef41Sopenharmony_cifunction getPackedSettings(settings) { 33421cb0ef41Sopenharmony_ci assertIsObject(settings, 'settings'); 33431cb0ef41Sopenharmony_ci validateSettings(settings); 33441cb0ef41Sopenharmony_ci updateSettingsBuffer({ ...settings }); 33451cb0ef41Sopenharmony_ci return binding.packSettings(); 33461cb0ef41Sopenharmony_ci} 33471cb0ef41Sopenharmony_ci 33481cb0ef41Sopenharmony_cifunction getUnpackedSettings(buf, options = kEmptyObject) { 33491cb0ef41Sopenharmony_ci if (!isArrayBufferView(buf) || buf.length === undefined) { 33501cb0ef41Sopenharmony_ci throw new ERR_INVALID_ARG_TYPE('buf', 33511cb0ef41Sopenharmony_ci ['Buffer', 'TypedArray'], buf); 33521cb0ef41Sopenharmony_ci } 33531cb0ef41Sopenharmony_ci if (buf.length % 6 !== 0) 33541cb0ef41Sopenharmony_ci throw new ERR_HTTP2_INVALID_PACKED_SETTINGS_LENGTH(); 33551cb0ef41Sopenharmony_ci const settings = {}; 33561cb0ef41Sopenharmony_ci let offset = 0; 33571cb0ef41Sopenharmony_ci while (offset < buf.length) { 33581cb0ef41Sopenharmony_ci const id = ReflectApply(readUInt16BE, buf, [offset]); 33591cb0ef41Sopenharmony_ci offset += 2; 33601cb0ef41Sopenharmony_ci const value = ReflectApply(readUInt32BE, buf, [offset]); 33611cb0ef41Sopenharmony_ci switch (id) { 33621cb0ef41Sopenharmony_ci case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: 33631cb0ef41Sopenharmony_ci settings.headerTableSize = value; 33641cb0ef41Sopenharmony_ci break; 33651cb0ef41Sopenharmony_ci case NGHTTP2_SETTINGS_ENABLE_PUSH: 33661cb0ef41Sopenharmony_ci settings.enablePush = value !== 0; 33671cb0ef41Sopenharmony_ci break; 33681cb0ef41Sopenharmony_ci case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: 33691cb0ef41Sopenharmony_ci settings.maxConcurrentStreams = value; 33701cb0ef41Sopenharmony_ci break; 33711cb0ef41Sopenharmony_ci case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: 33721cb0ef41Sopenharmony_ci settings.initialWindowSize = value; 33731cb0ef41Sopenharmony_ci break; 33741cb0ef41Sopenharmony_ci case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: 33751cb0ef41Sopenharmony_ci settings.maxFrameSize = value; 33761cb0ef41Sopenharmony_ci break; 33771cb0ef41Sopenharmony_ci case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: 33781cb0ef41Sopenharmony_ci settings.maxHeaderListSize = settings.maxHeaderSize = value; 33791cb0ef41Sopenharmony_ci break; 33801cb0ef41Sopenharmony_ci case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: 33811cb0ef41Sopenharmony_ci settings.enableConnectProtocol = value !== 0; 33821cb0ef41Sopenharmony_ci } 33831cb0ef41Sopenharmony_ci offset += 4; 33841cb0ef41Sopenharmony_ci } 33851cb0ef41Sopenharmony_ci 33861cb0ef41Sopenharmony_ci if (options != null && options.validate) 33871cb0ef41Sopenharmony_ci validateSettings(settings); 33881cb0ef41Sopenharmony_ci 33891cb0ef41Sopenharmony_ci return settings; 33901cb0ef41Sopenharmony_ci} 33911cb0ef41Sopenharmony_ci 33921cb0ef41Sopenharmony_cibinding.setCallbackFunctions( 33931cb0ef41Sopenharmony_ci onSessionInternalError, 33941cb0ef41Sopenharmony_ci onPriority, 33951cb0ef41Sopenharmony_ci onSettings, 33961cb0ef41Sopenharmony_ci onPing, 33971cb0ef41Sopenharmony_ci onSessionHeaders, 33981cb0ef41Sopenharmony_ci onFrameError, 33991cb0ef41Sopenharmony_ci onGoawayData, 34001cb0ef41Sopenharmony_ci onAltSvc, 34011cb0ef41Sopenharmony_ci onOrigin, 34021cb0ef41Sopenharmony_ci onStreamTrailers, 34031cb0ef41Sopenharmony_ci onStreamClose, 34041cb0ef41Sopenharmony_ci); 34051cb0ef41Sopenharmony_ci 34061cb0ef41Sopenharmony_ci// Exports 34071cb0ef41Sopenharmony_cimodule.exports = { 34081cb0ef41Sopenharmony_ci connect, 34091cb0ef41Sopenharmony_ci constants, 34101cb0ef41Sopenharmony_ci createServer, 34111cb0ef41Sopenharmony_ci createSecureServer, 34121cb0ef41Sopenharmony_ci getDefaultSettings, 34131cb0ef41Sopenharmony_ci getPackedSettings, 34141cb0ef41Sopenharmony_ci getUnpackedSettings, 34151cb0ef41Sopenharmony_ci sensitiveHeaders: kSensitiveHeaders, 34161cb0ef41Sopenharmony_ci Http2Session, 34171cb0ef41Sopenharmony_ci Http2Stream, 34181cb0ef41Sopenharmony_ci ServerHttp2Session, 34191cb0ef41Sopenharmony_ci Http2ServerRequest, 34201cb0ef41Sopenharmony_ci Http2ServerResponse, 34211cb0ef41Sopenharmony_ci}; 3422