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