• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2const common = require('../common');
3const assert = require('assert');
4const http = require('http');
5
6const REQ_TIMEOUT = 500; // Set max ms of request time before abort
7
8// Set total allowed test timeout to avoid infinite loop
9// that will hang test suite
10const TOTAL_TEST_TIMEOUT = 1000;
11
12// Placeholder for sockets handled, to make sure that we
13// will reach a socket re-use case.
14const handledSockets = new Set();
15
16let metReusedSocket = false; // Flag for request loop termination.
17
18const doubleEndResponse = (res) => {
19  // First end the request while sending some normal data
20  res.end('regular end of request', 'utf8', common.mustCall());
21  // Make sure the response socket is uncorked after first call of end
22  assert.strictEqual(res.writableCorked, 0);
23  res.end(); // Double end the response to prep for next socket re-use.
24};
25
26const sendDrainNeedingData = (res) => {
27  // Send data to socket more than the high watermark so that
28  // it definitely needs drain
29  const highWaterMark = res.socket.writableHighWaterMark;
30  const bufferToSend = Buffer.alloc(highWaterMark + 100);
31  const ret = res.write(bufferToSend); // Write the request data.
32  // Make sure that we had back pressure on response stream.
33  assert.strictEqual(ret, false);
34  res.once('drain', () => res.end()); // End on drain.
35};
36
37const server = http.createServer((req, res) => {
38  const { socket: responseSocket } = res;
39  if (handledSockets.has(responseSocket)) { // re-used socket, send big data!
40    metReusedSocket = true; // stop request loop
41    console.debug('FOUND REUSED SOCKET!');
42    sendDrainNeedingData(res);
43  } else { // not used again
44    // add to make sure we recognise it when we meet socket again
45    handledSockets.add(responseSocket);
46    doubleEndResponse(res);
47  }
48});
49
50server.listen(0); // Start the server on a random port.
51
52const sendRequest = (agent) => new Promise((resolve, reject) => {
53  const timeout = setTimeout(common.mustNotCall(() => {
54    reject(new Error('Request timed out'));
55  }), REQ_TIMEOUT);
56  http.get({
57    port: server.address().port,
58    path: '/',
59    agent
60  }, common.mustCall((res) => {
61    const resData = [];
62    res.on('data', (data) => resData.push(data));
63    res.on('end', common.mustCall(() => {
64      const totalData = resData.reduce((total, elem) => total + elem.length, 0);
65      clearTimeout(timeout); // Cancel rejection timeout.
66      resolve(totalData); // fulfill promise
67    }));
68  }));
69});
70
71server.once('listening', async () => {
72  const testTimeout = setTimeout(common.mustNotCall(() => {
73    console.error('Test running for a while but could not met re-used socket');
74    process.exit(1);
75  }), TOTAL_TEST_TIMEOUT);
76  // Explicitly start agent to force socket reuse.
77  const agent = new http.Agent({ keepAlive: true });
78  // Start the request loop
79  let reqNo = 0;
80  while (!metReusedSocket) {
81    try {
82      console.log(`Sending req no ${++reqNo}`);
83      const totalData = await sendRequest(agent);
84      console.log(`${totalData} bytes were received for request ${reqNo}`);
85    } catch (err) {
86      console.error(err);
87      process.exit(1);
88    }
89  }
90  // Successfully tested conditions and ended loop
91  clearTimeout(testTimeout);
92  console.log('Closing server');
93  agent.destroy();
94  server.close();
95});
96