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