11cb0ef41Sopenharmony_ci'use strict';
21cb0ef41Sopenharmony_ciconst common = require('../common');
31cb0ef41Sopenharmony_ciconst assert = require('assert');
41cb0ef41Sopenharmony_ciconst http = require('http');
51cb0ef41Sopenharmony_ci
61cb0ef41Sopenharmony_ciconst REQ_TIMEOUT = 500; // Set max ms of request time before abort
71cb0ef41Sopenharmony_ci
81cb0ef41Sopenharmony_ci// Set total allowed test timeout to avoid infinite loop
91cb0ef41Sopenharmony_ci// that will hang test suite
101cb0ef41Sopenharmony_ciconst TOTAL_TEST_TIMEOUT = 1000;
111cb0ef41Sopenharmony_ci
121cb0ef41Sopenharmony_ci// Placeholder for sockets handled, to make sure that we
131cb0ef41Sopenharmony_ci// will reach a socket re-use case.
141cb0ef41Sopenharmony_ciconst handledSockets = new Set();
151cb0ef41Sopenharmony_ci
161cb0ef41Sopenharmony_cilet metReusedSocket = false; // Flag for request loop termination.
171cb0ef41Sopenharmony_ci
181cb0ef41Sopenharmony_ciconst doubleEndResponse = (res) => {
191cb0ef41Sopenharmony_ci  // First end the request while sending some normal data
201cb0ef41Sopenharmony_ci  res.end('regular end of request', 'utf8', common.mustCall());
211cb0ef41Sopenharmony_ci  // Make sure the response socket is uncorked after first call of end
221cb0ef41Sopenharmony_ci  assert.strictEqual(res.writableCorked, 0);
231cb0ef41Sopenharmony_ci  res.end(); // Double end the response to prep for next socket re-use.
241cb0ef41Sopenharmony_ci};
251cb0ef41Sopenharmony_ci
261cb0ef41Sopenharmony_ciconst sendDrainNeedingData = (res) => {
271cb0ef41Sopenharmony_ci  // Send data to socket more than the high watermark so that
281cb0ef41Sopenharmony_ci  // it definitely needs drain
291cb0ef41Sopenharmony_ci  const highWaterMark = res.socket.writableHighWaterMark;
301cb0ef41Sopenharmony_ci  const bufferToSend = Buffer.alloc(highWaterMark + 100);
311cb0ef41Sopenharmony_ci  const ret = res.write(bufferToSend); // Write the request data.
321cb0ef41Sopenharmony_ci  // Make sure that we had back pressure on response stream.
331cb0ef41Sopenharmony_ci  assert.strictEqual(ret, false);
341cb0ef41Sopenharmony_ci  res.once('drain', () => res.end()); // End on drain.
351cb0ef41Sopenharmony_ci};
361cb0ef41Sopenharmony_ci
371cb0ef41Sopenharmony_ciconst server = http.createServer((req, res) => {
381cb0ef41Sopenharmony_ci  const { socket: responseSocket } = res;
391cb0ef41Sopenharmony_ci  if (handledSockets.has(responseSocket)) { // re-used socket, send big data!
401cb0ef41Sopenharmony_ci    metReusedSocket = true; // stop request loop
411cb0ef41Sopenharmony_ci    console.debug('FOUND REUSED SOCKET!');
421cb0ef41Sopenharmony_ci    sendDrainNeedingData(res);
431cb0ef41Sopenharmony_ci  } else { // not used again
441cb0ef41Sopenharmony_ci    // add to make sure we recognise it when we meet socket again
451cb0ef41Sopenharmony_ci    handledSockets.add(responseSocket);
461cb0ef41Sopenharmony_ci    doubleEndResponse(res);
471cb0ef41Sopenharmony_ci  }
481cb0ef41Sopenharmony_ci});
491cb0ef41Sopenharmony_ci
501cb0ef41Sopenharmony_ciserver.listen(0); // Start the server on a random port.
511cb0ef41Sopenharmony_ci
521cb0ef41Sopenharmony_ciconst sendRequest = (agent) => new Promise((resolve, reject) => {
531cb0ef41Sopenharmony_ci  const timeout = setTimeout(common.mustNotCall(() => {
541cb0ef41Sopenharmony_ci    reject(new Error('Request timed out'));
551cb0ef41Sopenharmony_ci  }), REQ_TIMEOUT);
561cb0ef41Sopenharmony_ci  http.get({
571cb0ef41Sopenharmony_ci    port: server.address().port,
581cb0ef41Sopenharmony_ci    path: '/',
591cb0ef41Sopenharmony_ci    agent
601cb0ef41Sopenharmony_ci  }, common.mustCall((res) => {
611cb0ef41Sopenharmony_ci    const resData = [];
621cb0ef41Sopenharmony_ci    res.on('data', (data) => resData.push(data));
631cb0ef41Sopenharmony_ci    res.on('end', common.mustCall(() => {
641cb0ef41Sopenharmony_ci      const totalData = resData.reduce((total, elem) => total + elem.length, 0);
651cb0ef41Sopenharmony_ci      clearTimeout(timeout); // Cancel rejection timeout.
661cb0ef41Sopenharmony_ci      resolve(totalData); // fulfill promise
671cb0ef41Sopenharmony_ci    }));
681cb0ef41Sopenharmony_ci  }));
691cb0ef41Sopenharmony_ci});
701cb0ef41Sopenharmony_ci
711cb0ef41Sopenharmony_ciserver.once('listening', async () => {
721cb0ef41Sopenharmony_ci  const testTimeout = setTimeout(common.mustNotCall(() => {
731cb0ef41Sopenharmony_ci    console.error('Test running for a while but could not met re-used socket');
741cb0ef41Sopenharmony_ci    process.exit(1);
751cb0ef41Sopenharmony_ci  }), TOTAL_TEST_TIMEOUT);
761cb0ef41Sopenharmony_ci  // Explicitly start agent to force socket reuse.
771cb0ef41Sopenharmony_ci  const agent = new http.Agent({ keepAlive: true });
781cb0ef41Sopenharmony_ci  // Start the request loop
791cb0ef41Sopenharmony_ci  let reqNo = 0;
801cb0ef41Sopenharmony_ci  while (!metReusedSocket) {
811cb0ef41Sopenharmony_ci    try {
821cb0ef41Sopenharmony_ci      console.log(`Sending req no ${++reqNo}`);
831cb0ef41Sopenharmony_ci      const totalData = await sendRequest(agent);
841cb0ef41Sopenharmony_ci      console.log(`${totalData} bytes were received for request ${reqNo}`);
851cb0ef41Sopenharmony_ci    } catch (err) {
861cb0ef41Sopenharmony_ci      console.error(err);
871cb0ef41Sopenharmony_ci      process.exit(1);
881cb0ef41Sopenharmony_ci    }
891cb0ef41Sopenharmony_ci  }
901cb0ef41Sopenharmony_ci  // Successfully tested conditions and ended loop
911cb0ef41Sopenharmony_ci  clearTimeout(testTimeout);
921cb0ef41Sopenharmony_ci  console.log('Closing server');
931cb0ef41Sopenharmony_ci  agent.destroy();
941cb0ef41Sopenharmony_ci  server.close();
951cb0ef41Sopenharmony_ci});
96