1'use strict'; 2 3const { 4 ArrayFrom, 5 ArrayIsArray, 6 ArrayPrototypeForEach, 7 ArrayPrototypePush, 8 ArrayPrototypeUnshift, 9 FunctionPrototypeBind, 10 FunctionPrototypeCall, 11 MathMin, 12 ObjectAssign, 13 ObjectCreate, 14 ObjectKeys, 15 ObjectDefineProperty, 16 ObjectPrototypeHasOwnProperty, 17 Promise, 18 PromisePrototypeThen, 19 Proxy, 20 ReflectApply, 21 ReflectGet, 22 ReflectGetPrototypeOf, 23 ReflectSet, 24 RegExpPrototypeExec, 25 SafeArrayIterator, 26 SafeMap, 27 SafeSet, 28 StringPrototypeSlice, 29 Symbol, 30 SymbolDispose, 31 TypedArrayPrototypeGetLength, 32 Uint32Array, 33 Uint8Array, 34} = primordials; 35 36const { 37 assertCrypto, 38 customInspectSymbol: kInspect, 39 kEmptyObject, 40 promisify, 41} = require('internal/util'); 42 43assertCrypto(); 44 45const assert = require('assert'); 46const EventEmitter = require('events'); 47const fs = require('fs'); 48const http = require('http'); 49const { readUInt16BE, readUInt32BE } = require('internal/buffer'); 50const { URL } = require('internal/url'); 51const net = require('net'); 52const { Duplex } = require('stream'); 53const tls = require('tls'); 54const { setImmediate, setTimeout, clearTimeout } = require('timers'); 55 56const { 57 kIncomingMessage, 58 _checkIsHttpToken: checkIsHttpToken, 59} = require('_http_common'); 60const { kServerResponse } = require('_http_server'); 61const JSStreamSocket = require('internal/js_stream_socket'); 62 63const { 64 defaultTriggerAsyncIdScope, 65 symbols: { 66 async_id_symbol, 67 owner_symbol, 68 }, 69} = require('internal/async_hooks'); 70const { 71 aggregateTwoErrors, 72 codes: { 73 ERR_HTTP2_ALTSVC_INVALID_ORIGIN, 74 ERR_HTTP2_ALTSVC_LENGTH, 75 ERR_HTTP2_CONNECT_AUTHORITY, 76 ERR_HTTP2_CONNECT_PATH, 77 ERR_HTTP2_CONNECT_SCHEME, 78 ERR_HTTP2_GOAWAY_SESSION, 79 ERR_HTTP2_HEADERS_AFTER_RESPOND, 80 ERR_HTTP2_HEADERS_SENT, 81 ERR_HTTP2_INVALID_INFO_STATUS, 82 ERR_HTTP2_INVALID_ORIGIN, 83 ERR_HTTP2_INVALID_PACKED_SETTINGS_LENGTH, 84 ERR_HTTP2_INVALID_SESSION, 85 ERR_HTTP2_INVALID_SETTING_VALUE, 86 ERR_HTTP2_INVALID_STREAM, 87 ERR_HTTP2_MAX_PENDING_SETTINGS_ACK, 88 ERR_HTTP2_NESTED_PUSH, 89 ERR_HTTP2_NO_MEM, 90 ERR_HTTP2_NO_SOCKET_MANIPULATION, 91 ERR_HTTP2_ORIGIN_LENGTH, 92 ERR_HTTP2_OUT_OF_STREAMS, 93 ERR_HTTP2_PAYLOAD_FORBIDDEN, 94 ERR_HTTP2_PING_CANCEL, 95 ERR_HTTP2_PING_LENGTH, 96 ERR_HTTP2_PUSH_DISABLED, 97 ERR_HTTP2_SEND_FILE, 98 ERR_HTTP2_SEND_FILE_NOSEEK, 99 ERR_HTTP2_SESSION_ERROR, 100 ERR_HTTP2_SETTINGS_CANCEL, 101 ERR_HTTP2_SOCKET_BOUND, 102 ERR_HTTP2_SOCKET_UNBOUND, 103 ERR_HTTP2_STATUS_101, 104 ERR_HTTP2_STATUS_INVALID, 105 ERR_HTTP2_STREAM_CANCEL, 106 ERR_HTTP2_STREAM_ERROR, 107 ERR_HTTP2_STREAM_SELF_DEPENDENCY, 108 ERR_HTTP2_TRAILERS_ALREADY_SENT, 109 ERR_HTTP2_TRAILERS_NOT_READY, 110 ERR_HTTP2_UNSUPPORTED_PROTOCOL, 111 ERR_INVALID_ARG_TYPE, 112 ERR_INVALID_ARG_VALUE, 113 ERR_INVALID_CHAR, 114 ERR_INVALID_HTTP_TOKEN, 115 ERR_OUT_OF_RANGE, 116 ERR_SOCKET_CLOSED, 117 }, 118 hideStackFrames, 119 AbortError, 120} = require('internal/errors'); 121const { 122 isUint32, 123 validateAbortSignal, 124 validateBuffer, 125 validateFunction, 126 validateInt32, 127 validateInteger, 128 validateNumber, 129 validateString, 130 validateUint32, 131} = require('internal/validators'); 132const fsPromisesInternal = require('internal/fs/promises'); 133const { utcDate } = require('internal/http'); 134const { 135 Http2ServerRequest, 136 Http2ServerResponse, 137 onServerStream, 138} = require('internal/http2/compat'); 139 140const { 141 assertIsObject, 142 assertValidPseudoHeader, 143 assertValidPseudoHeaderResponse, 144 assertValidPseudoHeaderTrailer, 145 assertWithinRange, 146 getAuthority, 147 getDefaultSettings, 148 getSessionState, 149 getSettings, 150 getStreamState, 151 isPayloadMeaningless, 152 kSensitiveHeaders, 153 kSocket, 154 kRequest, 155 kProxySocket, 156 mapToHeaders, 157 NghttpError, 158 sessionName, 159 toHeaderObject, 160 updateOptionsBuffer, 161 updateSettingsBuffer, 162} = require('internal/http2/util'); 163const { 164 writeGeneric, 165 writevGeneric, 166 onStreamRead, 167 kAfterAsyncWrite, 168 kMaybeDestroy, 169 kUpdateTimer, 170 kHandle, 171 kSession, 172 setStreamTimeout, 173} = require('internal/stream_base_commons'); 174const { kTimeout } = require('internal/timers'); 175const { isArrayBufferView } = require('internal/util/types'); 176const { format } = require('internal/util/inspect'); 177 178const { FileHandle } = internalBinding('fs'); 179const binding = internalBinding('http2'); 180const { 181 ShutdownWrap, 182 kReadBytesOrError, 183 streamBaseState, 184} = internalBinding('stream_wrap'); 185const { UV_EOF } = internalBinding('uv'); 186 187const { StreamPipe } = internalBinding('stream_pipe'); 188const { _connectionListener: httpConnectionListener } = http; 189let debug = require('internal/util/debuglog').debuglog('http2', (fn) => { 190 debug = fn; 191}); 192const debugEnabled = debug.enabled; 193 194function debugStream(id, sessionType, message, ...args) { 195 if (!debugEnabled) { 196 return; 197 } 198 debug('Http2Stream %s [Http2Session %s]: ' + message, 199 id, sessionName(sessionType), ...new SafeArrayIterator(args)); 200} 201 202function debugStreamObj(stream, message, ...args) { 203 const session = stream[kSession]; 204 const type = session ? session[kType] : undefined; 205 debugStream(stream[kID], type, message, ...new SafeArrayIterator(args)); 206} 207 208function debugSession(sessionType, message, ...args) { 209 debug('Http2Session %s: ' + message, sessionName(sessionType), 210 ...new SafeArrayIterator(args)); 211} 212 213function debugSessionObj(session, message, ...args) { 214 debugSession(session[kType], message, ...new SafeArrayIterator(args)); 215} 216 217const kMaxFrameSize = (2 ** 24) - 1; 218const kMaxInt = (2 ** 32) - 1; 219const kMaxStreams = (2 ** 32) - 1; 220const kMaxALTSVC = (2 ** 14) - 2; 221 222// eslint-disable-next-line no-control-regex 223const kQuotedString = /^[\x09\x20-\x5b\x5d-\x7e\x80-\xff]*$/; 224 225const { constants, nameForErrorCode } = binding; 226 227const NETServer = net.Server; 228const TLSServer = tls.Server; 229 230const kAlpnProtocol = Symbol('alpnProtocol'); 231const kAuthority = Symbol('authority'); 232const kEncrypted = Symbol('encrypted'); 233const kID = Symbol('id'); 234const kInit = Symbol('init'); 235const kInfoHeaders = Symbol('sent-info-headers'); 236const kLocalSettings = Symbol('local-settings'); 237const kNativeFields = Symbol('kNativeFields'); 238const kOptions = Symbol('options'); 239const kOwner = owner_symbol; 240const kOrigin = Symbol('origin'); 241const kPendingRequestCalls = Symbol('kPendingRequestCalls'); 242const kProceed = Symbol('proceed'); 243const kProtocol = Symbol('protocol'); 244const kRemoteSettings = Symbol('remote-settings'); 245const kSelectPadding = Symbol('select-padding'); 246const kSentHeaders = Symbol('sent-headers'); 247const kSentTrailers = Symbol('sent-trailers'); 248const kServer = Symbol('server'); 249const kState = Symbol('state'); 250const kType = Symbol('type'); 251const kWriteGeneric = Symbol('write-generic'); 252 253const { 254 kBitfield, 255 kSessionPriorityListenerCount, 256 kSessionFrameErrorListenerCount, 257 kSessionMaxInvalidFrames, 258 kSessionMaxRejectedStreams, 259 kSessionUint8FieldCount, 260 kSessionHasRemoteSettingsListeners, 261 kSessionRemoteSettingsIsUpToDate, 262 kSessionHasPingListeners, 263 kSessionHasAltsvcListeners, 264} = binding; 265 266const { 267 NGHTTP2_CANCEL, 268 NGHTTP2_REFUSED_STREAM, 269 NGHTTP2_DEFAULT_WEIGHT, 270 NGHTTP2_FLAG_END_STREAM, 271 NGHTTP2_HCAT_PUSH_RESPONSE, 272 NGHTTP2_HCAT_RESPONSE, 273 NGHTTP2_INTERNAL_ERROR, 274 NGHTTP2_NO_ERROR, 275 NGHTTP2_SESSION_CLIENT, 276 NGHTTP2_SESSION_SERVER, 277 NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE, 278 NGHTTP2_ERR_INVALID_ARGUMENT, 279 NGHTTP2_ERR_STREAM_CLOSED, 280 NGHTTP2_ERR_NOMEM, 281 282 HTTP2_HEADER_AUTHORITY, 283 HTTP2_HEADER_DATE, 284 HTTP2_HEADER_METHOD, 285 HTTP2_HEADER_PATH, 286 HTTP2_HEADER_PROTOCOL, 287 HTTP2_HEADER_SCHEME, 288 HTTP2_HEADER_STATUS, 289 HTTP2_HEADER_CONTENT_LENGTH, 290 291 NGHTTP2_SETTINGS_HEADER_TABLE_SIZE, 292 NGHTTP2_SETTINGS_ENABLE_PUSH, 293 NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 294 NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, 295 NGHTTP2_SETTINGS_MAX_FRAME_SIZE, 296 NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE, 297 NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL, 298 299 HTTP2_METHOD_GET, 300 HTTP2_METHOD_HEAD, 301 HTTP2_METHOD_CONNECT, 302 303 HTTP_STATUS_CONTINUE, 304 HTTP_STATUS_RESET_CONTENT, 305 HTTP_STATUS_OK, 306 HTTP_STATUS_NO_CONTENT, 307 HTTP_STATUS_NOT_MODIFIED, 308 HTTP_STATUS_SWITCHING_PROTOCOLS, 309 HTTP_STATUS_MISDIRECTED_REQUEST, 310 311 STREAM_OPTION_EMPTY_PAYLOAD, 312 STREAM_OPTION_GET_TRAILERS, 313} = constants; 314 315const STREAM_FLAGS_PENDING = 0x0; 316const STREAM_FLAGS_READY = 0x1; 317const STREAM_FLAGS_CLOSED = 0x2; 318const STREAM_FLAGS_HEADERS_SENT = 0x4; 319const STREAM_FLAGS_HEAD_REQUEST = 0x8; 320const STREAM_FLAGS_ABORTED = 0x10; 321const STREAM_FLAGS_HAS_TRAILERS = 0x20; 322 323const SESSION_FLAGS_PENDING = 0x0; 324const SESSION_FLAGS_READY = 0x1; 325const SESSION_FLAGS_CLOSED = 0x2; 326const SESSION_FLAGS_DESTROYED = 0x4; 327 328// Top level to avoid creating a closure 329function emit(self, ...args) { 330 ReflectApply(self.emit, self, args); 331} 332 333// Called when a new block of headers has been received for a given 334// stream. The stream may or may not be new. If the stream is new, 335// create the associated Http2Stream instance and emit the 'stream' 336// event. If the stream is not new, emit the 'headers' event to pass 337// the block of headers on. 338function onSessionHeaders(handle, id, cat, flags, headers, sensitiveHeaders) { 339 const session = this[kOwner]; 340 if (session.destroyed) 341 return; 342 343 const type = session[kType]; 344 session[kUpdateTimer](); 345 debugStream(id, type, 'headers received'); 346 const streams = session[kState].streams; 347 348 const endOfStream = !!(flags & NGHTTP2_FLAG_END_STREAM); 349 let stream = streams.get(id); 350 351 // Convert the array of header name value pairs into an object 352 const obj = toHeaderObject(headers, sensitiveHeaders); 353 354 if (stream === undefined) { 355 if (session.closed) { 356 // We are not accepting any new streams at this point. This callback 357 // should not be invoked at this point in time, but just in case it is, 358 // refuse the stream using an RST_STREAM and destroy the handle. 359 handle.rstStream(NGHTTP2_REFUSED_STREAM); 360 handle.destroy(); 361 return; 362 } 363 // session[kType] can be only one of two possible values 364 if (type === NGHTTP2_SESSION_SERVER) { 365 // eslint-disable-next-line no-use-before-define 366 stream = new ServerHttp2Stream(session, handle, id, {}, obj); 367 if (endOfStream) { 368 stream.push(null); 369 } 370 if (obj[HTTP2_HEADER_METHOD] === HTTP2_METHOD_HEAD) { 371 // For head requests, there must not be a body... 372 // end the writable side immediately. 373 stream.end(); 374 stream[kState].flags |= STREAM_FLAGS_HEAD_REQUEST; 375 } 376 } else { 377 // eslint-disable-next-line no-use-before-define 378 stream = new ClientHttp2Stream(session, handle, id, {}); 379 if (endOfStream) { 380 stream.push(null); 381 } 382 stream.end(); 383 } 384 if (endOfStream) 385 stream[kState].endAfterHeaders = true; 386 process.nextTick(emit, session, 'stream', stream, obj, flags, headers); 387 } else { 388 let event; 389 const status = obj[HTTP2_HEADER_STATUS]; 390 if (cat === NGHTTP2_HCAT_RESPONSE) { 391 if (!endOfStream && 392 status !== undefined && 393 status >= 100 && 394 status < 200) { 395 event = 'headers'; 396 } else { 397 event = 'response'; 398 } 399 } else if (cat === NGHTTP2_HCAT_PUSH_RESPONSE) { 400 event = 'push'; 401 } else if (status !== undefined && status >= 200) { 402 event = 'response'; 403 } else { 404 event = endOfStream ? 'trailers' : 'headers'; 405 } 406 const session = stream.session; 407 if (status === HTTP_STATUS_MISDIRECTED_REQUEST) { 408 const originSet = session[kState].originSet = initOriginSet(session); 409 originSet.delete(stream[kOrigin]); 410 } 411 debugStream(id, type, "emitting stream '%s' event", event); 412 process.nextTick(emit, stream, event, obj, flags, headers); 413 } 414 if (endOfStream) { 415 stream.push(null); 416 } 417} 418 419function tryClose(fd) { 420 // Try to close the file descriptor. If closing fails, assert because 421 // an error really should not happen at this point. 422 fs.close(fd, assert.ifError); 423} 424 425// Called when the Http2Stream has finished sending data and is ready for 426// trailers to be sent. This will only be called if the { hasOptions: true } 427// option is set. 428function onStreamTrailers() { 429 const stream = this[kOwner]; 430 stream[kState].trailersReady = true; 431 if (stream.destroyed || stream.closed) 432 return; 433 if (!stream.emit('wantTrailers')) { 434 // There are no listeners, send empty trailing HEADERS frame and close. 435 stream.sendTrailers({}); 436 } 437} 438 439// Submit an RST-STREAM frame to be sent to the remote peer. 440// This will cause the Http2Stream to be closed. 441function submitRstStream(code) { 442 if (this[kHandle] !== undefined) { 443 this[kHandle].rstStream(code); 444 } 445} 446 447// Keep track of the number/presence of JS event listeners. Knowing that there 448// are no listeners allows the C++ code to skip calling into JS for an event. 449function sessionListenerAdded(name) { 450 switch (name) { 451 case 'ping': 452 this[kNativeFields][kBitfield] |= 1 << kSessionHasPingListeners; 453 break; 454 case 'altsvc': 455 this[kNativeFields][kBitfield] |= 1 << kSessionHasAltsvcListeners; 456 break; 457 case 'remoteSettings': 458 this[kNativeFields][kBitfield] |= 1 << kSessionHasRemoteSettingsListeners; 459 break; 460 case 'priority': 461 this[kNativeFields][kSessionPriorityListenerCount]++; 462 break; 463 case 'frameError': 464 this[kNativeFields][kSessionFrameErrorListenerCount]++; 465 break; 466 } 467} 468 469function sessionListenerRemoved(name) { 470 switch (name) { 471 case 'ping': 472 if (this.listenerCount(name) > 0) return; 473 this[kNativeFields][kBitfield] &= ~(1 << kSessionHasPingListeners); 474 break; 475 case 'altsvc': 476 if (this.listenerCount(name) > 0) return; 477 this[kNativeFields][kBitfield] &= ~(1 << kSessionHasAltsvcListeners); 478 break; 479 case 'remoteSettings': 480 if (this.listenerCount(name) > 0) return; 481 this[kNativeFields][kBitfield] &= 482 ~(1 << kSessionHasRemoteSettingsListeners); 483 break; 484 case 'priority': 485 this[kNativeFields][kSessionPriorityListenerCount]--; 486 break; 487 case 'frameError': 488 this[kNativeFields][kSessionFrameErrorListenerCount]--; 489 break; 490 } 491} 492 493// Also keep track of listeners for the Http2Stream instances, as some events 494// are emitted on those objects. 495function streamListenerAdded(name) { 496 const session = this[kSession]; 497 if (!session) return; 498 switch (name) { 499 case 'priority': 500 session[kNativeFields][kSessionPriorityListenerCount]++; 501 break; 502 case 'frameError': 503 session[kNativeFields][kSessionFrameErrorListenerCount]++; 504 break; 505 } 506} 507 508function streamListenerRemoved(name) { 509 const session = this[kSession]; 510 if (!session) return; 511 switch (name) { 512 case 'priority': 513 session[kNativeFields][kSessionPriorityListenerCount]--; 514 break; 515 case 'frameError': 516 session[kNativeFields][kSessionFrameErrorListenerCount]--; 517 break; 518 } 519} 520 521function onPing(payload) { 522 const session = this[kOwner]; 523 if (session.destroyed) 524 return; 525 session[kUpdateTimer](); 526 debugSessionObj(session, 'new ping received'); 527 session.emit('ping', payload); 528} 529 530// Called when the stream is closed either by sending or receiving an 531// RST_STREAM frame, or through a natural end-of-stream. 532// If the writable and readable sides of the stream are still open at this 533// point, close them. If there is an open fd for file send, close that also. 534// At this point the underlying node::http2:Http2Stream handle is no 535// longer usable so destroy it also. 536function onStreamClose(code) { 537 const stream = this[kOwner]; 538 if (!stream || stream.destroyed) 539 return false; 540 541 debugStreamObj( 542 stream, 'closed with code %d, closed %s, readable %s', 543 code, stream.closed, stream.readable, 544 ); 545 546 if (!stream.closed) 547 closeStream(stream, code, kNoRstStream); 548 549 stream[kState].fd = -1; 550 // Defer destroy we actually emit end. 551 if (!stream.readable || code !== NGHTTP2_NO_ERROR) { 552 // If errored or ended, we can destroy immediately. 553 stream.destroy(); 554 } else { 555 // Wait for end to destroy. 556 stream.on('end', stream[kMaybeDestroy]); 557 // Push a null so the stream can end whenever the client consumes 558 // it completely. 559 stream.push(null); 560 561 // If the user hasn't tried to consume the stream (and this is a server 562 // session) then just dump the incoming data so that the stream can 563 // be destroyed. 564 if (stream[kSession][kType] === NGHTTP2_SESSION_SERVER && 565 !stream[kState].didRead && 566 stream.readableFlowing === null) 567 stream.resume(); 568 else 569 stream.read(0); 570 } 571 return true; 572} 573 574// Called when the remote peer settings have been updated. 575// Resets the cached settings. 576function onSettings() { 577 const session = this[kOwner]; 578 if (session.destroyed) 579 return; 580 session[kUpdateTimer](); 581 debugSessionObj(session, 'new settings received'); 582 session[kRemoteSettings] = undefined; 583 session.emit('remoteSettings', session.remoteSettings); 584} 585 586// If the stream exists, an attempt will be made to emit an event 587// on the stream object itself. Otherwise, forward it on to the 588// session (which may, in turn, forward it on to the server) 589function onPriority(id, parent, weight, exclusive) { 590 const session = this[kOwner]; 591 if (session.destroyed) 592 return; 593 debugStream(id, session[kType], 594 'priority [parent: %d, weight: %d, exclusive: %s]', 595 parent, weight, exclusive); 596 const emitter = session[kState].streams.get(id) || session; 597 if (!emitter.destroyed) { 598 emitter[kUpdateTimer](); 599 emitter.emit('priority', id, parent, weight, exclusive); 600 } 601} 602 603// Called by the native layer when an error has occurred sending a 604// frame. This should be exceedingly rare. 605function onFrameError(id, type, code) { 606 const session = this[kOwner]; 607 if (session.destroyed) 608 return; 609 debugSessionObj(session, 'error sending frame type %d on stream %d, code: %d', 610 type, id, code); 611 const emitter = session[kState].streams.get(id) || session; 612 emitter[kUpdateTimer](); 613 emitter.emit('frameError', type, code, id); 614 session[kState].streams.get(id).close(code); 615 session.close(); 616} 617 618function onAltSvc(stream, origin, alt) { 619 const session = this[kOwner]; 620 if (session.destroyed) 621 return; 622 debugSessionObj(session, 'altsvc received: stream: %d, origin: %s, alt: %s', 623 stream, origin, alt); 624 session[kUpdateTimer](); 625 session.emit('altsvc', alt, origin, stream); 626} 627 628function initOriginSet(session) { 629 let originSet = session[kState].originSet; 630 if (originSet === undefined) { 631 const socket = session[kSocket]; 632 session[kState].originSet = originSet = new SafeSet(); 633 if (socket.servername != null) { 634 let originString = `https://${socket.servername}`; 635 if (socket.remotePort != null) 636 originString += `:${socket.remotePort}`; 637 // We have to ensure that it is a properly serialized 638 // ASCII origin string. The socket.servername might not 639 // be properly ASCII encoded. 640 originSet.add((new URL(originString)).origin); 641 } 642 } 643 return originSet; 644} 645 646function onOrigin(origins) { 647 const session = this[kOwner]; 648 if (session.destroyed) 649 return; 650 debugSessionObj(session, 'origin received: %j', origins); 651 session[kUpdateTimer](); 652 if (!session.encrypted || session.destroyed) 653 return undefined; 654 const originSet = initOriginSet(session); 655 for (let n = 0; n < origins.length; n++) 656 originSet.add(origins[n]); 657 session.emit('origin', origins); 658} 659 660// Receiving a GOAWAY frame from the connected peer is a signal that no 661// new streams should be created. If the code === NGHTTP2_NO_ERROR, we 662// are going to send our close, but allow existing frames to close 663// normally. If code !== NGHTTP2_NO_ERROR, we are going to send our own 664// close using the same code then destroy the session with an error. 665// The goaway event will be emitted on next tick. 666function onGoawayData(code, lastStreamID, buf) { 667 const session = this[kOwner]; 668 if (session.destroyed) 669 return; 670 debugSessionObj(session, 'goaway %d received [last stream id: %d]', 671 code, lastStreamID); 672 673 const state = session[kState]; 674 state.goawayCode = code; 675 state.goawayLastStreamID = lastStreamID; 676 677 session.emit('goaway', code, lastStreamID, buf); 678 if (code === NGHTTP2_NO_ERROR) { 679 // If this is a no error goaway, begin shutting down. 680 // No new streams permitted, but existing streams may 681 // close naturally on their own. 682 session.close(); 683 } else { 684 // However, if the code is not NGHTTP_NO_ERROR, destroy the 685 // session immediately. We destroy with an error but send a 686 // goaway using NGHTTP2_NO_ERROR because there was no error 687 // condition on this side of the session that caused the 688 // shutdown. 689 session.destroy(new ERR_HTTP2_SESSION_ERROR(code), NGHTTP2_NO_ERROR); 690 } 691} 692 693// When a ClientHttp2Session is first created, the socket may not yet be 694// connected. If request() is called during this time, the actual request 695// will be deferred until the socket is ready to go. 696function requestOnConnect(headers, options) { 697 const session = this[kSession]; 698 699 // At this point, the stream should have already been destroyed during 700 // the session.destroy() method. Do nothing else. 701 if (session === undefined || session.destroyed) 702 return; 703 704 // If the session was closed while waiting for the connect, destroy 705 // the stream and do not continue with the request. 706 if (session.closed) { 707 const err = new ERR_HTTP2_GOAWAY_SESSION(); 708 this.destroy(err); 709 return; 710 } 711 712 debugSessionObj(session, 'connected, initializing request'); 713 714 let streamOptions = 0; 715 if (options.endStream) 716 streamOptions |= STREAM_OPTION_EMPTY_PAYLOAD; 717 718 if (options.waitForTrailers) 719 streamOptions |= STREAM_OPTION_GET_TRAILERS; 720 721 // `ret` will be either the reserved stream ID (if positive) 722 // or an error code (if negative) 723 const ret = session[kHandle].request(headers, 724 streamOptions, 725 options.parent | 0, 726 options.weight | 0, 727 !!options.exclusive); 728 729 // In an error condition, one of three possible response codes will be 730 // possible: 731 // * NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE - Maximum stream ID is reached, this 732 // is fatal for the session 733 // * NGHTTP2_ERR_INVALID_ARGUMENT - Stream was made dependent on itself, this 734 // impacts on this stream. 735 // For the first two, emit the error on the session, 736 // For the third, emit the error on the stream, it will bubble up to the 737 // session if not handled. 738 if (typeof ret === 'number') { 739 let err; 740 switch (ret) { 741 case NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE: 742 err = new ERR_HTTP2_OUT_OF_STREAMS(); 743 this.destroy(err); 744 break; 745 case NGHTTP2_ERR_INVALID_ARGUMENT: 746 err = new ERR_HTTP2_STREAM_SELF_DEPENDENCY(); 747 this.destroy(err); 748 break; 749 default: 750 session.destroy(new NghttpError(ret)); 751 } 752 return; 753 } 754 this[kInit](ret.id(), ret); 755} 756 757// Validates that priority options are correct, specifically: 758// 1. options.weight must be a number 759// 2. options.parent must be a positive number 760// 3. options.exclusive must be a boolean 761// 4. if specified, options.silent must be a boolean 762// 763// Also sets the default priority options if they are not set. 764const setAndValidatePriorityOptions = hideStackFrames((options) => { 765 if (options.weight === undefined) { 766 options.weight = NGHTTP2_DEFAULT_WEIGHT; 767 } else if (typeof options.weight !== 'number') { 768 throw new ERR_INVALID_ARG_VALUE('options.weight', options.weight); 769 } 770 771 if (options.parent === undefined) { 772 options.parent = 0; 773 } else if (typeof options.parent !== 'number' || options.parent < 0) { 774 throw new ERR_INVALID_ARG_VALUE('options.parent', options.parent); 775 } 776 777 if (options.exclusive === undefined) { 778 options.exclusive = false; 779 } else if (typeof options.exclusive !== 'boolean') { 780 throw new ERR_INVALID_ARG_VALUE('options.exclusive', options.exclusive); 781 } 782 783 if (options.silent === undefined) { 784 options.silent = false; 785 } else if (typeof options.silent !== 'boolean') { 786 throw new ERR_INVALID_ARG_VALUE('options.silent', options.silent); 787 } 788}); 789 790// When an error occurs internally at the binding level, immediately 791// destroy the session. 792function onSessionInternalError(integerCode, customErrorCode) { 793 if (this[kOwner] !== undefined) 794 this[kOwner].destroy(new NghttpError(integerCode, customErrorCode)); 795} 796 797function settingsCallback(cb, ack, duration) { 798 this[kState].pendingAck--; 799 this[kLocalSettings] = undefined; 800 if (ack) { 801 debugSessionObj(this, 'settings received'); 802 const settings = this.localSettings; 803 if (typeof cb === 'function') 804 cb(null, settings, duration); 805 this.emit('localSettings', settings); 806 } else { 807 debugSessionObj(this, 'settings canceled'); 808 if (typeof cb === 'function') 809 cb(new ERR_HTTP2_SETTINGS_CANCEL()); 810 } 811} 812 813// Submits a SETTINGS frame to be sent to the remote peer. 814function submitSettings(settings, callback) { 815 if (this.destroyed) 816 return; 817 debugSessionObj(this, 'submitting settings'); 818 this[kUpdateTimer](); 819 updateSettingsBuffer(settings); 820 if (!this[kHandle].settings(FunctionPrototypeBind(settingsCallback, 821 this, callback))) { 822 this.destroy(new ERR_HTTP2_MAX_PENDING_SETTINGS_ACK()); 823 } 824} 825 826// Submits a PRIORITY frame to be sent to the remote peer 827// Note: If the silent option is true, the change will be made 828// locally with no PRIORITY frame sent. 829function submitPriority(options) { 830 if (this.destroyed) 831 return; 832 this[kUpdateTimer](); 833 834 // If the parent is the id, do nothing because a 835 // stream cannot be made to depend on itself. 836 if (options.parent === this[kID]) 837 return; 838 839 this[kHandle].priority(options.parent | 0, 840 options.weight | 0, 841 !!options.exclusive, 842 !!options.silent); 843} 844 845// Submit a GOAWAY frame to be sent to the remote peer. 846// If the lastStreamID is set to <= 0, then the lastProcStreamID will 847// be used. The opaqueData must either be a typed array or undefined 848// (which will be checked elsewhere). 849function submitGoaway(code, lastStreamID, opaqueData) { 850 if (this.destroyed) 851 return; 852 debugSessionObj(this, 'submitting goaway'); 853 this[kUpdateTimer](); 854 this[kHandle].goaway(code, lastStreamID, opaqueData); 855} 856 857const proxySocketHandler = { 858 get(session, prop) { 859 switch (prop) { 860 case 'setTimeout': 861 case 'ref': 862 case 'unref': 863 return FunctionPrototypeBind(session[prop], session); 864 case 'destroy': 865 case 'emit': 866 case 'end': 867 case 'pause': 868 case 'read': 869 case 'resume': 870 case 'write': 871 case 'setEncoding': 872 case 'setKeepAlive': 873 case 'setNoDelay': 874 throw new ERR_HTTP2_NO_SOCKET_MANIPULATION(); 875 default: { 876 const socket = session[kSocket]; 877 if (socket === undefined) 878 throw new ERR_HTTP2_SOCKET_UNBOUND(); 879 const value = socket[prop]; 880 return typeof value === 'function' ? 881 FunctionPrototypeBind(value, socket) : 882 value; 883 } 884 } 885 }, 886 getPrototypeOf(session) { 887 const socket = session[kSocket]; 888 if (socket === undefined) 889 throw new ERR_HTTP2_SOCKET_UNBOUND(); 890 return ReflectGetPrototypeOf(socket); 891 }, 892 set(session, prop, value) { 893 switch (prop) { 894 case 'setTimeout': 895 case 'ref': 896 case 'unref': 897 session[prop] = value; 898 return true; 899 case 'destroy': 900 case 'emit': 901 case 'end': 902 case 'pause': 903 case 'read': 904 case 'resume': 905 case 'write': 906 case 'setEncoding': 907 case 'setKeepAlive': 908 case 'setNoDelay': 909 throw new ERR_HTTP2_NO_SOCKET_MANIPULATION(); 910 default: { 911 const socket = session[kSocket]; 912 if (socket === undefined) 913 throw new ERR_HTTP2_SOCKET_UNBOUND(); 914 socket[prop] = value; 915 return true; 916 } 917 } 918 }, 919}; 920 921// pingCallback() returns a function that is invoked when an HTTP2 PING 922// frame acknowledgement is received. The ack is either true or false to 923// indicate if the ping was successful or not. The duration indicates the 924// number of milliseconds elapsed since the ping was sent and the ack 925// received. The payload is a Buffer containing the 8 bytes of payload 926// data received on the PING acknowledgement. 927function pingCallback(cb) { 928 return function pingCallback(ack, duration, payload) { 929 if (ack) { 930 cb(null, duration, payload); 931 } else { 932 cb(new ERR_HTTP2_PING_CANCEL()); 933 } 934 }; 935} 936 937// Validates the values in a settings object. Specifically: 938// 1. headerTableSize must be a number in the range 0 <= n <= kMaxInt 939// 2. initialWindowSize must be a number in the range 0 <= n <= kMaxInt 940// 3. maxFrameSize must be a number in the range 16384 <= n <= kMaxFrameSize 941// 4. maxConcurrentStreams must be a number in the range 0 <= n <= kMaxStreams 942// 5. maxHeaderListSize must be a number in the range 0 <= n <= kMaxInt 943// 6. enablePush must be a boolean 944// 7. enableConnectProtocol must be a boolean 945// All settings are optional and may be left undefined 946const validateSettings = hideStackFrames((settings) => { 947 if (settings === undefined) return; 948 assertWithinRange('headerTableSize', 949 settings.headerTableSize, 950 0, kMaxInt); 951 assertWithinRange('initialWindowSize', 952 settings.initialWindowSize, 953 0, kMaxInt); 954 assertWithinRange('maxFrameSize', 955 settings.maxFrameSize, 956 16384, kMaxFrameSize); 957 assertWithinRange('maxConcurrentStreams', 958 settings.maxConcurrentStreams, 959 0, kMaxStreams); 960 assertWithinRange('maxHeaderListSize', 961 settings.maxHeaderListSize, 962 0, kMaxInt); 963 assertWithinRange('maxHeaderSize', 964 settings.maxHeaderSize, 965 0, kMaxInt); 966 if (settings.enablePush !== undefined && 967 typeof settings.enablePush !== 'boolean') { 968 throw new ERR_HTTP2_INVALID_SETTING_VALUE('enablePush', 969 settings.enablePush); 970 } 971 if (settings.enableConnectProtocol !== undefined && 972 typeof settings.enableConnectProtocol !== 'boolean') { 973 throw new ERR_HTTP2_INVALID_SETTING_VALUE('enableConnectProtocol', 974 settings.enableConnectProtocol); 975 } 976}); 977 978// Wrap a typed array in a proxy, and allow selectively copying the entries 979// that have explicitly been set to another typed array. 980function trackAssignmentsTypedArray(typedArray) { 981 const typedArrayLength = TypedArrayPrototypeGetLength(typedArray); 982 const modifiedEntries = new Uint8Array(typedArrayLength); 983 984 function copyAssigned(target) { 985 for (let i = 0; i < typedArrayLength; i++) { 986 if (modifiedEntries[i]) { 987 target[i] = typedArray[i]; 988 } 989 } 990 } 991 992 return new Proxy(typedArray, { 993 __proto__: null, 994 get(obj, prop, receiver) { 995 if (prop === 'copyAssigned') { 996 return copyAssigned; 997 } 998 return ReflectGet(obj, prop, receiver); 999 }, 1000 set(obj, prop, value) { 1001 if (`${+prop}` === prop) { 1002 modifiedEntries[prop] = 1; 1003 } 1004 return ReflectSet(obj, prop, value); 1005 }, 1006 }); 1007} 1008 1009// Creates the internal binding.Http2Session handle for an Http2Session 1010// instance. This occurs only after the socket connection has been 1011// established. Note: the binding.Http2Session will take over ownership 1012// of the socket. No other code should read from or write to the socket. 1013function setupHandle(socket, type, options) { 1014 // If the session has been destroyed, go ahead and emit 'connect', 1015 // but do nothing else. The various on('connect') handlers set by 1016 // core will check for session.destroyed before progressing, this 1017 // ensures that those at l`east get cleared out. 1018 if (this.destroyed) { 1019 process.nextTick(emit, this, 'connect', this, socket); 1020 return; 1021 } 1022 1023 assert(socket._handle !== undefined, 1024 'Internal HTTP/2 Failure. The socket is not connected. Please ' + 1025 'report this as a bug in Node.js'); 1026 1027 debugSession(type, 'setting up session handle'); 1028 this[kState].flags |= SESSION_FLAGS_READY; 1029 1030 updateOptionsBuffer(options); 1031 const handle = new binding.Http2Session(type); 1032 handle[kOwner] = this; 1033 1034 if (typeof options.selectPadding === 'function') 1035 this[kSelectPadding] = options.selectPadding; 1036 handle.consume(socket._handle); 1037 1038 this[kHandle] = handle; 1039 if (this[kNativeFields]) { 1040 // If some options have already been set before the handle existed, copy 1041 // those (and only those) that have manually been set over. 1042 this[kNativeFields].copyAssigned(handle.fields); 1043 } 1044 1045 this[kNativeFields] = handle.fields; 1046 1047 if (socket.encrypted) { 1048 this[kAlpnProtocol] = socket.alpnProtocol; 1049 this[kEncrypted] = true; 1050 } else { 1051 // 'h2c' is the protocol identifier for HTTP/2 over plain-text. We use 1052 // it here to identify any session that is not explicitly using an 1053 // encrypted socket. 1054 this[kAlpnProtocol] = 'h2c'; 1055 this[kEncrypted] = false; 1056 } 1057 1058 if (isUint32(options.maxSessionInvalidFrames)) { 1059 const uint32 = new Uint32Array( 1060 this[kNativeFields].buffer, kSessionMaxInvalidFrames, 1); 1061 uint32[0] = options.maxSessionInvalidFrames; 1062 } 1063 1064 if (isUint32(options.maxSessionRejectedStreams)) { 1065 const uint32 = new Uint32Array( 1066 this[kNativeFields].buffer, kSessionMaxRejectedStreams, 1); 1067 uint32[0] = options.maxSessionRejectedStreams; 1068 } 1069 1070 const settings = typeof options.settings === 'object' ? 1071 options.settings : {}; 1072 1073 this.settings(settings); 1074 1075 if (type === NGHTTP2_SESSION_SERVER && 1076 ArrayIsArray(options.origins)) { 1077 ReflectApply(this.origin, this, options.origins); 1078 } 1079 1080 process.nextTick(emit, this, 'connect', this, socket); 1081} 1082 1083// Emits a close event followed by an error event if err is truthy. Used 1084// by Http2Session.prototype.destroy() 1085function emitClose(self, error) { 1086 if (error) 1087 self.emit('error', error); 1088 self.emit('close'); 1089} 1090 1091function cleanupSession(session) { 1092 const socket = session[kSocket]; 1093 const handle = session[kHandle]; 1094 session[kProxySocket] = undefined; 1095 session[kSocket] = undefined; 1096 session[kHandle] = undefined; 1097 session[kNativeFields] = trackAssignmentsTypedArray( 1098 new Uint8Array(kSessionUint8FieldCount)); 1099 if (handle) 1100 handle.ondone = null; 1101 if (socket) { 1102 socket[kSession] = undefined; 1103 socket[kServer] = undefined; 1104 } 1105} 1106 1107function finishSessionClose(session, error) { 1108 debugSessionObj(session, 'finishSessionClose'); 1109 1110 const socket = session[kSocket]; 1111 cleanupSession(session); 1112 1113 if (socket && !socket.destroyed) { 1114 socket.on('close', () => { 1115 emitClose(session, error); 1116 }); 1117 if (session.closed) { 1118 // If we're gracefully closing the socket, call resume() so we can detect 1119 // the peer closing in case binding.Http2Session is already gone. 1120 socket.resume(); 1121 } 1122 1123 // Always wait for writable side to finish. 1124 socket.end((err) => { 1125 debugSessionObj(session, 'finishSessionClose socket end', err, error); 1126 // If session.destroy() was called, destroy the underlying socket. Delay 1127 // it a bit to try to avoid ECONNRESET on Windows. 1128 if (!session.closed) { 1129 setImmediate(() => { 1130 socket.destroy(error); 1131 }); 1132 } 1133 }); 1134 } else { 1135 process.nextTick(emitClose, session, error); 1136 } 1137} 1138 1139function closeSession(session, code, error) { 1140 debugSessionObj(session, 'start closing/destroying', error); 1141 1142 const state = session[kState]; 1143 state.flags |= SESSION_FLAGS_DESTROYED; 1144 state.destroyCode = code; 1145 1146 // Clear timeout and remove timeout listeners. 1147 session.setTimeout(0); 1148 session.removeAllListeners('timeout'); 1149 1150 // Destroy any pending and open streams 1151 if (state.pendingStreams.size > 0 || state.streams.size > 0) { 1152 const cancel = new ERR_HTTP2_STREAM_CANCEL(error); 1153 state.pendingStreams.forEach((stream) => stream.destroy(cancel)); 1154 state.streams.forEach((stream) => stream.destroy(error)); 1155 } 1156 1157 // Disassociate from the socket and server. 1158 const socket = session[kSocket]; 1159 const handle = session[kHandle]; 1160 1161 // Destroy the handle if it exists at this point. 1162 if (handle !== undefined) { 1163 handle.ondone = FunctionPrototypeBind(finishSessionClose, 1164 null, session, error); 1165 handle.destroy(code, socket.destroyed); 1166 } else { 1167 finishSessionClose(session, error); 1168 } 1169} 1170 1171// Upon creation, the Http2Session takes ownership of the socket. The session 1172// may not be ready to use immediately if the socket is not yet fully connected. 1173// In that case, the Http2Session will wait for the socket to connect. Once 1174// the Http2Session is ready, it will emit its own 'connect' event. 1175// 1176// The Http2Session.goaway() method will send a GOAWAY frame, signalling 1177// to the connected peer that a shutdown is in progress. Sending a goaway 1178// frame has no other effect, however. 1179// 1180// Receiving a GOAWAY frame will cause the Http2Session to first emit a 'goaway' 1181// event notifying the user that a shutdown is in progress. If the goaway 1182// error code equals 0 (NGHTTP2_NO_ERROR), session.close() will be called, 1183// causing the Http2Session to send its own GOAWAY frame and switch itself 1184// into a graceful closing state. In this state, new inbound or outbound 1185// Http2Streams will be rejected. Existing *pending* streams (those created 1186// but without an assigned stream ID or handle) will be destroyed with a 1187// cancel error. Existing open streams will be permitted to complete on their 1188// own. Once all existing streams close, session.destroy() will be called 1189// automatically. 1190// 1191// Calling session.destroy() will tear down the Http2Session immediately, 1192// making it no longer usable. Pending and existing streams will be destroyed. 1193// The bound socket will be destroyed. Once all resources have been freed up, 1194// the 'close' event will be emitted. Note that pending streams will be 1195// destroyed using a specific "ERR_HTTP2_STREAM_CANCEL" error. Existing open 1196// streams will be destroyed using the same error passed to session.destroy() 1197// 1198// If destroy is called with an error, an 'error' event will be emitted 1199// immediately following the 'close' event. 1200// 1201// The socket and Http2Session lifecycles are tightly bound. Once one is 1202// destroyed, the other should also be destroyed. When the socket is destroyed 1203// with an error, session.destroy() will be called with that same error. 1204// Likewise, when session.destroy() is called with an error, the same error 1205// will be sent to the socket. 1206class Http2Session extends EventEmitter { 1207 constructor(type, options, socket) { 1208 super(); 1209 1210 if (!socket._handle || !socket._handle.isStreamBase) { 1211 socket = new JSStreamSocket(socket); 1212 } 1213 socket.on('error', socketOnError); 1214 socket.on('close', socketOnClose); 1215 1216 // No validation is performed on the input parameters because this 1217 // constructor is not exported directly for users. 1218 1219 // If the session property already exists on the socket, 1220 // then it has already been bound to an Http2Session instance 1221 // and cannot be attached again. 1222 if (socket[kSession] !== undefined) 1223 throw new ERR_HTTP2_SOCKET_BOUND(); 1224 1225 socket[kSession] = this; 1226 1227 this[kState] = { 1228 destroyCode: NGHTTP2_NO_ERROR, 1229 flags: SESSION_FLAGS_PENDING, 1230 goawayCode: null, 1231 goawayLastStreamID: null, 1232 streams: new SafeMap(), 1233 pendingStreams: new SafeSet(), 1234 pendingAck: 0, 1235 shutdownWritableCalled: false, 1236 writeQueueSize: 0, 1237 originSet: undefined, 1238 }; 1239 1240 this[kEncrypted] = undefined; 1241 this[kAlpnProtocol] = undefined; 1242 this[kType] = type; 1243 this[kProxySocket] = null; 1244 this[kSocket] = socket; 1245 this[kTimeout] = null; 1246 this[kHandle] = undefined; 1247 1248 // Do not use nagle's algorithm 1249 if (typeof socket.setNoDelay === 'function') 1250 socket.setNoDelay(); 1251 1252 // Disable TLS renegotiation on the socket 1253 if (typeof socket.disableRenegotiation === 'function') 1254 socket.disableRenegotiation(); 1255 1256 const setupFn = FunctionPrototypeBind(setupHandle, this, 1257 socket, type, options); 1258 if (socket.connecting || socket.secureConnecting) { 1259 const connectEvent = 1260 socket instanceof tls.TLSSocket ? 'secureConnect' : 'connect'; 1261 socket.once(connectEvent, () => { 1262 try { 1263 setupFn(); 1264 } catch (error) { 1265 socket.destroy(error); 1266 } 1267 }); 1268 } else { 1269 setupFn(); 1270 } 1271 1272 if (!this[kNativeFields]) { 1273 this[kNativeFields] = trackAssignmentsTypedArray( 1274 new Uint8Array(kSessionUint8FieldCount)); 1275 } 1276 this.on('newListener', sessionListenerAdded); 1277 this.on('removeListener', sessionListenerRemoved); 1278 1279 // Process data on the next tick - a remoteSettings handler may be attached. 1280 // https://github.com/nodejs/node/issues/35981 1281 process.nextTick(() => { 1282 // Socket already has some buffered data - emulate receiving it 1283 // https://github.com/nodejs/node/issues/35475 1284 // https://github.com/nodejs/node/issues/34532 1285 if (socket.readableLength) { 1286 let buf; 1287 while ((buf = socket.read()) !== null) { 1288 debugSession(type, `${buf.length} bytes already in buffer`); 1289 this[kHandle].receive(buf); 1290 } 1291 } 1292 }); 1293 1294 debugSession(type, 'created'); 1295 } 1296 1297 // Returns undefined if the socket is not yet connected, true if the 1298 // socket is a TLSSocket, and false if it is not. 1299 get encrypted() { 1300 return this[kEncrypted]; 1301 } 1302 1303 // Returns undefined if the socket is not yet connected, `h2` if the 1304 // socket is a TLSSocket and the alpnProtocol is `h2`, or `h2c` if the 1305 // socket is not a TLSSocket. 1306 get alpnProtocol() { 1307 return this[kAlpnProtocol]; 1308 } 1309 1310 // TODO(jasnell): originSet is being added in preparation for ORIGIN frame 1311 // support. At the current time, the ORIGIN frame specification is awaiting 1312 // publication as an RFC and is awaiting implementation in nghttp2. Once 1313 // added, an ORIGIN frame will add to the origins included in the origin 1314 // set. 421 responses will remove origins from the set. 1315 get originSet() { 1316 if (!this.encrypted || this.destroyed) 1317 return undefined; 1318 return ArrayFrom(initOriginSet(this)); 1319 } 1320 1321 // True if the Http2Session is still waiting for the socket to connect 1322 get connecting() { 1323 return (this[kState].flags & SESSION_FLAGS_READY) === 0; 1324 } 1325 1326 // True if Http2Session.prototype.close() has been called 1327 get closed() { 1328 return !!(this[kState].flags & SESSION_FLAGS_CLOSED); 1329 } 1330 1331 // True if Http2Session.prototype.destroy() has been called 1332 get destroyed() { 1333 return !!(this[kState].flags & SESSION_FLAGS_DESTROYED); 1334 } 1335 1336 // Resets the timeout counter 1337 [kUpdateTimer]() { 1338 if (this.destroyed) 1339 return; 1340 if (this[kTimeout]) this[kTimeout].refresh(); 1341 } 1342 1343 // Sets the id of the next stream to be created by this Http2Session. 1344 // The value must be a number in the range 0 <= n <= kMaxStreams. The 1345 // value also needs to be larger than the current next stream ID. 1346 setNextStreamID(id) { 1347 if (this.destroyed) 1348 throw new ERR_HTTP2_INVALID_SESSION(); 1349 1350 validateNumber(id, 'id'); 1351 if (id <= 0 || id > kMaxStreams) 1352 throw new ERR_OUT_OF_RANGE('id', `> 0 and <= ${kMaxStreams}`, id); 1353 this[kHandle].setNextStreamID(id); 1354 } 1355 1356 // Sets the local window size (local endpoints's window size) 1357 // Returns 0 if success or throw an exception if NGHTTP2_ERR_NOMEM 1358 // if the window allocation fails 1359 setLocalWindowSize(windowSize) { 1360 if (this.destroyed) 1361 throw new ERR_HTTP2_INVALID_SESSION(); 1362 1363 validateInt32(windowSize, 'windowSize', 0); 1364 const ret = this[kHandle].setLocalWindowSize(windowSize); 1365 1366 if (ret === NGHTTP2_ERR_NOMEM) { 1367 this.destroy(new ERR_HTTP2_NO_MEM()); 1368 } 1369 } 1370 1371 // If ping is called while we are still connecting, or after close() has 1372 // been called, the ping callback will be invoked immediately with a ping 1373 // cancelled error and a duration of 0.0. 1374 ping(payload, callback) { 1375 if (this.destroyed) 1376 throw new ERR_HTTP2_INVALID_SESSION(); 1377 1378 if (typeof payload === 'function') { 1379 callback = payload; 1380 payload = undefined; 1381 } 1382 if (payload) { 1383 validateBuffer(payload, 'payload'); 1384 } 1385 if (payload && payload.length !== 8) { 1386 throw new ERR_HTTP2_PING_LENGTH(); 1387 } 1388 validateFunction(callback, 'callback'); 1389 1390 const cb = pingCallback(callback); 1391 if (this.connecting || this.closed) { 1392 process.nextTick(cb, false, 0.0, payload); 1393 return; 1394 } 1395 1396 return this[kHandle].ping(payload, cb); 1397 } 1398 1399 [kInspect](depth, opts) { 1400 if (typeof depth === 'number' && depth < 0) 1401 return this; 1402 1403 const obj = { 1404 type: this[kType], 1405 closed: this.closed, 1406 destroyed: this.destroyed, 1407 state: this.state, 1408 localSettings: this.localSettings, 1409 remoteSettings: this.remoteSettings, 1410 }; 1411 return `Http2Session ${format(obj)}`; 1412 } 1413 1414 // The socket owned by this session 1415 get socket() { 1416 const proxySocket = this[kProxySocket]; 1417 if (proxySocket === null) 1418 return this[kProxySocket] = new Proxy(this, proxySocketHandler); 1419 return proxySocket; 1420 } 1421 1422 // The session type 1423 get type() { 1424 return this[kType]; 1425 } 1426 1427 // If a GOAWAY frame has been received, gives the error code specified 1428 get goawayCode() { 1429 return this[kState].goawayCode || NGHTTP2_NO_ERROR; 1430 } 1431 1432 // If a GOAWAY frame has been received, gives the last stream ID reported 1433 get goawayLastStreamID() { 1434 return this[kState].goawayLastStreamID || 0; 1435 } 1436 1437 // True if the Http2Session is waiting for a settings acknowledgement 1438 get pendingSettingsAck() { 1439 return this[kState].pendingAck > 0; 1440 } 1441 1442 // Retrieves state information for the Http2Session 1443 get state() { 1444 return this.connecting || this.destroyed ? 1445 {} : getSessionState(this[kHandle]); 1446 } 1447 1448 // The settings currently in effect for the local peer. These will 1449 // be updated only when a settings acknowledgement has been received. 1450 get localSettings() { 1451 const settings = this[kLocalSettings]; 1452 if (settings !== undefined) 1453 return settings; 1454 1455 if (this.destroyed || this.connecting) 1456 return {}; 1457 1458 return this[kLocalSettings] = getSettings(this[kHandle], false); // Local 1459 } 1460 1461 // The settings currently in effect for the remote peer. 1462 get remoteSettings() { 1463 if (this[kNativeFields][kBitfield] & 1464 (1 << kSessionRemoteSettingsIsUpToDate)) { 1465 const settings = this[kRemoteSettings]; 1466 if (settings !== undefined) { 1467 return settings; 1468 } 1469 } 1470 1471 if (this.destroyed || this.connecting) 1472 return {}; 1473 1474 this[kNativeFields][kBitfield] |= (1 << kSessionRemoteSettingsIsUpToDate); 1475 return this[kRemoteSettings] = getSettings(this[kHandle], true); // Remote 1476 } 1477 1478 // Submits a SETTINGS frame to be sent to the remote peer. 1479 settings(settings, callback) { 1480 if (this.destroyed) 1481 throw new ERR_HTTP2_INVALID_SESSION(); 1482 assertIsObject(settings, 'settings'); 1483 validateSettings(settings); 1484 1485 if (callback) { 1486 validateFunction(callback, 'callback'); 1487 } 1488 debugSessionObj(this, 'sending settings'); 1489 1490 this[kState].pendingAck++; 1491 1492 const settingsFn = FunctionPrototypeBind(submitSettings, this, 1493 { ...settings }, callback); 1494 if (this.connecting) { 1495 this.once('connect', settingsFn); 1496 return; 1497 } 1498 settingsFn(); 1499 } 1500 1501 // Submits a GOAWAY frame to be sent to the remote peer. Note that this 1502 // is only a notification, and does not affect the usable state of the 1503 // session with the notable exception that new incoming streams will 1504 // be rejected automatically. 1505 goaway(code = NGHTTP2_NO_ERROR, lastStreamID = 0, opaqueData) { 1506 if (this.destroyed) 1507 throw new ERR_HTTP2_INVALID_SESSION(); 1508 1509 if (opaqueData !== undefined) { 1510 validateBuffer(opaqueData, 'opaqueData'); 1511 } 1512 validateNumber(code, 'code'); 1513 validateNumber(lastStreamID, 'lastStreamID'); 1514 1515 const goawayFn = FunctionPrototypeBind(submitGoaway, 1516 this, 1517 code, lastStreamID, opaqueData); 1518 if (this.connecting) { 1519 this.once('connect', goawayFn); 1520 return; 1521 } 1522 goawayFn(); 1523 } 1524 1525 // Destroy the Http2Session, making it no longer usable and cancelling 1526 // any pending activity. 1527 destroy(error = NGHTTP2_NO_ERROR, code) { 1528 if (this.destroyed) 1529 return; 1530 1531 debugSessionObj(this, 'destroying'); 1532 1533 if (typeof error === 'number') { 1534 code = error; 1535 error = 1536 code !== NGHTTP2_NO_ERROR ? 1537 new ERR_HTTP2_SESSION_ERROR(code) : undefined; 1538 } 1539 if (code === undefined && error != null) 1540 code = NGHTTP2_INTERNAL_ERROR; 1541 1542 closeSession(this, code, error); 1543 } 1544 1545 // Closing the session will: 1546 // 1. Send a goaway frame 1547 // 2. Mark the session as closed 1548 // 3. Prevent new inbound or outbound streams from being opened 1549 // 4. Optionally register a 'close' event handler 1550 // 5. Will cause the session to automatically destroy after the 1551 // last currently open Http2Stream closes. 1552 // 1553 // Close always assumes a good, non-error shutdown (NGHTTP_NO_ERROR) 1554 // 1555 // If the session has not connected yet, the closed flag will still be 1556 // set but the goaway will not be sent until after the connect event 1557 // is emitted. 1558 close(callback) { 1559 if (this.closed || this.destroyed) 1560 return; 1561 debugSessionObj(this, 'marking session closed'); 1562 this[kState].flags |= SESSION_FLAGS_CLOSED; 1563 if (typeof callback === 'function') 1564 this.once('close', callback); 1565 this.goaway(); 1566 this[kMaybeDestroy](); 1567 } 1568 1569 [EventEmitter.captureRejectionSymbol](err, event, ...args) { 1570 switch (event) { 1571 case 'stream': { 1572 const stream = args[0]; 1573 stream.destroy(err); 1574 break; 1575 } 1576 default: 1577 this.destroy(err); 1578 } 1579 } 1580 1581 // Destroy the session if: 1582 // * error is not undefined/null 1583 // * session is closed and there are no more pending or open streams 1584 [kMaybeDestroy](error) { 1585 if (error == null) { 1586 const state = this[kState]; 1587 // Do not destroy if we're not closed and there are pending/open streams 1588 if (!this.closed || 1589 state.streams.size > 0 || 1590 state.pendingStreams.size > 0) { 1591 return; 1592 } 1593 } 1594 this.destroy(error); 1595 } 1596 1597 _onTimeout() { 1598 callTimeout(this); 1599 } 1600 1601 ref() { 1602 if (this[kSocket]) { 1603 this[kSocket].ref(); 1604 } 1605 } 1606 1607 unref() { 1608 if (this[kSocket]) { 1609 this[kSocket].unref(); 1610 } 1611 } 1612} 1613 1614// ServerHttp2Session instances should never have to wait for the socket 1615// to connect as they are always created after the socket has already been 1616// established. 1617class ServerHttp2Session extends Http2Session { 1618 constructor(options, socket, server) { 1619 super(NGHTTP2_SESSION_SERVER, options, socket); 1620 this[kServer] = server; 1621 // This is a bit inaccurate because it does not reflect changes to 1622 // number of listeners made after the session was created. This should 1623 // not be an issue in practice. Additionally, the 'priority' event on 1624 // server instances (or any other object) is fully undocumented. 1625 this[kNativeFields][kSessionPriorityListenerCount] = 1626 server.listenerCount('priority'); 1627 } 1628 1629 get server() { 1630 return this[kServer]; 1631 } 1632 1633 // Submits an altsvc frame to be sent to the client. `stream` is a 1634 // numeric Stream ID. origin is a URL string that will be used to get 1635 // the origin. alt is a string containing the altsvc details. No fancy 1636 // API is provided for that. 1637 altsvc(alt, originOrStream) { 1638 if (this.destroyed) 1639 throw new ERR_HTTP2_INVALID_SESSION(); 1640 1641 let stream = 0; 1642 let origin; 1643 1644 if (typeof originOrStream === 'string') { 1645 origin = (new URL(originOrStream)).origin; 1646 if (origin === 'null') 1647 throw new ERR_HTTP2_ALTSVC_INVALID_ORIGIN(); 1648 } else if (typeof originOrStream === 'number') { 1649 if (originOrStream >>> 0 !== originOrStream || originOrStream === 0) { 1650 throw new ERR_OUT_OF_RANGE('originOrStream', 1651 `> 0 && < ${2 ** 32}`, originOrStream); 1652 } 1653 stream = originOrStream; 1654 } else if (originOrStream !== undefined) { 1655 // Allow origin to be passed a URL or object with origin property 1656 if (originOrStream !== null && typeof originOrStream === 'object') 1657 origin = originOrStream.origin; 1658 // Note: if originOrStream is an object with an origin property other 1659 // than a URL, then it is possible that origin will be malformed. 1660 // We do not verify that here. Users who go that route need to 1661 // ensure they are doing the right thing or the payload data will 1662 // be invalid. 1663 if (typeof origin !== 'string') { 1664 throw new ERR_INVALID_ARG_TYPE('originOrStream', 1665 ['string', 'number', 'URL', 'object'], 1666 originOrStream); 1667 } else if (origin === 'null' || origin.length === 0) { 1668 throw new ERR_HTTP2_ALTSVC_INVALID_ORIGIN(); 1669 } 1670 } 1671 1672 validateString(alt, 'alt'); 1673 if (RegExpPrototypeExec(kQuotedString, alt) === null) 1674 throw new ERR_INVALID_CHAR('alt'); 1675 1676 // Max length permitted for ALTSVC 1677 if ((alt.length + (origin !== undefined ? origin.length : 0)) > kMaxALTSVC) 1678 throw new ERR_HTTP2_ALTSVC_LENGTH(); 1679 1680 this[kHandle].altsvc(stream, origin || '', alt); 1681 } 1682 1683 // Submits an origin frame to be sent. 1684 origin(...origins) { 1685 if (this.destroyed) 1686 throw new ERR_HTTP2_INVALID_SESSION(); 1687 1688 if (origins.length === 0) 1689 return; 1690 1691 let arr = ''; 1692 let len = 0; 1693 const count = origins.length; 1694 for (let i = 0; i < count; i++) { 1695 let origin = origins[i]; 1696 if (typeof origin === 'string') { 1697 origin = (new URL(origin)).origin; 1698 } else if (origin != null && typeof origin === 'object') { 1699 origin = origin.origin; 1700 } 1701 validateString(origin, 'origin'); 1702 if (origin === 'null') 1703 throw new ERR_HTTP2_INVALID_ORIGIN(); 1704 1705 arr += `${origin}\0`; 1706 len += origin.length; 1707 } 1708 1709 if (len > kMaxALTSVC) 1710 throw new ERR_HTTP2_ORIGIN_LENGTH(); 1711 1712 this[kHandle].origin(arr, count); 1713 } 1714 1715} 1716 1717// ClientHttp2Session instances have to wait for the socket to connect after 1718// they have been created. Various operations such as request() may be used, 1719// but the actual protocol communication will only occur after the socket 1720// has been connected. 1721class ClientHttp2Session extends Http2Session { 1722 constructor(options, socket) { 1723 super(NGHTTP2_SESSION_CLIENT, options, socket); 1724 this[kPendingRequestCalls] = null; 1725 } 1726 1727 // Submits a new HTTP2 request to the connected peer. Returns the 1728 // associated Http2Stream instance. 1729 request(headers, options) { 1730 debugSessionObj(this, 'initiating request'); 1731 1732 if (this.destroyed) 1733 throw new ERR_HTTP2_INVALID_SESSION(); 1734 1735 if (this.closed) 1736 throw new ERR_HTTP2_GOAWAY_SESSION(); 1737 1738 this[kUpdateTimer](); 1739 1740 if (headers !== null && headers !== undefined) { 1741 const keys = ObjectKeys(headers); 1742 for (let i = 0; i < keys.length; i++) { 1743 const header = keys[i]; 1744 if (header[0] === ':') { 1745 assertValidPseudoHeader(header); 1746 } else if (header && !checkIsHttpToken(header)) 1747 this.destroy(new ERR_INVALID_HTTP_TOKEN('Header name', header)); 1748 } 1749 } 1750 1751 assertIsObject(headers, 'headers'); 1752 assertIsObject(options, 'options'); 1753 1754 headers = ObjectAssign(ObjectCreate(null), headers); 1755 options = { ...options }; 1756 1757 if (headers[HTTP2_HEADER_METHOD] === undefined) 1758 headers[HTTP2_HEADER_METHOD] = HTTP2_METHOD_GET; 1759 1760 const connect = headers[HTTP2_HEADER_METHOD] === HTTP2_METHOD_CONNECT; 1761 1762 if (!connect || headers[HTTP2_HEADER_PROTOCOL] !== undefined) { 1763 if (getAuthority(headers) === undefined) 1764 headers[HTTP2_HEADER_AUTHORITY] = this[kAuthority]; 1765 if (headers[HTTP2_HEADER_SCHEME] === undefined) 1766 headers[HTTP2_HEADER_SCHEME] = StringPrototypeSlice(this[kProtocol], 1767 0, -1); 1768 if (headers[HTTP2_HEADER_PATH] === undefined) 1769 headers[HTTP2_HEADER_PATH] = '/'; 1770 } else { 1771 if (headers[HTTP2_HEADER_AUTHORITY] === undefined) 1772 throw new ERR_HTTP2_CONNECT_AUTHORITY(); 1773 if (headers[HTTP2_HEADER_SCHEME] !== undefined) 1774 throw new ERR_HTTP2_CONNECT_SCHEME(); 1775 if (headers[HTTP2_HEADER_PATH] !== undefined) 1776 throw new ERR_HTTP2_CONNECT_PATH(); 1777 } 1778 1779 setAndValidatePriorityOptions(options); 1780 1781 if (options.endStream === undefined) { 1782 // For some methods, we know that a payload is meaningless, so end the 1783 // stream by default if the user has not specifically indicated a 1784 // preference. 1785 options.endStream = isPayloadMeaningless(headers[HTTP2_HEADER_METHOD]); 1786 } else if (typeof options.endStream !== 'boolean') { 1787 throw new ERR_INVALID_ARG_VALUE('options.endStream', options.endStream); 1788 } 1789 1790 const headersList = mapToHeaders(headers); 1791 1792 // eslint-disable-next-line no-use-before-define 1793 const stream = new ClientHttp2Stream(this, undefined, undefined, {}); 1794 stream[kSentHeaders] = headers; 1795 stream[kOrigin] = `${headers[HTTP2_HEADER_SCHEME]}://` + 1796 `${getAuthority(headers)}`; 1797 1798 // Close the writable side of the stream if options.endStream is set. 1799 if (options.endStream) 1800 stream.end(); 1801 1802 if (options.waitForTrailers) 1803 stream[kState].flags |= STREAM_FLAGS_HAS_TRAILERS; 1804 1805 const { signal } = options; 1806 if (signal) { 1807 validateAbortSignal(signal, 'options.signal'); 1808 const aborter = () => { 1809 stream.destroy(new AbortError(undefined, { cause: signal.reason })); 1810 }; 1811 if (signal.aborted) { 1812 aborter(); 1813 } else { 1814 const disposable = EventEmitter.addAbortListener(signal, aborter); 1815 stream.once('close', disposable[SymbolDispose]); 1816 } 1817 } 1818 1819 const onConnect = FunctionPrototypeBind(requestOnConnect, 1820 stream, headersList, options); 1821 if (this.connecting) { 1822 if (this[kPendingRequestCalls] !== null) { 1823 ArrayPrototypePush(this[kPendingRequestCalls], onConnect); 1824 } else { 1825 this[kPendingRequestCalls] = [onConnect]; 1826 this.once('connect', () => { 1827 ArrayPrototypeForEach(this[kPendingRequestCalls], (f) => f()); 1828 this[kPendingRequestCalls] = null; 1829 }); 1830 } 1831 } else { 1832 onConnect(); 1833 } 1834 return stream; 1835 } 1836} 1837 1838function trackWriteState(stream, bytes) { 1839 const session = stream[kSession]; 1840 stream[kState].writeQueueSize += bytes; 1841 session[kState].writeQueueSize += bytes; 1842 session[kHandle].chunksSentSinceLastWrite = 0; 1843} 1844 1845function streamOnResume() { 1846 if (!this.destroyed) 1847 this[kHandle].readStart(); 1848} 1849 1850function streamOnPause() { 1851 if (!this.destroyed && !this.pending) 1852 this[kHandle].readStop(); 1853} 1854 1855function afterShutdown(status) { 1856 const stream = this.handle[kOwner]; 1857 if (stream) { 1858 stream.on('finish', () => { 1859 stream[kMaybeDestroy](); 1860 }); 1861 } 1862 // Currently this status value is unused 1863 this.callback(); 1864} 1865 1866function shutdownWritable(callback) { 1867 const handle = this[kHandle]; 1868 if (!handle) return callback(); 1869 const state = this[kState]; 1870 if (state.shutdownWritableCalled) { 1871 debugStreamObj(this, 'shutdownWritable() already called'); 1872 return callback(); 1873 } 1874 state.shutdownWritableCalled = true; 1875 1876 const req = new ShutdownWrap(); 1877 req.oncomplete = afterShutdown; 1878 req.callback = callback; 1879 req.handle = handle; 1880 const err = handle.shutdown(req); 1881 if (err === 1) // synchronous finish 1882 return ReflectApply(afterShutdown, req, [0]); 1883} 1884 1885function finishSendTrailers(stream, headersList) { 1886 // The stream might be destroyed and in that case 1887 // there is nothing to do. 1888 // This can happen because finishSendTrailers is 1889 // scheduled via setImmediate. 1890 if (stream.destroyed) { 1891 return; 1892 } 1893 1894 stream[kState].flags &= ~STREAM_FLAGS_HAS_TRAILERS; 1895 1896 const ret = stream[kHandle].trailers(headersList); 1897 if (ret < 0) 1898 stream.destroy(new NghttpError(ret)); 1899 else 1900 stream[kMaybeDestroy](); 1901} 1902 1903const kNoRstStream = 0; 1904const kSubmitRstStream = 1; 1905const kForceRstStream = 2; 1906 1907function closeStream(stream, code, rstStreamStatus = kSubmitRstStream) { 1908 const state = stream[kState]; 1909 state.flags |= STREAM_FLAGS_CLOSED; 1910 state.rstCode = code; 1911 1912 // Clear timeout and remove timeout listeners 1913 stream.setTimeout(0); 1914 stream.removeAllListeners('timeout'); 1915 1916 const { ending } = stream._writableState; 1917 1918 if (!ending) { 1919 // If the writable side of the Http2Stream is still open, emit the 1920 // 'aborted' event and set the aborted flag. 1921 if (!stream.aborted) { 1922 state.flags |= STREAM_FLAGS_ABORTED; 1923 stream.emit('aborted'); 1924 } 1925 1926 // Close the writable side. 1927 stream.end(); 1928 } 1929 1930 if (rstStreamStatus !== kNoRstStream) { 1931 const finishFn = FunctionPrototypeBind(finishCloseStream, stream, code); 1932 if (!ending || stream.writableFinished || code !== NGHTTP2_NO_ERROR || 1933 rstStreamStatus === kForceRstStream) 1934 finishFn(); 1935 else 1936 stream.once('finish', finishFn); 1937 } 1938} 1939 1940function finishCloseStream(code) { 1941 const rstStreamFn = FunctionPrototypeBind(submitRstStream, this, code); 1942 // If the handle has not yet been assigned, queue up the request to 1943 // ensure that the RST_STREAM frame is sent after the stream ID has 1944 // been determined. 1945 if (this.pending) { 1946 this.push(null); 1947 this.once('ready', rstStreamFn); 1948 return; 1949 } 1950 rstStreamFn(); 1951} 1952 1953// An Http2Stream is a Duplex stream that is backed by a 1954// node::http2::Http2Stream handle implementing StreamBase. 1955class Http2Stream extends Duplex { 1956 constructor(session, options) { 1957 options.allowHalfOpen = true; 1958 options.decodeStrings = false; 1959 options.autoDestroy = false; 1960 super(options); 1961 this[async_id_symbol] = -1; 1962 1963 // Corking the stream automatically allows writes to happen 1964 // but ensures that those are buffered until the handle has 1965 // been assigned. 1966 this.cork(); 1967 this[kSession] = session; 1968 session[kState].pendingStreams.add(this); 1969 1970 // Allow our logic for determining whether any reads have happened to 1971 // work in all situations. This is similar to what we do in _http_incoming. 1972 this._readableState.readingMore = true; 1973 1974 this[kTimeout] = null; 1975 1976 this[kState] = { 1977 didRead: false, 1978 flags: STREAM_FLAGS_PENDING, 1979 rstCode: NGHTTP2_NO_ERROR, 1980 writeQueueSize: 0, 1981 trailersReady: false, 1982 endAfterHeaders: false, 1983 }; 1984 1985 // Fields used by the compat API to avoid megamorphisms. 1986 this[kRequest] = null; 1987 this[kProxySocket] = null; 1988 1989 this.on('pause', streamOnPause); 1990 1991 this.on('newListener', streamListenerAdded); 1992 this.on('removeListener', streamListenerRemoved); 1993 } 1994 1995 [kUpdateTimer]() { 1996 if (this.destroyed) 1997 return; 1998 if (this[kTimeout]) 1999 this[kTimeout].refresh(); 2000 if (this[kSession]) 2001 this[kSession][kUpdateTimer](); 2002 } 2003 2004 [kInit](id, handle) { 2005 const state = this[kState]; 2006 state.flags |= STREAM_FLAGS_READY; 2007 2008 const session = this[kSession]; 2009 session[kState].pendingStreams.delete(this); 2010 session[kState].streams.set(id, this); 2011 2012 this[kID] = id; 2013 this[async_id_symbol] = handle.getAsyncId(); 2014 handle[kOwner] = this; 2015 this[kHandle] = handle; 2016 handle.onread = onStreamRead; 2017 this.uncork(); 2018 this.emit('ready'); 2019 } 2020 2021 [kInspect](depth, opts) { 2022 if (typeof depth === 'number' && depth < 0) 2023 return this; 2024 2025 const obj = { 2026 id: this[kID] || '<pending>', 2027 closed: this.closed, 2028 destroyed: this.destroyed, 2029 state: this.state, 2030 readableState: this._readableState, 2031 writableState: this._writableState, 2032 }; 2033 return `Http2Stream ${format(obj)}`; 2034 } 2035 2036 get bufferSize() { 2037 // `bufferSize` properties of `net.Socket` are `undefined` when 2038 // their `_handle` are falsy. Here we avoid the behavior. 2039 return this[kState].writeQueueSize + this.writableLength; 2040 } 2041 2042 get endAfterHeaders() { 2043 return this[kState].endAfterHeaders; 2044 } 2045 2046 get sentHeaders() { 2047 return this[kSentHeaders]; 2048 } 2049 2050 get sentTrailers() { 2051 return this[kSentTrailers]; 2052 } 2053 2054 get sentInfoHeaders() { 2055 return this[kInfoHeaders]; 2056 } 2057 2058 get pending() { 2059 return this[kID] === undefined; 2060 } 2061 2062 // The id of the Http2Stream, will be undefined if the socket is not 2063 // yet connected. 2064 get id() { 2065 return this[kID]; 2066 } 2067 2068 // The Http2Session that owns this Http2Stream. 2069 get session() { 2070 return this[kSession]; 2071 } 2072 2073 _onTimeout() { 2074 callTimeout(this, kSession); 2075 } 2076 2077 // True if the HEADERS frame has been sent 2078 get headersSent() { 2079 return !!(this[kState].flags & STREAM_FLAGS_HEADERS_SENT); 2080 } 2081 2082 // True if the Http2Stream was aborted abnormally. 2083 get aborted() { 2084 return !!(this[kState].flags & STREAM_FLAGS_ABORTED); 2085 } 2086 2087 // True if dealing with a HEAD request 2088 get headRequest() { 2089 return !!(this[kState].flags & STREAM_FLAGS_HEAD_REQUEST); 2090 } 2091 2092 // The error code reported when this Http2Stream was closed. 2093 get rstCode() { 2094 return this[kState].rstCode; 2095 } 2096 2097 // State information for the Http2Stream 2098 get state() { 2099 const id = this[kID]; 2100 if (this.destroyed || id === undefined) 2101 return {}; 2102 return getStreamState(this[kHandle], id); 2103 } 2104 2105 [kProceed]() { 2106 assert.fail('Implementors MUST implement this. Please report this as a ' + 2107 'bug in Node.js'); 2108 } 2109 2110 [kAfterAsyncWrite]({ bytes }) { 2111 this[kState].writeQueueSize -= bytes; 2112 2113 if (this.session !== undefined) 2114 this.session[kState].writeQueueSize -= bytes; 2115 } 2116 2117 [kWriteGeneric](writev, data, encoding, cb) { 2118 // When the Http2Stream is first created, it is corked until the 2119 // handle and the stream ID is assigned. However, if the user calls 2120 // uncork() before that happens, the Duplex will attempt to pass 2121 // writes through. Those need to be queued up here. 2122 if (this.pending) { 2123 this.once( 2124 'ready', 2125 FunctionPrototypeBind(this[kWriteGeneric], 2126 this, writev, data, encoding, cb), 2127 ); 2128 return; 2129 } 2130 2131 // If the stream has been destroyed, there's nothing else we can do 2132 // because the handle has been destroyed. This should only be an 2133 // issue if a write occurs before the 'ready' event in the case where 2134 // the duplex is uncorked before the stream is ready to go. In that 2135 // case, drop the data on the floor. An error should have already been 2136 // emitted. 2137 if (this.destroyed) 2138 return; 2139 2140 this[kUpdateTimer](); 2141 if (!this.headersSent) 2142 this[kProceed](); 2143 2144 let req; 2145 2146 let waitingForWriteCallback = true; 2147 let waitingForEndCheck = true; 2148 let writeCallbackErr; 2149 let endCheckCallbackErr; 2150 const done = () => { 2151 if (waitingForEndCheck || waitingForWriteCallback) return; 2152 const err = aggregateTwoErrors(endCheckCallbackErr, writeCallbackErr); 2153 // writeGeneric does not destroy on error and 2154 // we cannot enable autoDestroy, 2155 // so make sure to destroy on error. 2156 if (err) { 2157 this.destroy(err); 2158 } 2159 cb(err); 2160 }; 2161 const writeCallback = (err) => { 2162 waitingForWriteCallback = false; 2163 writeCallbackErr = err; 2164 done(); 2165 }; 2166 const endCheckCallback = (err) => { 2167 waitingForEndCheck = false; 2168 endCheckCallbackErr = err; 2169 done(); 2170 }; 2171 // Shutdown write stream right after last chunk is sent 2172 // so final DATA frame can include END_STREAM flag 2173 process.nextTick(() => { 2174 if (writeCallbackErr || 2175 !this._writableState.ending || 2176 this._writableState.buffered.length || 2177 (this[kState].flags & STREAM_FLAGS_HAS_TRAILERS)) 2178 return endCheckCallback(); 2179 debugStreamObj(this, 'shutting down writable on last write'); 2180 shutdownWritable.call(this, endCheckCallback); 2181 }); 2182 2183 if (writev) 2184 req = writevGeneric(this, data, writeCallback); 2185 else 2186 req = writeGeneric(this, data, encoding, writeCallback); 2187 2188 trackWriteState(this, req.bytes); 2189 } 2190 2191 _write(data, encoding, cb) { 2192 this[kWriteGeneric](false, data, encoding, cb); 2193 } 2194 2195 _writev(data, cb) { 2196 this[kWriteGeneric](true, data, '', cb); 2197 } 2198 2199 _final(cb) { 2200 if (this.pending) { 2201 this.once('ready', () => this._final(cb)); 2202 return; 2203 } 2204 debugStreamObj(this, 'shutting down writable on _final'); 2205 ReflectApply(shutdownWritable, this, [cb]); 2206 } 2207 2208 _read(nread) { 2209 if (this.destroyed) { 2210 this.push(null); 2211 return; 2212 } 2213 if (!this[kState].didRead) { 2214 this._readableState.readingMore = false; 2215 this[kState].didRead = true; 2216 } 2217 if (!this.pending) { 2218 FunctionPrototypeCall(streamOnResume, this); 2219 } else { 2220 this.once('ready', streamOnResume); 2221 } 2222 } 2223 2224 priority(options) { 2225 if (this.destroyed) 2226 throw new ERR_HTTP2_INVALID_STREAM(); 2227 2228 assertIsObject(options, 'options'); 2229 options = { ...options }; 2230 setAndValidatePriorityOptions(options); 2231 2232 const priorityFn = FunctionPrototypeBind(submitPriority, this, options); 2233 2234 // If the handle has not yet been assigned, queue up the priority 2235 // frame to be sent as soon as the ready event is emitted. 2236 if (this.pending) { 2237 this.once('ready', priorityFn); 2238 return; 2239 } 2240 priorityFn(); 2241 } 2242 2243 sendTrailers(headers) { 2244 if (this.destroyed || this.closed) 2245 throw new ERR_HTTP2_INVALID_STREAM(); 2246 if (this[kSentTrailers]) 2247 throw new ERR_HTTP2_TRAILERS_ALREADY_SENT(); 2248 if (!this[kState].trailersReady) 2249 throw new ERR_HTTP2_TRAILERS_NOT_READY(); 2250 2251 assertIsObject(headers, 'headers'); 2252 headers = ObjectAssign(ObjectCreate(null), headers); 2253 2254 debugStreamObj(this, 'sending trailers'); 2255 2256 this[kUpdateTimer](); 2257 2258 const headersList = mapToHeaders(headers, assertValidPseudoHeaderTrailer); 2259 this[kSentTrailers] = headers; 2260 2261 // Send the trailers in setImmediate so we don't do it on nghttp2 stack. 2262 setImmediate(finishSendTrailers, this, headersList); 2263 } 2264 2265 get closed() { 2266 return !!(this[kState].flags & STREAM_FLAGS_CLOSED); 2267 } 2268 2269 // Close initiates closing the Http2Stream instance by sending an RST_STREAM 2270 // frame to the connected peer. The readable and writable sides of the 2271 // Http2Stream duplex are closed and the timeout timer is cleared. If 2272 // a callback is passed, it is registered to listen for the 'close' event. 2273 // 2274 // If the handle and stream ID have not been assigned yet, the close 2275 // will be queued up to wait for the ready event. As soon as the stream ID 2276 // is determined, the close will proceed. 2277 // 2278 // Submitting the RST_STREAM frame to the underlying handle will cause 2279 // the Http2Stream to be closed and ultimately destroyed. After calling 2280 // close, it is still possible to queue up PRIORITY and RST_STREAM frames, 2281 // but no DATA and HEADERS frames may be sent. 2282 close(code = NGHTTP2_NO_ERROR, callback) { 2283 validateInteger(code, 'code', 0, kMaxInt); 2284 2285 if (callback !== undefined) { 2286 validateFunction(callback, 'callback'); 2287 } 2288 2289 if (this.closed) 2290 return; 2291 2292 if (callback !== undefined) 2293 this.once('close', callback); 2294 2295 closeStream(this, code); 2296 } 2297 2298 // Called by this.destroy(). 2299 // * Will submit an RST stream to shutdown the stream if necessary. 2300 // This will cause the internal resources to be released. 2301 // * Then cleans up the resources on the js side 2302 _destroy(err, callback) { 2303 const session = this[kSession]; 2304 const handle = this[kHandle]; 2305 const id = this[kID]; 2306 2307 debugStream(this[kID] || 'pending', session[kType], 'destroying stream'); 2308 2309 const state = this[kState]; 2310 const sessionState = session[kState]; 2311 const sessionCode = sessionState.goawayCode || sessionState.destroyCode; 2312 2313 // If a stream has already closed successfully, there is no error 2314 // to report from this stream, even if the session has errored. 2315 // This can happen if the stream was already in process of destroying 2316 // after a successful close, but the session had a error between 2317 // this stream's close and destroy operations. 2318 // Previously, this always overrode a successful close operation code 2319 // NGHTTP2_NO_ERROR (0) with sessionCode because the use of the || operator. 2320 let code = this.closed ? this.rstCode : sessionCode; 2321 if (err != null) { 2322 if (sessionCode) { 2323 code = sessionCode; 2324 } else if (err instanceof AbortError) { 2325 // Enables using AbortController to cancel requests with RST code 8. 2326 code = NGHTTP2_CANCEL; 2327 } else { 2328 code = NGHTTP2_INTERNAL_ERROR; 2329 } 2330 } 2331 const hasHandle = handle !== undefined; 2332 2333 if (!this.closed) 2334 closeStream(this, code, hasHandle ? kForceRstStream : kNoRstStream); 2335 this.push(null); 2336 2337 if (hasHandle) { 2338 handle.destroy(); 2339 sessionState.streams.delete(id); 2340 } else { 2341 sessionState.pendingStreams.delete(this); 2342 } 2343 2344 // Adjust the write queue size for accounting 2345 sessionState.writeQueueSize -= state.writeQueueSize; 2346 state.writeQueueSize = 0; 2347 2348 // RST code 8 not emitted as an error as its used by clients to signify 2349 // abort and is already covered by aborted event, also allows more 2350 // seamless compatibility with http1 2351 if (err == null && code !== NGHTTP2_NO_ERROR && code !== NGHTTP2_CANCEL) 2352 err = new ERR_HTTP2_STREAM_ERROR(nameForErrorCode[code] || code); 2353 2354 this[kSession] = undefined; 2355 this[kHandle] = undefined; 2356 2357 // This notifies the session that this stream has been destroyed and 2358 // gives the session the opportunity to clean itself up. The session 2359 // will destroy if it has been closed and there are no other open or 2360 // pending streams. 2361 session[kMaybeDestroy](); 2362 callback(err); 2363 } 2364 // The Http2Stream can be destroyed if it has closed and if the readable 2365 // side has received the final chunk. 2366 [kMaybeDestroy](code = NGHTTP2_NO_ERROR) { 2367 if (code !== NGHTTP2_NO_ERROR) { 2368 this.destroy(); 2369 return; 2370 } 2371 2372 if (this.writableFinished) { 2373 if (!this.readable && this.closed) { 2374 this.destroy(); 2375 return; 2376 } 2377 2378 // We've submitted a response from our server session, have not attempted 2379 // to process any incoming data, and have no trailers. This means we can 2380 // attempt to gracefully close the session. 2381 const state = this[kState]; 2382 if (this.headersSent && 2383 this[kSession] && 2384 this[kSession][kType] === NGHTTP2_SESSION_SERVER && 2385 !(state.flags & STREAM_FLAGS_HAS_TRAILERS) && 2386 !state.didRead && 2387 this.readableFlowing === null) { 2388 // By using setImmediate we allow pushStreams to make it through 2389 // before the stream is officially closed. This prevents a bug 2390 // in most browsers where those pushStreams would be rejected. 2391 setImmediate(callStreamClose, this); 2392 } 2393 } 2394 } 2395} 2396 2397function callTimeout(self, kSession) { 2398 // If the session is destroyed, this should never actually be invoked, 2399 // but just in case... 2400 if (self.destroyed) 2401 return; 2402 // This checks whether a write is currently in progress and also whether 2403 // that write is actually sending data across the write. The kHandle 2404 // stored `chunksSentSinceLastWrite` is only updated when a timeout event 2405 // happens, meaning that if a write is ongoing it should never equal the 2406 // newly fetched, updated value. 2407 if (self[kState].writeQueueSize > 0) { 2408 const handle = kSession ? self[kSession][kHandle] : self[kHandle]; 2409 const chunksSentSinceLastWrite = handle !== undefined ? 2410 handle.chunksSentSinceLastWrite : null; 2411 if (chunksSentSinceLastWrite !== null && 2412 chunksSentSinceLastWrite !== handle.updateChunksSent()) { 2413 self[kUpdateTimer](); 2414 return; 2415 } 2416 } 2417 2418 self.emit('timeout'); 2419} 2420 2421function callStreamClose(stream) { 2422 stream.close(); 2423} 2424 2425function processHeaders(oldHeaders, options) { 2426 assertIsObject(oldHeaders, 'headers'); 2427 const headers = ObjectCreate(null); 2428 2429 if (oldHeaders !== null && oldHeaders !== undefined) { 2430 // This loop is here for performance reason. Do not change. 2431 for (const key in oldHeaders) { 2432 if (ObjectPrototypeHasOwnProperty(oldHeaders, key)) { 2433 headers[key] = oldHeaders[key]; 2434 } 2435 } 2436 headers[kSensitiveHeaders] = oldHeaders[kSensitiveHeaders]; 2437 } 2438 2439 const statusCode = 2440 headers[HTTP2_HEADER_STATUS] = 2441 headers[HTTP2_HEADER_STATUS] | 0 || HTTP_STATUS_OK; 2442 2443 if (options.sendDate == null || options.sendDate) { 2444 if (headers[HTTP2_HEADER_DATE] === null || 2445 headers[HTTP2_HEADER_DATE] === undefined) { 2446 headers[HTTP2_HEADER_DATE] = utcDate(); 2447 } 2448 } 2449 2450 // This is intentionally stricter than the HTTP/1 implementation, which 2451 // allows values between 100 and 999 (inclusive) in order to allow for 2452 // backwards compatibility with non-spec compliant code. With HTTP/2, 2453 // we have the opportunity to start fresh with stricter spec compliance. 2454 // This will have an impact on the compatibility layer for anyone using 2455 // non-standard, non-compliant status codes. 2456 if (statusCode < 200 || statusCode > 599) 2457 throw new ERR_HTTP2_STATUS_INVALID(headers[HTTP2_HEADER_STATUS]); 2458 2459 const neverIndex = headers[kSensitiveHeaders]; 2460 if (neverIndex !== undefined && !ArrayIsArray(neverIndex)) 2461 throw new ERR_INVALID_ARG_VALUE('headers[http2.neverIndex]', neverIndex); 2462 2463 return headers; 2464} 2465 2466 2467function onFileUnpipe() { 2468 const stream = this.sink[kOwner]; 2469 if (stream.ownsFd) 2470 PromisePrototypeThen(this.source.close(), undefined, 2471 FunctionPrototypeBind(stream.destroy, stream)); 2472 else 2473 this.source.releaseFD(); 2474} 2475 2476// This is only called once the pipe has returned back control, so 2477// it only has to handle errors and End-of-File. 2478function onPipedFileHandleRead() { 2479 const err = streamBaseState[kReadBytesOrError]; 2480 if (err < 0 && err !== UV_EOF) { 2481 this.stream.close(NGHTTP2_INTERNAL_ERROR); 2482 } 2483} 2484 2485function processRespondWithFD(self, fd, headers, offset = 0, length = -1, 2486 streamOptions = 0) { 2487 const state = self[kState]; 2488 state.flags |= STREAM_FLAGS_HEADERS_SENT; 2489 2490 let headersList; 2491 try { 2492 headersList = mapToHeaders(headers, assertValidPseudoHeaderResponse); 2493 } catch (err) { 2494 self.destroy(err); 2495 return; 2496 } 2497 self[kSentHeaders] = headers; 2498 2499 // Close the writable side of the stream, but only as far as the writable 2500 // stream implementation is concerned. 2501 self._final = null; 2502 self.end(); 2503 2504 const ret = self[kHandle].respond(headersList, streamOptions); 2505 2506 if (ret < 0) { 2507 self.destroy(new NghttpError(ret)); 2508 return; 2509 } 2510 2511 defaultTriggerAsyncIdScope(self[async_id_symbol], startFilePipe, 2512 self, fd, offset, length); 2513} 2514 2515function startFilePipe(self, fd, offset, length) { 2516 const handle = new FileHandle(fd, offset, length); 2517 handle.onread = onPipedFileHandleRead; 2518 handle.stream = self; 2519 2520 const pipe = new StreamPipe(handle, self[kHandle]); 2521 pipe.onunpipe = onFileUnpipe; 2522 pipe.start(); 2523 2524 // Exact length of the file doesn't matter here, since the 2525 // stream is closing anyway - just use 1 to signify that 2526 // a write does exist 2527 trackWriteState(self, 1); 2528} 2529 2530function doSendFD(session, options, fd, headers, streamOptions, err, stat) { 2531 if (err) { 2532 this.destroy(err); 2533 return; 2534 } 2535 2536 // This can happen if the stream is destroyed or closed while we are waiting 2537 // for the file descriptor to be opened or the stat call to be completed. 2538 // In either case, we do not want to continue because the we are shutting 2539 // down and should not attempt to send any data. 2540 if (this.destroyed || this.closed) { 2541 this.destroy(new ERR_HTTP2_INVALID_STREAM()); 2542 return; 2543 } 2544 2545 const statOptions = { 2546 offset: options.offset !== undefined ? options.offset : 0, 2547 length: options.length !== undefined ? options.length : -1, 2548 }; 2549 2550 // options.statCheck is a user-provided function that can be used to 2551 // verify stat values, override or set headers, or even cancel the 2552 // response operation. If statCheck explicitly returns false, the 2553 // response is canceled. The user code may also send a separate type 2554 // of response so check again for the HEADERS_SENT flag 2555 if ((typeof options.statCheck === 'function' && 2556 ReflectApply(options.statCheck, this, 2557 [stat, headers, statOptions]) === false) || 2558 (this[kState].flags & STREAM_FLAGS_HEADERS_SENT)) { 2559 return; 2560 } 2561 2562 processRespondWithFD(this, fd, headers, 2563 statOptions.offset | 0, 2564 statOptions.length | 0, 2565 streamOptions); 2566} 2567 2568function doSendFileFD(session, options, fd, headers, streamOptions, err, stat) { 2569 const onError = options.onError; 2570 2571 if (err) { 2572 tryClose(fd); 2573 if (onError) 2574 onError(err); 2575 else 2576 this.destroy(err); 2577 return; 2578 } 2579 2580 if (!stat.isFile()) { 2581 const isDirectory = stat.isDirectory(); 2582 if (options.offset !== undefined || options.offset > 0 || 2583 options.length !== undefined || options.length >= 0 || 2584 isDirectory) { 2585 const err = isDirectory ? 2586 new ERR_HTTP2_SEND_FILE() : new ERR_HTTP2_SEND_FILE_NOSEEK(); 2587 tryClose(fd); 2588 if (onError) 2589 onError(err); 2590 else 2591 this.destroy(err); 2592 return; 2593 } 2594 2595 options.offset = -1; 2596 options.length = -1; 2597 } 2598 2599 if (this.destroyed || this.closed) { 2600 tryClose(fd); 2601 this.destroy(new ERR_HTTP2_INVALID_STREAM()); 2602 return; 2603 } 2604 2605 const statOptions = { 2606 offset: options.offset !== undefined ? options.offset : 0, 2607 length: options.length !== undefined ? options.length : -1, 2608 }; 2609 2610 // options.statCheck is a user-provided function that can be used to 2611 // verify stat values, override or set headers, or even cancel the 2612 // response operation. If statCheck explicitly returns false, the 2613 // response is canceled. The user code may also send a separate type 2614 // of response so check again for the HEADERS_SENT flag 2615 if ((typeof options.statCheck === 'function' && 2616 ReflectApply(options.statCheck, this, [stat, headers]) === false) || 2617 (this[kState].flags & STREAM_FLAGS_HEADERS_SENT)) { 2618 tryClose(fd); 2619 return; 2620 } 2621 2622 if (stat.isFile()) { 2623 statOptions.length = 2624 statOptions.length < 0 ? stat.size - (+statOptions.offset) : 2625 MathMin(stat.size - (+statOptions.offset), 2626 statOptions.length); 2627 2628 headers[HTTP2_HEADER_CONTENT_LENGTH] = statOptions.length; 2629 } 2630 2631 processRespondWithFD(this, fd, headers, 2632 options.offset | 0, 2633 statOptions.length | 0, 2634 streamOptions); 2635} 2636 2637function afterOpen(session, options, headers, streamOptions, err, fd) { 2638 const state = this[kState]; 2639 const onError = options.onError; 2640 if (err) { 2641 if (onError) 2642 onError(err); 2643 else 2644 this.destroy(err); 2645 return; 2646 } 2647 if (this.destroyed || this.closed) { 2648 tryClose(fd); 2649 return; 2650 } 2651 state.fd = fd; 2652 2653 fs.fstat(fd, 2654 FunctionPrototypeBind(doSendFileFD, this, 2655 session, options, fd, 2656 headers, streamOptions)); 2657} 2658 2659class ServerHttp2Stream extends Http2Stream { 2660 constructor(session, handle, id, options, headers) { 2661 super(session, options); 2662 handle.owner = this; 2663 this[kInit](id, handle); 2664 this[kProtocol] = headers[HTTP2_HEADER_SCHEME]; 2665 this[kAuthority] = getAuthority(headers); 2666 } 2667 2668 // True if the remote peer accepts push streams 2669 get pushAllowed() { 2670 return !this.destroyed && 2671 !this.closed && 2672 !this.session.closed && 2673 !this.session.destroyed && 2674 this[kSession].remoteSettings.enablePush; 2675 } 2676 2677 // Create a push stream, call the given callback with the created 2678 // Http2Stream for the push stream. 2679 pushStream(headers, options, callback) { 2680 if (!this.pushAllowed) 2681 throw new ERR_HTTP2_PUSH_DISABLED(); 2682 if (this[kID] % 2 === 0) 2683 throw new ERR_HTTP2_NESTED_PUSH(); 2684 2685 const session = this[kSession]; 2686 2687 debugStreamObj(this, 'initiating push stream'); 2688 2689 this[kUpdateTimer](); 2690 2691 if (typeof options === 'function') { 2692 callback = options; 2693 options = undefined; 2694 } 2695 2696 validateFunction(callback, 'callback'); 2697 2698 assertIsObject(options, 'options'); 2699 options = { ...options }; 2700 options.endStream = !!options.endStream; 2701 2702 assertIsObject(headers, 'headers'); 2703 headers = ObjectAssign(ObjectCreate(null), headers); 2704 2705 if (headers[HTTP2_HEADER_METHOD] === undefined) 2706 headers[HTTP2_HEADER_METHOD] = HTTP2_METHOD_GET; 2707 if (getAuthority(headers) === undefined) 2708 headers[HTTP2_HEADER_AUTHORITY] = this[kAuthority]; 2709 if (headers[HTTP2_HEADER_SCHEME] === undefined) 2710 headers[HTTP2_HEADER_SCHEME] = this[kProtocol]; 2711 if (headers[HTTP2_HEADER_PATH] === undefined) 2712 headers[HTTP2_HEADER_PATH] = '/'; 2713 2714 let headRequest = false; 2715 if (headers[HTTP2_HEADER_METHOD] === HTTP2_METHOD_HEAD) 2716 headRequest = options.endStream = true; 2717 2718 const headersList = mapToHeaders(headers); 2719 2720 const streamOptions = options.endStream ? STREAM_OPTION_EMPTY_PAYLOAD : 0; 2721 2722 const ret = this[kHandle].pushPromise(headersList, streamOptions); 2723 let err; 2724 if (typeof ret === 'number') { 2725 switch (ret) { 2726 case NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE: 2727 err = new ERR_HTTP2_OUT_OF_STREAMS(); 2728 break; 2729 case NGHTTP2_ERR_STREAM_CLOSED: 2730 err = new ERR_HTTP2_INVALID_STREAM(); 2731 break; 2732 default: 2733 err = new NghttpError(ret); 2734 break; 2735 } 2736 process.nextTick(callback, err); 2737 return; 2738 } 2739 2740 const id = ret.id(); 2741 const stream = new ServerHttp2Stream(session, ret, id, options, headers); 2742 stream[kSentHeaders] = headers; 2743 2744 stream.push(null); 2745 2746 if (options.endStream) 2747 stream.end(); 2748 2749 if (headRequest) 2750 stream[kState].flags |= STREAM_FLAGS_HEAD_REQUEST; 2751 2752 process.nextTick(callback, null, stream, headers, 0); 2753 } 2754 2755 // Initiate a response on this Http2Stream 2756 respond(headers, options) { 2757 if (this.destroyed || this.closed) 2758 throw new ERR_HTTP2_INVALID_STREAM(); 2759 if (this.headersSent) 2760 throw new ERR_HTTP2_HEADERS_SENT(); 2761 2762 const state = this[kState]; 2763 2764 assertIsObject(options, 'options'); 2765 options = { ...options }; 2766 2767 debugStreamObj(this, 'initiating response'); 2768 this[kUpdateTimer](); 2769 2770 options.endStream = !!options.endStream; 2771 2772 let streamOptions = 0; 2773 if (options.endStream) 2774 streamOptions |= STREAM_OPTION_EMPTY_PAYLOAD; 2775 2776 if (options.waitForTrailers) { 2777 streamOptions |= STREAM_OPTION_GET_TRAILERS; 2778 state.flags |= STREAM_FLAGS_HAS_TRAILERS; 2779 } 2780 2781 headers = processHeaders(headers, options); 2782 const headersList = mapToHeaders(headers, assertValidPseudoHeaderResponse); 2783 this[kSentHeaders] = headers; 2784 2785 state.flags |= STREAM_FLAGS_HEADERS_SENT; 2786 2787 // Close the writable side if the endStream option is set or status 2788 // is one of known codes with no payload, or it's a head request 2789 const statusCode = headers[HTTP2_HEADER_STATUS] | 0; 2790 if (!!options.endStream || 2791 statusCode === HTTP_STATUS_NO_CONTENT || 2792 statusCode === HTTP_STATUS_RESET_CONTENT || 2793 statusCode === HTTP_STATUS_NOT_MODIFIED || 2794 this.headRequest === true) { 2795 options.endStream = true; 2796 this.end(); 2797 } 2798 2799 const ret = this[kHandle].respond(headersList, streamOptions); 2800 if (ret < 0) 2801 this.destroy(new NghttpError(ret)); 2802 } 2803 2804 // Initiate a response using an open FD. Note that there are fewer 2805 // protections with this approach. For one, the fd is not validated by 2806 // default. In respondWithFile, the file is checked to make sure it is a 2807 // regular file, here the fd is passed directly. If the underlying 2808 // mechanism is not able to read from the fd, then the stream will be 2809 // reset with an error code. 2810 respondWithFD(fd, headers, options) { 2811 if (this.destroyed || this.closed) 2812 throw new ERR_HTTP2_INVALID_STREAM(); 2813 if (this.headersSent) 2814 throw new ERR_HTTP2_HEADERS_SENT(); 2815 2816 const session = this[kSession]; 2817 2818 assertIsObject(options, 'options'); 2819 options = { ...options }; 2820 2821 if (options.offset !== undefined && typeof options.offset !== 'number') 2822 throw new ERR_INVALID_ARG_VALUE('options.offset', options.offset); 2823 2824 if (options.length !== undefined && typeof options.length !== 'number') 2825 throw new ERR_INVALID_ARG_VALUE('options.length', options.length); 2826 2827 if (options.statCheck !== undefined && 2828 typeof options.statCheck !== 'function') { 2829 throw new ERR_INVALID_ARG_VALUE('options.statCheck', options.statCheck); 2830 } 2831 2832 let streamOptions = 0; 2833 if (options.waitForTrailers) { 2834 streamOptions |= STREAM_OPTION_GET_TRAILERS; 2835 this[kState].flags |= STREAM_FLAGS_HAS_TRAILERS; 2836 } 2837 2838 if (fd instanceof fsPromisesInternal.FileHandle) 2839 fd = fd.fd; 2840 else if (typeof fd !== 'number') 2841 throw new ERR_INVALID_ARG_TYPE('fd', ['number', 'FileHandle'], fd); 2842 2843 debugStreamObj(this, 'initiating response from fd'); 2844 this[kUpdateTimer](); 2845 this.ownsFd = false; 2846 2847 headers = processHeaders(headers, options); 2848 const statusCode = headers[HTTP2_HEADER_STATUS] |= 0; 2849 // Payload/DATA frames are not permitted in these cases 2850 if (statusCode === HTTP_STATUS_NO_CONTENT || 2851 statusCode === HTTP_STATUS_RESET_CONTENT || 2852 statusCode === HTTP_STATUS_NOT_MODIFIED || 2853 this.headRequest) { 2854 throw new ERR_HTTP2_PAYLOAD_FORBIDDEN(statusCode); 2855 } 2856 2857 if (options.statCheck !== undefined) { 2858 fs.fstat(fd, 2859 FunctionPrototypeBind(doSendFD, this, 2860 session, options, fd, 2861 headers, streamOptions)); 2862 return; 2863 } 2864 2865 processRespondWithFD(this, fd, headers, 2866 options.offset, 2867 options.length, 2868 streamOptions); 2869 } 2870 2871 // Initiate a file response on this Http2Stream. The path is passed to 2872 // fs.open() to acquire the fd with mode 'r', then the fd is passed to 2873 // fs.fstat(). Assuming fstat is successful, a check is made to ensure 2874 // that the file is a regular file, then options.statCheck is called, 2875 // giving the user an opportunity to verify the details and set additional 2876 // headers. If statCheck returns false, the operation is aborted and no 2877 // file details are sent. 2878 respondWithFile(path, headers, options) { 2879 if (this.destroyed || this.closed) 2880 throw new ERR_HTTP2_INVALID_STREAM(); 2881 if (this.headersSent) 2882 throw new ERR_HTTP2_HEADERS_SENT(); 2883 2884 assertIsObject(options, 'options'); 2885 options = { ...options }; 2886 2887 if (options.offset !== undefined && typeof options.offset !== 'number') 2888 throw new ERR_INVALID_ARG_VALUE('options.offset', options.offset); 2889 2890 if (options.length !== undefined && typeof options.length !== 'number') 2891 throw new ERR_INVALID_ARG_VALUE('options.length', options.length); 2892 2893 if (options.statCheck !== undefined && 2894 typeof options.statCheck !== 'function') { 2895 throw new ERR_INVALID_ARG_VALUE('options.statCheck', options.statCheck); 2896 } 2897 2898 let streamOptions = 0; 2899 if (options.waitForTrailers) { 2900 streamOptions |= STREAM_OPTION_GET_TRAILERS; 2901 this[kState].flags |= STREAM_FLAGS_HAS_TRAILERS; 2902 } 2903 2904 const session = this[kSession]; 2905 debugStreamObj(this, 'initiating response from file'); 2906 this[kUpdateTimer](); 2907 this.ownsFd = true; 2908 2909 headers = processHeaders(headers, options); 2910 const statusCode = headers[HTTP2_HEADER_STATUS] |= 0; 2911 // Payload/DATA frames are not permitted in these cases 2912 if (statusCode === HTTP_STATUS_NO_CONTENT || 2913 statusCode === HTTP_STATUS_RESET_CONTENT || 2914 statusCode === HTTP_STATUS_NOT_MODIFIED || 2915 this.headRequest) { 2916 throw new ERR_HTTP2_PAYLOAD_FORBIDDEN(statusCode); 2917 } 2918 2919 fs.open(path, 'r', 2920 FunctionPrototypeBind(afterOpen, this, 2921 session, options, headers, streamOptions)); 2922 } 2923 2924 // Sends a block of informational headers. In theory, the HTTP/2 spec 2925 // allows sending a HEADER block at any time during a streams lifecycle, 2926 // but the HTTP request/response semantics defined in HTTP/2 places limits 2927 // such that HEADERS may only be sent *before* or *after* DATA frames. 2928 // If the block of headers being sent includes a status code, it MUST be 2929 // a 1xx informational code and it MUST be sent before the request/response 2930 // headers are sent, or an error will be thrown. 2931 additionalHeaders(headers) { 2932 if (this.destroyed || this.closed) 2933 throw new ERR_HTTP2_INVALID_STREAM(); 2934 if (this.headersSent) 2935 throw new ERR_HTTP2_HEADERS_AFTER_RESPOND(); 2936 2937 assertIsObject(headers, 'headers'); 2938 headers = ObjectAssign(ObjectCreate(null), headers); 2939 2940 debugStreamObj(this, 'sending additional headers'); 2941 2942 if (headers[HTTP2_HEADER_STATUS] != null) { 2943 const statusCode = headers[HTTP2_HEADER_STATUS] |= 0; 2944 if (statusCode === HTTP_STATUS_SWITCHING_PROTOCOLS) 2945 throw new ERR_HTTP2_STATUS_101(); 2946 if (statusCode < 100 || statusCode >= 200) { 2947 throw new ERR_HTTP2_INVALID_INFO_STATUS(headers[HTTP2_HEADER_STATUS]); 2948 } 2949 } 2950 2951 this[kUpdateTimer](); 2952 2953 const headersList = mapToHeaders(headers, assertValidPseudoHeaderResponse); 2954 if (!this[kInfoHeaders]) 2955 this[kInfoHeaders] = [headers]; 2956 else 2957 ArrayPrototypePush(this[kInfoHeaders], headers); 2958 2959 const ret = this[kHandle].info(headersList); 2960 if (ret < 0) 2961 this.destroy(new NghttpError(ret)); 2962 } 2963} 2964 2965ServerHttp2Stream.prototype[kProceed] = ServerHttp2Stream.prototype.respond; 2966 2967class ClientHttp2Stream extends Http2Stream { 2968 constructor(session, handle, id, options) { 2969 super(session, options); 2970 this[kState].flags |= STREAM_FLAGS_HEADERS_SENT; 2971 if (id !== undefined) 2972 this[kInit](id, handle); 2973 this.on('headers', handleHeaderContinue); 2974 } 2975} 2976 2977function handleHeaderContinue(headers) { 2978 if (headers[HTTP2_HEADER_STATUS] === HTTP_STATUS_CONTINUE) 2979 this.emit('continue'); 2980} 2981 2982const setTimeoutValue = { 2983 configurable: true, 2984 enumerable: true, 2985 writable: true, 2986 value: setStreamTimeout, 2987}; 2988ObjectDefineProperty(Http2Stream.prototype, 'setTimeout', setTimeoutValue); 2989ObjectDefineProperty(Http2Session.prototype, 'setTimeout', setTimeoutValue); 2990 2991 2992// When the socket emits an error, destroy the associated Http2Session and 2993// forward it the same error. 2994function socketOnError(error) { 2995 const session = this[kSession]; 2996 if (session !== undefined) { 2997 // We can ignore ECONNRESET after GOAWAY was received as there's nothing 2998 // we can do and the other side is fully within its rights to do so. 2999 if (error.code === 'ECONNRESET' && session[kState].goawayCode !== null) 3000 return session.destroy(); 3001 debugSessionObj(this, 'socket error [%s]', error.message); 3002 session.destroy(error); 3003 } 3004} 3005 3006// Handles the on('stream') event for a session and forwards 3007// it on to the server object. 3008function sessionOnStream(stream, headers, flags, rawHeaders) { 3009 if (this[kServer] !== undefined) 3010 this[kServer].emit('stream', stream, headers, flags, rawHeaders); 3011} 3012 3013function sessionOnPriority(stream, parent, weight, exclusive) { 3014 if (this[kServer] !== undefined) 3015 this[kServer].emit('priority', stream, parent, weight, exclusive); 3016} 3017 3018function sessionOnError(error) { 3019 if (this[kServer] !== undefined) 3020 this[kServer].emit('sessionError', error, this); 3021} 3022 3023// When the session times out on the server, try emitting a timeout event. 3024// If no handler is registered, destroy the session. 3025function sessionOnTimeout() { 3026 // If destroyed or closed already, do nothing 3027 if (this.destroyed || this.closed) 3028 return; 3029 const server = this[kServer]; 3030 if (!server.emit('timeout', this)) 3031 this.destroy(); // No error code, just things down. 3032} 3033 3034function connectionListener(socket) { 3035 debug('Http2Session server: received a connection'); 3036 const options = this[kOptions] || {}; 3037 3038 if (socket.alpnProtocol === false || socket.alpnProtocol === 'http/1.1') { 3039 // Fallback to HTTP/1.1 3040 if (options.allowHTTP1 === true) { 3041 socket.server[kIncomingMessage] = options.Http1IncomingMessage; 3042 socket.server[kServerResponse] = options.Http1ServerResponse; 3043 return FunctionPrototypeCall(httpConnectionListener, this, socket); 3044 } 3045 // Let event handler deal with the socket 3046 debug('Unknown protocol from %s:%s', 3047 socket.remoteAddress, socket.remotePort); 3048 if (!this.emit('unknownProtocol', socket)) { 3049 debug('Unknown protocol timeout: %s', options.unknownProtocolTimeout); 3050 // Install a timeout if the socket was not successfully closed, then 3051 // destroy the socket to ensure that the underlying resources are 3052 // released. 3053 const timer = setTimeout(() => { 3054 if (!socket.destroyed) { 3055 debug('UnknownProtocol socket timeout, destroy socket'); 3056 socket.destroy(); 3057 } 3058 }, options.unknownProtocolTimeout); 3059 // Un-reference the timer to avoid blocking of application shutdown and 3060 // clear the timeout if the socket was successfully closed. 3061 timer.unref(); 3062 3063 socket.once('close', () => clearTimeout(timer)); 3064 3065 // We don't know what to do, so let's just tell the other side what's 3066 // going on in a format that they *might* understand. 3067 socket.end('HTTP/1.0 403 Forbidden\r\n' + 3068 'Content-Type: text/plain\r\n\r\n' + 3069 'Unknown ALPN Protocol, expected `h2` to be available.\n' + 3070 'If this is a HTTP request: The server was not ' + 3071 'configured with the `allowHTTP1` option or a ' + 3072 'listener for the `unknownProtocol` event.\n'); 3073 } 3074 return; 3075 } 3076 3077 // Set up the Session 3078 const session = new ServerHttp2Session(options, socket, this); 3079 3080 session.on('stream', sessionOnStream); 3081 session.on('error', sessionOnError); 3082 // Don't count our own internal listener. 3083 session.on('priority', sessionOnPriority); 3084 session[kNativeFields][kSessionPriorityListenerCount]--; 3085 3086 if (this.timeout) 3087 session.setTimeout(this.timeout, sessionOnTimeout); 3088 3089 socket[kServer] = this; 3090 3091 this.emit('session', session); 3092} 3093 3094function initializeOptions(options) { 3095 assertIsObject(options, 'options'); 3096 options = { ...options }; 3097 assertIsObject(options.settings, 'options.settings'); 3098 options.settings = { ...options.settings }; 3099 3100 if (options.maxSessionInvalidFrames !== undefined) 3101 validateUint32(options.maxSessionInvalidFrames, 'maxSessionInvalidFrames'); 3102 3103 if (options.maxSessionRejectedStreams !== undefined) { 3104 validateUint32( 3105 options.maxSessionRejectedStreams, 3106 'maxSessionRejectedStreams', 3107 ); 3108 } 3109 3110 if (options.unknownProtocolTimeout !== undefined) 3111 validateUint32(options.unknownProtocolTimeout, 'unknownProtocolTimeout'); 3112 else 3113 // TODO(danbev): is this a good default value? 3114 options.unknownProtocolTimeout = 10000; 3115 3116 3117 // Used only with allowHTTP1 3118 options.Http1IncomingMessage = options.Http1IncomingMessage || 3119 http.IncomingMessage; 3120 options.Http1ServerResponse = options.Http1ServerResponse || 3121 http.ServerResponse; 3122 3123 options.Http2ServerRequest = options.Http2ServerRequest || 3124 Http2ServerRequest; 3125 options.Http2ServerResponse = options.Http2ServerResponse || 3126 Http2ServerResponse; 3127 return options; 3128} 3129 3130function initializeTLSOptions(options, servername) { 3131 options = initializeOptions(options); 3132 options.ALPNProtocols = ['h2']; 3133 if (options.allowHTTP1 === true) 3134 ArrayPrototypePush(options.ALPNProtocols, 'http/1.1'); 3135 if (servername !== undefined && !options.servername) 3136 options.servername = servername; 3137 return options; 3138} 3139 3140function onErrorSecureServerSession(err, socket) { 3141 if (!this.emit('clientError', err, socket)) 3142 socket.destroy(err); 3143} 3144 3145class Http2SecureServer extends TLSServer { 3146 constructor(options, requestListener) { 3147 options = initializeTLSOptions(options); 3148 super(options, connectionListener); 3149 this[kOptions] = options; 3150 this.timeout = 0; 3151 this.on('newListener', setupCompat); 3152 if (typeof requestListener === 'function') 3153 this.on('request', requestListener); 3154 this.on('tlsClientError', onErrorSecureServerSession); 3155 } 3156 3157 setTimeout(msecs, callback) { 3158 this.timeout = msecs; 3159 if (callback !== undefined) { 3160 validateFunction(callback, 'callback'); 3161 this.on('timeout', callback); 3162 } 3163 return this; 3164 } 3165 3166 updateSettings(settings) { 3167 assertIsObject(settings, 'settings'); 3168 validateSettings(settings); 3169 this[kOptions].settings = { ...this[kOptions].settings, ...settings }; 3170 } 3171} 3172 3173class Http2Server extends NETServer { 3174 constructor(options, requestListener) { 3175 options = initializeOptions(options); 3176 super(options, connectionListener); 3177 this[kOptions] = options; 3178 this.timeout = 0; 3179 this.on('newListener', setupCompat); 3180 if (typeof requestListener === 'function') 3181 this.on('request', requestListener); 3182 } 3183 3184 setTimeout(msecs, callback) { 3185 this.timeout = msecs; 3186 if (callback !== undefined) { 3187 validateFunction(callback, 'callback'); 3188 this.on('timeout', callback); 3189 } 3190 return this; 3191 } 3192 3193 updateSettings(settings) { 3194 assertIsObject(settings, 'settings'); 3195 validateSettings(settings); 3196 this[kOptions].settings = { ...this[kOptions].settings, ...settings }; 3197 } 3198} 3199 3200Http2Server.prototype[EventEmitter.captureRejectionSymbol] = function( 3201 err, event, ...args) { 3202 3203 switch (event) { 3204 case 'stream': { 3205 // TODO(mcollina): we might want to match this with what we do on 3206 // the compat side. 3207 const { 0: stream } = args; 3208 if (stream.sentHeaders) { 3209 stream.destroy(err); 3210 } else { 3211 stream.respond({ [HTTP2_HEADER_STATUS]: 500 }); 3212 stream.end(); 3213 } 3214 break; 3215 } 3216 case 'request': { 3217 const { 1: res } = args; 3218 if (!res.headersSent && !res.finished) { 3219 // Don't leak headers. 3220 for (const name of res.getHeaderNames()) { 3221 res.removeHeader(name); 3222 } 3223 res.statusCode = 500; 3224 res.end(http.STATUS_CODES[500]); 3225 } else { 3226 res.destroy(); 3227 } 3228 break; 3229 } 3230 default: 3231 ArrayPrototypeUnshift(args, err, event); 3232 ReflectApply(net.Server.prototype[EventEmitter.captureRejectionSymbol], 3233 this, args); 3234 } 3235}; 3236 3237function setupCompat(ev) { 3238 if (ev === 'request') { 3239 this.removeListener('newListener', setupCompat); 3240 this.on('stream', FunctionPrototypeBind(onServerStream, 3241 this, 3242 this[kOptions].Http2ServerRequest, 3243 this[kOptions].Http2ServerResponse), 3244 ); 3245 } 3246} 3247 3248function socketOnClose() { 3249 const session = this[kSession]; 3250 if (session !== undefined) { 3251 debugSessionObj(session, 'socket closed'); 3252 const err = session.connecting ? new ERR_SOCKET_CLOSED() : null; 3253 const state = session[kState]; 3254 state.streams.forEach((stream) => stream.close(NGHTTP2_CANCEL)); 3255 state.pendingStreams.forEach((stream) => stream.close(NGHTTP2_CANCEL)); 3256 session.close(); 3257 session[kMaybeDestroy](err); 3258 } 3259} 3260 3261function connect(authority, options, listener) { 3262 if (typeof options === 'function') { 3263 listener = options; 3264 options = undefined; 3265 } 3266 3267 assertIsObject(options, 'options'); 3268 options = { ...options }; 3269 3270 if (typeof authority === 'string') 3271 authority = new URL(authority); 3272 3273 assertIsObject(authority, 'authority', ['string', 'Object', 'URL']); 3274 3275 const protocol = authority.protocol || options.protocol || 'https:'; 3276 const port = '' + (authority.port !== '' ? 3277 authority.port : (authority.protocol === 'http:' ? 80 : 443)); 3278 let host = 'localhost'; 3279 3280 if (authority.hostname) { 3281 host = authority.hostname; 3282 3283 if (host[0] === '[') 3284 host = StringPrototypeSlice(host, 1, -1); 3285 } else if (authority.host) { 3286 host = authority.host; 3287 } 3288 3289 let socket; 3290 if (typeof options.createConnection === 'function') { 3291 socket = options.createConnection(authority, options); 3292 } else { 3293 switch (protocol) { 3294 case 'http:': 3295 socket = net.connect({ port, host, ...options }); 3296 break; 3297 case 'https:': 3298 socket = tls.connect(port, host, initializeTLSOptions(options, host)); 3299 break; 3300 default: 3301 throw new ERR_HTTP2_UNSUPPORTED_PROTOCOL(protocol); 3302 } 3303 } 3304 3305 const session = new ClientHttp2Session(options, socket); 3306 3307 session[kAuthority] = `${options.servername || host}:${port}`; 3308 session[kProtocol] = protocol; 3309 3310 if (typeof listener === 'function') 3311 session.once('connect', listener); 3312 3313 return session; 3314} 3315 3316// Support util.promisify 3317ObjectDefineProperty(connect, promisify.custom, { 3318 __proto__: null, 3319 value: (authority, options) => { 3320 return new Promise((resolve) => { 3321 const server = connect(authority, options, () => resolve(server)); 3322 }); 3323 }, 3324}); 3325 3326function createSecureServer(options, handler) { 3327 return new Http2SecureServer(options, handler); 3328} 3329 3330function createServer(options, handler) { 3331 if (typeof options === 'function') { 3332 handler = options; 3333 options = kEmptyObject; 3334 } 3335 return new Http2Server(options, handler); 3336} 3337 3338// Returns a Base64 encoded settings frame payload from the given 3339// object. The value is suitable for passing as the value of the 3340// HTTP2-Settings header frame. 3341function getPackedSettings(settings) { 3342 assertIsObject(settings, 'settings'); 3343 validateSettings(settings); 3344 updateSettingsBuffer({ ...settings }); 3345 return binding.packSettings(); 3346} 3347 3348function getUnpackedSettings(buf, options = kEmptyObject) { 3349 if (!isArrayBufferView(buf) || buf.length === undefined) { 3350 throw new ERR_INVALID_ARG_TYPE('buf', 3351 ['Buffer', 'TypedArray'], buf); 3352 } 3353 if (buf.length % 6 !== 0) 3354 throw new ERR_HTTP2_INVALID_PACKED_SETTINGS_LENGTH(); 3355 const settings = {}; 3356 let offset = 0; 3357 while (offset < buf.length) { 3358 const id = ReflectApply(readUInt16BE, buf, [offset]); 3359 offset += 2; 3360 const value = ReflectApply(readUInt32BE, buf, [offset]); 3361 switch (id) { 3362 case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: 3363 settings.headerTableSize = value; 3364 break; 3365 case NGHTTP2_SETTINGS_ENABLE_PUSH: 3366 settings.enablePush = value !== 0; 3367 break; 3368 case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: 3369 settings.maxConcurrentStreams = value; 3370 break; 3371 case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: 3372 settings.initialWindowSize = value; 3373 break; 3374 case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: 3375 settings.maxFrameSize = value; 3376 break; 3377 case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: 3378 settings.maxHeaderListSize = settings.maxHeaderSize = value; 3379 break; 3380 case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: 3381 settings.enableConnectProtocol = value !== 0; 3382 } 3383 offset += 4; 3384 } 3385 3386 if (options != null && options.validate) 3387 validateSettings(settings); 3388 3389 return settings; 3390} 3391 3392binding.setCallbackFunctions( 3393 onSessionInternalError, 3394 onPriority, 3395 onSettings, 3396 onPing, 3397 onSessionHeaders, 3398 onFrameError, 3399 onGoawayData, 3400 onAltSvc, 3401 onOrigin, 3402 onStreamTrailers, 3403 onStreamClose, 3404); 3405 3406// Exports 3407module.exports = { 3408 connect, 3409 constants, 3410 createServer, 3411 createSecureServer, 3412 getDefaultSettings, 3413 getPackedSettings, 3414 getUnpackedSettings, 3415 sensitiveHeaders: kSensitiveHeaders, 3416 Http2Session, 3417 Http2Stream, 3418 ServerHttp2Session, 3419 Http2ServerRequest, 3420 Http2ServerResponse, 3421}; 3422