Added support for customizing proxy bypass address & network test URL

Signed-off-by: Loren Eteval <loren.eteval@proton.me>
This commit is contained in:
Loren Eteval
2026-04-05 00:07:46 +08:00
parent 3ed8db3cf9
commit f547c16aa2
7 changed files with 315 additions and 17 deletions
+9 -1
View File
@@ -73,7 +73,15 @@ class NetworkStateManager(Mixins.ConnectionAware, WebGETManager):
self.jobArrangeTimer.start(self.recalculateJobInterval(jobStatus=False))
def startSingleTest(self):
networkReply = self.webGET(NETWORK_STATE_TEST_URL)
# Use custom network state test URL if possible
settings = AppSettings.get('CustomNetworkStateTestURL')
if settings is None:
url = NETWORK_SPEED_TEST_URL
else:
url = settings
networkReply = self.webGET(url)
def abort(_networkReply):
if isinstance(_networkReply, QNetworkReply):
+35 -12
View File
@@ -253,21 +253,44 @@ class ConnectAction(AppQAction):
if self.actionQueue.empty():
if success:
SystemProxy.set(httpProxy, PROXY_SERVER_BYPASS)
# Use custom proxy bypass address if possible
settings = AppSettings.get('CustomProxyBypass')
self.doConnected()
APP().systemTray.showMessage(
f'{config.coreName()}: ' + _('Connected')
)
if AppSettings.isStateON_('PowerSaveMode'):
# Power optimization
self.actionTimer.start(CORE_CHECK_ALIVE_INTERVAL * 2)
if settings is None:
proxyServerBypass = PROXY_SERVER_BYPASS
else:
self.actionTimer.start(CORE_CHECK_ALIVE_INTERVAL)
proxyServerBypass = settings
self.doConnectedCallOnceOnly()
try:
SystemProxy.set(httpProxy, proxyServerBypass)
except Exception as ex:
# Any non-exit exceptions
logger.error(f'error while setting http proxy: {ex}')
logger.error(
f'failed to connect successfully. '
f'This may be due to improper proxy '
f'server bypass settings: {proxyServerBypass}'
)
self.coreManager.stopAll()
self.doDisconnectWithTrayMessage(
f'{config.coreName()}: ' + _('Unknown error')
)
else:
self.doConnected()
APP().systemTray.showMessage(
f'{config.coreName()}: ' + _('Connected')
)
if AppSettings.isStateON_('PowerSaveMode'):
# Power optimization
self.actionTimer.start(CORE_CHECK_ALIVE_INTERVAL * 2)
else:
self.actionTimer.start(CORE_CHECK_ALIVE_INTERVAL)
self.doConnectedCallOnceOnly()
else:
logger.error('failed to start core manager')
+137
View File
@@ -0,0 +1,137 @@
# Copyright (C) 2024–present Loren Eteval & contributors <loren.eteval@proton.me>
#
# This file is part of Furious.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from __future__ import annotations
from Furious.Frozenlib import *
from Furious.Qt import *
from Furious.Qt import gettext as _
from PySide6 import QtCore
from PySide6.QtWidgets import *
import logging
import functools
__all__ = ['GuiCustomizeNetworkTestDialog']
logger = logging.getLogger(__name__)
registerAppSettings('CustomNetworkSpeedTestURL')
registerAppSettings('CustomNetworkStateTestURL')
class GuiCustomizeNetworkTestDialog(AppQDialog):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setWindowTitle(_('Customize Network Test URL'))
self.setWindowModality(QtCore.Qt.WindowModality.WindowModal)
self.speedTestURLText = AppQLabel(_('Enter network speed test URL:'))
self.speedTestURLEdit = QLineEdit()
speedTestSettings = AppSettings.get('CustomNetworkSpeedTestURL')
if speedTestSettings is None:
self.speedTestURLEdit.setText(NETWORK_SPEED_TEST_URL)
else:
self.speedTestURLEdit.setText(speedTestSettings)
self.stateTestURLText = AppQLabel(_('Enter network state test URL:'))
self.stateTestURLEdit = QLineEdit()
stateTestSettings = AppSettings.get('CustomNetworkStateTestURL')
if stateTestSettings is None:
self.stateTestURLEdit.setText(NETWORK_STATE_TEST_URL)
else:
self.stateTestURLEdit.setText(stateTestSettings)
self.dialogBtns = AppQDialogButtonBox(QtCore.Qt.Orientation.Horizontal)
self.dialogBtns.addButton(_('OK'), AppQDialogButtonBox.ButtonRole.AcceptRole)
self.dialogBtns.addButton(
_('Cancel'), AppQDialogButtonBox.ButtonRole.RejectRole
)
self.dialogBtns.accepted.connect(self.accept)
self.dialogBtns.rejected.connect(self.reject)
self.speedTestResetBtn = AppQPushButton(_('Reset'))
self.speedTestResetBtn.clicked.connect(
lambda: self.speedTestURLEdit.setText(NETWORK_SPEED_TEST_URL)
)
self.stateTestResetBtn = AppQPushButton(_('Reset'))
self.stateTestResetBtn.clicked.connect(
lambda: self.stateTestURLEdit.setText(NETWORK_STATE_TEST_URL)
)
self.speedTestHboxLayout = QHBoxLayout()
self.speedTestHboxLayout.addWidget(self.speedTestURLEdit)
self.speedTestHboxLayout.addWidget(self.speedTestResetBtn)
self.stateTestHboxLayout = QHBoxLayout()
self.stateTestHboxLayout.addWidget(self.stateTestURLEdit)
self.stateTestHboxLayout.addWidget(self.stateTestResetBtn)
layout = QFormLayout()
layout.addRow(self.speedTestURLText)
layout.addRow(self.speedTestHboxLayout)
layout.addRow(self.stateTestURLText)
layout.addRow(self.stateTestHboxLayout)
layout.addRow(self.dialogBtns)
layout.setFormAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.setLayout(layout)
self.finished.connect(functools.partial(self.handleResultCode))
def setWidthAndHeight(self):
self.resize(656, 180)
def handleResultCode(self, code):
for widget, info, settingsName, reset in [
(
self.speedTestURLEdit,
'speed test URL',
'CustomNetworkSpeedTestURL',
NETWORK_SPEED_TEST_URL,
),
(
self.stateTestURLEdit,
'state test URL',
'CustomNetworkStateTestURL',
NETWORK_STATE_TEST_URL,
),
]:
if code == PySide6Legacy.enumValueWrapper(AppQDialog.DialogCode.Accepted):
value = widget.text()
logger.info(f'setting {info} to \'{value}\'')
AppSettings.set(settingsName, value)
else:
try:
settings = AppSettings.get(settingsName)
if settings is not None:
widget.setText(str(settings))
else:
widget.setText(reset)
except Exception as ex:
# Any non-exit exceptions
logger.error(f'error restoring {info}: {ex}')
+107
View File
@@ -0,0 +1,107 @@
# Copyright (C) 2024–present Loren Eteval & contributors <loren.eteval@proton.me>
#
# This file is part of Furious.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from __future__ import annotations
from Furious.Frozenlib import *
from Furious.Qt import *
from Furious.Qt import gettext as _
from PySide6 import QtCore
from PySide6.QtWidgets import *
import logging
import functools
__all__ = ['GuiCustomizeProxyBypassDialog']
logger = logging.getLogger(__name__)
registerAppSettings('CustomProxyBypass')
class GuiCustomizeProxyBypassDialog(AppQDialog):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setWindowTitle(_('Customize Proxy Bypass Address'))
self.setWindowModality(QtCore.Qt.WindowModality.WindowModal)
self.proxyBypassText = AppQLabel(
_('Enter proxy bypass address (separated by semicolons):')
)
self.proxyBypassEdit = QLineEdit()
settings = AppSettings.get('CustomProxyBypass')
if settings is None:
self.proxyBypassEdit.setText(PROXY_SERVER_BYPASS)
else:
self.proxyBypassEdit.setText(settings)
self.dialogBtns = AppQDialogButtonBox(QtCore.Qt.Orientation.Horizontal)
self.dialogBtns.addButton(_('OK'), AppQDialogButtonBox.ButtonRole.AcceptRole)
self.dialogBtns.addButton(
_('Cancel'), AppQDialogButtonBox.ButtonRole.RejectRole
)
self.dialogBtns.accepted.connect(self.accept)
self.dialogBtns.rejected.connect(self.reject)
self.resetBtn = AppQPushButton(_('Reset'))
self.resetBtn.clicked.connect(functools.partial(self.handleResetButtonClicked))
self.hboxLayout = QHBoxLayout()
self.hboxLayout.addWidget(self.resetBtn)
self.hboxLayout.addWidget(self.dialogBtns)
layout = QFormLayout()
layout.addRow(self.proxyBypassText)
layout.addRow(self.proxyBypassEdit)
layout.addRow(self.hboxLayout)
layout.setFormAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.setLayout(layout)
self.finished.connect(functools.partial(self.handleResultCode))
def setWidthAndHeight(self):
self.resize(656, 125)
def handleResultCode(self, code):
if code == PySide6Legacy.enumValueWrapper(AppQDialog.DialogCode.Accepted):
value = self.proxyBypassEdit.text()
logger.info(f'setting custom proxy bypass address to \'{value}\'')
AppSettings.set('CustomProxyBypass', value)
showMBoxNewChangesNextTime()
else:
try:
settings = AppSettings.get('CustomProxyBypass')
if settings is not None:
self.proxyBypassEdit.setText(str(settings))
else:
self.proxyBypassEdit.setText(PROXY_SERVER_BYPASS)
except Exception as ex:
# Any non-exit exceptions
logger.error(f'error restoring proxy bypass address: {ex}')
def handleResetButtonClicked(self):
self.proxyBypassEdit.setText(PROXY_SERVER_BYPASS)
+9 -3
View File
@@ -596,9 +596,15 @@ class TestDownloadSpeedWorker(WebGETManager):
self.configureHttpProxy(f'127.0.0.1:{self.port}')
self.networkReply = self.webGET(
NETWORK_SPEED_TEST_URL, **self.kwargs
)
# Use custom network speed test URL if possible
settings = AppSettings.get('CustomNetworkSpeedTestURL')
if settings is None:
url = NETWORK_SPEED_TEST_URL
else:
url = settings
self.networkReply = self.webGET(url, **self.kwargs)
self.elapsedTimer.start()
self.timeoutTimer.start(self.timeout)
+2
View File
@@ -17,6 +17,8 @@
from .Application import *
from .ConnectProgressBar import *
from .GuiCustomizeNetworkTest import *
from .GuiCustomizeProxyBypass import *
from .GuiHysteria1 import *
from .GuiHysteria2 import *
from .GuiShadowsocks import *
+16 -1
View File
@@ -23,6 +23,8 @@ from Furious.Library import *
from Furious.Qt import *
from Furious.Qt import gettext as _
from Furious.Widget.UserServersQTableWidget import *
from Furious.Widget.GuiCustomizeNetworkTest import *
from Furious.Widget.GuiCustomizeProxyBypass import *
from Furious.Widget.GuiTUNSettings import *
from Furious.Window.UserSubsWindow import *
from Furious.Window.XrayAssetViewerWindow import *
@@ -129,6 +131,8 @@ class AppMainWindow(AppQMainWindow):
),
)
self.xrayAssetViewerWindow = XrayAssetViewerWindow(parent=self)
self.customizeProxyBypassDialog = GuiCustomizeProxyBypassDialog(parent=self)
self.customizeNetworkTestDialog = GuiCustomizeNetworkTestDialog(parent=self)
self.mainTab = AppQTabWidget()
self.mainTab.addTab(self.userServersQTableWidget, _('Server'))
@@ -248,7 +252,6 @@ class AppMainWindow(AppQMainWindow):
checkable=False,
callback=lambda: self.getGuiTUNSettings().open(),
),
AppQSeperator(),
]
if PLATFORM == 'Windows' or PLATFORM == 'Darwin':
@@ -285,6 +288,18 @@ class AppMainWindow(AppQMainWindow):
toolsActions = [
*customizeTUNSettingsAction,
AppQAction(
_('Customize Proxy Bypass Address...'),
checkable=False,
callback=lambda: self.customizeProxyBypassDialog.open(),
),
AppQAction(
_('Customize Network Test URL...'),
icon=bootstrapIcon('speedometer2.svg'),
checkable=False,
callback=lambda: self.customizeNetworkTestDialog.open(),
),
AppQSeperator(),
*restartAsAdminAction,
*openAppFolderAction,
AppQAction(