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