Skip to content

Commit 5a07709

Browse files
feat: block QUIC by default + UI toggle (Android & desktop) (#805)
QUIC over the TCP-based tunnel causes TCP-over-TCP meltdown — users see <1 Mbps where HTTPS/TCP would do >50. The existing `block_quic` config option was off by default and had no UI on either platform, so most users suffered QUIC degradation without knowing why. Changes: - Default `block_quic` to `true` (was `false`). Browsers detect the silent UDP/443 drop and fall back to TCP/HTTPS within seconds. - Add "Block QUIC" toggle in Android Advanced UI. - Add "Block QUIC (UDP/443)" checkbox in desktop UI (was config-only, issue #213). - Android: always emit `block_quic` in JSON so the Rust default doesn't silently override the user's choice. Closes #793. Co-authored-by: yyoyoian-pixel <279225925+yyoyoian-pixel@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent ea624e8 commit 5a07709

4 files changed

Lines changed: 44 additions & 2 deletions

File tree

android/app/src/main/java/com/therealaleph/mhrv/ConfigStore.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ data class MhrvConfig(
9898
val parallelRelay: Int = 1,
9999
val coalesceStepMs: Int = 10,
100100
val coalesceMaxMs: Int = 1000,
101+
/** Block QUIC (UDP/443). QUIC over TCP tunnel causes meltdown. */
102+
val blockQuic: Boolean = true,
101103
val upstreamSocks5: String = "",
102104

103105
/**
@@ -219,6 +221,7 @@ data class MhrvConfig(
219221
put("parallel_relay", parallelRelay)
220222
if (coalesceStepMs != 10) put("coalesce_step_ms", coalesceStepMs)
221223
if (coalesceMaxMs != 1000) put("coalesce_max_ms", coalesceMaxMs)
224+
put("block_quic", blockQuic)
222225
if (upstreamSocks5.isNotBlank()) {
223226
put("upstream_socks5", upstreamSocks5.trim())
224227
}
@@ -330,6 +333,7 @@ object ConfigStore {
330333
if (cfg.parallelRelay != defaults.parallelRelay) obj.put("parallel_relay", cfg.parallelRelay)
331334
if (cfg.coalesceStepMs != defaults.coalesceStepMs) obj.put("coalesce_step_ms", cfg.coalesceStepMs)
332335
if (cfg.coalesceMaxMs != defaults.coalesceMaxMs) obj.put("coalesce_max_ms", cfg.coalesceMaxMs)
336+
if (cfg.blockQuic != defaults.blockQuic) obj.put("block_quic", cfg.blockQuic)
333337
if (cfg.upstreamSocks5.isNotBlank()) obj.put("upstream_socks5", cfg.upstreamSocks5)
334338
if (cfg.passthroughHosts.isNotEmpty()) obj.put("passthrough_hosts", JSONArray().apply { cfg.passthroughHosts.forEach { put(it) } })
335339
if (cfg.tunnelDoh != defaults.tunnelDoh) obj.put("tunnel_doh", cfg.tunnelDoh)
@@ -433,6 +437,7 @@ object ConfigStore {
433437
parallelRelay = obj.optInt("parallel_relay", 1),
434438
coalesceStepMs = obj.optInt("coalesce_step_ms", 10),
435439
coalesceMaxMs = obj.optInt("coalesce_max_ms", 1000),
440+
blockQuic = obj.optBoolean("block_quic", true),
436441
upstreamSocks5 = obj.optString("upstream_socks5", ""),
437442
passthroughHosts = obj.optJSONArray("passthrough_hosts")?.let { arr ->
438443
buildList { for (i in 0 until arr.length()) add(arr.optString(i)) }

android/app/src/main/java/com/therealaleph/mhrv/ui/HomeScreen.kt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1265,6 +1265,28 @@ private fun AdvancedSettings(
12651265
)
12661266
}
12671267

1268+
// Block QUIC toggle
1269+
Row(
1270+
verticalAlignment = Alignment.CenterVertically,
1271+
modifier = Modifier.fillMaxWidth(),
1272+
) {
1273+
Column(modifier = Modifier.weight(1f)) {
1274+
Text(
1275+
"Block QUIC",
1276+
style = MaterialTheme.typography.bodyMedium,
1277+
)
1278+
Text(
1279+
"Drop UDP/443 so browsers use TCP/HTTPS. QUIC over TCP tunnel causes meltdown.",
1280+
style = MaterialTheme.typography.bodySmall,
1281+
color = MaterialTheme.colorScheme.onSurfaceVariant,
1282+
)
1283+
}
1284+
Switch(
1285+
checked = cfg.blockQuic,
1286+
onCheckedChange = { onChange(cfg.copy(blockQuic = it)) },
1287+
)
1288+
}
1289+
12681290
// Block DoH toggle
12691291
Row(
12701292
verticalAlignment = Alignment.CenterVertically,

src/bin/ui.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ fn load_form() -> (FormState, Option<String>) {
420420
normalize_x_graphql: false,
421421
youtube_via_relay: false,
422422
passthrough_hosts: Vec::new(),
423-
block_quic: false,
423+
block_quic: true,
424424
disable_padding: false,
425425
tunnel_doh: true,
426426
bypass_doh_hosts: Vec::new(),
@@ -1239,6 +1239,16 @@ impl eframe::App for App {
12391239
Script relay instead — slower for video, but the visible SNI matches the site.",
12401240
);
12411241
});
1242+
ui.horizontal(|ui| {
1243+
ui.add_space(120.0 + 8.0);
1244+
ui.checkbox(&mut self.form.block_quic, "Block QUIC (UDP/443)")
1245+
.on_hover_text(
1246+
"Drop QUIC (UDP port 443) so browsers fall back to TCP/HTTPS. \
1247+
QUIC over the TCP-based tunnel causes TCP-over-TCP meltdown \
1248+
(<1 Mbps). Browsers detect the drop and switch to TCP within seconds. \
1249+
Issue #213, #793.",
1250+
);
1251+
});
12421252
});
12431253
});
12441254

src/config.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ pub struct Config {
202202
/// flag lets users who care about consistency over peak speed
203203
/// opt out of QUIC at the source rather than discovering its
204204
/// failure modes later. Issue #213.
205-
#[serde(default)]
205+
#[serde(default = "default_block_quic")]
206206
pub block_quic: bool,
207207
/// When true, suppress the random `_pad` field that v1.8.0+ adds
208208
/// to outbound Apps Script requests for DPI evasion. Default off
@@ -481,6 +481,11 @@ fn default_google_ip_validation() -> bool {true}
481481
/// opt back in with `tunnel_doh: false`.
482482
fn default_tunnel_doh() -> bool { true }
483483

484+
/// Default for `block_quic`: `true`. QUIC over the TCP-based tunnel
485+
/// causes TCP-over-TCP meltdown (<1 Mbps). Browsers fall back to
486+
/// HTTPS/TCP within seconds of the silent UDP drop. Issue #793.
487+
fn default_block_quic() -> bool { true }
488+
484489
/// Default for `block_doh`: `true` (browser DoH is rejected so the
485490
/// browser falls back to system DNS, which `tun2proxy` resolves
486491
/// instantly via virtual DNS — saves the ~1.5s tunnel round-trip per

0 commit comments

Comments
 (0)