11cb0ef41Sopenharmony_ci// Copyright Joyent, Inc. and other Node contributors.
21cb0ef41Sopenharmony_ci//
31cb0ef41Sopenharmony_ci// Permission is hereby granted, free of charge, to any person obtaining a
41cb0ef41Sopenharmony_ci// copy of this software and associated documentation files (the
51cb0ef41Sopenharmony_ci// "Software"), to deal in the Software without restriction, including
61cb0ef41Sopenharmony_ci// without limitation the rights to use, copy, modify, merge, publish,
71cb0ef41Sopenharmony_ci// distribute, sublicense, and/or sell copies of the Software, and to permit
81cb0ef41Sopenharmony_ci// persons to whom the Software is furnished to do so, subject to the
91cb0ef41Sopenharmony_ci// following conditions:
101cb0ef41Sopenharmony_ci//
111cb0ef41Sopenharmony_ci// The above copyright notice and this permission notice shall be included
121cb0ef41Sopenharmony_ci// in all copies or substantial portions of the Software.
131cb0ef41Sopenharmony_ci//
141cb0ef41Sopenharmony_ci// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
151cb0ef41Sopenharmony_ci// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
161cb0ef41Sopenharmony_ci// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
171cb0ef41Sopenharmony_ci// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
181cb0ef41Sopenharmony_ci// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
191cb0ef41Sopenharmony_ci// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
201cb0ef41Sopenharmony_ci// USE OR OTHER DEALINGS IN THE SOFTWARE.
211cb0ef41Sopenharmony_ci
221cb0ef41Sopenharmony_ci'use strict';
231cb0ef41Sopenharmony_ci
241cb0ef41Sopenharmony_ciconst {
251cb0ef41Sopenharmony_ci  MathMin,
261cb0ef41Sopenharmony_ci  Symbol,
271cb0ef41Sopenharmony_ci  RegExpPrototypeExec,
281cb0ef41Sopenharmony_ci} = primordials;
291cb0ef41Sopenharmony_ciconst { setImmediate } = require('timers');
301cb0ef41Sopenharmony_ci
311cb0ef41Sopenharmony_ciconst { methods, HTTPParser } = internalBinding('http_parser');
321cb0ef41Sopenharmony_ciconst { getOptionValue } = require('internal/options');
331cb0ef41Sopenharmony_ciconst insecureHTTPParser = getOptionValue('--insecure-http-parser');
341cb0ef41Sopenharmony_ci
351cb0ef41Sopenharmony_ciconst FreeList = require('internal/freelist');
361cb0ef41Sopenharmony_ciconst incoming = require('_http_incoming');
371cb0ef41Sopenharmony_ciconst {
381cb0ef41Sopenharmony_ci  IncomingMessage,
391cb0ef41Sopenharmony_ci  readStart,
401cb0ef41Sopenharmony_ci  readStop,
411cb0ef41Sopenharmony_ci} = incoming;
421cb0ef41Sopenharmony_ci
431cb0ef41Sopenharmony_ciconst kIncomingMessage = Symbol('IncomingMessage');
441cb0ef41Sopenharmony_ciconst kOnMessageBegin = HTTPParser.kOnMessageBegin | 0;
451cb0ef41Sopenharmony_ciconst kOnHeaders = HTTPParser.kOnHeaders | 0;
461cb0ef41Sopenharmony_ciconst kOnHeadersComplete = HTTPParser.kOnHeadersComplete | 0;
471cb0ef41Sopenharmony_ciconst kOnBody = HTTPParser.kOnBody | 0;
481cb0ef41Sopenharmony_ciconst kOnMessageComplete = HTTPParser.kOnMessageComplete | 0;
491cb0ef41Sopenharmony_ciconst kOnExecute = HTTPParser.kOnExecute | 0;
501cb0ef41Sopenharmony_ciconst kOnTimeout = HTTPParser.kOnTimeout | 0;
511cb0ef41Sopenharmony_ci
521cb0ef41Sopenharmony_ciconst MAX_HEADER_PAIRS = 2000;
531cb0ef41Sopenharmony_ci
541cb0ef41Sopenharmony_ci// Only called in the slow case where slow means
551cb0ef41Sopenharmony_ci// that the request headers were either fragmented
561cb0ef41Sopenharmony_ci// across multiple TCP packets or too large to be
571cb0ef41Sopenharmony_ci// processed in a single run. This method is also
581cb0ef41Sopenharmony_ci// called to process trailing HTTP headers.
591cb0ef41Sopenharmony_cifunction parserOnHeaders(headers, url) {
601cb0ef41Sopenharmony_ci  // Once we exceeded headers limit - stop collecting them
611cb0ef41Sopenharmony_ci  if (this.maxHeaderPairs <= 0 ||
621cb0ef41Sopenharmony_ci      this._headers.length < this.maxHeaderPairs) {
631cb0ef41Sopenharmony_ci    this._headers.push(...headers);
641cb0ef41Sopenharmony_ci  }
651cb0ef41Sopenharmony_ci  this._url += url;
661cb0ef41Sopenharmony_ci}
671cb0ef41Sopenharmony_ci
681cb0ef41Sopenharmony_ci// `headers` and `url` are set only if .onHeaders() has not been called for
691cb0ef41Sopenharmony_ci// this request.
701cb0ef41Sopenharmony_ci// `url` is not set for response parsers but that's not applicable here since
711cb0ef41Sopenharmony_ci// all our parsers are request parsers.
721cb0ef41Sopenharmony_cifunction parserOnHeadersComplete(versionMajor, versionMinor, headers, method,
731cb0ef41Sopenharmony_ci                                 url, statusCode, statusMessage, upgrade,
741cb0ef41Sopenharmony_ci                                 shouldKeepAlive) {
751cb0ef41Sopenharmony_ci  const parser = this;
761cb0ef41Sopenharmony_ci  const { socket } = parser;
771cb0ef41Sopenharmony_ci
781cb0ef41Sopenharmony_ci  if (headers === undefined) {
791cb0ef41Sopenharmony_ci    headers = parser._headers;
801cb0ef41Sopenharmony_ci    parser._headers = [];
811cb0ef41Sopenharmony_ci  }
821cb0ef41Sopenharmony_ci
831cb0ef41Sopenharmony_ci  if (url === undefined) {
841cb0ef41Sopenharmony_ci    url = parser._url;
851cb0ef41Sopenharmony_ci    parser._url = '';
861cb0ef41Sopenharmony_ci  }
871cb0ef41Sopenharmony_ci
881cb0ef41Sopenharmony_ci  // Parser is also used by http client
891cb0ef41Sopenharmony_ci  const ParserIncomingMessage = (socket && socket.server &&
901cb0ef41Sopenharmony_ci                                 socket.server[kIncomingMessage]) ||
911cb0ef41Sopenharmony_ci                                 IncomingMessage;
921cb0ef41Sopenharmony_ci
931cb0ef41Sopenharmony_ci  const incoming = parser.incoming = new ParserIncomingMessage(socket);
941cb0ef41Sopenharmony_ci  incoming.httpVersionMajor = versionMajor;
951cb0ef41Sopenharmony_ci  incoming.httpVersionMinor = versionMinor;
961cb0ef41Sopenharmony_ci  incoming.httpVersion = `${versionMajor}.${versionMinor}`;
971cb0ef41Sopenharmony_ci  incoming.joinDuplicateHeaders = socket?.server?.joinDuplicateHeaders ||
981cb0ef41Sopenharmony_ci                                  parser.joinDuplicateHeaders;
991cb0ef41Sopenharmony_ci  incoming.url = url;
1001cb0ef41Sopenharmony_ci  incoming.upgrade = upgrade;
1011cb0ef41Sopenharmony_ci
1021cb0ef41Sopenharmony_ci  let n = headers.length;
1031cb0ef41Sopenharmony_ci
1041cb0ef41Sopenharmony_ci  // If parser.maxHeaderPairs <= 0 assume that there's no limit.
1051cb0ef41Sopenharmony_ci  if (parser.maxHeaderPairs > 0)
1061cb0ef41Sopenharmony_ci    n = MathMin(n, parser.maxHeaderPairs);
1071cb0ef41Sopenharmony_ci
1081cb0ef41Sopenharmony_ci  incoming._addHeaderLines(headers, n);
1091cb0ef41Sopenharmony_ci
1101cb0ef41Sopenharmony_ci  if (typeof method === 'number') {
1111cb0ef41Sopenharmony_ci    // server only
1121cb0ef41Sopenharmony_ci    incoming.method = methods[method];
1131cb0ef41Sopenharmony_ci  } else {
1141cb0ef41Sopenharmony_ci    // client only
1151cb0ef41Sopenharmony_ci    incoming.statusCode = statusCode;
1161cb0ef41Sopenharmony_ci    incoming.statusMessage = statusMessage;
1171cb0ef41Sopenharmony_ci  }
1181cb0ef41Sopenharmony_ci
1191cb0ef41Sopenharmony_ci  return parser.onIncoming(incoming, shouldKeepAlive);
1201cb0ef41Sopenharmony_ci}
1211cb0ef41Sopenharmony_ci
1221cb0ef41Sopenharmony_cifunction parserOnBody(b) {
1231cb0ef41Sopenharmony_ci  const stream = this.incoming;
1241cb0ef41Sopenharmony_ci
1251cb0ef41Sopenharmony_ci  // If the stream has already been removed, then drop it.
1261cb0ef41Sopenharmony_ci  if (stream === null)
1271cb0ef41Sopenharmony_ci    return;
1281cb0ef41Sopenharmony_ci
1291cb0ef41Sopenharmony_ci  // Pretend this was the result of a stream._read call.
1301cb0ef41Sopenharmony_ci  if (!stream._dumped) {
1311cb0ef41Sopenharmony_ci    const ret = stream.push(b);
1321cb0ef41Sopenharmony_ci    if (!ret)
1331cb0ef41Sopenharmony_ci      readStop(this.socket);
1341cb0ef41Sopenharmony_ci  }
1351cb0ef41Sopenharmony_ci}
1361cb0ef41Sopenharmony_ci
1371cb0ef41Sopenharmony_cifunction parserOnMessageComplete() {
1381cb0ef41Sopenharmony_ci  const parser = this;
1391cb0ef41Sopenharmony_ci  const stream = parser.incoming;
1401cb0ef41Sopenharmony_ci
1411cb0ef41Sopenharmony_ci  if (stream !== null) {
1421cb0ef41Sopenharmony_ci    stream.complete = true;
1431cb0ef41Sopenharmony_ci    // Emit any trailing headers.
1441cb0ef41Sopenharmony_ci    const headers = parser._headers;
1451cb0ef41Sopenharmony_ci    if (headers.length) {
1461cb0ef41Sopenharmony_ci      stream._addHeaderLines(headers, headers.length);
1471cb0ef41Sopenharmony_ci      parser._headers = [];
1481cb0ef41Sopenharmony_ci      parser._url = '';
1491cb0ef41Sopenharmony_ci    }
1501cb0ef41Sopenharmony_ci
1511cb0ef41Sopenharmony_ci    // For emit end event
1521cb0ef41Sopenharmony_ci    stream.push(null);
1531cb0ef41Sopenharmony_ci  }
1541cb0ef41Sopenharmony_ci
1551cb0ef41Sopenharmony_ci  // Force to read the next incoming message
1561cb0ef41Sopenharmony_ci  readStart(parser.socket);
1571cb0ef41Sopenharmony_ci}
1581cb0ef41Sopenharmony_ci
1591cb0ef41Sopenharmony_ci
1601cb0ef41Sopenharmony_ciconst parsers = new FreeList('parsers', 1000, function parsersCb() {
1611cb0ef41Sopenharmony_ci  const parser = new HTTPParser();
1621cb0ef41Sopenharmony_ci
1631cb0ef41Sopenharmony_ci  cleanParser(parser);
1641cb0ef41Sopenharmony_ci
1651cb0ef41Sopenharmony_ci  parser[kOnHeaders] = parserOnHeaders;
1661cb0ef41Sopenharmony_ci  parser[kOnHeadersComplete] = parserOnHeadersComplete;
1671cb0ef41Sopenharmony_ci  parser[kOnBody] = parserOnBody;
1681cb0ef41Sopenharmony_ci  parser[kOnMessageComplete] = parserOnMessageComplete;
1691cb0ef41Sopenharmony_ci
1701cb0ef41Sopenharmony_ci  return parser;
1711cb0ef41Sopenharmony_ci});
1721cb0ef41Sopenharmony_ci
1731cb0ef41Sopenharmony_cifunction closeParserInstance(parser) { parser.close(); }
1741cb0ef41Sopenharmony_ci
1751cb0ef41Sopenharmony_ci// Free the parser and also break any links that it
1761cb0ef41Sopenharmony_ci// might have to any other things.
1771cb0ef41Sopenharmony_ci// TODO: All parser data should be attached to a
1781cb0ef41Sopenharmony_ci// single object, so that it can be easily cleaned
1791cb0ef41Sopenharmony_ci// up by doing `parser.data = {}`, which should
1801cb0ef41Sopenharmony_ci// be done in FreeList.free.  `parsers.free(parser)`
1811cb0ef41Sopenharmony_ci// should be all that is needed.
1821cb0ef41Sopenharmony_cifunction freeParser(parser, req, socket) {
1831cb0ef41Sopenharmony_ci  if (parser) {
1841cb0ef41Sopenharmony_ci    if (parser._consumed)
1851cb0ef41Sopenharmony_ci      parser.unconsume();
1861cb0ef41Sopenharmony_ci    cleanParser(parser);
1871cb0ef41Sopenharmony_ci    parser.remove();
1881cb0ef41Sopenharmony_ci    if (parsers.free(parser) === false) {
1891cb0ef41Sopenharmony_ci      // Make sure the parser's stack has unwound before deleting the
1901cb0ef41Sopenharmony_ci      // corresponding C++ object through .close().
1911cb0ef41Sopenharmony_ci      setImmediate(closeParserInstance, parser);
1921cb0ef41Sopenharmony_ci    } else {
1931cb0ef41Sopenharmony_ci      // Since the Parser destructor isn't going to run the destroy() callbacks
1941cb0ef41Sopenharmony_ci      // it needs to be triggered manually.
1951cb0ef41Sopenharmony_ci      parser.free();
1961cb0ef41Sopenharmony_ci    }
1971cb0ef41Sopenharmony_ci  }
1981cb0ef41Sopenharmony_ci  if (req) {
1991cb0ef41Sopenharmony_ci    req.parser = null;
2001cb0ef41Sopenharmony_ci  }
2011cb0ef41Sopenharmony_ci  if (socket) {
2021cb0ef41Sopenharmony_ci    socket.parser = null;
2031cb0ef41Sopenharmony_ci  }
2041cb0ef41Sopenharmony_ci}
2051cb0ef41Sopenharmony_ci
2061cb0ef41Sopenharmony_ciconst tokenRegExp = /^[\^_`a-zA-Z\-0-9!#$%&'*+.|~]+$/;
2071cb0ef41Sopenharmony_ci/**
2081cb0ef41Sopenharmony_ci * Verifies that the given val is a valid HTTP token
2091cb0ef41Sopenharmony_ci * per the rules defined in RFC 7230
2101cb0ef41Sopenharmony_ci * See https://tools.ietf.org/html/rfc7230#section-3.2.6
2111cb0ef41Sopenharmony_ci */
2121cb0ef41Sopenharmony_cifunction checkIsHttpToken(val) {
2131cb0ef41Sopenharmony_ci  return RegExpPrototypeExec(tokenRegExp, val) !== null;
2141cb0ef41Sopenharmony_ci}
2151cb0ef41Sopenharmony_ci
2161cb0ef41Sopenharmony_ciconst headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/;
2171cb0ef41Sopenharmony_ci/**
2181cb0ef41Sopenharmony_ci * True if val contains an invalid field-vchar
2191cb0ef41Sopenharmony_ci *  field-value    = *( field-content / obs-fold )
2201cb0ef41Sopenharmony_ci *  field-content  = field-vchar [ 1*( SP / HTAB ) field-vchar ]
2211cb0ef41Sopenharmony_ci *  field-vchar    = VCHAR / obs-text
2221cb0ef41Sopenharmony_ci */
2231cb0ef41Sopenharmony_cifunction checkInvalidHeaderChar(val) {
2241cb0ef41Sopenharmony_ci  return RegExpPrototypeExec(headerCharRegex, val) !== null;
2251cb0ef41Sopenharmony_ci}
2261cb0ef41Sopenharmony_ci
2271cb0ef41Sopenharmony_cifunction cleanParser(parser) {
2281cb0ef41Sopenharmony_ci  parser._headers = [];
2291cb0ef41Sopenharmony_ci  parser._url = '';
2301cb0ef41Sopenharmony_ci  parser.socket = null;
2311cb0ef41Sopenharmony_ci  parser.incoming = null;
2321cb0ef41Sopenharmony_ci  parser.outgoing = null;
2331cb0ef41Sopenharmony_ci  parser.maxHeaderPairs = MAX_HEADER_PAIRS;
2341cb0ef41Sopenharmony_ci  parser[kOnMessageBegin] = null;
2351cb0ef41Sopenharmony_ci  parser[kOnExecute] = null;
2361cb0ef41Sopenharmony_ci  parser[kOnTimeout] = null;
2371cb0ef41Sopenharmony_ci  parser._consumed = false;
2381cb0ef41Sopenharmony_ci  parser.onIncoming = null;
2391cb0ef41Sopenharmony_ci  parser.joinDuplicateHeaders = null;
2401cb0ef41Sopenharmony_ci}
2411cb0ef41Sopenharmony_ci
2421cb0ef41Sopenharmony_cifunction prepareError(err, parser, rawPacket) {
2431cb0ef41Sopenharmony_ci  err.rawPacket = rawPacket || parser.getCurrentBuffer();
2441cb0ef41Sopenharmony_ci  if (typeof err.reason === 'string')
2451cb0ef41Sopenharmony_ci    err.message = `Parse Error: ${err.reason}`;
2461cb0ef41Sopenharmony_ci}
2471cb0ef41Sopenharmony_ci
2481cb0ef41Sopenharmony_cilet warnedLenient = false;
2491cb0ef41Sopenharmony_ci
2501cb0ef41Sopenharmony_cifunction isLenient() {
2511cb0ef41Sopenharmony_ci  if (insecureHTTPParser && !warnedLenient) {
2521cb0ef41Sopenharmony_ci    warnedLenient = true;
2531cb0ef41Sopenharmony_ci    process.emitWarning('Using insecure HTTP parsing');
2541cb0ef41Sopenharmony_ci  }
2551cb0ef41Sopenharmony_ci  return insecureHTTPParser;
2561cb0ef41Sopenharmony_ci}
2571cb0ef41Sopenharmony_ci
2581cb0ef41Sopenharmony_cimodule.exports = {
2591cb0ef41Sopenharmony_ci  _checkInvalidHeaderChar: checkInvalidHeaderChar,
2601cb0ef41Sopenharmony_ci  _checkIsHttpToken: checkIsHttpToken,
2611cb0ef41Sopenharmony_ci  chunkExpression: /(?:^|\W)chunked(?:$|\W)/i,
2621cb0ef41Sopenharmony_ci  continueExpression: /(?:^|\W)100-continue(?:$|\W)/i,
2631cb0ef41Sopenharmony_ci  CRLF: '\r\n', // TODO: Deprecate this.
2641cb0ef41Sopenharmony_ci  freeParser,
2651cb0ef41Sopenharmony_ci  methods,
2661cb0ef41Sopenharmony_ci  parsers,
2671cb0ef41Sopenharmony_ci  kIncomingMessage,
2681cb0ef41Sopenharmony_ci  HTTPParser,
2691cb0ef41Sopenharmony_ci  isLenient,
2701cb0ef41Sopenharmony_ci  prepareError,
2711cb0ef41Sopenharmony_ci};
272