feat(preferences): add cache.httpHttpsAgents.enabled preference

This commit is contained in:
lohit-bruno
2026-03-03 09:18:59 +05:30
parent 37d1b3c5f9
commit 3e88cd6759
6 changed files with 52 additions and 35 deletions

View File

@@ -429,10 +429,12 @@ const runSingleRequest = async function (
// Prepare TLS options for agent caching
const tlsOptions = {
...httpsAgentRequestFields,
keepAlive: true
...httpsAgentRequestFields
};
// HTTP agent options — separate from tlsOptions to avoid leaking TLS fields
const httpAgentOptions = { keepAlive: true };
const parsedRequestUrl = new URL(request.url);
const isHttpsRequest = parsedRequestUrl.protocol === 'https:';
@@ -459,13 +461,13 @@ const runSingleRequest = async function (
if (isHttpsRequest) {
request.httpsAgent = getOrCreateAgent(SocksProxyAgent, tlsOptions, proxyUri);
} else {
request.httpAgent = getOrCreateHttpAgent(SocksProxyAgent, { keepAlive: true }, proxyUri);
request.httpAgent = getOrCreateHttpAgent(SocksProxyAgent, httpAgentOptions, proxyUri);
}
} else {
if (isHttpsRequest) {
request.httpsAgent = getOrCreateAgent(PatchedHttpsProxyAgent, tlsOptions, proxyUri);
} else {
request.httpAgent = getOrCreateHttpAgent(HttpProxyAgent, { keepAlive: true }, proxyUri);
request.httpAgent = getOrCreateHttpAgent(HttpProxyAgent, httpAgentOptions, proxyUri);
}
}
}
@@ -477,7 +479,7 @@ const runSingleRequest = async function (
try {
if (http_proxy?.length && !isHttpsRequest) {
new URL(http_proxy);
request.httpAgent = getOrCreateHttpAgent(HttpProxyAgent, { keepAlive: true }, http_proxy);
request.httpAgent = getOrCreateHttpAgent(HttpProxyAgent, httpAgentOptions, http_proxy);
}
} catch (error) {
throw new Error('Invalid system http_proxy');
@@ -501,7 +503,7 @@ const runSingleRequest = async function (
if (isHttpsRequest) {
request.httpsAgent = getOrCreateAgent(https.Agent, tlsOptions, null);
} else {
request.httpAgent = getOrCreateHttpAgent(http.Agent, { keepAlive: true }, null);
request.httpAgent = getOrCreateHttpAgent(http.Agent, httpAgentOptions, null);
}
}
@@ -609,7 +611,7 @@ const runSingleRequest = async function (
let token;
if (oauth2RequestUrl) {
const tlsOptions = {
const oauth2ConfigOptions = {
noproxy: options.noproxy,
shouldVerifyTls: !insecure,
shouldUseCustomCaCertificate: !!options['cacert'],
@@ -626,7 +628,7 @@ const runSingleRequest = async function (
const { httpAgent: oauth2HttpAgent, httpsAgent: oauth2HttpsAgent } = await getHttpHttpsAgents({
requestUrl: oauth2RequestUrl,
collectionPath,
options: tlsOptions,
options: oauth2ConfigOptions,
clientCertificates: interpolatedClientCertificates,
collectionLevelProxy: interpolatedProxyConfig,
systemProxyConfig

View File

@@ -106,6 +106,11 @@ const defaultPreferences = {
},
display: {
zoomPercentage: 100
},
cache: {
httpHttpsAgents: {
enabled: true
}
}
};
@@ -164,7 +169,12 @@ const preferencesSchema = Yup.object().shape({
}),
display: Yup.object({
zoomPercentage: Yup.number().min(50).max(150)
})
}),
cache: Yup.object({
httpHttpsAgents: Yup.object({
enabled: Yup.boolean()
})
}).optional()
});
class PreferencesStore {
@@ -351,6 +361,9 @@ const preferencesUtil = {
getZoomPercentage: () => {
return get(getPreferences(), 'display.zoomPercentage', 100);
},
isHttpHttpsAgentCachingEnabled: () => {
return get(getPreferences(), 'cache.httpHttpsAgents.enabled', true);
},
hasLaunchedBefore: () => {
return get(getPreferences(), 'onboarding.hasLaunchedBefore', false);
},

View File

@@ -6,7 +6,7 @@ const { interpolateString } = require('../ipc/network/interpolate-string');
const { SocksProxyAgent } = require('socks-proxy-agent');
const { HttpProxyAgent } = require('http-proxy-agent');
const { isEmpty, get, isUndefined, isNull } = require('lodash');
const { getOrCreateAgent, getOrCreateHttpAgent, clearAgentCache, getAgentCacheSize } = require('@usebruno/requests');
const { getOrCreateAgent, getOrCreateHttpAgent } = require('@usebruno/requests');
const DEFAULT_PORTS = {
ftp: 21,
@@ -200,9 +200,5 @@ function setupProxyAgents({
module.exports = {
shouldUseProxy,
PatchedHttpsProxyAgent,
setupProxyAgents,
clearAgentCache,
getAgentCacheSize,
getOrCreateAgent,
getOrCreateHttpAgent
setupProxyAgents
};

View File

@@ -61,6 +61,14 @@ describe('Agent Cache', () => {
expect(httpsAgent).not.toBe(httpAgent);
expect(getAgentCacheSize()).toBe(2);
});
it('creates separate agents for different keepAlive values', () => {
const agent1 = getOrCreateAgent(https.Agent, { keepAlive: true });
const agent2 = getOrCreateAgent(https.Agent, { keepAlive: false });
expect(agent1).not.toBe(agent2);
expect(getAgentCacheSize()).toBe(2);
});
});
describe('timeline support', () => {

View File

@@ -1,6 +1,4 @@
import crypto from 'node:crypto';
import http from 'node:http';
import https from 'node:https';
import type { Agent as HttpAgent } from 'node:http';
import type { Agent as HttpsAgent } from 'node:https';
import { createTimelineAgentClass, createTimelineHttpAgentClass, type TimelineEntry, type AgentOptions, type HttpAgentOptions, type AgentClass, type HttpAgentClass } from './timeline-agent';
@@ -82,6 +80,7 @@ function getAgentCacheKey(agentClassId: number, options: AgentOptions, proxyUri:
const keyData = {
agentClassId,
proxyUri,
keepAlive: options.keepAlive,
rejectUnauthorized: options.rejectUnauthorized,
// Hash certificates and passphrase instead of including full content
ca: hashCaValue(options.ca),
@@ -233,7 +232,7 @@ function getOrCreateAgent(
timeline,
getAgentCacheKey,
getTimelineAgentClass,
'Reusing cached agent (SSL session reuse enabled)'
'Reusing cached https agent'
);
}
@@ -261,7 +260,7 @@ function getOrCreateHttpAgent(
timeline,
getHttpAgentCacheKey,
getTimelineHttpAgentClass,
'Reusing cached agent (connection reuse enabled)'
'Reusing cached http agent'
) as HttpAgent;
}

View File

@@ -1,5 +1,6 @@
import * as fs from 'node:fs';
import * as path from 'node:path';
import http from 'node:http';
import https from 'node:https';
import type { Agent as HttpAgent } from 'node:http';
import type { Agent as HttpsAgent } from 'node:https';
@@ -11,6 +12,7 @@ import { isEmpty, get, isUndefined, isNull } from 'lodash';
import { getCACertificates } from './ca-cert';
import { transformProxyConfig } from './proxy-util';
import { getOrCreateAgent, getOrCreateHttpAgent } from './agent-cache';
import type { TimelineEntry } from './timeline-agent';
const DEFAULT_PORTS: Record<string, number> = {
ftp: 21,
@@ -114,12 +116,6 @@ type GetCertsAndProxyConfigResult = {
certsConfig: CertsConfig;
};
type TimelineEntry = {
timestamp: Date;
type: 'info' | 'tls' | 'error';
message: string;
};
type CreateAgentsParams = {
requestUrl?: string;
proxyMode: ProxyMode;
@@ -368,9 +364,10 @@ function createAgents({
let httpAgent: HttpAgent | undefined;
let httpsAgent: HttpsAgent | HttpsProxyAgent<any> | SocksProxyAgent | undefined;
// Determine if this is an HTTPS request
const isHttpsRequest = requestUrl ? requestUrl.startsWith('https:') : true;
if (proxyMode === 'on') {
// Determine if this is an HTTPS request
const isHttpsRequest = requestUrl ? requestUrl.startsWith('https:') : true;
const shouldProxy = shouldUseProxy(requestUrl, get(proxyConfig, 'bypassProxy', ''));
if (shouldProxy) {
const proxyProtocol = get(proxyConfig, 'protocol');
@@ -421,7 +418,7 @@ function createAgents({
const shouldUseSystemProxy = shouldUseProxy(requestUrl, no_proxy || '');
if (shouldUseSystemProxy) {
try {
if (http_proxy?.length) {
if (http_proxy?.length && !isHttpsRequest) {
new URL(http_proxy);
httpAgent = getOrCreateHttpAgent(HttpProxyAgent, { keepAlive: true }, http_proxy, timeline || null);
}
@@ -429,20 +426,22 @@ function createAgents({
throw new Error('Invalid system http_proxy');
}
try {
if (https_proxy?.length) {
if (https_proxy?.length && isHttpsRequest) {
new URL(https_proxy);
httpsAgent = getOrCreateAgent(PatchedHttpsProxyAgent, tlsOptions as any, https_proxy, timeline || null) as HttpsAgent;
} else {
httpsAgent = getOrCreateAgent(https.Agent, tlsOptions as any, null, timeline || null) as HttpsAgent;
}
} catch (error) {
throw new Error('Invalid system https_proxy');
}
} else {
httpsAgent = getOrCreateAgent(https.Agent, tlsOptions as any, null, timeline || null) as HttpsAgent;
}
} else {
httpsAgent = getOrCreateAgent(https.Agent, tlsOptions as any, null, timeline || null) as HttpsAgent;
}
if (!httpAgent && !httpsAgent) {
if (isHttpsRequest) {
httpsAgent = getOrCreateAgent(https.Agent, tlsOptions as any, null, timeline || null) as HttpsAgent;
} else {
httpAgent = getOrCreateHttpAgent(http.Agent, { keepAlive: true }, null, timeline || null);
}
}
return { httpAgent, httpsAgent };