diff --git a/core/server/gen/libcore.proto b/core/server/gen/libcore.proto
index 3e40550..4c07518 100644
--- a/core/server/gen/libcore.proto
+++ b/core/server/gen/libcore.proto
@@ -53,6 +53,7 @@ message TestReq {
optional string url = 4 [default = ""];
optional bool test_current = 5 [default = false];
optional int32 max_concurrency = 6 [default = 0];
+ optional int32 test_timeout_ms = 7 [default = 0];
}
message TestResp {
@@ -98,6 +99,7 @@ message SpeedTestRequest {
optional bool test_upload = 6 [default = false];
optional bool simple_download = 7 [default = false];
optional string simple_download_addr = 8 [default = ""];
+ optional int32 timeout_ms = 9 [default = 0];
}
message SpeedTestResult {
diff --git a/core/server/server.go b/core/server/server.go
index dd05646..9deed14 100644
--- a/core/server/server.go
+++ b/core/server/server.go
@@ -182,7 +182,7 @@ func (s *server) Test(in *gen.TestReq, out *gen.TestResp) error {
if maxConcurrency >= 500 || maxConcurrency == 0 {
maxConcurrency = MaxConcurrentTests
}
- results := BatchURLTest(testCtx, testInstance, outboundTags, *in.Url, int(maxConcurrency), twice)
+ results := BatchURLTest(testCtx, testInstance, outboundTags, *in.Url, int(maxConcurrency), twice, time.Duration(*in.TestTimeoutMs)*time.Millisecond)
res := make([]*gen.URLTestResp, 0)
for idx, data := range results {
@@ -339,7 +339,7 @@ func (s *server) SpeedTest(in *gen.SpeedTestRequest, out *gen.SpeedTestResponse)
outboundTags = []string{outbound.Tag()}
}
- results := BatchSpeedTest(testCtx, testInstance, outboundTags, *in.TestDownload, *in.TestUpload, *in.SimpleDownload, *in.SimpleDownloadAddr)
+ results := BatchSpeedTest(testCtx, testInstance, outboundTags, *in.TestDownload, *in.TestUpload, *in.SimpleDownload, *in.SimpleDownloadAddr, time.Duration(*in.TimeoutMs)*time.Millisecond)
res := make([]*gen.SpeedTestResult, 0)
for _, data := range results {
diff --git a/core/server/test_utils.go b/core/server/test_utils.go
index 2743ca7..4da92d2 100644
--- a/core/server/test_utils.go
+++ b/core/server/test_utils.go
@@ -24,6 +24,7 @@ var SpTQuerier SpeedTestResultQuerier
var URLReporter URLTestReporter
const URLTestTimeout = 3 * time.Second
+const FetchServersTimeout = 8 * time.Second
const MaxConcurrentTests = 100
type URLTestResult struct {
@@ -84,7 +85,10 @@ func (s *SpeedTestResultQuerier) setIsRunning(isRunning bool) {
s.isRunning = isRunning
}
-func BatchURLTest(ctx context.Context, i *boxbox.Box, outboundTags []string, url string, maxConcurrency int, twice bool) []*URLTestResult {
+func BatchURLTest(ctx context.Context, i *boxbox.Box, outboundTags []string, url string, maxConcurrency int, twice bool, timeout time.Duration) []*URLTestResult {
+ if timeout <= 0 {
+ timeout = URLTestTimeout
+ }
outbounds := service.FromContext[adapter.OutboundManager](i.Context())
resMap := make(map[string]*URLTestResult)
resAccess := sync.Mutex{}
@@ -113,11 +117,11 @@ func BatchURLTest(ctx context.Context, i *boxbox.Box, outboundTags []string, url
}
client := &http.Client{
Transport: &http.Transport{
- DialContext: func(_ context.Context, network string, addr string) (net.Conn, error) {
+ DialContext: func(ctx context.Context, network string, addr string) (net.Conn, error) {
return outbound.DialContext(ctx, "tcp", metadata.ParseSocksaddr(addr))
},
},
- Timeout: URLTestTimeout,
+ Timeout: timeout,
}
// to properly measure muxed configs, let's do the test twice
duration, err := urlTest(ctx, client, url)
@@ -148,8 +152,6 @@ func BatchURLTest(ctx context.Context, i *boxbox.Box, outboundTags []string, url
}
func urlTest(ctx context.Context, client *http.Client, url string) (time.Duration, error) {
- ctx, cancel := context.WithTimeout(ctx, URLTestTimeout)
- defer cancel()
begin := time.Now()
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
@@ -169,7 +171,7 @@ func getNetDialer(dialer func(ctx context.Context, network string, destination m
}
}
-func BatchSpeedTest(ctx context.Context, i *boxbox.Box, outboundTags []string, testDl, testUl bool, simpleDL bool, simpleAddress string) []*SpeedTestResult {
+func BatchSpeedTest(ctx context.Context, i *boxbox.Box, outboundTags []string, testDl, testUl bool, simpleDL bool, simpleAddress string, timeout time.Duration) []*SpeedTestResult {
outbounds := service.FromContext[adapter.OutboundManager](i.Context())
results := make([]*SpeedTestResult, 0)
@@ -189,9 +191,9 @@ func BatchSpeedTest(ctx context.Context, i *boxbox.Box, outboundTags []string, t
var err error
if simpleDL {
- err = simpleDownloadTest(ctx, getNetDialer(outbound.DialContext), res, simpleAddress)
+ err = simpleDownloadTest(ctx, getNetDialer(outbound.DialContext), res, simpleAddress, timeout)
} else {
- err = speedTestWithDialer(ctx, getNetDialer(outbound.DialContext), res, testDl, testUl)
+ err = speedTestWithDialer(ctx, getNetDialer(outbound.DialContext), res, testDl, testUl, timeout)
}
if err != nil && !errors.Is(err, context.Canceled) {
res.Error = err
@@ -208,14 +210,17 @@ func BatchSpeedTest(ctx context.Context, i *boxbox.Box, outboundTags []string, t
return results
}
-func simpleDownloadTest(ctx context.Context, dialer func(ctx context.Context, network string, address string) (net.Conn, error), res *SpeedTestResult, testURL string) error {
+func simpleDownloadTest(ctx context.Context, dialer func(ctx context.Context, network string, address string) (net.Conn, error), res *SpeedTestResult, testURL string, timeout time.Duration) error {
+ if timeout <= 0 {
+ timeout = URLTestTimeout
+ }
client := &http.Client{
Transport: &http.Transport{
DialContext: func(ctx context.Context, network string, addr string) (net.Conn, error) {
return dialer(ctx, network, addr)
},
},
- Timeout: URLTestTimeout,
+ Timeout: timeout,
}
res.ServerName = "N/A"
@@ -268,13 +273,15 @@ func simpleDownloadTest(ctx context.Context, dialer func(ctx context.Context, ne
}
}
-func speedTestWithDialer(ctx context.Context, dialer func(ctx context.Context, network string, address string) (net.Conn, error), res *SpeedTestResult, testDl, testUl bool) error {
+func speedTestWithDialer(ctx context.Context, dialer func(ctx context.Context, network string, address string) (net.Conn, error), res *SpeedTestResult, testDl, testUl bool, timeout time.Duration) error {
clt := speedtest.New(speedtest.WithUserConfig(&speedtest.UserConfig{
DialContextFunc: dialer,
PingMode: speedtest.HTTP,
MaxConnections: 8,
}))
- srv, err := clt.FetchServerListContext(ctx)
+ fetchCtx, cancel := context.WithTimeout(ctx, FetchServersTimeout)
+ defer cancel()
+ srv, err := clt.FetchServerListContext(fetchCtx)
if err != nil {
return err
}
@@ -297,14 +304,18 @@ func speedTestWithDialer(ctx context.Context, dialer func(ctx context.Context, n
go func() {
defer func() { close(done) }()
if testDl {
- err = srv[0].DownloadTestContext(ctx)
+ timeoutCtx, cancel := context.WithTimeout(ctx, timeout)
+ defer cancel()
+ err = srv[0].DownloadTestContext(timeoutCtx)
if err != nil && !errors.Is(err, context.Canceled) {
res.Error = err
return
}
}
if testUl {
- err = srv[0].UploadTestContext(ctx)
+ timeoutCtx, cancel := context.WithTimeout(ctx, timeout)
+ defer cancel()
+ err = srv[0].UploadTestContext(timeoutCtx)
if err != nil && !errors.Is(err, context.Canceled) {
res.Error = err
return
diff --git a/include/global/DataStore.hpp b/include/global/DataStore.hpp
index 74c4fce..9918a38 100644
--- a/include/global/DataStore.hpp
+++ b/include/global/DataStore.hpp
@@ -79,6 +79,7 @@ namespace Configs {
// Misc
QString log_level = "info";
QString test_latency_url = "http://cp.cloudflare.com/";
+ int url_test_timeout_ms = 3000;
bool disable_tray = false;
int test_concurrent = 10;
bool disable_traffic_stats = false;
@@ -99,6 +100,7 @@ namespace Configs {
bool enable_stats = true;
int stats_tab = 0; // either connection or log
int speed_test_mode = TestConfig::FULL;
+ int speed_test_timeout_ms = 3000;
QString simple_dl_url = "http://cachefly.cachefly.net/1mb.test";
bool allow_beta_update = false;
diff --git a/include/ui/setting/dialog_basic_settings.ui b/include/ui/setting/dialog_basic_settings.ui
index 29f8719..718f656 100644
--- a/include/ui/setting/dialog_basic_settings.ui
+++ b/include/ui/setting/dialog_basic_settings.ui
@@ -6,7 +6,7 @@
0
0
- 728
+ 767
472
@@ -20,7 +20,7 @@
Basic Settings
- -
+
-
Qt::Orientation::Horizontal
@@ -165,27 +165,52 @@
-
-
+
-
-
-
- Latency Test URL
-
+
+
+
-
+
+
+ Latency Test URL
+
+
+
+ -
+
+
+
-
-
-
- -
-
-
- Concurrent
-
+
+
+
-
+
+
+ Concurrent
+
+
+
+ -
+
+
+ -
+
+
+ <html><head/><body><p>Timeout for URLtest in ms<br/>Note that muxed connections take a much longer time for their initial request, and setting this value too low will cause the test to falsely report that the config is not working</p></body></html>
+
+
+ Timeout
+
+
+
+ -
+
+
+
- -
-
-
@@ -260,6 +285,19 @@
+ -
+
+
+ <html><head/><body><p>timeout in milliseconds<br/>applies to all tests individually</p></body></html>
+
+
+ Timeout
+
+
+
+ -
+
+
-
@@ -534,80 +572,80 @@
Subscription
-
-
-
-
-
-
-
-
- 0
- 0
-
-
-
- Enable
-
-
-
- -
-
-
- Interval (minute, invalid if less than 30)
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
-
-
- -
-
-
- -
-
-
- Clear servers before updating subscription
-
-
-
- -
-
-
- <html><head/><body><p>HWID=%1</p><p>OS=%2</p><p>OS Version=%3</p><p>Model=%4</p></body></html>
-
-
- Enable sending HWID, device model, and OS version when updating subscription
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Automatic update
-
-
-
- -
-
-
- User Agent
-
-
-
+ -
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Enable
+
+
+
+ -
+
+
+ Interval (minute, invalid if less than 30)
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+ Clear servers before updating subscription
+
+
+
+ -
+
+
+ <html><head/><body><p>HWID=%1</p><p>OS=%2</p><p>OS Version=%3</p><p>Model=%4</p></body></html>
+
+
+ Enable sending HWID, device model, and OS version when updating subscription
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Automatic update
+
+
+
+ -
+
+
+ User Agent
+
+
+
diff --git a/src/global/Configs.cpp b/src/global/Configs.cpp
index 697eb80..b54f650 100644
--- a/src/global/Configs.cpp
+++ b/src/global/Configs.cpp
@@ -318,6 +318,8 @@ namespace Configs {
_add(new configItem("use_mozilla_certs", &use_mozilla_certs, itemType::boolean));
_add(new configItem("allow_beta_update", &allow_beta_update, itemType::boolean));
_add(new configItem("adblock_enable", &adblock_enable, itemType::boolean));
+ _add(new configItem("speed_test_timeout_ms", &speed_test_timeout_ms, itemType::integer));
+ _add(new configItem("url_test_timeout_ms", &url_test_timeout_ms, itemType::integer));
}
void DataStore::UpdateStartedId(int id) {
diff --git a/src/ui/mainwindow.cpp b/src/ui/mainwindow.cpp
index e80e47c..4c54cae 100644
--- a/src/ui/mainwindow.cpp
+++ b/src/ui/mainwindow.cpp
@@ -2384,6 +2384,7 @@ void MainWindow::loadShortcuts()
auto mp = Configs::dataStore->shortcuts->shortcuts;
for (QList actions = findChildren(); QAction *action : actions)
{
+ if (action->data().isNull() || action->data().toString().isEmpty()) continue;
if (mp.count(action->data().toString()) == 0) action->setShortcut(QKeySequence());
else action->setShortcut(mp[action->data().toString()]);
}
diff --git a/src/ui/mainwindow_grpc.cpp b/src/ui/mainwindow_grpc.cpp
index cb928ab..6ef7b60 100644
--- a/src/ui/mainwindow_grpc.cpp
+++ b/src/ui/mainwindow_grpc.cpp
@@ -43,6 +43,7 @@ void MainWindow::runURLTest(const QString& config, bool useDefault, const QStrin
req.url = Configs::dataStore->test_latency_url.toStdString();
req.use_default_outbound = useDefault;
req.max_concurrency = Configs::dataStore->test_concurrent;
+ req.test_timeout_ms = Configs::dataStore->url_test_timeout_ms;
auto done = new QMutex;
done->lock();
@@ -51,7 +52,7 @@ void MainWindow::runURLTest(const QString& config, bool useDefault, const QStrin
bool ok;
while (true)
{
- QThread::msleep(1500);
+ QThread::msleep(200);
if (done->try_lock()) break;
auto resp = defaultClient->QueryURLTest(&ok);
if (!ok || resp.results.empty())
@@ -285,6 +286,7 @@ void MainWindow::runSpeedTest(const QString& config, bool useDefault, bool testC
req.simple_download = speedtestConf == Configs::TestConfig::SIMPLEDL;
req.simple_download_addr = Configs::dataStore->simple_dl_url.toStdString();
req.test_current = testCurrent;
+ req.timeout_ms = Configs::dataStore->speed_test_timeout_ms;
// loop query result
auto doneMu = new QMutex;
diff --git a/src/ui/setting/dialog_basic_settings.cpp b/src/ui/setting/dialog_basic_settings.cpp
index 9297e7e..eb2369d 100644
--- a/src/ui/setting/dialog_basic_settings.cpp
+++ b/src/ui/setting/dialog_basic_settings.cpp
@@ -36,7 +36,9 @@ DialogBasicSettings::DialogBasicSettings(QWidget *parent)
D_LOAD_INT(test_concurrent)
D_LOAD_STRING(test_latency_url)
D_LOAD_BOOL(disable_tray)
+ ui->url_timeout->setText(Int2String(Configs::dataStore->url_test_timeout_ms));
ui->speedtest_mode->setCurrentIndex(Configs::dataStore->speed_test_mode);
+ ui->test_timeout->setText(Int2String(Configs::dataStore->speed_test_timeout_ms));
ui->simple_down_url->setText(Configs::dataStore->simple_dl_url);
ui->allow_beta->setChecked(Configs::dataStore->allow_beta_update);
@@ -184,6 +186,8 @@ void DialogBasicSettings::accept() {
Configs::dataStore->proxy_scheme = ui->proxy_scheme->currentText().toLower();
Configs::dataStore->speed_test_mode = ui->speedtest_mode->currentIndex();
Configs::dataStore->simple_dl_url = ui->simple_down_url->text();
+ Configs::dataStore->url_test_timeout_ms = ui->url_timeout->text().toInt();
+ Configs::dataStore->speed_test_timeout_ms = ui->test_timeout->text().toInt();
Configs::dataStore->allow_beta_update = ui->allow_beta->isChecked();
// Style