From 8a1d66db1ecb65e7da51a4016c8818ab4da0c355 Mon Sep 17 00:00:00 2001 From: 3846masa <3846masahiro+git@gmail.com> Date: Mon, 2 May 2022 14:36:44 +0900 Subject: [PATCH 1/3] chore(test): call req.end from test cases to send the payload --- src/__tests__/http.spec.ts | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/__tests__/http.spec.ts b/src/__tests__/http.spec.ts index c257b666..0afd8bcf 100644 --- a/src/__tests__/http.spec.ts +++ b/src/__tests__/http.spec.ts @@ -16,7 +16,6 @@ export function request(url: string, options: http.RequestOptions) { }); req.on('error', (err) => reject(err)); }); - req.end(); return { req, promise }; } @@ -32,10 +31,11 @@ test('should set cookies to CookieJar from Set-Cookie header', async (t) => { }, ]); - const { promise } = request(`http://localhost:${port}`, { + const { promise, req } = request(`http://localhost:${port}`, { method: 'GET', agent, }); + req.end(); await promise; const cookies = await jar.getCookies(`http://localhost:${port}`); @@ -56,10 +56,11 @@ test('should set cookies to CookieJar from multiple Set-Cookie headers', async ( }, ]); - const { promise } = request(`http://localhost:${port}`, { + const { promise, req } = request(`http://localhost:${port}`, { method: 'GET', agent, }); + req.end(); await promise; const cookies = await jar.getCookies(`http://localhost:${port}`); @@ -83,10 +84,11 @@ test('should send cookies from CookieJar', async (t) => { await jar.setCookie('key=value', `http://localhost:${port}`); - const { promise } = request(`http://localhost:${port}`, { + const { promise, req } = request(`http://localhost:${port}`, { method: 'GET', agent, }); + req.end(); await promise; t.plan(1); @@ -105,10 +107,11 @@ test('should send cookies from CookieJar when value is url-encoded', async (t) = await jar.setCookie('key=hello%20world', `http://localhost:${port}`); - const { promise } = request(`http://localhost:${port}`, { + const { promise, req } = request(`http://localhost:${port}`, { method: 'GET', agent, }); + req.end(); await promise; t.plan(1); @@ -127,11 +130,12 @@ test('should send cookies from both a request options and CookieJar', async (t) await jar.setCookie('key1=value1', `http://localhost:${port}`); - const { promise } = request(`http://localhost:${port}`, { + const { promise, req } = request(`http://localhost:${port}`, { method: 'GET', agent, headers: { Cookie: 'key2=value2' }, }); + req.end(); await promise; t.plan(1); @@ -150,11 +154,12 @@ test('should send cookies from a request options when the key is duplicated in b await jar.setCookie('key=notexpected', `http://localhost:${port}`); - const { promise } = request(`http://localhost:${port}`, { + const { promise, req } = request(`http://localhost:${port}`, { method: 'GET', agent, headers: { Cookie: 'key=expected' }, }); + req.end(); await promise; t.plan(1); @@ -174,10 +179,11 @@ test('should emit error when CookieJar#getCookies throws error.', async (t) => { }, ]); - const { promise } = request(`http://localhost:${port}`, { + const { promise, req } = request(`http://localhost:${port}`, { method: 'GET', agent, }); + req.end(); await t.throwsAsync(() => promise); t.plan(1); @@ -197,10 +203,11 @@ test('should emit error when CookieJar#setCookie throws error.', async (t) => { }, ]); - const { promise } = request(`http://localhost:${port}`, { + const { promise, req } = request(`http://localhost:${port}`, { method: 'GET', agent, }); + req.end(); await t.throwsAsync(() => promise); t.plan(1); @@ -225,17 +232,19 @@ test('should send cookies even when target is same host but different port', asy ]); { - const { promise } = request(`http://localhost:${firstServerPort}`, { + const { promise, req } = request(`http://localhost:${firstServerPort}`, { method: 'GET', agent, }); + req.end(); await promise; } { - const { promise } = request(`http://localhost:${secondServerPort}`, { + const { promise, req } = request(`http://localhost:${secondServerPort}`, { method: 'GET', agent, }); + req.end(); await promise; } From 0e6d6c497d07432285c16b67530136f575590948 Mon Sep 17 00:00:00 2001 From: 3846masa <3846masahiro+git@gmail.com> Date: Mon, 2 May 2022 14:49:55 +0900 Subject: [PATCH 2/3] chore(test): add test cases for sending post data when keepalive is enabled --- src/__tests__/axios.spec.ts | 29 ++++++++++++++++++++++++++++- src/__tests__/got.spec.ts | 32 +++++++++++++++++++++++++++++++- src/__tests__/helpers.ts | 14 ++++++++++++++ src/__tests__/http.spec.ts | 32 +++++++++++++++++++++++++++++++- src/__tests__/needle.spec.ts | 29 ++++++++++++++++++++++++++++- src/__tests__/node-fetch.spec.ts | 31 ++++++++++++++++++++++++++++++- src/__tests__/phin.spec.ts | 32 +++++++++++++++++++++++++++++++- src/__tests__/request.spec.ts | 32 +++++++++++++++++++++++++++++++- src/__tests__/superagent.spec.ts | 27 ++++++++++++++++++++++++++- src/__tests__/urllib.spec.ts | 31 ++++++++++++++++++++++++++++++- src/__tests__/wreck.spec.ts | 30 +++++++++++++++++++++++++++++- 11 files changed, 309 insertions(+), 10 deletions(-) diff --git a/src/__tests__/axios.spec.ts b/src/__tests__/axios.spec.ts index 3801bb56..a4808d5c 100644 --- a/src/__tests__/axios.spec.ts +++ b/src/__tests__/axios.spec.ts @@ -3,7 +3,7 @@ import { CookieJar } from 'tough-cookie'; import axios from 'axios'; import { HttpCookieAgent } from '../'; -import { createTestServer } from './helpers'; +import { createTestServer, readStream } from './helpers'; test('should set cookies to CookieJar from Set-Cookie header', async (t) => { const jar = new CookieJar(); @@ -181,3 +181,30 @@ test('should emit error when CookieJar#setCookie throws error.', async (t) => { t.plan(1); }); + +test('should send post data when keepalive is enabled', async (t) => { + const times = 2; + + const jar = new CookieJar(); + const agent = new HttpCookieAgent({ jar, keepAlive: true }); + + const { port } = await createTestServer( + Array.from({ length: times }, (_, idx) => { + return async (req, res) => { + t.is(await readStream(req), `{ "index": "${idx}" }`); + t.is(req.headers['cookie'], 'key=expected'); + res.end(); + }; + }), + ); + + await jar.setCookie('key=expected', `http://localhost:${port}`); + + for (let idx = 0; idx < times; idx++) { + await axios.post(`http://localhost:${port}`, `{ "index": "${idx}" }`, { + httpAgent: agent, + }); + } + + t.plan(times * 2); +}); diff --git a/src/__tests__/got.spec.ts b/src/__tests__/got.spec.ts index 92d57fc9..ecabc7b4 100644 --- a/src/__tests__/got.spec.ts +++ b/src/__tests__/got.spec.ts @@ -3,7 +3,7 @@ import { CookieJar } from 'tough-cookie'; import got from 'got'; import { HttpCookieAgent } from '../'; -import { createTestServer } from './helpers'; +import { createTestServer, readStream } from './helpers'; test('should set cookies to CookieJar from Set-Cookie header', async (t) => { const jar = new CookieJar(); @@ -183,3 +183,33 @@ test('should emit error when CookieJar#setCookie throws error.', async (t) => { t.plan(1); }); + +test('should send post data when keepalive is enabled', async (t) => { + const times = 2; + + const jar = new CookieJar(); + const agent = new HttpCookieAgent({ jar, keepAlive: true }); + + const { port } = await createTestServer( + Array.from({ length: times }, (_, idx) => { + return async (req, res) => { + t.is(await readStream(req), `{ "index": "${idx}" }`); + t.is(req.headers['cookie'], 'key=expected'); + res.end(); + }; + }), + ); + + await jar.setCookie('key=expected', `http://localhost:${port}`); + + for (let idx = 0; idx < times; idx++) { + await got(`http://localhost:${port}`, { + agent: { http: agent }, + method: 'POST', + body: `{ "index": "${idx}" }`, + retry: 0, + }); + } + + t.plan(times * 2); +}); diff --git a/src/__tests__/helpers.ts b/src/__tests__/helpers.ts index 4d5b4efc..4104397f 100644 --- a/src/__tests__/helpers.ts +++ b/src/__tests__/helpers.ts @@ -2,6 +2,7 @@ import http from 'http'; import httpProxy from 'http-proxy'; import { promisify } from 'util'; import { URL } from 'url'; +import { Readable } from 'stream'; export async function createTestServer( stories: http.RequestListener[], @@ -25,6 +26,11 @@ export async function createTestServer( } }); + server.on('clientError', (err, socket) => { + console.error(err); + socket.end(); + }); + return { server, port: serverInfo.port, @@ -67,3 +73,11 @@ export async function createTestServerWithProxy( proxyPort: serverInfo.port, }; } + +export async function readStream(stream: Readable): Promise { + let data = ''; + for await (const chunk of stream) { + data += chunk; + } + return data; +} diff --git a/src/__tests__/http.spec.ts b/src/__tests__/http.spec.ts index 0afd8bcf..917d4974 100644 --- a/src/__tests__/http.spec.ts +++ b/src/__tests__/http.spec.ts @@ -3,7 +3,7 @@ import { CookieJar } from 'tough-cookie'; import http from 'http'; import { HttpCookieAgent } from '../'; -import { createTestServer } from './helpers'; +import { createTestServer, readStream } from './helpers'; export function request(url: string, options: http.RequestOptions) { const req = http.request(url, options); @@ -250,3 +250,33 @@ test('should send cookies even when target is same host but different port', asy t.plan(1); }); + +test('should send post data when keepalive is enabled', async (t) => { + const times = 2; + + const jar = new CookieJar(); + const agent = new HttpCookieAgent({ jar, keepAlive: true }); + + const { port } = await createTestServer( + Array.from({ length: times }, (_, idx) => { + return async (req, res) => { + t.is(await readStream(req), `{ "index": "${idx}" }`); + t.is(req.headers['cookie'], 'key=expected'); + res.end(); + }; + }), + ); + + await jar.setCookie('key=expected', `http://localhost:${port}`); + + for (let idx = 0; idx < times; idx++) { + const { promise, req } = request(`http://localhost:${port}`, { + method: 'POST', + agent, + }); + req.end(`{ "index": "${idx}" }`); + await promise; + } + + t.plan(times * 2); +}); diff --git a/src/__tests__/needle.spec.ts b/src/__tests__/needle.spec.ts index a9d7d0b4..42d53f7f 100644 --- a/src/__tests__/needle.spec.ts +++ b/src/__tests__/needle.spec.ts @@ -3,7 +3,7 @@ import { CookieJar } from 'tough-cookie'; import needle from 'needle'; import { HttpCookieAgent } from '../'; -import { createTestServer } from './helpers'; +import { createTestServer, readStream } from './helpers'; test('should set cookies to CookieJar from Set-Cookie header', async (t) => { const jar = new CookieJar(); @@ -182,3 +182,30 @@ test('should emit error when CookieJar#setCookie throws error.', async (t) => { t.plan(1); }); + +test('should send post data when keepalive is enabled', async (t) => { + const times = 2; + + const jar = new CookieJar(); + const agent = new HttpCookieAgent({ jar, keepAlive: true }); + + const { port } = await createTestServer( + Array.from({ length: times }, (_, idx) => { + return async (req, res) => { + t.is(await readStream(req), `{ "index": "${idx}" }`); + t.is(req.headers['cookie'], 'key=expected'); + res.end(); + }; + }), + ); + + await jar.setCookie('key=expected', `http://localhost:${port}`); + + for (let idx = 0; idx < times; idx++) { + await needle('post', `http://localhost:${port}`, `{ "index": "${idx}" }`, { + agent, + }); + } + + t.plan(times * 2); +}); diff --git a/src/__tests__/node-fetch.spec.ts b/src/__tests__/node-fetch.spec.ts index 423136ee..55b3bc08 100644 --- a/src/__tests__/node-fetch.spec.ts +++ b/src/__tests__/node-fetch.spec.ts @@ -3,7 +3,7 @@ import { CookieJar } from 'tough-cookie'; import fetch from 'node-fetch'; import { HttpCookieAgent } from '../'; -import { createTestServer } from './helpers'; +import { createTestServer, readStream } from './helpers'; test('should set cookies to CookieJar from Set-Cookie header', async (t) => { const jar = new CookieJar(); @@ -181,3 +181,32 @@ test('should emit error when CookieJar#setCookie throws error.', async (t) => { t.plan(1); }); + +test('should send post data when keepalive is enabled', async (t) => { + const times = 2; + + const jar = new CookieJar(); + const agent = new HttpCookieAgent({ jar, keepAlive: true }); + + const { port } = await createTestServer( + Array.from({ length: times }, (_, idx) => { + return async (req, res) => { + t.is(await readStream(req), `{ "index": "${idx}" }`); + t.is(req.headers['cookie'], 'key=expected'); + res.end(); + }; + }), + ); + + await jar.setCookie('key=expected', `http://localhost:${port}`); + + for (let idx = 0; idx < times; idx++) { + await fetch(`http://localhost:${port}`, { + method: 'POST', + body: `{ "index": "${idx}" }`, + agent, + }); + } + + t.plan(times * 2); +}); diff --git a/src/__tests__/phin.spec.ts b/src/__tests__/phin.spec.ts index c59d05a3..56326f30 100644 --- a/src/__tests__/phin.spec.ts +++ b/src/__tests__/phin.spec.ts @@ -3,7 +3,7 @@ import { CookieJar } from 'tough-cookie'; import phin from 'phin'; import { HttpCookieAgent } from '../'; -import { createTestServer } from './helpers'; +import { createTestServer, readStream } from './helpers'; test('should set cookies to CookieJar from Set-Cookie header', async (t) => { const jar = new CookieJar(); @@ -190,3 +190,33 @@ test('should emit error when CookieJar#setCookie throws error.', async (t) => { t.plan(1); }); + +test('should send post data when keepalive is enabled', async (t) => { + const times = 2; + + const jar = new CookieJar(); + const agent = new HttpCookieAgent({ jar, keepAlive: true }); + + const { port } = await createTestServer( + Array.from({ length: times }, (_, idx) => { + return async (req, res) => { + t.is(await readStream(req), `{ "index": "${idx}" }`); + t.is(req.headers['cookie'], 'key=expected'); + res.end(); + }; + }), + ); + + await jar.setCookie('key=expected', `http://localhost:${port}`); + + for (let idx = 0; idx < times; idx++) { + await phin({ + method: 'POST', + data: `{ "index": "${idx}" }`, + url: `http://localhost:${port}`, + core: { agent }, + }); + } + + t.plan(times * 2); +}); diff --git a/src/__tests__/request.spec.ts b/src/__tests__/request.spec.ts index aa97f042..5e55b4b9 100644 --- a/src/__tests__/request.spec.ts +++ b/src/__tests__/request.spec.ts @@ -4,7 +4,7 @@ import { CookieJar } from 'tough-cookie'; import request from 'request'; import { HttpCookieAgent } from '../'; -import { createTestServer } from './helpers'; +import { createTestServer, readStream } from './helpers'; test('should set cookies to CookieJar from Set-Cookie header', async (t) => { const jar = new CookieJar(); @@ -190,3 +190,33 @@ test('should emit error when CookieJar#setCookie throws error.', async (t) => { t.plan(1); }); + +test('should send post data when keepalive is enabled', async (t) => { + const times = 2; + + const jar = new CookieJar(); + const agent = new HttpCookieAgent({ jar, keepAlive: true }); + + const { port } = await createTestServer( + Array.from({ length: times }, (_, idx) => { + return async (req, res) => { + t.is(await readStream(req), `{ "index": "${idx}" }`); + t.is(req.headers['cookie'], 'key=expected'); + res.end(); + }; + }), + ); + + await jar.setCookie('key=expected', `http://localhost:${port}`); + + for (let idx = 0; idx < times; idx++) { + await promisify(request)({ + agent, + method: 'POST', + body: `{ "index": "${idx}" }`, + url: `http://localhost:${port}`, + }); + } + + t.plan(times * 2); +}); diff --git a/src/__tests__/superagent.spec.ts b/src/__tests__/superagent.spec.ts index e8aba9c1..80bcc8fe 100644 --- a/src/__tests__/superagent.spec.ts +++ b/src/__tests__/superagent.spec.ts @@ -3,7 +3,7 @@ import { CookieJar } from 'tough-cookie'; import superagent from 'superagent'; import { HttpCookieAgent } from '../'; -import { createTestServer } from './helpers'; +import { createTestServer, readStream } from './helpers'; test('should set cookies to CookieJar from Set-Cookie header', async (t) => { const jar = new CookieJar(); @@ -163,3 +163,28 @@ test('should emit error when CookieJar#setCookie throws error.', async (t) => { t.plan(1); }); + +test('should send post data when keepalive is enabled', async (t) => { + const times = 2; + + const jar = new CookieJar(); + const agent = new HttpCookieAgent({ jar, keepAlive: true }); + + const { port } = await createTestServer( + Array.from({ length: times }, (_, idx) => { + return async (req, res) => { + t.is(await readStream(req), `{ "index": "${idx}" }`); + t.is(req.headers['cookie'], 'key=expected'); + res.end(); + }; + }), + ); + + await jar.setCookie('key=expected', `http://localhost:${port}`); + + for (let idx = 0; idx < times; idx++) { + await superagent.post(`http://localhost:${port}`).send(`{ "index": "${idx}" }`).agent(agent); + } + + t.plan(times * 2); +}); diff --git a/src/__tests__/urllib.spec.ts b/src/__tests__/urllib.spec.ts index 0ce63fdd..cb7dd769 100644 --- a/src/__tests__/urllib.spec.ts +++ b/src/__tests__/urllib.spec.ts @@ -3,7 +3,7 @@ import { CookieJar } from 'tough-cookie'; import urllib from 'urllib'; import { HttpCookieAgent } from '../'; -import { createTestServer } from './helpers'; +import { createTestServer, readStream } from './helpers'; test('should set cookies to CookieJar from Set-Cookie header', async (t) => { const jar = new CookieJar(); @@ -182,3 +182,32 @@ test('should emit error when CookieJar#setCookie throws error.', async (t) => { t.plan(1); }); + +test('should send post data when keepalive is enabled', async (t) => { + const times = 2; + + const jar = new CookieJar(); + const agent = new HttpCookieAgent({ jar, keepAlive: true }); + + const { port } = await createTestServer( + Array.from({ length: times }, (_, idx) => { + return async (req, res) => { + t.is(await readStream(req), `{ "index": "${idx}" }`); + t.is(req.headers['cookie'], 'key=expected'); + res.end(); + }; + }), + ); + + await jar.setCookie('key=expected', `http://localhost:${port}`); + + for (let idx = 0; idx < times; idx++) { + await urllib.request(`http://localhost:${port}`, { + agent, + method: 'POST', + data: `{ "index": "${idx}" }`, + }); + } + + t.plan(times * 2); +}); diff --git a/src/__tests__/wreck.spec.ts b/src/__tests__/wreck.spec.ts index 707144e0..777c5bdd 100644 --- a/src/__tests__/wreck.spec.ts +++ b/src/__tests__/wreck.spec.ts @@ -3,7 +3,7 @@ import { CookieJar } from 'tough-cookie'; import Wreck from '@hapi/wreck'; import { HttpCookieAgent } from '../'; -import { createTestServer } from './helpers'; +import { createTestServer, readStream } from './helpers'; test('should set cookies to CookieJar from Set-Cookie header', async (t) => { const jar = new CookieJar(); @@ -182,3 +182,31 @@ test('should emit error when CookieJar#setCookie throws error.', async (t) => { t.plan(1); }); + +test('should send post data when keepalive is enabled', async (t) => { + const times = 2; + + const jar = new CookieJar(); + const agent = new HttpCookieAgent({ jar, keepAlive: true }); + + const { port } = await createTestServer( + Array.from({ length: times }, (_, idx) => { + return async (req, res) => { + t.is(await readStream(req), `{ "index": "${idx}" }`); + t.is(req.headers['cookie'], 'key=expected'); + res.end(); + }; + }), + ); + + await jar.setCookie('key=expected', `http://localhost:${port}`); + + for (let idx = 0; idx < times; idx++) { + await Wreck.post(`http://localhost:${port}`, { + agent, + payload: `{ "index": "${idx}" }`, + }); + } + + t.plan(times * 2); +}); From b54a5e5c29bde2ff38c3322fb58f6bccb210db9f Mon Sep 17 00:00:00 2001 From: 3846masa <3846masahiro+git@gmail.com> Date: Mon, 2 May 2022 14:52:59 +0900 Subject: [PATCH 3/3] fix: send data correctly when keepalive is enabled closed https://github.com/3846masa/http-cookie-agent/issues/65 --- src/create_cookie_agent.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/create_cookie_agent.ts b/src/create_cookie_agent.ts index 0e052c3d..bae68344 100644 --- a/src/create_cookie_agent.ts +++ b/src/create_cookie_agent.ts @@ -104,6 +104,7 @@ export function createCookieAgent< req._header = null; req.setHeader('Cookie', cookieHeader); req._implicitHeader(); + req._headerSent = alreadyHeaderSent; if (alreadyHeaderSent !== true) { return;