mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-06-03 17:57:30 +03:00
Update xhttp
This commit is contained in:
@@ -2,10 +2,10 @@ package option
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
Xbadoption "github.com/sagernet/sing-box/common/xray/json/badoption"
|
||||
"github.com/sagernet/sing-box/common/xray/utils"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
@@ -122,14 +122,29 @@ type V2RayXHTTPBaseOptions struct {
|
||||
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"`
|
||||
}
|
||||
|
||||
type V2RayXHTTPOptions struct {
|
||||
type _V2RayXHTTPOptions struct {
|
||||
Mode string `json:"mode"`
|
||||
V2RayXHTTPBaseOptions
|
||||
Download *V2RayXHTTPDownloadOptions `json:"download"`
|
||||
}
|
||||
|
||||
type V2RayXHTTPOptions _V2RayXHTTPOptions
|
||||
|
||||
type V2RayXHTTPDownloadOptions struct {
|
||||
V2RayXHTTPBaseOptions
|
||||
ServerOptions
|
||||
@@ -137,6 +152,153 @@ type V2RayXHTTPDownloadOptions struct {
|
||||
Detour string `json:"detour,omitempty"`
|
||||
}
|
||||
|
||||
const (
|
||||
PlacementQueryInHeader = "queryInHeader"
|
||||
PlacementCookie = "cookie"
|
||||
PlacementHeader = "header"
|
||||
PlacementQuery = "query"
|
||||
PlacementPath = "path"
|
||||
PlacementBody = "body"
|
||||
)
|
||||
|
||||
func (c *V2RayXHTTPOptions) UnmarshalJSON(bytes []byte) error {
|
||||
err := json.Unmarshal(bytes, (*_V2RayXHTTPOptions)(c))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch c.Mode {
|
||||
case "":
|
||||
c.Mode = "auto"
|
||||
case "auto", "packet-up", "stream-up", "stream-one":
|
||||
default:
|
||||
return E.New("unsupported mode: " + c.Mode)
|
||||
}
|
||||
err = checkV2RayXHTTPBaseOptions(c.Mode, &c.V2RayXHTTPBaseOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c.Download != nil {
|
||||
err = checkV2RayXHTTPBaseOptions(c.Mode, &c.Download.V2RayXHTTPBaseOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkV2RayXHTTPBaseOptions(mode string, options *V2RayXHTTPBaseOptions) error {
|
||||
// Priority (client): host > serverName > address
|
||||
for k := range options.Headers {
|
||||
if strings.ToLower(k) == "host" {
|
||||
return E.New(`"headers" can't contain "host"`)
|
||||
}
|
||||
}
|
||||
if options.XPaddingBytes.From <= 0 || options.XPaddingBytes.To <= 0 {
|
||||
return E.New("xPaddingBytes cannot be disabled")
|
||||
}
|
||||
if options.XPaddingKey == "" {
|
||||
options.XPaddingKey = "x_padding"
|
||||
}
|
||||
if options.XPaddingHeader == "" {
|
||||
options.XPaddingHeader = "X-Padding"
|
||||
}
|
||||
switch options.XPaddingPlacement {
|
||||
case "":
|
||||
options.XPaddingPlacement = "queryInHeader"
|
||||
case "cookie", "header", "query", "queryInHeader":
|
||||
default:
|
||||
return E.New("unsupported padding placement: " + options.XPaddingPlacement)
|
||||
}
|
||||
switch options.XPaddingMethod {
|
||||
case "":
|
||||
options.XPaddingMethod = "repeat-x"
|
||||
case "repeat-x", "tokenish":
|
||||
default:
|
||||
return E.New("unsupported padding method: " + options.XPaddingMethod)
|
||||
}
|
||||
switch options.UplinkDataPlacement {
|
||||
case "":
|
||||
options.UplinkDataPlacement = "body"
|
||||
case "body":
|
||||
case "cookie", "header":
|
||||
if mode != "packet-up" {
|
||||
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("uplinkHTTPMethod can be GET only in packet-up mode")
|
||||
}
|
||||
switch options.SessionPlacement {
|
||||
case "":
|
||||
options.SessionPlacement = "path"
|
||||
case "path", "cookie", "header", "query":
|
||||
default:
|
||||
return E.New("unsupported session placement: " + options.SessionPlacement)
|
||||
}
|
||||
switch options.SeqPlacement {
|
||||
case "":
|
||||
options.SeqPlacement = "path"
|
||||
case "path", "cookie", "header", "query":
|
||||
if options.SessionPlacement == "path" {
|
||||
return E.New("SeqPlacement must be path when SessionPlacement is path")
|
||||
}
|
||||
default:
|
||||
return E.New("unsupported seq placement: " + options.SeqPlacement)
|
||||
}
|
||||
if options.SessionPlacement != "path" && options.SessionKey == "" {
|
||||
switch options.SessionPlacement {
|
||||
case "cookie", "query":
|
||||
options.SessionKey = "x_session"
|
||||
case "header":
|
||||
options.SessionKey = "X-Session"
|
||||
}
|
||||
}
|
||||
if options.SeqPlacement != "path" && options.SeqKey == "" {
|
||||
switch options.SeqPlacement {
|
||||
case "cookie", "query":
|
||||
options.SeqKey = "x_seq"
|
||||
case "header":
|
||||
options.SeqKey = "X-Seq"
|
||||
}
|
||||
}
|
||||
if options.UplinkDataPlacement != "body" && options.UplinkDataKey == "" {
|
||||
switch options.UplinkDataPlacement {
|
||||
case "cookie":
|
||||
options.UplinkDataKey = "x_data"
|
||||
case "header":
|
||||
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.Xmux == nil {
|
||||
options.Xmux = &V2RayXHTTPXmuxOptions{}
|
||||
options.Xmux.MaxConcurrency.From = 1
|
||||
options.Xmux.MaxConcurrency.To = 1
|
||||
options.Xmux.HMaxRequestTimes.From = 600
|
||||
options.Xmux.HMaxRequestTimes.To = 900
|
||||
options.Xmux.HMaxReusableSecs.From = 1800
|
||||
options.Xmux.HMaxReusableSecs.To = 3000
|
||||
} else if options.Xmux.MaxConnections.To > 0 && options.Xmux.MaxConcurrency.To > 0 {
|
||||
return E.New("maxConnections cannot be specified together with maxConcurrency")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *V2RayXHTTPBaseOptions) GetNormalizedPath() string {
|
||||
pathAndQuery := strings.SplitN(c.Path, "?", 2)
|
||||
path := pathAndQuery[0]
|
||||
@@ -158,19 +320,14 @@ func (c *V2RayXHTTPBaseOptions) GetNormalizedQuery() string {
|
||||
return query
|
||||
}
|
||||
|
||||
func (c *V2RayXHTTPBaseOptions) GetRequestHeader(rawURL string) http.Header {
|
||||
func (c *V2RayXHTTPBaseOptions) GetRequestHeader() http.Header {
|
||||
header := http.Header{}
|
||||
for k, v := range c.Headers {
|
||||
header.Add(k, v)
|
||||
}
|
||||
u, _ := url.Parse(rawURL)
|
||||
// https://www.rfc-editor.org/rfc/rfc7541.html#appendix-B
|
||||
// h2's HPACK Header Compression feature employs a huffman encoding using a static table.
|
||||
// 'X' is assigned an 8 bit code, so HPACK compression won't change actual padding length on the wire.
|
||||
// https://www.rfc-editor.org/rfc/rfc9204.html#section-4.1.2-2
|
||||
// h3's similar QPACK feature uses the same huffman table.
|
||||
u.RawQuery = "x_padding=" + strings.Repeat("X", int(c.GetNormalizedXPaddingBytes().Rand()))
|
||||
header.Set("Referer", u.String())
|
||||
if header.Get("User-Agent") == "" {
|
||||
header.Set("User-Agent", utils.ChromeUA)
|
||||
}
|
||||
return header
|
||||
}
|
||||
|
||||
@@ -184,6 +341,13 @@ func (c *V2RayXHTTPBaseOptions) GetNormalizedXPaddingBytes() Xbadoption.Range {
|
||||
return c.XPaddingBytes
|
||||
}
|
||||
|
||||
func (c *V2RayXHTTPBaseOptions) GetNormalizedUplinkHTTPMethod() string {
|
||||
if c.UplinkHTTPMethod == "" {
|
||||
return "POST"
|
||||
}
|
||||
return c.UplinkHTTPMethod
|
||||
}
|
||||
|
||||
func (c *V2RayXHTTPBaseOptions) GetNormalizedScMaxEachPostBytes() Xbadoption.Range {
|
||||
if c.ScMaxEachPostBytes.To == 0 {
|
||||
return Xbadoption.Range{
|
||||
@@ -222,6 +386,55 @@ func (c *V2RayXHTTPBaseOptions) GetNormalizedScStreamUpServerSecs() Xbadoption.R
|
||||
return c.ScStreamUpServerSecs
|
||||
}
|
||||
|
||||
func (c *V2RayXHTTPBaseOptions) GetNormalizedSessionPlacement() string {
|
||||
if c.SessionPlacement == "" {
|
||||
return PlacementPath
|
||||
}
|
||||
return c.SessionPlacement
|
||||
}
|
||||
|
||||
func (c *V2RayXHTTPBaseOptions) GetNormalizedSeqPlacement() string {
|
||||
if c.SeqPlacement == "" {
|
||||
return PlacementPath
|
||||
}
|
||||
return c.SeqPlacement
|
||||
}
|
||||
|
||||
func (c *V2RayXHTTPBaseOptions) GetNormalizedUplinkDataPlacement() string {
|
||||
if c.UplinkDataPlacement == "" {
|
||||
return PlacementBody
|
||||
}
|
||||
return c.UplinkDataPlacement
|
||||
}
|
||||
|
||||
func (c *V2RayXHTTPBaseOptions) GetNormalizedSessionKey() string {
|
||||
if c.SessionKey != "" {
|
||||
return c.SessionKey
|
||||
}
|
||||
switch c.GetNormalizedSessionPlacement() {
|
||||
case PlacementHeader:
|
||||
return "X-Session"
|
||||
case PlacementCookie, PlacementQuery:
|
||||
return "x_session"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func (c *V2RayXHTTPBaseOptions) GetNormalizedSeqKey() string {
|
||||
if c.SeqKey != "" {
|
||||
return c.SeqKey
|
||||
}
|
||||
switch c.GetNormalizedSeqPlacement() {
|
||||
case PlacementHeader:
|
||||
return "X-Seq"
|
||||
case PlacementCookie, PlacementQuery:
|
||||
return "x_seq"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
type V2RayXHTTPXmuxOptions struct {
|
||||
MaxConcurrency Xbadoption.Range `json:"max_concurrency"`
|
||||
MaxConnections Xbadoption.Range `json:"max_connections"`
|
||||
|
||||
Reference in New Issue
Block a user