• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const {
4  ArrayIsArray,
5  ArrayPrototypePush,
6  Boolean,
7  FunctionPrototypeBind,
8  ObjectAssign,
9  ObjectCreate,
10  ObjectKeys,
11  ObjectPrototypeHasOwnProperty,
12  Proxy,
13  ReflectApply,
14  ReflectGetPrototypeOf,
15  StringPrototypeIncludes,
16  SafeArrayIterator,
17  StringPrototypeToLowerCase,
18  StringPrototypeTrim,
19  Symbol,
20} = primordials;
21
22const assert = require('internal/assert');
23const Stream = require('stream');
24const { Readable } = Stream;
25const {
26  constants: {
27    HTTP2_HEADER_AUTHORITY,
28    HTTP2_HEADER_CONNECTION,
29    HTTP2_HEADER_METHOD,
30    HTTP2_HEADER_PATH,
31    HTTP2_HEADER_SCHEME,
32    HTTP2_HEADER_STATUS,
33
34    HTTP_STATUS_CONTINUE,
35    HTTP_STATUS_EARLY_HINTS,
36    HTTP_STATUS_EXPECTATION_FAILED,
37    HTTP_STATUS_METHOD_NOT_ALLOWED,
38    HTTP_STATUS_OK,
39  },
40} = internalBinding('http2');
41const {
42  codes: {
43    ERR_HTTP2_HEADERS_SENT,
44    ERR_HTTP2_INFO_STATUS_NOT_ALLOWED,
45    ERR_HTTP2_INVALID_HEADER_VALUE,
46    ERR_HTTP2_INVALID_STREAM,
47    ERR_HTTP2_NO_SOCKET_MANIPULATION,
48    ERR_HTTP2_PSEUDOHEADER_NOT_ALLOWED,
49    ERR_HTTP2_STATUS_INVALID,
50    ERR_INVALID_ARG_VALUE,
51    ERR_INVALID_HTTP_TOKEN,
52    ERR_STREAM_WRITE_AFTER_END,
53  },
54  hideStackFrames,
55} = require('internal/errors');
56const {
57  validateFunction,
58  validateString,
59  validateLinkHeaderValue,
60  validateObject,
61} = require('internal/validators');
62const {
63  kSocket,
64  kRequest,
65  kProxySocket,
66  assertValidPseudoHeader,
67  getAuthority,
68} = require('internal/http2/util');
69const { _checkIsHttpToken: checkIsHttpToken } = require('_http_common');
70
71const kBeginSend = Symbol('begin-send');
72const kState = Symbol('state');
73const kStream = Symbol('stream');
74const kResponse = Symbol('response');
75const kHeaders = Symbol('headers');
76const kRawHeaders = Symbol('rawHeaders');
77const kTrailers = Symbol('trailers');
78const kRawTrailers = Symbol('rawTrailers');
79const kSetHeader = Symbol('setHeader');
80const kAborted = Symbol('aborted');
81
82let statusMessageWarned = false;
83let statusConnectionHeaderWarned = false;
84
85// Defines and implements an API compatibility layer on top of the core
86// HTTP/2 implementation, intended to provide an interface that is as
87// close as possible to the current require('http') API
88
89const assertValidHeader = hideStackFrames((name, value) => {
90  if (name === '' ||
91      typeof name !== 'string' ||
92      StringPrototypeIncludes(name, ' ')) {
93    throw new ERR_INVALID_HTTP_TOKEN('Header name', name);
94  }
95  if (isPseudoHeader(name)) {
96    throw new ERR_HTTP2_PSEUDOHEADER_NOT_ALLOWED();
97  }
98  if (value === undefined || value === null) {
99    throw new ERR_HTTP2_INVALID_HEADER_VALUE(value, name);
100  }
101  if (!isConnectionHeaderAllowed(name, value)) {
102    connectionHeaderMessageWarn();
103  }
104});
105
106function isPseudoHeader(name) {
107  switch (name) {
108    case HTTP2_HEADER_STATUS:    // :status
109    case HTTP2_HEADER_METHOD:    // :method
110    case HTTP2_HEADER_PATH:      // :path
111    case HTTP2_HEADER_AUTHORITY: // :authority
112    case HTTP2_HEADER_SCHEME:    // :scheme
113      return true;
114    default:
115      return false;
116  }
117}
118
119function statusMessageWarn() {
120  if (statusMessageWarned === false) {
121    process.emitWarning(
122      'Status message is not supported by HTTP/2 (RFC7540 8.1.2.4)',
123      'UnsupportedWarning',
124    );
125    statusMessageWarned = true;
126  }
127}
128
129function isConnectionHeaderAllowed(name, value) {
130  return name !== HTTP2_HEADER_CONNECTION ||
131         value === 'trailers';
132}
133
134function connectionHeaderMessageWarn() {
135  if (statusConnectionHeaderWarned === false) {
136    process.emitWarning(
137      'The provided connection header is not valid, ' +
138      'the value will be dropped from the header and ' +
139      'will never be in use.',
140      'UnsupportedWarning',
141    );
142    statusConnectionHeaderWarned = true;
143  }
144}
145
146function onStreamData(chunk) {
147  const request = this[kRequest];
148  if (request !== undefined && !request.push(chunk))
149    this.pause();
150}
151
152function onStreamTrailers(trailers, flags, rawTrailers) {
153  const request = this[kRequest];
154  if (request !== undefined) {
155    ObjectAssign(request[kTrailers], trailers);
156    ArrayPrototypePush(request[kRawTrailers],
157                       ...new SafeArrayIterator(rawTrailers));
158  }
159}
160
161function onStreamEnd() {
162  // Cause the request stream to end as well.
163  const request = this[kRequest];
164  if (request !== undefined)
165    this[kRequest].push(null);
166}
167
168function onStreamError(error) {
169  // This is purposefully left blank
170  //
171  // errors in compatibility mode are
172  // not forwarded to the request
173  // and response objects.
174}
175
176function onRequestPause() {
177  this[kStream].pause();
178}
179
180function onRequestResume() {
181  this[kStream].resume();
182}
183
184function onStreamDrain() {
185  const response = this[kResponse];
186  if (response !== undefined)
187    response.emit('drain');
188}
189
190function onStreamAbortedRequest() {
191  const request = this[kRequest];
192  if (request !== undefined && request[kState].closed === false) {
193    request[kAborted] = true;
194    request.emit('aborted');
195  }
196}
197
198function onStreamAbortedResponse() {
199  // non-op for now
200}
201
202function resumeStream(stream) {
203  stream.resume();
204}
205
206const proxySocketHandler = {
207  has(stream, prop) {
208    const ref = stream.session !== undefined ? stream.session[kSocket] : stream;
209    return (prop in stream) || (prop in ref);
210  },
211
212  get(stream, prop) {
213    switch (prop) {
214      case 'on':
215      case 'once':
216      case 'end':
217      case 'emit':
218      case 'destroy':
219        return FunctionPrototypeBind(stream[prop], stream);
220      case 'writable':
221      case 'destroyed':
222        return stream[prop];
223      case 'readable': {
224        if (stream.destroyed)
225          return false;
226        const request = stream[kRequest];
227        return request ? request.readable : stream.readable;
228      }
229      case 'setTimeout': {
230        const session = stream.session;
231        if (session !== undefined)
232          return FunctionPrototypeBind(session.setTimeout, session);
233        return FunctionPrototypeBind(stream.setTimeout, stream);
234      }
235      case 'write':
236      case 'read':
237      case 'pause':
238      case 'resume':
239        throw new ERR_HTTP2_NO_SOCKET_MANIPULATION();
240      default: {
241        const ref = stream.session !== undefined ?
242          stream.session[kSocket] : stream;
243        const value = ref[prop];
244        return typeof value === 'function' ?
245          FunctionPrototypeBind(value, ref) :
246          value;
247      }
248    }
249  },
250  getPrototypeOf(stream) {
251    if (stream.session !== undefined)
252      return ReflectGetPrototypeOf(stream.session[kSocket]);
253    return ReflectGetPrototypeOf(stream);
254  },
255  set(stream, prop, value) {
256    switch (prop) {
257      case 'writable':
258      case 'readable':
259      case 'destroyed':
260      case 'on':
261      case 'once':
262      case 'end':
263      case 'emit':
264      case 'destroy':
265        stream[prop] = value;
266        return true;
267      case 'setTimeout': {
268        const session = stream.session;
269        if (session !== undefined)
270          session.setTimeout = value;
271        else
272          stream.setTimeout = value;
273        return true;
274      }
275      case 'write':
276      case 'read':
277      case 'pause':
278      case 'resume':
279        throw new ERR_HTTP2_NO_SOCKET_MANIPULATION();
280      default: {
281        const ref = stream.session !== undefined ?
282          stream.session[kSocket] : stream;
283        ref[prop] = value;
284        return true;
285      }
286    }
287  },
288};
289
290function onStreamCloseRequest() {
291  const req = this[kRequest];
292
293  if (req === undefined)
294    return;
295
296  const state = req[kState];
297  state.closed = true;
298
299  req.push(null);
300  // If the user didn't interact with incoming data and didn't pipe it,
301  // dump it for compatibility with http1
302  if (!state.didRead && !req._readableState.resumeScheduled)
303    req.resume();
304
305  this[kProxySocket] = null;
306  this[kRequest] = undefined;
307
308  req.emit('close');
309}
310
311function onStreamTimeout(kind) {
312  return function onStreamTimeout() {
313    const obj = this[kind];
314    obj.emit('timeout');
315  };
316}
317
318class Http2ServerRequest extends Readable {
319  constructor(stream, headers, options, rawHeaders) {
320    super({ autoDestroy: false, ...options });
321    this[kState] = {
322      closed: false,
323      didRead: false,
324    };
325    // Headers in HTTP/1 are not initialized using Object.create(null) which,
326    // although preferable, would simply break too much code. Ergo header
327    // initialization using Object.create(null) in HTTP/2 is intentional.
328    this[kHeaders] = headers;
329    this[kRawHeaders] = rawHeaders;
330    this[kTrailers] = {};
331    this[kRawTrailers] = [];
332    this[kStream] = stream;
333    this[kAborted] = false;
334    stream[kProxySocket] = null;
335    stream[kRequest] = this;
336
337    // Pause the stream..
338    stream.on('trailers', onStreamTrailers);
339    stream.on('end', onStreamEnd);
340    stream.on('error', onStreamError);
341    stream.on('aborted', onStreamAbortedRequest);
342    stream.on('close', onStreamCloseRequest);
343    stream.on('timeout', onStreamTimeout(kRequest));
344    this.on('pause', onRequestPause);
345    this.on('resume', onRequestResume);
346  }
347
348  get aborted() {
349    return this[kAborted];
350  }
351
352  get complete() {
353    return this[kAborted] ||
354           this.readableEnded ||
355           this[kState].closed ||
356           this[kStream].destroyed;
357  }
358
359  get stream() {
360    return this[kStream];
361  }
362
363  get headers() {
364    return this[kHeaders];
365  }
366
367  get rawHeaders() {
368    return this[kRawHeaders];
369  }
370
371  get trailers() {
372    return this[kTrailers];
373  }
374
375  get rawTrailers() {
376    return this[kRawTrailers];
377  }
378
379  get httpVersionMajor() {
380    return 2;
381  }
382
383  get httpVersionMinor() {
384    return 0;
385  }
386
387  get httpVersion() {
388    return '2.0';
389  }
390
391  get socket() {
392    const stream = this[kStream];
393    const proxySocket = stream[kProxySocket];
394    if (proxySocket === null)
395      return stream[kProxySocket] = new Proxy(stream, proxySocketHandler);
396    return proxySocket;
397  }
398
399  get connection() {
400    return this.socket;
401  }
402
403  _read(nread) {
404    const state = this[kState];
405    assert(!state.closed);
406    if (!state.didRead) {
407      state.didRead = true;
408      this[kStream].on('data', onStreamData);
409    } else {
410      process.nextTick(resumeStream, this[kStream]);
411    }
412  }
413
414  get method() {
415    return this[kHeaders][HTTP2_HEADER_METHOD];
416  }
417
418  set method(method) {
419    validateString(method, 'method');
420    if (StringPrototypeTrim(method) === '')
421      throw new ERR_INVALID_ARG_VALUE('method', method);
422
423    this[kHeaders][HTTP2_HEADER_METHOD] = method;
424  }
425
426  get authority() {
427    return getAuthority(this[kHeaders]);
428  }
429
430  get scheme() {
431    return this[kHeaders][HTTP2_HEADER_SCHEME];
432  }
433
434  get url() {
435    return this[kHeaders][HTTP2_HEADER_PATH];
436  }
437
438  set url(url) {
439    this[kHeaders][HTTP2_HEADER_PATH] = url;
440  }
441
442  setTimeout(msecs, callback) {
443    if (!this[kState].closed)
444      this[kStream].setTimeout(msecs, callback);
445    return this;
446  }
447}
448
449function onStreamTrailersReady() {
450  this.sendTrailers(this[kResponse][kTrailers]);
451}
452
453function onStreamCloseResponse() {
454  const res = this[kResponse];
455
456  if (res === undefined)
457    return;
458
459  const state = res[kState];
460
461  if (this.headRequest !== state.headRequest)
462    return;
463
464  state.closed = true;
465
466  this[kProxySocket] = null;
467
468  this.removeListener('wantTrailers', onStreamTrailersReady);
469  this[kResponse] = undefined;
470
471  res.emit('finish');
472  res.emit('close');
473}
474
475class Http2ServerResponse extends Stream {
476  constructor(stream, options) {
477    super(options);
478    this[kState] = {
479      closed: false,
480      ending: false,
481      destroyed: false,
482      headRequest: false,
483      sendDate: true,
484      statusCode: HTTP_STATUS_OK,
485    };
486    this[kHeaders] = ObjectCreate(null);
487    this[kTrailers] = ObjectCreate(null);
488    this[kStream] = stream;
489    stream[kProxySocket] = null;
490    stream[kResponse] = this;
491    this.writable = true;
492    this.req = stream[kRequest];
493    stream.on('drain', onStreamDrain);
494    stream.on('aborted', onStreamAbortedResponse);
495    stream.on('close', onStreamCloseResponse);
496    stream.on('wantTrailers', onStreamTrailersReady);
497    stream.on('timeout', onStreamTimeout(kResponse));
498  }
499
500  // User land modules such as finalhandler just check truthiness of this
501  // but if someone is actually trying to use this for more than that
502  // then we simply can't support such use cases
503  get _header() {
504    return this.headersSent;
505  }
506
507  get writableEnded() {
508    const state = this[kState];
509    return state.ending;
510  }
511
512  get finished() {
513    const state = this[kState];
514    return state.ending;
515  }
516
517  get socket() {
518    // This is compatible with http1 which removes socket reference
519    // only from ServerResponse but not IncomingMessage
520    if (this[kState].closed)
521      return undefined;
522
523    const stream = this[kStream];
524    const proxySocket = stream[kProxySocket];
525    if (proxySocket === null)
526      return stream[kProxySocket] = new Proxy(stream, proxySocketHandler);
527    return proxySocket;
528  }
529
530  get connection() {
531    return this.socket;
532  }
533
534  get stream() {
535    return this[kStream];
536  }
537
538  get headersSent() {
539    return this[kStream].headersSent;
540  }
541
542  get sendDate() {
543    return this[kState].sendDate;
544  }
545
546  set sendDate(bool) {
547    this[kState].sendDate = Boolean(bool);
548  }
549
550  get statusCode() {
551    return this[kState].statusCode;
552  }
553
554  get writableCorked() {
555    return this[kStream].writableCorked;
556  }
557
558  get writableHighWaterMark() {
559    return this[kStream].writableHighWaterMark;
560  }
561
562  get writableFinished() {
563    return this[kStream].writableFinished;
564  }
565
566  get writableLength() {
567    return this[kStream].writableLength;
568  }
569
570  set statusCode(code) {
571    code |= 0;
572    if (code >= 100 && code < 200)
573      throw new ERR_HTTP2_INFO_STATUS_NOT_ALLOWED();
574    if (code < 100 || code > 599)
575      throw new ERR_HTTP2_STATUS_INVALID(code);
576    this[kState].statusCode = code;
577  }
578
579  setTrailer(name, value) {
580    validateString(name, 'name');
581    name = StringPrototypeToLowerCase(StringPrototypeTrim(name));
582    assertValidHeader(name, value);
583    this[kTrailers][name] = value;
584  }
585
586  addTrailers(headers) {
587    const keys = ObjectKeys(headers);
588    let key = '';
589    for (let i = 0; i < keys.length; i++) {
590      key = keys[i];
591      this.setTrailer(key, headers[key]);
592    }
593  }
594
595  getHeader(name) {
596    validateString(name, 'name');
597    name = StringPrototypeToLowerCase(StringPrototypeTrim(name));
598    return this[kHeaders][name];
599  }
600
601  getHeaderNames() {
602    return ObjectKeys(this[kHeaders]);
603  }
604
605  getHeaders() {
606    const headers = ObjectCreate(null);
607    return ObjectAssign(headers, this[kHeaders]);
608  }
609
610  hasHeader(name) {
611    validateString(name, 'name');
612    name = StringPrototypeToLowerCase(StringPrototypeTrim(name));
613    return ObjectPrototypeHasOwnProperty(this[kHeaders], name);
614  }
615
616  removeHeader(name) {
617    validateString(name, 'name');
618    if (this[kStream].headersSent)
619      throw new ERR_HTTP2_HEADERS_SENT();
620
621    name = StringPrototypeToLowerCase(StringPrototypeTrim(name));
622
623    if (name === 'date') {
624      this[kState].sendDate = false;
625
626      return;
627    }
628
629    delete this[kHeaders][name];
630  }
631
632  setHeader(name, value) {
633    validateString(name, 'name');
634    if (this[kStream].headersSent)
635      throw new ERR_HTTP2_HEADERS_SENT();
636
637    this[kSetHeader](name, value);
638  }
639
640  [kSetHeader](name, value) {
641    name = StringPrototypeToLowerCase(StringPrototypeTrim(name));
642    assertValidHeader(name, value);
643
644    if (!isConnectionHeaderAllowed(name, value)) {
645      return;
646    }
647
648    if (name[0] === ':')
649      assertValidPseudoHeader(name);
650    else if (!checkIsHttpToken(name))
651      this.destroy(new ERR_INVALID_HTTP_TOKEN('Header name', name));
652
653    this[kHeaders][name] = value;
654  }
655
656  get statusMessage() {
657    statusMessageWarn();
658
659    return '';
660  }
661
662  set statusMessage(msg) {
663    statusMessageWarn();
664  }
665
666  flushHeaders() {
667    const state = this[kState];
668    if (!state.closed && !this[kStream].headersSent)
669      this.writeHead(state.statusCode);
670  }
671
672  writeHead(statusCode, statusMessage, headers) {
673    const state = this[kState];
674
675    if (state.closed || this.stream.destroyed)
676      return this;
677    if (this[kStream].headersSent)
678      throw new ERR_HTTP2_HEADERS_SENT();
679
680    if (typeof statusMessage === 'string')
681      statusMessageWarn();
682
683    if (headers === undefined && typeof statusMessage === 'object')
684      headers = statusMessage;
685
686    let i;
687    if (ArrayIsArray(headers)) {
688      if (headers.length && ArrayIsArray(headers[0])) {
689        for (i = 0; i < headers.length; i++) {
690          const header = headers[i];
691          this[kSetHeader](header[0], header[1]);
692        }
693      } else {
694        if (headers.length % 2 !== 0) {
695          throw new ERR_INVALID_ARG_VALUE('headers', headers);
696        }
697
698        for (i = 0; i < headers.length; i += 2) {
699          this[kSetHeader](headers[i], headers[i + 1]);
700        }
701      }
702    } else if (typeof headers === 'object') {
703      const keys = ObjectKeys(headers);
704      let key = '';
705      for (i = 0; i < keys.length; i++) {
706        key = keys[i];
707        this[kSetHeader](key, headers[key]);
708      }
709    }
710
711    state.statusCode = statusCode;
712    this[kBeginSend]();
713
714    return this;
715  }
716
717  cork() {
718    this[kStream].cork();
719  }
720
721  uncork() {
722    this[kStream].uncork();
723  }
724
725  write(chunk, encoding, cb) {
726    const state = this[kState];
727
728    if (typeof encoding === 'function') {
729      cb = encoding;
730      encoding = 'utf8';
731    }
732
733    let err;
734    if (state.ending) {
735      err = new ERR_STREAM_WRITE_AFTER_END();
736    } else if (state.closed) {
737      err = new ERR_HTTP2_INVALID_STREAM();
738    } else if (state.destroyed) {
739      return false;
740    }
741
742    if (err) {
743      if (typeof cb === 'function')
744        process.nextTick(cb, err);
745      this.destroy(err);
746      return false;
747    }
748
749    const stream = this[kStream];
750    if (!stream.headersSent)
751      this.writeHead(state.statusCode);
752    return stream.write(chunk, encoding, cb);
753  }
754
755  end(chunk, encoding, cb) {
756    const stream = this[kStream];
757    const state = this[kState];
758
759    if (typeof chunk === 'function') {
760      cb = chunk;
761      chunk = null;
762    } else if (typeof encoding === 'function') {
763      cb = encoding;
764      encoding = 'utf8';
765    }
766
767    if ((state.closed || state.ending) &&
768        state.headRequest === stream.headRequest) {
769      if (typeof cb === 'function') {
770        process.nextTick(cb);
771      }
772      return this;
773    }
774
775    if (chunk !== null && chunk !== undefined)
776      this.write(chunk, encoding);
777
778    state.headRequest = stream.headRequest;
779    state.ending = true;
780
781    if (typeof cb === 'function') {
782      if (stream.writableEnded)
783        this.once('finish', cb);
784      else
785        stream.once('finish', cb);
786    }
787
788    if (!stream.headersSent)
789      this.writeHead(this[kState].statusCode);
790
791    if (this[kState].closed || stream.destroyed)
792      ReflectApply(onStreamCloseResponse, stream, []);
793    else
794      stream.end();
795
796    return this;
797  }
798
799  destroy(err) {
800    if (this[kState].destroyed)
801      return;
802
803    this[kState].destroyed = true;
804    this[kStream].destroy(err);
805  }
806
807  setTimeout(msecs, callback) {
808    if (this[kState].closed)
809      return;
810    this[kStream].setTimeout(msecs, callback);
811  }
812
813  createPushResponse(headers, callback) {
814    validateFunction(callback, 'callback');
815    if (this[kState].closed) {
816      process.nextTick(callback, new ERR_HTTP2_INVALID_STREAM());
817      return;
818    }
819    this[kStream].pushStream(headers, {}, (err, stream, headers, options) => {
820      if (err) {
821        callback(err);
822        return;
823      }
824      callback(null, new Http2ServerResponse(stream));
825    });
826  }
827
828  [kBeginSend]() {
829    const state = this[kState];
830    const headers = this[kHeaders];
831    headers[HTTP2_HEADER_STATUS] = state.statusCode;
832    const options = {
833      endStream: state.ending,
834      waitForTrailers: true,
835      sendDate: state.sendDate,
836    };
837    this[kStream].respond(headers, options);
838  }
839
840  // TODO doesn't support callbacks
841  writeContinue() {
842    const stream = this[kStream];
843    if (stream.headersSent || this[kState].closed)
844      return false;
845    stream.additionalHeaders({
846      [HTTP2_HEADER_STATUS]: HTTP_STATUS_CONTINUE,
847    });
848    return true;
849  }
850
851  writeEarlyHints(hints) {
852    validateObject(hints, 'hints');
853
854    const headers = ObjectCreate(null);
855
856    const linkHeaderValue = validateLinkHeaderValue(hints.link);
857
858    for (const key of ObjectKeys(hints)) {
859      if (key !== 'link') {
860        headers[key] = hints[key];
861      }
862    }
863
864    if (linkHeaderValue.length === 0) {
865      return false;
866    }
867
868    const stream = this[kStream];
869
870    if (stream.headersSent || this[kState].closed)
871      return false;
872
873    stream.additionalHeaders({
874      ...headers,
875      [HTTP2_HEADER_STATUS]: HTTP_STATUS_EARLY_HINTS,
876      'Link': linkHeaderValue,
877    });
878
879    return true;
880  }
881}
882
883function onServerStream(ServerRequest, ServerResponse,
884                        stream, headers, flags, rawHeaders) {
885  const server = this;
886  const request = new ServerRequest(stream, headers, undefined, rawHeaders);
887  const response = new ServerResponse(stream);
888
889  // Check for the CONNECT method
890  const method = headers[HTTP2_HEADER_METHOD];
891  if (method === 'CONNECT') {
892    if (!server.emit('connect', request, response)) {
893      response.statusCode = HTTP_STATUS_METHOD_NOT_ALLOWED;
894      response.end();
895    }
896    return;
897  }
898
899  // Check for Expectations
900  if (headers.expect !== undefined) {
901    if (headers.expect === '100-continue') {
902      if (server.listenerCount('checkContinue')) {
903        server.emit('checkContinue', request, response);
904      } else {
905        response.writeContinue();
906        server.emit('request', request, response);
907      }
908    } else if (server.listenerCount('checkExpectation')) {
909      server.emit('checkExpectation', request, response);
910    } else {
911      response.statusCode = HTTP_STATUS_EXPECTATION_FAILED;
912      response.end();
913    }
914    return;
915  }
916
917  server.emit('request', request, response);
918}
919
920module.exports = {
921  onServerStream,
922  Http2ServerRequest,
923  Http2ServerResponse,
924};
925