diff --git a/v2rayN/ServiceLib/Models/SingboxConfig.cs b/v2rayN/ServiceLib/Models/SingboxConfig.cs index 6dde5e7c..cbf674fa 100644 --- a/v2rayN/ServiceLib/Models/SingboxConfig.cs +++ b/v2rayN/ServiceLib/Models/SingboxConfig.cs @@ -216,6 +216,8 @@ public class Transport4Sbox public string? idle_timeout { get; set; } public string? ping_timeout { get; set; } public bool? permit_without_stream { get; set; } + public int? max_early_data { get; set; } + public string? early_data_header_name { get; set; } } public class Headers4Sbox diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs index 5188d177..b04c6e9f 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs @@ -364,7 +364,35 @@ public partial class CoreConfigSingboxService case nameof(ETransport.ws): transport.type = nameof(ETransport.ws); - transport.path = node.Path.IsNullOrEmpty() ? null : node.Path; + var wsPath = node.Path; + + // Parse eh and ed parameters from path using regex + if (!wsPath.IsNullOrEmpty()) + { + var edRegex = new Regex(@"[?&]ed=(\d+)"); + var edMatch = edRegex.Match(wsPath); + if (edMatch.Success && int.TryParse(edMatch.Groups[1].Value, out var edValue)) + { + transport.max_early_data = edValue; + transport.early_data_header_name = "Sec-WebSocket-Protocol"; + + wsPath = edRegex.Replace(wsPath, ""); + wsPath = wsPath.Replace("?&", "?"); + if (wsPath.EndsWith('?')) + { + wsPath = wsPath.TrimEnd('?'); + } + } + + var ehRegex = new Regex(@"[?&]eh=([^&]+)"); + var ehMatch = ehRegex.Match(wsPath); + if (ehMatch.Success) + { + transport.early_data_header_name = Uri.UnescapeDataString(ehMatch.Groups[1].Value); + } + } + + transport.path = wsPath.IsNullOrEmpty() ? null : wsPath; if (node.RequestHost.IsNotEmpty()) { transport.headers = new()