Skip to content

Commit 189d755

Browse files
DHR60Copilot
andcommitted
Support new hysteria2 stream settings
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 66cd746 commit 189d755

File tree

3 files changed

+70
-48
lines changed

3 files changed

+70
-48
lines changed

V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -292,16 +292,8 @@ data class V2rayConfig(
292292

293293
data class HysteriaSettingsBean(
294294
var version: Int,
295-
var auth: String? = null,
296-
var up: String? = null,
297-
var down: String? = null,
298-
var udphop: HysteriaUdpHopBean? = null
299-
) {
300-
data class HysteriaUdpHopBean(
301-
var port: String? = null,
302-
var interval: Int? = null
303-
)
304-
}
295+
var auth: String? = null
296+
)
305297

306298
//https://xtls.github.io/config/transport.html#finalmaskobject
307299
data class FinalMaskBean(
@@ -320,19 +312,9 @@ data class V2rayConfig(
320312
}
321313
data class QuicParamsBean(
322314
var congestion: String? = null,
323-
var debug: Boolean? = null,
324315
var brutalUp: String? = null,
325-
var brutalDown: Int? = null,
316+
var brutalDown: String? = null,
326317
var udpHop: UdpHopBean? = null,
327-
// Using Long for large memory/window size values
328-
var initStreamReceiveWindow: Long? = null,
329-
var maxStreamReceiveWindow: Long? = null,
330-
var initConnectionReceiveWindow: Long? = null,
331-
var maxConnectionReceiveWindow: Long? = null,
332-
var maxIdleTimeout: Int? = null,
333-
var keepAlivePeriod: Int? = null,
334-
var disablePathMTUDiscovery: Boolean? = null,
335-
var maxIncomingStreams: Int? = null
336318
) {
337319
// Nested data class for the udpHop JSON object
338320
data class UdpHopBean(

V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package com.v2ray.ang.fmt
33
import com.v2ray.ang.AppConfig
44
import com.v2ray.ang.dto.ProfileItem
55
import com.v2ray.ang.dto.V2rayConfig.OutboundBean
6-
import com.v2ray.ang.dto.V2rayConfig.OutboundBean.StreamSettingsBean.FinalMaskBean
76
import com.v2ray.ang.enums.EConfigType
87
import com.v2ray.ang.enums.NetworkType
98
import com.v2ray.ang.extension.idnHost
@@ -73,7 +72,15 @@ object Hysteria2Fmt : FmtBase() {
7372
dicQuery["mport"] = config.portHopping.orEmpty()
7473
}
7574
if (config.portHoppingInterval.isNotNullEmpty()) {
76-
dicQuery["mportHopInt"] = config.portHoppingInterval.orEmpty()
75+
var portHoppingInterval = config.portHoppingInterval.orEmpty()
76+
if (portHoppingInterval.contains('-')) {
77+
// interval range
78+
portHoppingInterval = portHoppingInterval.substringBefore('-')
79+
}
80+
val trimmedPortHoppingInterval = portHoppingInterval.trim()
81+
if (trimmedPortHoppingInterval.isNotEmpty()) {
82+
dicQuery["mportHopInt"] = trimmedPortHoppingInterval
83+
}
7784
}
7885
if (config.pinnedCA256.isNotNullEmpty()) {
7986
dicQuery["pinSHA256"] = config.pinnedCA256.orEmpty()
@@ -107,18 +114,6 @@ object Hysteria2Fmt : FmtBase() {
107114
V2rayConfigManager.populateTlsSettings(it, profileItem, sni)
108115
}
109116

110-
if (profileItem.obfsPassword.isNotNullEmpty()) {
111-
outboundBean.streamSettings?.finalmask = FinalMaskBean(
112-
udp = listOf(
113-
FinalMaskBean.MaskBean(
114-
type = "salamander",
115-
settings = FinalMaskBean.MaskBean.MaskSettingsBean(
116-
password = profileItem.obfsPassword
117-
)
118-
)
119-
)
120-
)
121-
}
122117
return outboundBean
123118
}
124119
}

V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1177,9 +1177,6 @@ object V2rayConfigManager {
11771177
val finalMask = profileItem.finalMask
11781178
var sni: String? = null
11791179
streamSettings.network = transport.ifEmpty { NetworkType.TCP.type }
1180-
finalMask?.let {
1181-
streamSettings.finalmask = JsonUtil.parseString(finalMask)
1182-
}
11831180
when (streamSettings.network) {
11841181
NetworkType.TCP.type -> {
11851182
val tcpSetting = StreamSettingsBean.TcpSettingsBean()
@@ -1293,21 +1290,69 @@ object V2rayConfigManager {
12931290
val hysteriaSetting = StreamSettingsBean.HysteriaSettingsBean(
12941291
version = 2,
12951292
auth = profileItem.password.orEmpty(),
1296-
up = profileItem.bandwidthUp?.ifEmpty { "0" }.orEmpty(),
1297-
down = profileItem.bandwidthDown?.ifEmpty { "0" }.orEmpty(),
1298-
udphop = null
12991293
)
1294+
val quicParams = StreamSettingsBean.FinalMaskBean.QuicParamsBean(
1295+
brutalUp = profileItem.bandwidthUp?.nullIfBlank(),
1296+
brutalDown = profileItem.bandwidthDown?.nullIfBlank(),
1297+
)
1298+
quicParams.congestion = if (quicParams.brutalUp != null || quicParams.brutalDown != null) "brutal" else null
13001299
if (profileItem.portHopping.isNotNullEmpty()) {
1301-
hysteriaSetting.udphop = StreamSettingsBean.HysteriaSettingsBean.HysteriaUdpHopBean(
1302-
port = profileItem.portHopping,
1303-
interval = profileItem.portHoppingInterval
1304-
?.trim()
1305-
?.toIntOrNull()
1306-
?.takeIf { it >= 5 }
1307-
?: 30
1300+
val rawInterval = profileItem.portHoppingInterval?.trim().nullIfBlank()
1301+
val interval = if (rawInterval == null) {
1302+
"30"
1303+
} else {
1304+
val singleValue = rawInterval.toIntOrNull()
1305+
if (singleValue != null) {
1306+
if (singleValue < 5) {
1307+
"30"
1308+
} else {
1309+
rawInterval
1310+
}
1311+
} else {
1312+
val parts = rawInterval.split('-')
1313+
if (parts.size == 2) {
1314+
val start = parts[0].trim().toIntOrNull()
1315+
val end = parts[1].trim().toIntOrNull()
1316+
if (start != null && end != null) {
1317+
val minStart = maxOf(5, start)
1318+
val minEnd = maxOf(minStart, end)
1319+
"$minStart-$minEnd"
1320+
} else {
1321+
"30"
1322+
}
1323+
} else {
1324+
"30"
1325+
}
1326+
}
1327+
}
1328+
quicParams.udpHop = StreamSettingsBean.FinalMaskBean.QuicParamsBean.UdpHopBean(
1329+
ports = profileItem.portHopping,
1330+
interval = interval
1331+
)
1332+
}
1333+
val finalmask = StreamSettingsBean.FinalMaskBean(
1334+
quicParams = quicParams
1335+
)
1336+
if (profileItem.obfsPassword.isNotNullEmpty()) {
1337+
finalmask.udp = listOf(
1338+
StreamSettingsBean.FinalMaskBean.MaskBean(
1339+
type = "salamander",
1340+
settings = StreamSettingsBean.FinalMaskBean.MaskBean.MaskSettingsBean(
1341+
password = profileItem.obfsPassword.orEmpty()
1342+
)
1343+
)
13081344
)
13091345
}
13101346
streamSettings.hysteriaSettings = hysteriaSetting
1347+
streamSettings.finalmask = finalmask
1348+
}
1349+
}
1350+
finalMask?.let {
1351+
val parsedFinalMask = JsonUtil.parseString(finalMask)
1352+
if (parsedFinalMask != null) {
1353+
streamSettings.finalmask = parsedFinalMask
1354+
} else {
1355+
Log.w("V2rayConfigManager", "Invalid finalMask JSON, keeping previously generated finalmask")
13111356
}
13121357
}
13131358
return sni

0 commit comments

Comments
 (0)