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