sindresorhus / got

🌐 Human-friendly and powerful HTTP request library for Node.js
MIT License
14.27k stars 935 forks source link

Internal conflicts among options may hangs response forever #1657

Closed loynoir closed 3 years ago

loynoir commented 3 years ago

Describe the bug

I am a new user following README.md, combining option cache and option agent together. By doing this, response may never come out, or hangs forever.

  1. When cache is Map, response never come out.
  2. When cache is KeyvRedis, response hangs forever.
  3. If port number is being tested at first time, bug appear. With small chance, test OK. Start from second time, bug appear. With very small chance, test OK.

Actual behavior

# node ./portClient.js 5000
url=http://127.0.0.1:5000
cache only port=36842
cache only port=36844
agentkeepalive agent1 keepAlive only port=36846
agentkeepalive agent1 keepAlive only port=36846
agentkeepalive agent2 keepAlive only port=36848

early exit or hangs forever

Expected behavior

# node ./portClient.js 5000
url=http://127.0.0.1:5000
cache only port=36842
cache only port=36844
agentkeepalive agent1 keepAlive only port=36846
agentkeepalive agent1 keepAlive only port=36846
agentkeepalive agent2 keepAlive only port=36848
agentkeepalive agent1 keepAlive + cache port=36846
DONE!

Code to reproduce

Server

Any server running at http://127.0.0.1:5000, can response different content to different connection, response same content to same connection.

Simple Server

# node ./portApp.js 5000
app listening at 127.0.0.1:5000
```js const express = require('express'); const port = process.argv[2]; const app = express(); const host = '127.0.0.1'; const keepAliveTimeout = 60000; app.get('/', (req, res) => { // const clientPort = req.connection.remotePort; const clientPort = req.socket.remotePort; res.send(clientPort.toString()); }); const srv = app.listen(port, host, () => { console.log(`app listening at ${host}:${srv.address().port}`); }); srv.keepAliveTimeout = keepAliveTimeout; ```
Simple Server Test

#!/usr/bin/env python
from requests import session
[s1, s2] = [session(), session()]
url='http://127.0.0.1:5000'
assert(s1.get(url).text == s1.get(url).text)
assert(s2.get(url).text == s2.get(url).text)
assert(s1.get(url).text != s2.get(url).text)
print("OK")
Client
# node ./portClient.js 5000
const http = require('http');
const https = require('https');
const got = require('got');
const HttpAgent = require('agentkeepalive');
const _KeyvRedis = require.resolve('@keyv/redis') ? require('@keyv/redis') : undefined;

const { HttpsAgent } = HttpAgent;

const [port, useNativeAgent, _] = process.argv.slice(2);
const url = `http://127.0.0.1:${port}`;
console.log(`url=${url}`);
const agentName = useNativeAgent || 'agentkeepalive';
const cacheName = 'Map';

const cacheFac = {
  Map: () => new Map(),
  Redis: () => new _KeyvRedis('redis://localhost:6379'),
};
const agentFac = {
  native: () => ({
    http: new http.Agent({ keepAlive: true }),
    https: new https.Agent({ keepAlive: true }),
  }),
  agentkeepalive: () => ({
    http: new HttpAgent(),
    https: new HttpsAgent(),
  }),
};

const cache = cacheFac[cacheName]();
const agent = agentFac[agentName]();
const agent2 = agentFac[agentName]();

(async () => {
  let response;

  response = await got(url, { cache });
  console.log(`cache only port=${response.body}`);

  response = await got(url, { cache });
  console.log(`cache only port=${response.body}`);

  response = await got(url, { agent });
  console.log(`${agentName} agent1 keepAlive only port=${response.body}`);

  response = await got(url, { agent });
  console.log(`${agentName} agent1 keepAlive only port=${response.body}`);

  response = await got(url, { agent: agent2 });
  console.log(`${agentName} agent2 keepAlive only port=${response.body}`);

  // FIXME: below never come out, or hangs forever.
  response = await got(url, { agent, cache });
  console.log(`${agentName} agent1 keepAlive + cache port=${response.body}`);

  console.log('DONE!');
})();

Checklist

szmarczak commented 3 years ago

Possibly related to https://github.com/sindresorhus/got/issues/1385

Can you create an example server in Node.js please?

loynoir commented 3 years ago

Hi, @szmarczak Node.js version example server updated.

szmarczak commented 3 years ago

Hmm... It works as expected on Node.js 12, but fails on Node.js 14...

szmarczak commented 3 years ago

Duplicate of #1385