<commit_before>import {Agent as HttpAgent} from 'http';
import {Agent as HttpsAgent} from 'https';
import {Socket} from 'net';
import {TLSSocket} from 'tls';
import test, {Constructor} from 'ava';
import sinon = require('sinon');
import {ExtendedTestServer} from './helpers/types';
import withServer from './helpers/with-server';

const prepareServer = (server: ExtendedTestServer): void => {
	server.get('/', (request, response) => {
		if (request.socket instanceof TLSSocket) {
			response.end('https');
		} else {
			response.end('http');
		}
	});

	server.get('/httpsToHttp', (_request, response) => {
		response.writeHead(302, {
			location: server.url
		});
		response.end();
	});

	server.get('/httpToHttps', (_request, response) => {
		response.writeHead(302, {
			location: server.sslUrl
		});
		response.end();
	});
};

const createAgentSpy = <T extends HttpsAgent>(AgentClass: Constructor): {agent: T; spy: sinon.SinonSpy} => {
	const agent: T = new AgentClass({keepAlive: true});
	// @ts-ignore This IS correct
	const spy = sinon.spy(agent, 'addRequest');
	return {agent, spy};
};

test('non-object agent option works with http', withServer, async (t, server, got) => {
	server.get('/', (_request, response) => {
		response.end('ok');
	});

	const {agent, spy} = createAgentSpy(HttpAgent);

	t.truthy((await got({
		rejectUnauthorized: false,
		agent: {
			http: agent
		}
	})).body);
	t.true(spy.calledOnce);

	// Make sure to close all open sockets
	agent.destroy();
});

test('non-object agent option works with https', withServer, async (t, server, got) => {
	server.get('/', (_request, response) => {
		response.end('ok');
	});

	const {agent, spy} = createAgentSpy(HttpsAgent);

	t.truthy((await got.secure({
		rejectUnauthorized: false,
		agent: {
			https: agent
		}
	})).body);
	t.true(spy.calledOnce);

	// Make sure to close all open sockets
	agent.destroy();
});

test('redirects from http to https work with an agent object', withServer, async (t, server, got) => {
	prepareServer(server);

	const {agent: httpAgent, spy: httpSpy} = createAgentSpy(HttpAgent);
	const {agent: httpsAgent, spy: httpsSpy} = createAgentSpy<HttpsAgent>(HttpsAgent);

	t.truthy((await got('httpToHttps', {
		rejectUnauthorized: false,
		agent: {
			http: httpAgent,
			https: httpsAgent
		}
	})).body);
	t.true(httpSpy.calledOnce);
	t.true(httpsSpy.calledOnce);

	// Make sure to close all open sockets
	httpAgent.destroy();
	httpsAgent.destroy();
});

test('redirects from https to http work with an agent object', withServer, async (t, server, got) => {
	prepareServer(server);

	const {agent: httpAgent, spy: httpSpy} = createAgentSpy(HttpAgent);
	const {agent: httpsAgent, spy: httpsSpy} = createAgentSpy(HttpsAgent);

	t.truthy((await got.secure('httpsToHttp', {
		rejectUnauthorized: false,
		agent: {
			http: httpAgent,
			https: httpsAgent
		}
	})).body);
	t.true(httpSpy.calledOnce);
	t.true(httpsSpy.calledOnce);

	// Make sure to close all open sockets
	httpAgent.destroy();
	httpsAgent.destroy();
});

test('socket connect listener cleaned up after request', withServer, async (t, server, got) => {
	server.get('/', (_request, response) => {
		response.end('ok');
	});

	const {agent} = createAgentSpy(HttpsAgent);

	// Make sure there are no memory leaks when reusing keep-alive sockets
	for (let i = 0; i < 20; i++) {
		// eslint-disable-next-line no-await-in-loop
		await got.secure({
			rejectUnauthorized: false,
			agent: {
				https: agent
			}
		});
	}

	// @ts-ignore
	for (const value of Object.values(agent.freeSockets) as [Socket[]]) {
		for (const sock of value) {
			t.is(sock.listenerCount('connect'), 0);
		}
	}

	// Make sure to close all open sockets
	agent.destroy();
});
<commit_msg>Test `socket hung up` error regression<commit_after>import {Agent as HttpAgent} from 'http';
import {Agent as HttpsAgent} from 'https';
import {Socket} from 'net';
import {TLSSocket} from 'tls';
import test, {Constructor} from 'ava';
import sinon = require('sinon');
import {ExtendedTestServer} from './helpers/types';
import withServer from './helpers/with-server';

