fix: system proxy resolver updates (#6273)

This commit is contained in:
lohit
2026-01-29 08:55:13 +00:00
committed by GitHub
parent 4f327b7b77
commit b3a66e9c3c
26 changed files with 2232 additions and 116 deletions

View File

@@ -1,7 +1,7 @@
const fs = require('node:fs');
const path = require('path');
const { get } = require('lodash');
const { getCACertificates } = require('@usebruno/requests');
const { getCACertificates, getSystemProxy } = require('@usebruno/requests');
const { preferencesUtil } = require('../../store/preferences');
const { getBrunoConfig } = require('../../store/bruno-config');
const { interpolateString } = require('./interpolate-string');
@@ -144,6 +144,8 @@ const getCertsAndProxyConfig = async ({
} else if (!globalDisabled && globalInherit) {
// Use system proxy
proxyMode = 'system';
const systemProxyConfig = await getSystemProxy();
proxyConfig = systemProxyConfig;
}
// else: global proxy is disabled, proxyMode stays 'off'
}

View File

@@ -1,18 +1,14 @@
const { ipcMain, nativeTheme } = require('electron');
const { getPreferences, savePreferences, preferencesUtil } = require('../store/preferences');
const { getPreferences, savePreferences } = require('../store/preferences');
const { globalEnvironmentsStore } = require('../store/global-environments');
const { getSystemProxy } = require('@usebruno/requests');
const registerPreferencesIpc = (mainWindow, watcher) => {
const registerPreferencesIpc = (mainWindow) => {
ipcMain.handle('renderer:ready', async (event) => {
// load preferences
const preferences = getPreferences();
mainWindow.webContents.send('main:load-preferences', preferences);
// load system proxy vars
const systemProxyVars = preferencesUtil.getSystemProxyEnvVariables();
const { http_proxy, https_proxy, no_proxy } = systemProxyVars || {};
mainWindow.webContents.send('main:load-system-proxy-env', { http_proxy, https_proxy, no_proxy });
try {
// load global environments
const globalEnvironments = globalEnvironmentsStore.getGlobalEnvironments();
@@ -42,6 +38,11 @@ const registerPreferencesIpc = (mainWindow, watcher) => {
ipcMain.on('renderer:theme-change', (event, theme) => {
nativeTheme.themeSource = theme;
});
ipcMain.handle('renderer:get-system-proxy-variables', async () => {
const systemProxyConfig = await getSystemProxy();
return systemProxyConfig;
});
};
module.exports = registerPreferencesIpc;

View File

@@ -124,9 +124,27 @@ class PreferencesStore {
getPreferences() {
let preferences = this.store.get('preferences', {});
// Migrate proxy configuration from old formats to new format
const proxyMigrated = get(preferences, '_migrations.proxyConfigFormat', false);
if (!proxyMigrated && preferences?.proxy) {
// Handle existing users without proxy settings
// They should get disabled proxy by default, not inherit from system
// New users (empty preferences) will get defaultPreferences.proxy via merge
if (Object.keys(preferences).length > 0 && !preferences.proxy) {
preferences.proxy = {
inherit: false,
disabled: true,
config: {
protocol: 'http',
hostname: '',
port: null,
auth: {
username: '',
password: ''
},
bypassProxy: ''
}
};
}
if (preferences?.proxy) {
const proxy = preferences.proxy || {};
// Check if this is an old format that needs migration
@@ -181,15 +199,6 @@ class PreferencesStore {
}
preferences.proxy = newProxy;
// Mark migration as complete // ?
// if (!preferences._migrations) {
// preferences._migrations = {};
// }
// preferences._migrations.proxyConfigFormat = true;
// Save the migrated preferences back to the store
// this.store.set('preferences', preferences);
}
}
@@ -260,7 +269,7 @@ const preferencesUtil = {
return get(getPreferences(), 'request.timeout', 0);
},
getGlobalProxyConfig: () => {
return get(getPreferences(), 'proxy', {});
return get(getPreferences(), 'proxy', defaultPreferences.proxy);
},
shouldStoreCookies: () => {
return get(getPreferences(), 'request.storeCookies', true);
@@ -274,14 +283,6 @@ const preferencesUtil = {
getResponsePaneOrientation: () => {
return get(getPreferences(), 'layout.responsePaneOrientation', 'horizontal');
},
getSystemProxyEnvVariables: () => {
const { http_proxy, HTTP_PROXY, https_proxy, HTTPS_PROXY, no_proxy, NO_PROXY } = process.env;
return {
http_proxy: http_proxy || HTTP_PROXY,
https_proxy: https_proxy || HTTPS_PROXY,
no_proxy: no_proxy || NO_PROXY
};
},
isBetaFeatureEnabled: (featureName) => {
return get(getPreferences(), `beta.${featureName}`, false);
},

View File

@@ -4,7 +4,6 @@ const { HttpsProxyAgent } = require('https-proxy-agent');
const { interpolateString } = require('../ipc/network/interpolate-string');
const { SocksProxyAgent } = require('socks-proxy-agent');
const { HttpProxyAgent } = require('http-proxy-agent');
const { preferencesUtil } = require('../store/preferences');
const { isEmpty, get, isUndefined, isNull } = require('lodash');
const DEFAULT_PORTS = {
@@ -84,6 +83,29 @@ class PatchedHttpsProxyAgent extends HttpsProxyAgent {
}
}
function createTimelineHttpAgentClass(BaseAgentClass) {
return class extends BaseAgentClass {
constructor(options, timeline) {
// For proxy agents, the first argument is the proxy URI and the second is options
const { proxy: proxyUri, httpProxyAgentOptions } = options || {};
if (!proxyUri) {
throw new Error('TimelineHttpProxyAgent requires options.proxy to be set');
}
super(proxyUri, httpProxyAgentOptions);
this.timeline = Array.isArray(timeline) ? timeline : [];
// Log the proxy details
this.timeline.push({
timestamp: new Date(),
type: 'info',
message: `Using proxy: ${proxyUri}`
});
}
};
}
function createTimelineAgentClass(BaseAgentClass) {
return class extends BaseAgentClass {
constructor(options, timeline) {
@@ -312,6 +334,10 @@ function setupProxyAgents({
rejectUnauthorized: httpsAgentRequestFields.rejectUnauthorized !== undefined ? httpsAgentRequestFields.rejectUnauthorized : true
};
const httpProxyAgentOptions = {
keepAlive: true
};
if (proxyMode === 'on') {
const shouldProxy = shouldUseProxy(requestConfig.url, get(proxyConfig, 'bypassProxy', ''));
if (shouldProxy) {
@@ -337,8 +363,9 @@ function setupProxyAgents({
requestConfig.httpAgent = new TimelineSocksProxyAgent({ proxy: proxyUri }, timeline);
requestConfig.httpsAgent = new TimelineSocksProxyAgent({ proxy: proxyUri, ...tlsOptions }, timeline);
} else {
const TimelineHttpProxyAgent = createTimelineHttpAgentClass(HttpProxyAgent);
const TimelineHttpsProxyAgent = createTimelineAgentClass(PatchedHttpsProxyAgent);
requestConfig.httpAgent = new HttpProxyAgent(proxyUri); // For http, no need for timeline
requestConfig.httpAgent = new TimelineHttpProxyAgent({ proxy: proxyUri, httpProxyAgentOptions }, timeline);
requestConfig.httpsAgent = new TimelineHttpsProxyAgent(
{ proxy: proxyUri, ...tlsOptions },
timeline
@@ -350,19 +377,22 @@ function setupProxyAgents({
requestConfig.httpsAgent = new TimelineHttpsAgent(tlsOptions, timeline);
}
} else if (proxyMode === 'system') {
const { http_proxy, https_proxy, no_proxy } = preferencesUtil.getSystemProxyEnvVariables();
const { http_proxy, https_proxy, no_proxy } = proxyConfig || {};
const shouldUseSystemProxy = shouldUseProxy(requestConfig.url, no_proxy || '');
const parsedUrl = parseUrl(requestConfig.url);
const isHttpsRequest = parsedUrl.protocol === 'https:';
if (shouldUseSystemProxy) {
try {
if (http_proxy?.length) {
if (http_proxy?.length && !isHttpsRequest) {
new URL(http_proxy);
requestConfig.httpAgent = new HttpProxyAgent(http_proxy);
const TimelineHttpProxyAgent = createTimelineHttpAgentClass(HttpProxyAgent);
requestConfig.httpAgent = new TimelineHttpProxyAgent({ proxy: http_proxy, httpProxyAgentOptions }, timeline);
}
} catch (error) {
throw new Error('Invalid system http_proxy');
throw new Error(`Invalid system http_proxy "${http_proxy}": ${error.message}`);
}
try {
if (https_proxy?.length) {
if (https_proxy?.length && isHttpsRequest) {
new URL(https_proxy);
const TimelineHttpsProxyAgent = createTimelineAgentClass(PatchedHttpsProxyAgent);
requestConfig.httpsAgent = new TimelineHttpsProxyAgent(
@@ -374,7 +404,7 @@ function setupProxyAgents({
requestConfig.httpsAgent = new TimelineHttpsAgent(tlsOptions, timeline);
}
} catch (error) {
throw new Error('Invalid system https_proxy');
throw new Error(`Invalid system https_proxy "${https_proxy}": ${error.message}`);
}
} else {
const TimelineHttpsAgent = createTimelineAgentClass(https.Agent);