Add new admin panel, failover, dns fallback, providers, limiters. Update XHTTP

This commit is contained in:
Sergei Maklagin
2026-05-11 00:59:35 +03:00
parent 652e0baf57
commit 3bd162ed6f
241 changed files with 36409 additions and 4086 deletions

View File

@@ -48,7 +48,7 @@ func (o V2RayTransportOptions) MarshalJSON() ([]byte, error) {
default:
return nil, E.New("unknown transport type: " + o.Type)
}
return badjson.MarshallObjects((_V2RayTransportOptions)(o), v)
return badjson.MarshallObjects(_V2RayTransportOptions(o), v)
}
func (o *V2RayTransportOptions) UnmarshalJSON(bytes []byte) error {
@@ -115,31 +115,33 @@ type V2RayHTTPUpgradeOptions struct {
}
type V2RayXHTTPBaseOptions struct {
Host string `json:"host,omitempty"`
Path string `json:"path,omitempty"`
Headers map[string]string `json:"headers,omitempty"`
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
XPaddingBytes Xbadoption.Range `json:"x_padding_bytes"`
NoGRPCHeader bool `json:"no_grpc_header,omitempty"`
NoSSEHeader bool `json:"no_sse_header,omitempty"`
ScMaxEachPostBytes Xbadoption.Range `json:"sc_max_each_post_bytes"`
ScMinPostsIntervalMs Xbadoption.Range `json:"sc_min_posts_interval_ms"`
ScMaxBufferedPosts int64 `json:"sc_max_buffered_posts,omitempty"`
ScStreamUpServerSecs Xbadoption.Range `json:"sc_stream_up_server_secs"`
Xmux *V2RayXHTTPXmuxOptions `json:"xmux"`
XPaddingObfsMode bool `json:"x_padding_obfs_mode,omitempty"`
XPaddingKey string `json:"x_padding_key,omitempty"`
XPaddingHeader string `json:"x_padding_header,omitempty"`
XPaddingPlacement string `json:"x_padding_placement,omitempty"`
XPaddingMethod string `json:"x_padding_method,omitempty"`
UplinkHTTPMethod string `json:"uplink_http_method,omitempty"`
SessionPlacement string `json:"session_placement,omitempty"`
SessionKey string `json:"session_key,omitempty"`
SeqPlacement string `json:"seq_placement,omitempty"`
SeqKey string `json:"seq_key,omitempty"`
UplinkDataPlacement string `json:"uplink_data_placement,omitempty"`
UplinkDataKey string `json:"uplink_data_key,omitempty"`
UplinkChunkSize uint32 `json:"uplink_chunk_size,omitempty"`
Host string `json:"host,omitempty"`
Path string `json:"path,omitempty"`
Headers map[string]string `json:"headers,omitempty"`
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
XPaddingBytes Xbadoption.Range `json:"x_padding_bytes"`
NoGRPCHeader bool `json:"no_grpc_header,omitempty"`
NoSSEHeader bool `json:"no_sse_header,omitempty"`
ScMaxEachPostBytes *Xbadoption.Range `json:"sc_max_each_post_bytes"`
ScMinPostsIntervalMs *Xbadoption.Range `json:"sc_min_posts_interval_ms"`
ScMaxBufferedPosts int64 `json:"sc_max_buffered_posts,omitempty"`
ScStreamUpServerSecs *Xbadoption.Range `json:"sc_stream_up_server_secs"`
ServerMaxHeaderBytes int `json:"server_max_header_bytes"`
TrustedXForwardedFor badoption.Listable[string] `json:"trusted_x_forwarded_for,omitempty"`
Xmux *V2RayXHTTPXmuxOptions `json:"xmux"`
XPaddingObfsMode bool `json:"x_padding_obfs_mode,omitempty"`
XPaddingKey string `json:"x_padding_key,omitempty"`
XPaddingHeader string `json:"x_padding_header,omitempty"`
XPaddingPlacement string `json:"x_padding_placement,omitempty"`
XPaddingMethod string `json:"x_padding_method,omitempty"`
UplinkHTTPMethod string `json:"uplink_http_method,omitempty"`
SessionPlacement string `json:"session_placement,omitempty"`
SessionKey string `json:"session_key,omitempty"`
SeqPlacement string `json:"seq_placement,omitempty"`
SeqKey string `json:"seq_key,omitempty"`
UplinkDataPlacement string `json:"uplink_data_placement,omitempty"`
UplinkDataKey string `json:"uplink_data_key,omitempty"`
UplinkChunkSize *Xbadoption.Range `json:"uplink_chunk_size,omitempty"`
}
type _V2RayXHTTPOptions struct {
@@ -164,6 +166,7 @@ const (
PlacementQuery = "query"
PlacementPath = "path"
PlacementBody = "body"
PlacementAuto = "auto"
)
func (c V2RayXHTTPOptions) MarshalJSON() ([]byte, error) {
@@ -202,15 +205,19 @@ func checkV2RayXHTTPBaseOptions(mode string, options *V2RayXHTTPBaseOptions) err
return E.New(`"headers" can't contain "host"`)
}
}
if options.XPaddingBytes.From <= 0 || options.XPaddingBytes.To <= 0 {
return E.New("x_padding_bytes cannot be disabled")
}
if options.XPaddingKey == "" {
options.XPaddingKey = "x_padding"
}
if options.XPaddingHeader == "" {
options.XPaddingHeader = "X-Padding"
}
switch options.XPaddingPlacement {
case "":
options.XPaddingPlacement = "queryInHeader"
@@ -218,6 +225,7 @@ func checkV2RayXHTTPBaseOptions(mode string, options *V2RayXHTTPBaseOptions) err
default:
return E.New("unsupported padding placement: " + options.XPaddingPlacement)
}
switch options.XPaddingMethod {
case "":
options.XPaddingMethod = "repeat-x"
@@ -225,24 +233,28 @@ func checkV2RayXHTTPBaseOptions(mode string, options *V2RayXHTTPBaseOptions) err
default:
return E.New("unsupported padding method: " + options.XPaddingMethod)
}
switch options.UplinkDataPlacement {
case "":
options.UplinkDataPlacement = "body"
case "body":
case "cookie", "header":
options.UplinkDataPlacement = PlacementAuto
case PlacementAuto, PlacementBody:
case PlacementCookie, PlacementHeader:
if mode != "packet-up" {
return E.New("uplink_data_placement can be " + options.UplinkDataPlacement + " only in packet-up mode")
return E.New("UplinkDataPlacement can be " + options.UplinkDataPlacement + " only in packet-up mode")
}
default:
return E.New("unsupported uplink data placement: " + options.UplinkDataPlacement)
}
if options.UplinkHTTPMethod == "" {
options.UplinkHTTPMethod = "POST"
}
options.UplinkHTTPMethod = strings.ToUpper(options.UplinkHTTPMethod)
if options.UplinkHTTPMethod == "GET" && mode != "packet-up" {
return E.New("uplink_http_method can be GET only in packet-up mode")
}
switch options.SessionPlacement {
case "":
options.SessionPlacement = "path"
@@ -250,17 +262,15 @@ func checkV2RayXHTTPBaseOptions(mode string, options *V2RayXHTTPBaseOptions) err
default:
return E.New("unsupported session placement: " + options.SessionPlacement)
}
switch options.SeqPlacement {
case "":
options.SeqPlacement = "path"
case "path":
case "cookie", "header", "query":
if options.SessionPlacement == "path" {
return E.New("seq_placement must be path when session_placement is path")
}
case "path", "cookie", "header", "query":
default:
return E.New("unsupported seq placement: " + options.SeqPlacement)
}
if options.SessionPlacement != "path" && options.SessionKey == "" {
switch options.SessionPlacement {
case "cookie", "query":
@@ -269,6 +279,7 @@ func checkV2RayXHTTPBaseOptions(mode string, options *V2RayXHTTPBaseOptions) err
options.SessionKey = "X-Session"
}
}
if options.SeqPlacement != "path" && options.SeqKey == "" {
switch options.SeqPlacement {
case "cookie", "query":
@@ -277,24 +288,20 @@ func checkV2RayXHTTPBaseOptions(mode string, options *V2RayXHTTPBaseOptions) err
options.SeqKey = "X-Seq"
}
}
if options.UplinkDataPlacement != "body" && options.UplinkDataKey == "" {
if options.UplinkDataPlacement != PlacementBody && options.UplinkDataKey == "" {
switch options.UplinkDataPlacement {
case "cookie":
case PlacementCookie:
options.UplinkDataKey = "x_data"
case "header":
case PlacementAuto, PlacementHeader:
options.UplinkDataKey = "X-Data"
}
}
if options.UplinkChunkSize == 0 {
switch options.UplinkDataPlacement {
case "cookie":
options.UplinkChunkSize = 3 * 1024 // 3KB
case "header":
options.UplinkChunkSize = 4 * 1024 // 4KB
}
} else if options.UplinkChunkSize < 64 {
options.UplinkChunkSize = 64
if options.ServerMaxHeaderBytes < 0 {
return E.New("invalid negative value of maxHeaderBytes")
}
if options.Xmux == nil {
options.Xmux = &V2RayXHTTPXmuxOptions{}
options.Xmux.MaxConcurrency.From = 1
@@ -335,9 +342,7 @@ func (c *V2RayXHTTPBaseOptions) GetRequestHeader() http.Header {
for k, v := range c.Headers {
header.Add(k, v)
}
if header.Get("User-Agent") == "" {
header.Set("User-Agent", utils.ChromeUA)
}
utils.TryDefaultHeadersWith(header, "fetch")
return header
}
@@ -359,23 +364,23 @@ func (c *V2RayXHTTPBaseOptions) GetNormalizedUplinkHTTPMethod() string {
}
func (c *V2RayXHTTPBaseOptions) GetNormalizedScMaxEachPostBytes() Xbadoption.Range {
if c.ScMaxEachPostBytes.To == 0 {
if c.ScMaxEachPostBytes == nil {
return Xbadoption.Range{
From: 1000000,
To: 1000000,
}
}
return c.ScMaxEachPostBytes
return *c.ScMaxEachPostBytes
}
func (c *V2RayXHTTPBaseOptions) GetNormalizedScMinPostsIntervalMs() Xbadoption.Range {
if c.ScMinPostsIntervalMs.To == 0 {
if c.ScMinPostsIntervalMs == nil {
return Xbadoption.Range{
From: 30,
To: 30,
}
}
return c.ScMinPostsIntervalMs
return *c.ScMinPostsIntervalMs
}
func (c *V2RayXHTTPBaseOptions) GetNormalizedScMaxBufferedPosts() int {
@@ -387,13 +392,47 @@ func (c *V2RayXHTTPBaseOptions) GetNormalizedScMaxBufferedPosts() int {
}
func (c *V2RayXHTTPBaseOptions) GetNormalizedScStreamUpServerSecs() Xbadoption.Range {
if c.ScStreamUpServerSecs.To == 0 {
if c.ScStreamUpServerSecs == nil {
return Xbadoption.Range{
From: 20,
To: 80,
}
}
return c.ScStreamUpServerSecs
return *c.ScStreamUpServerSecs
}
func (c *V2RayXHTTPBaseOptions) GetNormalizedUplinkChunkSize() Xbadoption.Range {
if c.UplinkChunkSize == nil || c.UplinkChunkSize.To == 0 {
switch c.UplinkDataPlacement {
case PlacementCookie:
return Xbadoption.Range{
From: 2 * 1024, // 2 KiB
To: 3 * 1024, // 3 KiB
}
case PlacementHeader:
return Xbadoption.Range{
From: 3 * 1000, // 3 KB
To: 4 * 1000, // 4 KB
}
default:
return c.GetNormalizedScMaxEachPostBytes()
}
} else if c.UplinkChunkSize.From < 64 {
return Xbadoption.Range{
From: 64,
To: max(64, c.UplinkChunkSize.To),
}
}
return *c.UplinkChunkSize
}
func (c *V2RayXHTTPBaseOptions) GetNormalizedServerMaxHeaderBytes() int {
if c.ServerMaxHeaderBytes <= 0 {
return 8192
} else {
return c.ServerMaxHeaderBytes
}
}
func (c *V2RayXHTTPBaseOptions) GetNormalizedSessionPlacement() string {