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