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

@@ -2,12 +2,5 @@ package option
type AdminPanelServiceOptions struct {
ListenOptions
Manager string `json:"manager"`
Database AdminPanelServiceDatabase `json:"database"`
InboundTLSOptionsContainer
}
type AdminPanelServiceDatabase struct {
Driver string `json:"driver"`
DSN string `json:"dsn"`
}

View File

@@ -332,10 +332,10 @@ func (o *DNSServerOptions) Upgrade(ctx context.Context) error {
if !serverAddr.IsValid() {
return E.New("invalid server address")
}
o.Options = &SDNSDNSServerOptions{
RemoteDNSServerOptions: remoteOptions,
Stamp: serverAddr.AddrString(),
}
o.Options = &SDNSDNSServerOptions{
RemoteDNSServerOptions: remoteOptions,
Stamp: serverAddr.AddrString(),
}
default:
return E.New("unsupported DNS server scheme: ", serverType)
}
@@ -424,4 +424,9 @@ type DHCPDNSServerOptions struct {
type SDNSDNSServerOptions struct {
RemoteDNSServerOptions
Stamp string `json:"stamp"`
}
}
type FallbackDNSServerOptions struct {
Servers []string `json:"servers"`
Strategy string `json:"strategy,omitempty"`
}

View File

@@ -1,9 +1,13 @@
package option
import "github.com/sagernet/sing/common/json/badoption"
type FailoverInboundOptions struct {
Inbounds []Inbound `json:"inbounds"`
}
type FailoverOutboundOptions struct {
Outbounds []Outbound `json:"outbounds"`
Strategy string `json:"strategy,omitempty"`
Delay badoption.Duration `json:"delay,omitempty"`
Outbounds []Outbound `json:"outbounds"`
}

View File

