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