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_ciconst { mustCall, mustNotCall } = require('../common');
241cb0ef41Sopenharmony_ciconst assert = require('assert');
251cb0ef41Sopenharmony_ci
261cb0ef41Sopenharmony_ciconst { methods, HTTPParser } = require('_http_common');
271cb0ef41Sopenharmony_ciconst { REQUEST, RESPONSE } = HTTPParser;
281cb0ef41Sopenharmony_ci
291cb0ef41Sopenharmony_ciconst kOnHeaders = HTTPParser.kOnHeaders | 0;
301cb0ef41Sopenharmony_ciconst kOnHeadersComplete = HTTPParser.kOnHeadersComplete | 0;
311cb0ef41Sopenharmony_ciconst kOnBody = HTTPParser.kOnBody | 0;
321cb0ef41Sopenharmony_ciconst kOnMessageComplete = HTTPParser.kOnMessageComplete | 0;
331cb0ef41Sopenharmony_ci
341cb0ef41Sopenharmony_ci// The purpose of this test is not to check HTTP compliance but to test the
351cb0ef41Sopenharmony_ci// binding. Tests for pathological http messages should be submitted
361cb0ef41Sopenharmony_ci// upstream to https://github.com/joyent/http-parser for inclusion into
371cb0ef41Sopenharmony_ci// deps/http-parser/test.c
381cb0ef41Sopenharmony_ci
391cb0ef41Sopenharmony_ci
401cb0ef41Sopenharmony_cifunction newParser(type) {
411cb0ef41Sopenharmony_ci  const parser = new HTTPParser();
421cb0ef41Sopenharmony_ci  parser.initialize(type, {});
431cb0ef41Sopenharmony_ci
441cb0ef41Sopenharmony_ci  parser.headers = [];
451cb0ef41Sopenharmony_ci  parser.url = '';
461cb0ef41Sopenharmony_ci
471cb0ef41Sopenharmony_ci  parser[kOnHeaders] = function(headers, url) {
481cb0ef41Sopenharmony_ci    parser.headers = parser.headers.concat(headers);
491cb0ef41Sopenharmony_ci    parser.url += url;
501cb0ef41Sopenharmony_ci  };
511cb0ef41Sopenharmony_ci
521cb0ef41Sopenharmony_ci  parser[kOnHeadersComplete] = function() {
531cb0ef41Sopenharmony_ci  };
541cb0ef41Sopenharmony_ci
551cb0ef41Sopenharmony_ci  parser[kOnBody] = mustNotCall('kOnBody should not be called');
561cb0ef41Sopenharmony_ci
571cb0ef41Sopenharmony_ci  parser[kOnMessageComplete] = function() {
581cb0ef41Sopenharmony_ci  };
591cb0ef41Sopenharmony_ci
601cb0ef41Sopenharmony_ci  return parser;
611cb0ef41Sopenharmony_ci}
621cb0ef41Sopenharmony_ci
631cb0ef41Sopenharmony_ci
641cb0ef41Sopenharmony_cifunction expectBody(expected) {
651cb0ef41Sopenharmony_ci  return mustCall(function(buf) {
661cb0ef41Sopenharmony_ci    const body = String(buf);
671cb0ef41Sopenharmony_ci    assert.strictEqual(body, expected);
681cb0ef41Sopenharmony_ci  });
691cb0ef41Sopenharmony_ci}
701cb0ef41Sopenharmony_ci
711cb0ef41Sopenharmony_ci
721cb0ef41Sopenharmony_ci//
731cb0ef41Sopenharmony_ci// Simple request test.
741cb0ef41Sopenharmony_ci//
751cb0ef41Sopenharmony_ci{
761cb0ef41Sopenharmony_ci  const request = Buffer.from('GET /hello HTTP/1.1\r\n\r\n');
771cb0ef41Sopenharmony_ci
781cb0ef41Sopenharmony_ci  const onHeadersComplete = (versionMajor, versionMinor, headers,
791cb0ef41Sopenharmony_ci                             method, url) => {
801cb0ef41Sopenharmony_ci    assert.strictEqual(versionMajor, 1);
811cb0ef41Sopenharmony_ci    assert.strictEqual(versionMinor, 1);
821cb0ef41Sopenharmony_ci    assert.strictEqual(method, methods.indexOf('GET'));
831cb0ef41Sopenharmony_ci    assert.strictEqual(url || parser.url, '/hello');
841cb0ef41Sopenharmony_ci  };
851cb0ef41Sopenharmony_ci
861cb0ef41Sopenharmony_ci  const parser = newParser(REQUEST);
871cb0ef41Sopenharmony_ci  parser[kOnHeadersComplete] = mustCall(onHeadersComplete);
881cb0ef41Sopenharmony_ci  parser.execute(request, 0, request.length);
891cb0ef41Sopenharmony_ci
901cb0ef41Sopenharmony_ci  //
911cb0ef41Sopenharmony_ci  // Check that if we throw an error in the callbacks that error will be
921cb0ef41Sopenharmony_ci  // thrown from parser.execute()
931cb0ef41Sopenharmony_ci  //
941cb0ef41Sopenharmony_ci
951cb0ef41Sopenharmony_ci  parser[kOnHeadersComplete] = function() {
961cb0ef41Sopenharmony_ci    throw new Error('hello world');
971cb0ef41Sopenharmony_ci  };
981cb0ef41Sopenharmony_ci
991cb0ef41Sopenharmony_ci  parser.initialize(REQUEST, {});
1001cb0ef41Sopenharmony_ci
1011cb0ef41Sopenharmony_ci  assert.throws(
1021cb0ef41Sopenharmony_ci    () => { parser.execute(request, 0, request.length); },
1031cb0ef41Sopenharmony_ci    { name: 'Error', message: 'hello world' }
1041cb0ef41Sopenharmony_ci  );
1051cb0ef41Sopenharmony_ci}
1061cb0ef41Sopenharmony_ci
1071cb0ef41Sopenharmony_ci
1081cb0ef41Sopenharmony_ci//
1091cb0ef41Sopenharmony_ci// Simple response test.
1101cb0ef41Sopenharmony_ci//
1111cb0ef41Sopenharmony_ci{
1121cb0ef41Sopenharmony_ci  const request = Buffer.from(
1131cb0ef41Sopenharmony_ci    'HTTP/1.1 200 OK\r\n' +
1141cb0ef41Sopenharmony_ci    'Content-Type: text/plain\r\n' +
1151cb0ef41Sopenharmony_ci    'Content-Length: 4\r\n' +
1161cb0ef41Sopenharmony_ci    '\r\n' +
1171cb0ef41Sopenharmony_ci    'pong'
1181cb0ef41Sopenharmony_ci  );
1191cb0ef41Sopenharmony_ci
1201cb0ef41Sopenharmony_ci  const onHeadersComplete = (versionMajor, versionMinor, headers,
1211cb0ef41Sopenharmony_ci                             method, url, statusCode, statusMessage) => {
1221cb0ef41Sopenharmony_ci    assert.strictEqual(method, undefined);
1231cb0ef41Sopenharmony_ci    assert.strictEqual(versionMajor, 1);
1241cb0ef41Sopenharmony_ci    assert.strictEqual(versionMinor, 1);
1251cb0ef41Sopenharmony_ci    assert.strictEqual(statusCode, 200);
1261cb0ef41Sopenharmony_ci    assert.strictEqual(statusMessage, 'OK');
1271cb0ef41Sopenharmony_ci  };
1281cb0ef41Sopenharmony_ci
1291cb0ef41Sopenharmony_ci  const onBody = (buf) => {
1301cb0ef41Sopenharmony_ci    const body = String(buf);
1311cb0ef41Sopenharmony_ci    assert.strictEqual(body, 'pong');
1321cb0ef41Sopenharmony_ci  };
1331cb0ef41Sopenharmony_ci
1341cb0ef41Sopenharmony_ci  const parser = newParser(RESPONSE);
1351cb0ef41Sopenharmony_ci  parser[kOnHeadersComplete] = mustCall(onHeadersComplete);
1361cb0ef41Sopenharmony_ci  parser[kOnBody] = mustCall(onBody);
1371cb0ef41Sopenharmony_ci  parser.execute(request, 0, request.length);
1381cb0ef41Sopenharmony_ci}
1391cb0ef41Sopenharmony_ci
1401cb0ef41Sopenharmony_ci
1411cb0ef41Sopenharmony_ci//
1421cb0ef41Sopenharmony_ci// Response with no headers.
1431cb0ef41Sopenharmony_ci//
1441cb0ef41Sopenharmony_ci{
1451cb0ef41Sopenharmony_ci  const request = Buffer.from(
1461cb0ef41Sopenharmony_ci    'HTTP/1.0 200 Connection established\r\n\r\n');
1471cb0ef41Sopenharmony_ci
1481cb0ef41Sopenharmony_ci  const onHeadersComplete = (versionMajor, versionMinor, headers,
1491cb0ef41Sopenharmony_ci                             method, url, statusCode, statusMessage) => {
1501cb0ef41Sopenharmony_ci    assert.strictEqual(versionMajor, 1);
1511cb0ef41Sopenharmony_ci    assert.strictEqual(versionMinor, 0);
1521cb0ef41Sopenharmony_ci    assert.strictEqual(method, undefined);
1531cb0ef41Sopenharmony_ci    assert.strictEqual(statusCode, 200);
1541cb0ef41Sopenharmony_ci    assert.strictEqual(statusMessage, 'Connection established');
1551cb0ef41Sopenharmony_ci    assert.deepStrictEqual(headers || parser.headers, []);
1561cb0ef41Sopenharmony_ci  };
1571cb0ef41Sopenharmony_ci
1581cb0ef41Sopenharmony_ci  const parser = newParser(RESPONSE);
1591cb0ef41Sopenharmony_ci  parser[kOnHeadersComplete] = mustCall(onHeadersComplete);
1601cb0ef41Sopenharmony_ci  parser.execute(request, 0, request.length);
1611cb0ef41Sopenharmony_ci}
1621cb0ef41Sopenharmony_ci
1631cb0ef41Sopenharmony_ci
1641cb0ef41Sopenharmony_ci//
1651cb0ef41Sopenharmony_ci// Trailing headers.
1661cb0ef41Sopenharmony_ci//
1671cb0ef41Sopenharmony_ci{
1681cb0ef41Sopenharmony_ci  const request = Buffer.from(
1691cb0ef41Sopenharmony_ci    'POST /it HTTP/1.1\r\n' +
1701cb0ef41Sopenharmony_ci    'Transfer-Encoding: chunked\r\n' +
1711cb0ef41Sopenharmony_ci    '\r\n' +
1721cb0ef41Sopenharmony_ci    '4\r\n' +
1731cb0ef41Sopenharmony_ci    'ping\r\n' +
1741cb0ef41Sopenharmony_ci    '0\r\n' +
1751cb0ef41Sopenharmony_ci    'Vary: *\r\n' +
1761cb0ef41Sopenharmony_ci    'Content-Type: text/plain\r\n' +
1771cb0ef41Sopenharmony_ci    '\r\n'
1781cb0ef41Sopenharmony_ci  );
1791cb0ef41Sopenharmony_ci
1801cb0ef41Sopenharmony_ci  let seen_body = false;
1811cb0ef41Sopenharmony_ci
1821cb0ef41Sopenharmony_ci  const onHeaders = (headers) => {
1831cb0ef41Sopenharmony_ci    assert.ok(seen_body); // Trailers should come after the body
1841cb0ef41Sopenharmony_ci    assert.deepStrictEqual(headers,
1851cb0ef41Sopenharmony_ci                           ['Vary', '*', 'Content-Type', 'text/plain']);
1861cb0ef41Sopenharmony_ci  };
1871cb0ef41Sopenharmony_ci
1881cb0ef41Sopenharmony_ci  const onHeadersComplete = (versionMajor, versionMinor, headers,
1891cb0ef41Sopenharmony_ci                             method, url) => {
1901cb0ef41Sopenharmony_ci    assert.strictEqual(method, methods.indexOf('POST'));
1911cb0ef41Sopenharmony_ci    assert.strictEqual(url || parser.url, '/it');
1921cb0ef41Sopenharmony_ci    assert.strictEqual(versionMajor, 1);
1931cb0ef41Sopenharmony_ci    assert.strictEqual(versionMinor, 1);
1941cb0ef41Sopenharmony_ci    // Expect to see trailing headers now
1951cb0ef41Sopenharmony_ci    parser[kOnHeaders] = mustCall(onHeaders);
1961cb0ef41Sopenharmony_ci  };
1971cb0ef41Sopenharmony_ci
1981cb0ef41Sopenharmony_ci  const onBody = (buf) => {
1991cb0ef41Sopenharmony_ci    const body = String(buf);
2001cb0ef41Sopenharmony_ci    assert.strictEqual(body, 'ping');
2011cb0ef41Sopenharmony_ci    seen_body = true;
2021cb0ef41Sopenharmony_ci  };
2031cb0ef41Sopenharmony_ci
2041cb0ef41Sopenharmony_ci  const parser = newParser(REQUEST);
2051cb0ef41Sopenharmony_ci  parser[kOnHeadersComplete] = mustCall(onHeadersComplete);
2061cb0ef41Sopenharmony_ci  parser[kOnBody] = mustCall(onBody);
2071cb0ef41Sopenharmony_ci  parser.execute(request, 0, request.length);
2081cb0ef41Sopenharmony_ci}
2091cb0ef41Sopenharmony_ci
2101cb0ef41Sopenharmony_ci
2111cb0ef41Sopenharmony_ci//
2121cb0ef41Sopenharmony_ci// Test header ordering.
2131cb0ef41Sopenharmony_ci//
2141cb0ef41Sopenharmony_ci{
2151cb0ef41Sopenharmony_ci  const request = Buffer.from(
2161cb0ef41Sopenharmony_ci    'GET / HTTP/1.0\r\n' +
2171cb0ef41Sopenharmony_ci    'X-Filler: 1337\r\n' +
2181cb0ef41Sopenharmony_ci    'X-Filler:   42\r\n' +
2191cb0ef41Sopenharmony_ci    'X-Filler2:  42\r\n' +
2201cb0ef41Sopenharmony_ci    '\r\n'
2211cb0ef41Sopenharmony_ci  );
2221cb0ef41Sopenharmony_ci
2231cb0ef41Sopenharmony_ci  const onHeadersComplete = (versionMajor, versionMinor, headers,
2241cb0ef41Sopenharmony_ci                             method) => {
2251cb0ef41Sopenharmony_ci    assert.strictEqual(method, methods.indexOf('GET'));
2261cb0ef41Sopenharmony_ci    assert.strictEqual(versionMajor, 1);
2271cb0ef41Sopenharmony_ci    assert.strictEqual(versionMinor, 0);
2281cb0ef41Sopenharmony_ci    assert.deepStrictEqual(
2291cb0ef41Sopenharmony_ci      headers || parser.headers,
2301cb0ef41Sopenharmony_ci      ['X-Filler', '1337', 'X-Filler', '42', 'X-Filler2', '42']);
2311cb0ef41Sopenharmony_ci  };
2321cb0ef41Sopenharmony_ci
2331cb0ef41Sopenharmony_ci  const parser = newParser(REQUEST);
2341cb0ef41Sopenharmony_ci  parser[kOnHeadersComplete] = mustCall(onHeadersComplete);
2351cb0ef41Sopenharmony_ci  parser.execute(request, 0, request.length);
2361cb0ef41Sopenharmony_ci}
2371cb0ef41Sopenharmony_ci
2381cb0ef41Sopenharmony_ci
2391cb0ef41Sopenharmony_ci//
2401cb0ef41Sopenharmony_ci// Test large number of headers
2411cb0ef41Sopenharmony_ci//
2421cb0ef41Sopenharmony_ci{
2431cb0ef41Sopenharmony_ci  // 256 X-Filler headers
2441cb0ef41Sopenharmony_ci  const lots_of_headers = 'X-Filler: 42\r\n'.repeat(256);
2451cb0ef41Sopenharmony_ci
2461cb0ef41Sopenharmony_ci  const request = Buffer.from(
2471cb0ef41Sopenharmony_ci    'GET /foo/bar/baz?quux=42#1337 HTTP/1.0\r\n' +
2481cb0ef41Sopenharmony_ci    lots_of_headers +
2491cb0ef41Sopenharmony_ci    '\r\n'
2501cb0ef41Sopenharmony_ci  );
2511cb0ef41Sopenharmony_ci
2521cb0ef41Sopenharmony_ci  const onHeadersComplete = (versionMajor, versionMinor, headers,
2531cb0ef41Sopenharmony_ci                             method, url) => {
2541cb0ef41Sopenharmony_ci    assert.strictEqual(method, methods.indexOf('GET'));
2551cb0ef41Sopenharmony_ci    assert.strictEqual(url || parser.url, '/foo/bar/baz?quux=42#1337');
2561cb0ef41Sopenharmony_ci    assert.strictEqual(versionMajor, 1);
2571cb0ef41Sopenharmony_ci    assert.strictEqual(versionMinor, 0);
2581cb0ef41Sopenharmony_ci
2591cb0ef41Sopenharmony_ci    headers = headers || parser.headers;
2601cb0ef41Sopenharmony_ci
2611cb0ef41Sopenharmony_ci    assert.strictEqual(headers.length, 2 * 256); // 256 key/value pairs
2621cb0ef41Sopenharmony_ci    for (let i = 0; i < headers.length; i += 2) {
2631cb0ef41Sopenharmony_ci      assert.strictEqual(headers[i], 'X-Filler');
2641cb0ef41Sopenharmony_ci      assert.strictEqual(headers[i + 1], '42');
2651cb0ef41Sopenharmony_ci    }
2661cb0ef41Sopenharmony_ci  };
2671cb0ef41Sopenharmony_ci
2681cb0ef41Sopenharmony_ci  const parser = newParser(REQUEST);
2691cb0ef41Sopenharmony_ci  parser[kOnHeadersComplete] = mustCall(onHeadersComplete);
2701cb0ef41Sopenharmony_ci  parser.execute(request, 0, request.length);
2711cb0ef41Sopenharmony_ci}
2721cb0ef41Sopenharmony_ci
2731cb0ef41Sopenharmony_ci
2741cb0ef41Sopenharmony_ci//
2751cb0ef41Sopenharmony_ci// Test request body
2761cb0ef41Sopenharmony_ci//
2771cb0ef41Sopenharmony_ci{
2781cb0ef41Sopenharmony_ci  const request = Buffer.from(
2791cb0ef41Sopenharmony_ci    'POST /it HTTP/1.1\r\n' +
2801cb0ef41Sopenharmony_ci    'Content-Type: application/x-www-form-urlencoded\r\n' +
2811cb0ef41Sopenharmony_ci    'Content-Length: 15\r\n' +
2821cb0ef41Sopenharmony_ci    '\r\n' +
2831cb0ef41Sopenharmony_ci    'foo=42&bar=1337'
2841cb0ef41Sopenharmony_ci  );
2851cb0ef41Sopenharmony_ci
2861cb0ef41Sopenharmony_ci  const onHeadersComplete = (versionMajor, versionMinor, headers,
2871cb0ef41Sopenharmony_ci                             method, url) => {
2881cb0ef41Sopenharmony_ci    assert.strictEqual(method, methods.indexOf('POST'));
2891cb0ef41Sopenharmony_ci    assert.strictEqual(url || parser.url, '/it');
2901cb0ef41Sopenharmony_ci    assert.strictEqual(versionMajor, 1);
2911cb0ef41Sopenharmony_ci    assert.strictEqual(versionMinor, 1);
2921cb0ef41Sopenharmony_ci  };
2931cb0ef41Sopenharmony_ci
2941cb0ef41Sopenharmony_ci  const onBody = (buf) => {
2951cb0ef41Sopenharmony_ci    const body = String(buf);
2961cb0ef41Sopenharmony_ci    assert.strictEqual(body, 'foo=42&bar=1337');
2971cb0ef41Sopenharmony_ci  };
2981cb0ef41Sopenharmony_ci
2991cb0ef41Sopenharmony_ci  const parser = newParser(REQUEST);
3001cb0ef41Sopenharmony_ci  parser[kOnHeadersComplete] = mustCall(onHeadersComplete);
3011cb0ef41Sopenharmony_ci  parser[kOnBody] = mustCall(onBody);
3021cb0ef41Sopenharmony_ci  parser.execute(request, 0, request.length);
3031cb0ef41Sopenharmony_ci}
3041cb0ef41Sopenharmony_ci
3051cb0ef41Sopenharmony_ci
3061cb0ef41Sopenharmony_ci//
3071cb0ef41Sopenharmony_ci// Test chunked request body
3081cb0ef41Sopenharmony_ci//
3091cb0ef41Sopenharmony_ci{
3101cb0ef41Sopenharmony_ci  const request = Buffer.from(
3111cb0ef41Sopenharmony_ci    'POST /it HTTP/1.1\r\n' +
3121cb0ef41Sopenharmony_ci    'Content-Type: text/plain\r\n' +
3131cb0ef41Sopenharmony_ci    'Transfer-Encoding: chunked\r\n' +
3141cb0ef41Sopenharmony_ci    '\r\n' +
3151cb0ef41Sopenharmony_ci    '3\r\n' +
3161cb0ef41Sopenharmony_ci    '123\r\n' +
3171cb0ef41Sopenharmony_ci    '6\r\n' +
3181cb0ef41Sopenharmony_ci    '123456\r\n' +
3191cb0ef41Sopenharmony_ci    'A\r\n' +
3201cb0ef41Sopenharmony_ci    '1234567890\r\n' +
3211cb0ef41Sopenharmony_ci    '0\r\n'
3221cb0ef41Sopenharmony_ci  );
3231cb0ef41Sopenharmony_ci
3241cb0ef41Sopenharmony_ci  const onHeadersComplete = (versionMajor, versionMinor, headers,
3251cb0ef41Sopenharmony_ci                             method, url) => {
3261cb0ef41Sopenharmony_ci    assert.strictEqual(method, methods.indexOf('POST'));
3271cb0ef41Sopenharmony_ci    assert.strictEqual(url || parser.url, '/it');
3281cb0ef41Sopenharmony_ci    assert.strictEqual(versionMajor, 1);
3291cb0ef41Sopenharmony_ci    assert.strictEqual(versionMinor, 1);
3301cb0ef41Sopenharmony_ci  };
3311cb0ef41Sopenharmony_ci
3321cb0ef41Sopenharmony_ci  let body_part = 0;
3331cb0ef41Sopenharmony_ci  const body_parts = ['123', '123456', '1234567890'];
3341cb0ef41Sopenharmony_ci
3351cb0ef41Sopenharmony_ci  const onBody = (buf) => {
3361cb0ef41Sopenharmony_ci    const body = String(buf);
3371cb0ef41Sopenharmony_ci    assert.strictEqual(body, body_parts[body_part++]);
3381cb0ef41Sopenharmony_ci  };
3391cb0ef41Sopenharmony_ci
3401cb0ef41Sopenharmony_ci  const parser = newParser(REQUEST);
3411cb0ef41Sopenharmony_ci  parser[kOnHeadersComplete] = mustCall(onHeadersComplete);
3421cb0ef41Sopenharmony_ci  parser[kOnBody] = mustCall(onBody, body_parts.length);
3431cb0ef41Sopenharmony_ci  parser.execute(request, 0, request.length);
3441cb0ef41Sopenharmony_ci}
3451cb0ef41Sopenharmony_ci
3461cb0ef41Sopenharmony_ci
3471cb0ef41Sopenharmony_ci//
3481cb0ef41Sopenharmony_ci// Test chunked request body spread over multiple buffers (packets)
3491cb0ef41Sopenharmony_ci//
3501cb0ef41Sopenharmony_ci{
3511cb0ef41Sopenharmony_ci  let request = Buffer.from(
3521cb0ef41Sopenharmony_ci    'POST /it HTTP/1.1\r\n' +
3531cb0ef41Sopenharmony_ci    'Content-Type: text/plain\r\n' +
3541cb0ef41Sopenharmony_ci    'Transfer-Encoding: chunked\r\n' +
3551cb0ef41Sopenharmony_ci    '\r\n' +
3561cb0ef41Sopenharmony_ci    '3\r\n' +
3571cb0ef41Sopenharmony_ci    '123\r\n' +
3581cb0ef41Sopenharmony_ci    '6\r\n' +
3591cb0ef41Sopenharmony_ci    '123456\r\n'
3601cb0ef41Sopenharmony_ci  );
3611cb0ef41Sopenharmony_ci
3621cb0ef41Sopenharmony_ci  const onHeadersComplete = (versionMajor, versionMinor, headers,
3631cb0ef41Sopenharmony_ci                             method, url) => {
3641cb0ef41Sopenharmony_ci    assert.strictEqual(method, methods.indexOf('POST'));
3651cb0ef41Sopenharmony_ci    assert.strictEqual(url || parser.url, '/it');
3661cb0ef41Sopenharmony_ci    assert.strictEqual(versionMajor, 1);
3671cb0ef41Sopenharmony_ci    assert.strictEqual(versionMinor, 1);
3681cb0ef41Sopenharmony_ci  };
3691cb0ef41Sopenharmony_ci
3701cb0ef41Sopenharmony_ci  let body_part = 0;
3711cb0ef41Sopenharmony_ci  const body_parts =
3721cb0ef41Sopenharmony_ci          ['123', '123456', '123456789', '123456789ABC', '123456789ABCDEF'];
3731cb0ef41Sopenharmony_ci
3741cb0ef41Sopenharmony_ci  const onBody = (buf) => {
3751cb0ef41Sopenharmony_ci    const body = String(buf);
3761cb0ef41Sopenharmony_ci    assert.strictEqual(body, body_parts[body_part++]);
3771cb0ef41Sopenharmony_ci  };
3781cb0ef41Sopenharmony_ci
3791cb0ef41Sopenharmony_ci  const parser = newParser(REQUEST);
3801cb0ef41Sopenharmony_ci  parser[kOnHeadersComplete] = mustCall(onHeadersComplete);
3811cb0ef41Sopenharmony_ci  parser[kOnBody] = mustCall(onBody, body_parts.length);
3821cb0ef41Sopenharmony_ci  parser.execute(request, 0, request.length);
3831cb0ef41Sopenharmony_ci
3841cb0ef41Sopenharmony_ci  request = Buffer.from(
3851cb0ef41Sopenharmony_ci    '9\r\n' +
3861cb0ef41Sopenharmony_ci    '123456789\r\n' +
3871cb0ef41Sopenharmony_ci    'C\r\n' +
3881cb0ef41Sopenharmony_ci    '123456789ABC\r\n' +
3891cb0ef41Sopenharmony_ci    'F\r\n' +
3901cb0ef41Sopenharmony_ci    '123456789ABCDEF\r\n' +
3911cb0ef41Sopenharmony_ci    '0\r\n'
3921cb0ef41Sopenharmony_ci  );
3931cb0ef41Sopenharmony_ci
3941cb0ef41Sopenharmony_ci  parser.execute(request, 0, request.length);
3951cb0ef41Sopenharmony_ci}
3961cb0ef41Sopenharmony_ci
3971cb0ef41Sopenharmony_ci
3981cb0ef41Sopenharmony_ci//
3991cb0ef41Sopenharmony_ci// Stress test.
4001cb0ef41Sopenharmony_ci//
4011cb0ef41Sopenharmony_ci{
4021cb0ef41Sopenharmony_ci  const request = Buffer.from(
4031cb0ef41Sopenharmony_ci    'POST /helpme HTTP/1.1\r\n' +
4041cb0ef41Sopenharmony_ci    'Content-Type: text/plain\r\n' +
4051cb0ef41Sopenharmony_ci    'Transfer-Encoding: chunked\r\n' +
4061cb0ef41Sopenharmony_ci    '\r\n' +
4071cb0ef41Sopenharmony_ci    '3\r\n' +
4081cb0ef41Sopenharmony_ci    '123\r\n' +
4091cb0ef41Sopenharmony_ci    '6\r\n' +
4101cb0ef41Sopenharmony_ci    '123456\r\n' +
4111cb0ef41Sopenharmony_ci    '9\r\n' +
4121cb0ef41Sopenharmony_ci    '123456789\r\n' +
4131cb0ef41Sopenharmony_ci    'C\r\n' +
4141cb0ef41Sopenharmony_ci    '123456789ABC\r\n' +
4151cb0ef41Sopenharmony_ci    'F\r\n' +
4161cb0ef41Sopenharmony_ci    '123456789ABCDEF\r\n' +
4171cb0ef41Sopenharmony_ci    '0\r\n'
4181cb0ef41Sopenharmony_ci  );
4191cb0ef41Sopenharmony_ci
4201cb0ef41Sopenharmony_ci  function test(a, b) {
4211cb0ef41Sopenharmony_ci    const onHeadersComplete = (versionMajor, versionMinor, headers,
4221cb0ef41Sopenharmony_ci                               method, url) => {
4231cb0ef41Sopenharmony_ci      assert.strictEqual(method, methods.indexOf('POST'));
4241cb0ef41Sopenharmony_ci      assert.strictEqual(url || parser.url, '/helpme');
4251cb0ef41Sopenharmony_ci      assert.strictEqual(versionMajor, 1);
4261cb0ef41Sopenharmony_ci      assert.strictEqual(versionMinor, 1);
4271cb0ef41Sopenharmony_ci    };
4281cb0ef41Sopenharmony_ci
4291cb0ef41Sopenharmony_ci    let expected_body = '123123456123456789123456789ABC123456789ABCDEF';
4301cb0ef41Sopenharmony_ci
4311cb0ef41Sopenharmony_ci    const onBody = (buf) => {
4321cb0ef41Sopenharmony_ci      const chunk = String(buf);
4331cb0ef41Sopenharmony_ci      assert.strictEqual(expected_body.indexOf(chunk), 0);
4341cb0ef41Sopenharmony_ci      expected_body = expected_body.slice(chunk.length);
4351cb0ef41Sopenharmony_ci    };
4361cb0ef41Sopenharmony_ci
4371cb0ef41Sopenharmony_ci    const parser = newParser(REQUEST);
4381cb0ef41Sopenharmony_ci    parser[kOnHeadersComplete] = mustCall(onHeadersComplete);
4391cb0ef41Sopenharmony_ci    parser[kOnBody] = onBody;
4401cb0ef41Sopenharmony_ci    parser.execute(a, 0, a.length);
4411cb0ef41Sopenharmony_ci    parser.execute(b, 0, b.length);
4421cb0ef41Sopenharmony_ci
4431cb0ef41Sopenharmony_ci    assert.strictEqual(expected_body, '');
4441cb0ef41Sopenharmony_ci  }
4451cb0ef41Sopenharmony_ci
4461cb0ef41Sopenharmony_ci  for (let i = 1; i < request.length - 1; ++i) {
4471cb0ef41Sopenharmony_ci    const a = request.slice(0, i);
4481cb0ef41Sopenharmony_ci    const b = request.slice(i);
4491cb0ef41Sopenharmony_ci    test(a, b);
4501cb0ef41Sopenharmony_ci  }
4511cb0ef41Sopenharmony_ci}
4521cb0ef41Sopenharmony_ci
4531cb0ef41Sopenharmony_ci
4541cb0ef41Sopenharmony_ci//
4551cb0ef41Sopenharmony_ci// Byte by byte test.
4561cb0ef41Sopenharmony_ci//
4571cb0ef41Sopenharmony_ci{
4581cb0ef41Sopenharmony_ci  const request = Buffer.from(
4591cb0ef41Sopenharmony_ci    'POST /it HTTP/1.1\r\n' +
4601cb0ef41Sopenharmony_ci    'Content-Type: text/plain\r\n' +
4611cb0ef41Sopenharmony_ci    'Transfer-Encoding: chunked\r\n' +
4621cb0ef41Sopenharmony_ci    '\r\n' +
4631cb0ef41Sopenharmony_ci    '3\r\n' +
4641cb0ef41Sopenharmony_ci    '123\r\n' +
4651cb0ef41Sopenharmony_ci    '6\r\n' +
4661cb0ef41Sopenharmony_ci    '123456\r\n' +
4671cb0ef41Sopenharmony_ci    '9\r\n' +
4681cb0ef41Sopenharmony_ci    '123456789\r\n' +
4691cb0ef41Sopenharmony_ci    'C\r\n' +
4701cb0ef41Sopenharmony_ci    '123456789ABC\r\n' +
4711cb0ef41Sopenharmony_ci    'F\r\n' +
4721cb0ef41Sopenharmony_ci    '123456789ABCDEF\r\n' +
4731cb0ef41Sopenharmony_ci    '0\r\n'
4741cb0ef41Sopenharmony_ci  );
4751cb0ef41Sopenharmony_ci
4761cb0ef41Sopenharmony_ci  const onHeadersComplete = (versionMajor, versionMinor, headers,
4771cb0ef41Sopenharmony_ci                             method, url) => {
4781cb0ef41Sopenharmony_ci    assert.strictEqual(method, methods.indexOf('POST'));
4791cb0ef41Sopenharmony_ci    assert.strictEqual(url || parser.url, '/it');
4801cb0ef41Sopenharmony_ci    assert.strictEqual(versionMajor, 1);
4811cb0ef41Sopenharmony_ci    assert.strictEqual(versionMinor, 1);
4821cb0ef41Sopenharmony_ci    assert.deepStrictEqual(
4831cb0ef41Sopenharmony_ci      headers || parser.headers,
4841cb0ef41Sopenharmony_ci      ['Content-Type', 'text/plain', 'Transfer-Encoding', 'chunked']);
4851cb0ef41Sopenharmony_ci  };
4861cb0ef41Sopenharmony_ci
4871cb0ef41Sopenharmony_ci  let expected_body = '123123456123456789123456789ABC123456789ABCDEF';
4881cb0ef41Sopenharmony_ci
4891cb0ef41Sopenharmony_ci  const onBody = (buf) => {
4901cb0ef41Sopenharmony_ci    const chunk = String(buf);
4911cb0ef41Sopenharmony_ci    assert.strictEqual(expected_body.indexOf(chunk), 0);
4921cb0ef41Sopenharmony_ci    expected_body = expected_body.slice(chunk.length);
4931cb0ef41Sopenharmony_ci  };
4941cb0ef41Sopenharmony_ci
4951cb0ef41Sopenharmony_ci  const parser = newParser(REQUEST);
4961cb0ef41Sopenharmony_ci  parser[kOnHeadersComplete] = mustCall(onHeadersComplete);
4971cb0ef41Sopenharmony_ci  parser[kOnBody] = onBody;
4981cb0ef41Sopenharmony_ci
4991cb0ef41Sopenharmony_ci  for (let i = 0; i < request.length; ++i) {
5001cb0ef41Sopenharmony_ci    parser.execute(request, i, 1);
5011cb0ef41Sopenharmony_ci  }
5021cb0ef41Sopenharmony_ci
5031cb0ef41Sopenharmony_ci  assert.strictEqual(expected_body, '');
5041cb0ef41Sopenharmony_ci}
5051cb0ef41Sopenharmony_ci
5061cb0ef41Sopenharmony_ci
5071cb0ef41Sopenharmony_ci//
5081cb0ef41Sopenharmony_ci// Test parser reinit sequence.
5091cb0ef41Sopenharmony_ci//
5101cb0ef41Sopenharmony_ci{
5111cb0ef41Sopenharmony_ci  const req1 = Buffer.from(
5121cb0ef41Sopenharmony_ci    'PUT /this HTTP/1.1\r\n' +
5131cb0ef41Sopenharmony_ci    'Content-Type: text/plain\r\n' +
5141cb0ef41Sopenharmony_ci    'Transfer-Encoding: chunked\r\n' +
5151cb0ef41Sopenharmony_ci    '\r\n' +
5161cb0ef41Sopenharmony_ci    '4\r\n' +
5171cb0ef41Sopenharmony_ci    'ping\r\n' +
5181cb0ef41Sopenharmony_ci    '0\r\n'
5191cb0ef41Sopenharmony_ci  );
5201cb0ef41Sopenharmony_ci
5211cb0ef41Sopenharmony_ci  const req2 = Buffer.from(
5221cb0ef41Sopenharmony_ci    'POST /that HTTP/1.0\r\n' +
5231cb0ef41Sopenharmony_ci    'Content-Type: text/plain\r\n' +
5241cb0ef41Sopenharmony_ci    'Content-Length: 4\r\n' +
5251cb0ef41Sopenharmony_ci    '\r\n' +
5261cb0ef41Sopenharmony_ci    'pong'
5271cb0ef41Sopenharmony_ci  );
5281cb0ef41Sopenharmony_ci
5291cb0ef41Sopenharmony_ci  const onHeadersComplete1 = (versionMajor, versionMinor, headers,
5301cb0ef41Sopenharmony_ci                              method, url) => {
5311cb0ef41Sopenharmony_ci    assert.strictEqual(method, methods.indexOf('PUT'));
5321cb0ef41Sopenharmony_ci    assert.strictEqual(url, '/this');
5331cb0ef41Sopenharmony_ci    assert.strictEqual(versionMajor, 1);
5341cb0ef41Sopenharmony_ci    assert.strictEqual(versionMinor, 1);
5351cb0ef41Sopenharmony_ci    assert.deepStrictEqual(
5361cb0ef41Sopenharmony_ci      headers,
5371cb0ef41Sopenharmony_ci      ['Content-Type', 'text/plain', 'Transfer-Encoding', 'chunked']);
5381cb0ef41Sopenharmony_ci  };
5391cb0ef41Sopenharmony_ci
5401cb0ef41Sopenharmony_ci  const onHeadersComplete2 = (versionMajor, versionMinor, headers,
5411cb0ef41Sopenharmony_ci                              method, url) => {
5421cb0ef41Sopenharmony_ci    assert.strictEqual(method, methods.indexOf('POST'));
5431cb0ef41Sopenharmony_ci    assert.strictEqual(url, '/that');
5441cb0ef41Sopenharmony_ci    assert.strictEqual(versionMajor, 1);
5451cb0ef41Sopenharmony_ci    assert.strictEqual(versionMinor, 0);
5461cb0ef41Sopenharmony_ci    assert.deepStrictEqual(
5471cb0ef41Sopenharmony_ci      headers,
5481cb0ef41Sopenharmony_ci      ['Content-Type', 'text/plain', 'Content-Length', '4']
5491cb0ef41Sopenharmony_ci    );
5501cb0ef41Sopenharmony_ci  };
5511cb0ef41Sopenharmony_ci
5521cb0ef41Sopenharmony_ci  const parser = newParser(REQUEST);
5531cb0ef41Sopenharmony_ci  parser[kOnHeadersComplete] = onHeadersComplete1;
5541cb0ef41Sopenharmony_ci  parser[kOnBody] = expectBody('ping');
5551cb0ef41Sopenharmony_ci  parser.execute(req1, 0, req1.length);
5561cb0ef41Sopenharmony_ci
5571cb0ef41Sopenharmony_ci  parser.initialize(REQUEST, req2);
5581cb0ef41Sopenharmony_ci  parser[kOnBody] = expectBody('pong');
5591cb0ef41Sopenharmony_ci  parser[kOnHeadersComplete] = onHeadersComplete2;
5601cb0ef41Sopenharmony_ci  parser.execute(req2, 0, req2.length);
5611cb0ef41Sopenharmony_ci}
5621cb0ef41Sopenharmony_ci
5631cb0ef41Sopenharmony_ci// Test parser 'this' safety
5641cb0ef41Sopenharmony_ci// https://github.com/joyent/node/issues/6690
5651cb0ef41Sopenharmony_ciassert.throws(function() {
5661cb0ef41Sopenharmony_ci  const request = Buffer.from('GET /hello HTTP/1.1\r\n\r\n');
5671cb0ef41Sopenharmony_ci
5681cb0ef41Sopenharmony_ci  const parser = newParser(REQUEST);
5691cb0ef41Sopenharmony_ci  const notparser = { execute: parser.execute };
5701cb0ef41Sopenharmony_ci  notparser.execute(request, 0, request.length);
5711cb0ef41Sopenharmony_ci}, TypeError);
572