@@ -53,7 +53,7 @@ func (m Hysteria2Masquerade) MarshalJSON() ([]byte, error) {
default:
return nil, E.New("unknown masquerade type: ", m.Type)
}
return badjson.MarshallObjects((_Hysteria2Masquerade)(m), v)
return badjson.MarshallObjects(_Hysteria2Masquerade(m), v)
}
func (m *Hysteria2Masquerade) UnmarshalJSON(bytes []byte) error {

View File

@@ -90,7 +90,7 @@ type ListenOptions struct {
type UDPTimeoutCompat badoption.Duration
func (c UDPTimeoutCompat) MarshalJSON() ([]byte, error) {
return json.Marshal((time.Duration)(c).String())
return json.Marshal(time.Duration(c).String())
}
func (c *UDPTimeoutCompat) UnmarshalJSON(data []byte) error {

View File

@@ -2,12 +2,14 @@ package option
import (
"github.com/sagernet/sing/common/byteformats"
"github.com/sagernet/sing/common/json/badoption"
)
type BandwidthLimiterOutboundOptions struct {
Strategy string `json:"strategy"`
Mode string `json:"mode"`
ConnectionType string `json:"connection_type,omitempty"`
Mode string `json:"mode"`
FlowKeys []string `json:"flow_keys,omitempty"`
Speed *byteformats.NetworkBytesCompat `json:"speed"`
Users []BandwidthLimiterUser `json:"users,omitempty"`
Route RouteOptions `json:"route"`
@@ -16,11 +18,26 @@ type BandwidthLimiterOutboundOptions struct {
type BandwidthLimiterUser struct {
Name string `json:"name"`
Strategy string `json:"strategy"`
Mode string `json:"mode"`
ConnectionType string `json:"connection_type,omitempty"`
Mode string `json:"mode"`
Speed *byteformats.NetworkBytesCompat `json:"speed"`
}
type TrafficLimiterOutboundOptions struct {
Strategy string `json:"strategy"`
Mode string `json:"mode"`
Total *byteformats.Bytes `json:"total"`
Users []TrafficLimiterUser `json:"users,omitempty"`
Route RouteOptions `json:"route"`
}
type TrafficLimiterUser struct {
Name string `json:"name"`
Strategy string `json:"strategy"`
Mode string `json:"mode"`
Total *byteformats.Bytes `json:"total"`
}
type ConnectionLimiterOutboundOptions struct {
Strategy string `json:"strategy"`
ConnectionType string `json:"connection_type,omitempty"`
@@ -35,3 +52,20 @@ type ConnectionLimiterUser struct {
ConnectionType string `json:"connection_type,omitempty"`
Count uint32 `json:"count"`
}
type RateLimiterOutboundOptions struct {
Strategy string `json:"strategy"`
ConnectionType string `json:"connection_type,omitempty"`
Count uint32 `json:"count"`
Interval badoption.Duration `json:"interval"`
Users []RateLimiterUser `json:"users,omitempty"`
Route RouteOptions `json:"route"`
}
type RateLimiterUser struct {
Name string `json:"name"`
Strategy string `json:"strategy"`
ConnectionType string `json:"connection_type,omitempty"`
Count uint32 `json:"count"`
Interval badoption.Duration `json:"interval"`
}

77
option/manager_api.go Normal file
View File

@@ -0,0 +1,77 @@
package option
import (
C "github.com/sagernet/sing-box/constant"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/json"
"github.com/sagernet/sing/common/json/badjson"
)
type _ManagerAPIOptions struct {
APIType string `json:"api_type"`
ProtocolType string `json:"protocol_type"`
ServerOptions ManagerAPIServerOptions `json:"-"`
ClientOptions ManagerAPIClientOptions `json:"-"`
}
type ManagerAPIOptions _ManagerAPIOptions
func (o ManagerAPIOptions) MarshalJSON() ([]byte, error) {
var v any
switch o.APIType {
case C.ManagerAPIServer:
v = o.ServerOptions
case C.ManagerAPIClient:
v = o.ClientOptions
case "":
return nil, E.New("missing api type")
default:
return nil, E.New("unknown api type: " + o.APIType)
}
return badjson.MarshallObjects(_ManagerAPIOptions(o), v)
}
func (o *ManagerAPIOptions) UnmarshalJSON(bytes []byte) error {
err := json.Unmarshal(bytes, (*_ManagerAPIOptions)(o))
if err != nil {
return err
}
var v any
switch o.APIType {
case C.ManagerAPIServer:
v = &o.ServerOptions
case C.ManagerAPIClient:
v = &o.ClientOptions
case "":
return E.New("missing api type")
default:
return E.New("unknown api type: " + o.APIType)
}
err = badjson.UnmarshallExcluded(bytes, (*_ManagerAPIOptions)(o), v)
if err != nil {
return err
}
return nil
}
type ManagerAPIServerOptions struct {
ListenOptions
InboundTLSOptionsContainer
Manager string `json:"manager"`
APIKey string `json:"api_key"`
CORS *ManagerAPICORSOptions `json:"cors,omitempty"`
}
type ManagerAPICORSOptions struct {
AllowedOrigins []string `json:"allowed_origins,omitempty"`
ExposedHeaders []string `json:"exposed_headers,omitempty"`
MaxAge int `json:"max_age,omitempty"`
}
type ManagerAPIClientOptions struct {
DialerOptions
ServerOptions
OutboundTLSOptionsContainer
APIKey string `json:"api_key"`
}

View File

@@ -5,5 +5,7 @@ type NodeServiceOptions struct {
Inbounds []string `json:"inbounds"`
ConnectionLimiters []string `json:"connection_limiters"`
BandwidthLimiters []string `json:"bandwidth_limiters"`
TrafficLimiters []string `json:"traffic_limiters"`
RateLimiters []string `json:"rate_limiters"`
Manager string `json:"manager"`
}

View File

@@ -1,13 +0,0 @@
package option
type NodeManagerServerServiceOptions struct {
ListenOptions
InboundTLSOptionsContainer
Manager string `json:"manager"`
}
type NodeManagerClientServiceOptions struct {
DialerOptions
ServerOptions
OutboundTLSOptionsContainer
}

View File

@@ -0,0 +1,69 @@
package option
import (
C "github.com/sagernet/sing-box/constant"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/json"
"github.com/sagernet/sing/common/json/badjson"
)
type _NodeManagerAPIOptions struct {
APIType string `json:"api_type"`
ServerOptions NodeManagerAPIServerOptions `json:"-"`
ClientOptions NodeManagerAPIClientOptions `json:"-"`
}
type NodeManagerAPIOptions _NodeManagerAPIOptions
func (o NodeManagerAPIOptions) MarshalJSON() ([]byte, error) {
var v any
switch o.APIType {
case C.NodeManagerAPIServer:
v = o.ServerOptions
case C.NodeManagerAPIClient:
v = o.ClientOptions
case "":
return nil, E.New("missing api type")
default:
return nil, E.New("unknown api type: " + o.APIType)
}
return badjson.MarshallObjects(_NodeManagerAPIOptions(o), v)
}
func (o *NodeManagerAPIOptions) UnmarshalJSON(bytes []byte) error {
err := json.Unmarshal(bytes, (*_NodeManagerAPIOptions)(o))
if err != nil {
return err
}
var v any
switch o.APIType {
case C.NodeManagerAPIServer:
v = &o.ServerOptions
case C.NodeManagerAPIClient:
v = &o.ClientOptions
case "":
return E.New("missing api type")
default:
return E.New("unknown api type: " + o.APIType)
}
err = badjson.UnmarshallExcluded(bytes, (*_NodeManagerAPIOptions)(o), v)
if err != nil {
return err
}
return nil
}
type NodeManagerAPIServerOptions struct {
ListenOptions
InboundTLSOptionsContainer
Manager string `json:"manager"`
APIKey string `json:"api_key"`
}
type NodeManagerAPIClientOptions struct {
DialerOptions
ServerOptions
OutboundTLSOptionsContainer
APIKey string `json:"api_key"`
}

View File

@@ -111,7 +111,7 @@ func (o DomainResolveOptions) MarshalJSON() ([]byte, error) {
o.ClientSubnet == nil {
return json.Marshal(o.Server)
} else {
return json.Marshal((_DomainResolveOptions)(o))
return json.Marshal(_DomainResolveOptions(o))
}
}

9
option/profiler.go Normal file
View File

@@ -0,0 +1,9 @@
package option
import "github.com/sagernet/sing/common/json/badoption"
type ProfilerServiceOptions struct {
Listen string `json:"listen,omitempty"`
ReadTimeout badoption.Duration `json:"read_timeout,omitempty"`
WriteTimeout badoption.Duration `json:"write_timeout,omitempty"`
}

View File

@@ -16,7 +16,7 @@ type _ResolvedServiceOptions struct {
type ResolvedServiceOptions _ResolvedServiceOptions
func (r ResolvedServiceOptions) MarshalJSONContext(ctx context.Context) ([]byte, error) {
if r.Listen != nil && netip.Addr(*r.Listen) == (netip.AddrFrom4([4]byte{127, 0, 0, 53})) {
if r.Listen != nil && netip.Addr(*r.Listen) == netip.AddrFrom4([4]byte{127, 0, 0, 53}) {
r.Listen = nil
}
if r.ListenPort == 53 {

View File

@@ -30,7 +30,7 @@ func (r Rule) MarshalJSON() ([]byte, error) {
default:
return nil, E.New("unknown rule type: " + r.Type)
}
return badjson.MarshallObjects((_Rule)(r), v)
return badjson.MarshallObjects(_Rule(r), v)
}
func (r *Rule) UnmarshalJSON(bytes []byte) error {

View File

@@ -53,9 +53,9 @@ func (r RuleAction) MarshalJSON() ([]byte, error) {
return nil, E.New("unknown rule action: " + r.Action)
}
if v == nil {
return badjson.MarshallObjects((_RuleAction)(r))
return badjson.MarshallObjects(_RuleAction(r))
}
return badjson.MarshallObjects((_RuleAction)(r), v)
return badjson.MarshallObjects(_RuleAction(r), v)
}
func (r *RuleAction) UnmarshalJSON(data []byte) error {
@@ -124,7 +124,7 @@ func (r DNSRuleAction) MarshalJSON() ([]byte, error) {
default:
return nil, E.New("unknown DNS rule action: " + r.Action)
}
return badjson.MarshallObjects((_DNSRuleAction)(r), v)
return badjson.MarshallObjects(_DNSRuleAction(r), v)
}
func (r *DNSRuleAction) UnmarshalJSONContext(ctx context.Context, data []byte) error {
@@ -281,7 +281,7 @@ func (r RejectActionOptions) MarshalJSON() ([]byte, error) {
case C.RuleActionRejectMethodDefault:
r.Method = ""
}
return json.Marshal((_RejectActionOptions)(r))
return json.Marshal(_RejectActionOptions(r))
}
func (r *RejectActionOptions) UnmarshalJSON(bytes []byte) error {

View File

@@ -31,7 +31,7 @@ func (r DNSRule) MarshalJSON() ([]byte, error) {
default:
return nil, E.New("unknown rule type: " + r.Type)
}
return badjson.MarshallObjects((_DNSRule)(r), v)
return badjson.MarshallObjects(_DNSRule(r), v)
}
func (r *DNSRule) UnmarshalJSONContext(ctx context.Context, bytes []byte) error {

View File

@@ -53,7 +53,7 @@ func (r RuleSet) MarshalJSON() ([]byte, error) {
default:
return nil, E.New("unknown rule-set type: " + r.Type)
}
return badjson.MarshallObjects((_RuleSet)(r), v)
return badjson.MarshallObjects(_RuleSet(r), v)
}
func (r *RuleSet) UnmarshalJSON(bytes []byte) error {
@@ -145,7 +145,7 @@ func (r HeadlessRule) MarshalJSON() ([]byte, error) {
default:
return nil, E.New("unknown rule type: " + r.Type)
}
return badjson.MarshallObjects((_HeadlessRule)(r), v)
return badjson.MarshallObjects(_HeadlessRule(r), v)
}
func (r *HeadlessRule) UnmarshalJSON(bytes []byte) error {
@@ -248,7 +248,7 @@ func (r PlainRuleSetCompat) MarshalJSON() ([]byte, error) {
default:
return nil, E.New("unknown rule-set version: ", r.Version)
}
return badjson.MarshallObjects((_PlainRuleSetCompat)(r), v)
return badjson.MarshallObjects(_PlainRuleSetCompat(r), v)
}
func (r *PlainRuleSetCompat) UnmarshalJSON(bytes []byte) error {

View File

@@ -50,7 +50,7 @@ func (o ACMEDNS01ChallengeOptions) MarshalJSON() ([]byte, error) {
default:
return nil, E.New("unknown provider type: " + o.Provider)
}
return badjson.MarshallObjects((_ACMEDNS01ChallengeOptions)(o), v)
return badjson.MarshallObjects(_ACMEDNS01ChallengeOptions(o), v)
}
func (o *ACMEDNS01ChallengeOptions) UnmarshalJSON(bytes []byte) error {

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 {