const prepareServer = (server: ExtendedTestServer): void => {
	server.get('/', (request, response) => {
		if (request.socket instanceof TLSSocket) {
			response.end('https');
		} else {
			response.end('http');
		}
	});

	server.get('/httpsToHttp', (_request, response) => {
		response.writeHead(302, {
			location: server.url
		});
		response.end();
	});

	server.get('/httpToHttps', (_request, response) => {
		response.writeHead(302, {
			location: server.sslUrl
		});
		response.end();
	});
};

const createAgentSpy = <T extends HttpsAgent>(AgentClass: Constructor): {agent: T; spy: sinon.SinonSpy} => {
	const agent: T = new AgentClass({keepAlive: true});
	// @ts-ignore This IS correct
	const spy = sinon.spy(agent, 'addRequest');
	return {agent, spy};
};

test('non-object agent option works with http', withServer, async (t, server, got) => {
	server.get('/', (_request, response) => {
		response.end('ok');
	});

	const {agent, spy} = createAgentSpy(HttpAgent);

	t.truthy((await got({
		rejectUnauthorized: false,
		agent: {
			http: agent
		}
	})).body);
	t.true(spy.calledOnce);

	// Make sure to close all open sockets
	agent.destroy();
});

test('non-object agent option works with https', withServer, async (t, server, got) => {
	server.get('/', (_request, response) => {
		response.end('ok');
	});

	const {agent, spy} = createAgentSpy(HttpsAgent);

	t.truthy((await got.secure({
		rejectUnauthorized: false,
		agent: {
			https: agent
		}
	})).body);
	t.true(spy.calledOnce);

	// Make sure to close all open sockets
	agent.destroy();
});

test('redirects from http to https work with an agent object', withServer, async (t, server, got) => {
	prepareServer(server);

	const {agent: httpAgent, spy: httpSpy} = createAgentSpy(HttpAgent);
	const {agent: httpsAgent, spy: httpsSpy} = createAgentSpy<HttpsAgent>(HttpsAgent);

	t.truthy((await got('httpToHttps', {
		rejectUnauthorized: false,
		agent: {
			http: httpAgent,
			https: httpsAgent
		}
	})).body);
	t.true(httpSpy.calledOnce);
	t.true(httpsSpy.calledOnce);

	// Make sure to close all open sockets
	httpAgent.destroy();
	httpsAgent.destroy();
});

test('redirects from https to http work with an agent object', withServer, async (t, server, got) => {
	prepareServer(server);

	const {agent: httpAgent, spy: httpSpy} = createAgentSpy(HttpAgent);
	const {agent: httpsAgent, spy: httpsSpy} = createAgentSpy(HttpsAgent);

	t.truthy((await got.secure('httpsToHttp', {
		rejectUnauthorized: false,
		agent: {
			http: httpAgent,
			https: httpsAgent
		}
	})).body);
	t.true(httpSpy.calledOnce);
	t.true(httpsSpy.calledOnce);

	// Make sure to close all open sockets
	httpAgent.destroy();
	httpsAgent.destroy();
});

test('socket connect listener cleaned up after request', withServer, async (t, server, got) => {
	server.get('/', (_request, response) => {
		response.end('ok');
	});

	const {agent} = createAgentSpy(HttpsAgent);

	// Make sure there are no memory leaks when reusing keep-alive sockets
	for (let i = 0; i < 20; i++) {
		// eslint-disable-next-line no-await-in-loop
		await got.secure({
			rejectUnauthorized: false,
			agent: {
				https: agent
			}
		});
	}

	// @ts-ignore
	for (const value of Object.values(agent.freeSockets) as [Socket[]]) {
		for (const sock of value) {
			t.is(sock.listenerCount('connect'), 0);
		}
	}

	// Make sure to close all open sockets
	agent.destroy();
});

test('no socket hung up regression', withServer, async (t, server, got) => {
	const agent = new HttpAgent({keepAlive: true});
	const token = 'helloworld';

	server.get('/', (request, response) => {
		if (request.headers.token !== token) {
			response.statusCode = 401;
			response.end();
			return;
		}

		response.end('ok');
	})

	const {body} = await got({
		prefixUrl: 'http://127.0.0.1:3000',
		agent: {
			http: agent,
		},
		hooks: {
			afterResponse: [
				async (response, retryWithMergedOptions) => {
					// Force clean-up
					response.socket.destroy();

					// Unauthorized
					if (response.statusCode === 401) {
						return retryWithMergedOptions({
							headers: {
								token
							}
						});
					}

					// No changes otherwise
					return response;
				},
			],
		},
		// Disable automatic retries, manual retries are allowed
		retry: 0
	});

	t.is(body, 'ok');

	agent.destroy();
});

{'fraction_non_alphanumeric': 0.162904574226681, 'fraction_numerical': 0.004418965723157769, 'mean_word_length': 4.011370436864153, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 36, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 1, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5580302', 'n_tokens_mistral': 3110, 'n_tokens_neox': 2831, 'n_words': 738}