Custom sessionID

This commit is contained in:
Fangliding
2026-06-03 22:06:15 +08:00
parent fdb9b616fc
commit 2f121bd6a2
6 changed files with 109 additions and 43 deletions
+15
View File
@@ -233,6 +233,8 @@ type SplitHTTPConfig struct {
ScMaxBufferedPosts int64 `json:"scMaxBufferedPosts"` ScMaxBufferedPosts int64 `json:"scMaxBufferedPosts"`
ScStreamUpServerSecs Int32Range `json:"scStreamUpServerSecs"` ScStreamUpServerSecs Int32Range `json:"scStreamUpServerSecs"`
ServerMaxHeaderBytes int32 `json:"serverMaxHeaderBytes"` ServerMaxHeaderBytes int32 `json:"serverMaxHeaderBytes"`
SessionIDTable string `json:"sessionIDTable"`
SessionIDLength Int32Range `json:"sessionIDLength"`
Xmux XmuxConfig `json:"xmux"` Xmux XmuxConfig `json:"xmux"`
DownloadSettings *StreamConfig `json:"downloadSettings"` DownloadSettings *StreamConfig `json:"downloadSettings"`
Extra json.RawMessage `json:"extra"` Extra json.RawMessage `json:"extra"`
@@ -378,6 +380,17 @@ func (c *SplitHTTPConfig) Build() (proto.Message, error) {
return nil, errors.New("invalid negative value of maxHeaderBytes") return nil, errors.New("invalid negative value of maxHeaderBytes")
} }
if c.SessionIDTable != "" {
if c.SessionIDLength.From <= 0 {
return nil, errors.New("sessionIDLength.from must be greater than 0")
}
for i := 0; i < len(c.SessionIDTable); i++ {
if c.SessionIDTable[i] >= 0x80 {
return nil, errors.New("sessionIDTable must contain only ASCII characters")
}
}
}
if c.Xmux.MaxConnections.To > 0 && c.Xmux.MaxConcurrency.To > 0 { if c.Xmux.MaxConnections.To > 0 && c.Xmux.MaxConcurrency.To > 0 {
return nil, errors.New("maxConnections cannot be specified together with maxConcurrency") return nil, errors.New("maxConnections cannot be specified together with maxConcurrency")
} }
@@ -416,6 +429,8 @@ func (c *SplitHTTPConfig) Build() (proto.Message, error) {
ScMaxBufferedPosts: c.ScMaxBufferedPosts, ScMaxBufferedPosts: c.ScMaxBufferedPosts,
ScStreamUpServerSecs: newRangeConfig(c.ScStreamUpServerSecs), ScStreamUpServerSecs: newRangeConfig(c.ScStreamUpServerSecs),
ServerMaxHeaderBytes: c.ServerMaxHeaderBytes, ServerMaxHeaderBytes: c.ServerMaxHeaderBytes,
SessionIDTable: c.SessionIDTable,
SessionIDLength: newRangeConfig(c.SessionIDLength),
Xmux: &splithttp.XmuxConfig{ Xmux: &splithttp.XmuxConfig{
MaxConcurrency: newRangeConfig(c.Xmux.MaxConcurrency), MaxConcurrency: newRangeConfig(c.Xmux.MaxConcurrency),
MaxConnections: newRangeConfig(c.Xmux.MaxConnections), MaxConnections: newRangeConfig(c.Xmux.MaxConnections),
+62 -30
View File
@@ -4,6 +4,7 @@ import (
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"io" "io"
"math/rand/v2"
"net/http" "net/http"
"strings" "strings"
@@ -11,6 +12,7 @@ import (
"github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/crypto" "github.com/xtls/xray-core/common/crypto"
"github.com/xtls/xray-core/common/utils" "github.com/xtls/xray-core/common/utils"
"github.com/xtls/xray-core/common/uuid"
"github.com/xtls/xray-core/transport/internet" "github.com/xtls/xray-core/transport/internet"
) )
@@ -131,26 +133,26 @@ func (c *Config) GetNormalizedUplinkHTTPMethod() string {
return c.UplinkHTTPMethod return c.UplinkHTTPMethod
} }
func (c *Config) GetNormalizedScMaxEachPostBytes() RangeConfig { func (c *Config) GetNormalizedScMaxEachPostBytes() *RangeConfig {
if c.ScMaxEachPostBytes == nil || c.ScMaxEachPostBytes.To == 0 { if c.ScMaxEachPostBytes == nil || c.ScMaxEachPostBytes.To == 0 {
return RangeConfig{ return &RangeConfig{
From: 1000000, From: 1000000,
To: 1000000, To: 1000000,
} }
} }
return *c.ScMaxEachPostBytes return c.ScMaxEachPostBytes
} }
func (c *Config) GetNormalizedScMinPostsIntervalMs() RangeConfig { func (c *Config) GetNormalizedScMinPostsIntervalMs() *RangeConfig {
if c.ScMinPostsIntervalMs == nil || c.ScMinPostsIntervalMs.To == 0 { if c.ScMinPostsIntervalMs == nil || c.ScMinPostsIntervalMs.To == 0 {
return RangeConfig{ return &RangeConfig{
From: 30, From: 30,
To: 30, To: 30,
} }
} }
return *c.ScMinPostsIntervalMs return c.ScMinPostsIntervalMs
} }
func (c *Config) GetNormalizedScMaxBufferedPosts() int { func (c *Config) GetNormalizedScMaxBufferedPosts() int {
@@ -161,27 +163,27 @@ func (c *Config) GetNormalizedScMaxBufferedPosts() int {
return int(c.ScMaxBufferedPosts) return int(c.ScMaxBufferedPosts)
} }
func (c *Config) GetNormalizedScStreamUpServerSecs() RangeConfig { func (c *Config) GetNormalizedScStreamUpServerSecs() *RangeConfig {
if c.ScStreamUpServerSecs == nil || c.ScStreamUpServerSecs.To == 0 { if c.ScStreamUpServerSecs == nil || c.ScStreamUpServerSecs.To == 0 {
return RangeConfig{ return &RangeConfig{
From: 20, From: 20,
To: 80, To: 80,
} }
} }
return *c.ScStreamUpServerSecs return c.ScStreamUpServerSecs
} }
func (c *Config) GetNormalizedUplinkChunkSize() RangeConfig { func (c *Config) GetNormalizedUplinkChunkSize() *RangeConfig {
if c.UplinkChunkSize == nil || c.UplinkChunkSize.To == 0 { if c.UplinkChunkSize == nil || c.UplinkChunkSize.To == 0 {
switch c.UplinkDataPlacement { switch c.UplinkDataPlacement {
case PlacementCookie: case PlacementCookie:
return RangeConfig{ return &RangeConfig{
From: 2 * 1024, // 2 KiB From: 2 * 1024, // 2 KiB
To: 3 * 1024, // 3 KiB To: 3 * 1024, // 3 KiB
} }
case PlacementHeader: case PlacementHeader:
return RangeConfig{ return &RangeConfig{
From: 3 * 1000, // 3 KB From: 3 * 1000, // 3 KB
To: 4 * 1000, // 4 KB To: 4 * 1000, // 4 KB
} }
@@ -189,13 +191,13 @@ func (c *Config) GetNormalizedUplinkChunkSize() RangeConfig {
return c.GetNormalizedScMaxEachPostBytes() return c.GetNormalizedScMaxEachPostBytes()
} }
} else if c.UplinkChunkSize.From < 64 { } else if c.UplinkChunkSize.From < 64 {
return RangeConfig{ return &RangeConfig{
From: 64, From: 64,
To: max(64, c.UplinkChunkSize.To), To: max(64, c.UplinkChunkSize.To),
} }
} }
return *c.UplinkChunkSize return c.UplinkChunkSize
} }
func (c *Config) GetNormalizedServerMaxHeaderBytes() int { func (c *Config) GetNormalizedServerMaxHeaderBytes() int {
@@ -417,59 +419,59 @@ func (c *Config) ExtractMetaFromRequest(req *http.Request, path string) (session
return sessionId, seqStr return sessionId, seqStr
} }
func (m *XmuxConfig) GetNormalizedMaxConcurrency() RangeConfig { func (m *XmuxConfig) GetNormalizedMaxConcurrency() *RangeConfig {
if m.MaxConcurrency == nil { if m.MaxConcurrency == nil {
return RangeConfig{ return &RangeConfig{
From: 0, From: 0,
To: 0, To: 0,
} }
} }
return *m.MaxConcurrency return m.MaxConcurrency
} }
func (m *XmuxConfig) GetNormalizedMaxConnections() RangeConfig { func (m *XmuxConfig) GetNormalizedMaxConnections() *RangeConfig {
if m.MaxConnections == nil { if m.MaxConnections == nil {
return RangeConfig{ return &RangeConfig{
From: 0, From: 0,
To: 0, To: 0,
} }
} }
return *m.MaxConnections return m.MaxConnections
} }
func (m *XmuxConfig) GetNormalizedCMaxReuseTimes() RangeConfig { func (m *XmuxConfig) GetNormalizedCMaxReuseTimes() *RangeConfig {
if m.CMaxReuseTimes == nil { if m.CMaxReuseTimes == nil {
return RangeConfig{ return &RangeConfig{
From: 0, From: 0,
To: 0, To: 0,
} }
} }
return *m.CMaxReuseTimes return m.CMaxReuseTimes
} }
func (m *XmuxConfig) GetNormalizedHMaxRequestTimes() RangeConfig { func (m *XmuxConfig) GetNormalizedHMaxRequestTimes() *RangeConfig {
if m.HMaxRequestTimes == nil { if m.HMaxRequestTimes == nil {
return RangeConfig{ return &RangeConfig{
From: 0, From: 0,
To: 0, To: 0,
} }
} }
return *m.HMaxRequestTimes return m.HMaxRequestTimes
} }
func (m *XmuxConfig) GetNormalizedHMaxReusableSecs() RangeConfig { func (m *XmuxConfig) GetNormalizedHMaxReusableSecs() *RangeConfig {
if m.HMaxReusableSecs == nil { if m.HMaxReusableSecs == nil {
return RangeConfig{ return &RangeConfig{
From: 0, From: 0,
To: 0, To: 0,
} }
} }
return *m.HMaxReusableSecs return m.HMaxReusableSecs
} }
func init() { func init() {
@@ -478,10 +480,40 @@ func init() {
})) }))
} }
func (c RangeConfig) rand() int32 { func (c *RangeConfig) rand() int32 {
return int32(crypto.RandBetween(int64(c.From), int64(c.To))) return int32(crypto.RandBetween(int64(c.From), int64(c.To)))
} }
// predefined
var (
base62Table = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
hexTable = "0123456789abcdef"
hexTableUpper = "0123456789ABCDEF"
)
func (c *Config) GenerateSessionID() string {
length := c.SessionIDLength.rand()
table := c.SessionIDTable
switch table {
case "base62":
table = base62Table
case "hex":
table = hexTable
case "HEX":
table = hexTableUpper
}
if table != "" && length > 0 {
id := make([]byte, length)
for i := range id {
id[i] = table[rand.N(len(table))]
}
return string(id)
} else {
uuid := uuid.New()
return uuid.String()
}
}
func appendToPath(path, value string) string { func appendToPath(path, value string) string {
if strings.HasSuffix(path, "/") { if strings.HasSuffix(path, "/") {
return path + value return path + value
+26 -7
View File
@@ -187,6 +187,8 @@ type Config struct {
UplinkDataKey string `protobuf:"bytes,25,opt,name=uplinkDataKey,proto3" json:"uplinkDataKey,omitempty"` UplinkDataKey string `protobuf:"bytes,25,opt,name=uplinkDataKey,proto3" json:"uplinkDataKey,omitempty"`
UplinkChunkSize *RangeConfig `protobuf:"bytes,26,opt,name=uplinkChunkSize,proto3" json:"uplinkChunkSize,omitempty"` UplinkChunkSize *RangeConfig `protobuf:"bytes,26,opt,name=uplinkChunkSize,proto3" json:"uplinkChunkSize,omitempty"`
ServerMaxHeaderBytes int32 `protobuf:"varint,27,opt,name=serverMaxHeaderBytes,proto3" json:"serverMaxHeaderBytes,omitempty"` ServerMaxHeaderBytes int32 `protobuf:"varint,27,opt,name=serverMaxHeaderBytes,proto3" json:"serverMaxHeaderBytes,omitempty"`
SessionIDTable string `protobuf:"bytes,28,opt,name=sessionIDTable,proto3" json:"sessionIDTable,omitempty"`
SessionIDLength *RangeConfig `protobuf:"bytes,29,opt,name=sessionIDLength,proto3" json:"sessionIDLength,omitempty"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@@ -410,6 +412,20 @@ func (x *Config) GetServerMaxHeaderBytes() int32 {
return 0 return 0
} }
func (x *Config) GetSessionIDTable() string {
if x != nil {
return x.SessionIDTable
}
return ""
}
func (x *Config) GetSessionIDLength() *RangeConfig {
if x != nil {
return x.SessionIDLength
}
return nil
}
var File_transport_internet_splithttp_config_proto protoreflect.FileDescriptor var File_transport_internet_splithttp_config_proto protoreflect.FileDescriptor
const file_transport_internet_splithttp_config_proto_rawDesc = "" + const file_transport_internet_splithttp_config_proto_rawDesc = "" +
@@ -425,7 +441,7 @@ const file_transport_internet_splithttp_config_proto_rawDesc = "" +
"\x0ecMaxReuseTimes\x18\x03 \x01(\v2..xray.transport.internet.splithttp.RangeConfigR\x0ecMaxReuseTimes\x12Z\n" + "\x0ecMaxReuseTimes\x18\x03 \x01(\v2..xray.transport.internet.splithttp.RangeConfigR\x0ecMaxReuseTimes\x12Z\n" +
"\x10hMaxRequestTimes\x18\x04 \x01(\v2..xray.transport.internet.splithttp.RangeConfigR\x10hMaxRequestTimes\x12Z\n" + "\x10hMaxRequestTimes\x18\x04 \x01(\v2..xray.transport.internet.splithttp.RangeConfigR\x10hMaxRequestTimes\x12Z\n" +
"\x10hMaxReusableSecs\x18\x05 \x01(\v2..xray.transport.internet.splithttp.RangeConfigR\x10hMaxReusableSecs\x12*\n" + "\x10hMaxReusableSecs\x18\x05 \x01(\v2..xray.transport.internet.splithttp.RangeConfigR\x10hMaxReusableSecs\x12*\n" +
"\x10hKeepAlivePeriod\x18\x06 \x01(\x03R\x10hKeepAlivePeriod\"\xc2\v\n" + "\x10hKeepAlivePeriod\x18\x06 \x01(\x03R\x10hKeepAlivePeriod\"\xc4\f\n" +
"\x06Config\x12\x12\n" + "\x06Config\x12\x12\n" +
"\x04host\x18\x01 \x01(\tR\x04host\x12\x12\n" + "\x04host\x18\x01 \x01(\tR\x04host\x12\x12\n" +
"\x04path\x18\x02 \x01(\tR\x04path\x12\x12\n" + "\x04path\x18\x02 \x01(\tR\x04path\x12\x12\n" +
@@ -456,7 +472,9 @@ const file_transport_internet_splithttp_config_proto_rawDesc = "" +
"\x13uplinkDataPlacement\x18\x18 \x01(\tR\x13uplinkDataPlacement\x12$\n" + "\x13uplinkDataPlacement\x18\x18 \x01(\tR\x13uplinkDataPlacement\x12$\n" +
"\ruplinkDataKey\x18\x19 \x01(\tR\ruplinkDataKey\x12X\n" + "\ruplinkDataKey\x18\x19 \x01(\tR\ruplinkDataKey\x12X\n" +
"\x0fuplinkChunkSize\x18\x1a \x01(\v2..xray.transport.internet.splithttp.RangeConfigR\x0fuplinkChunkSize\x122\n" + "\x0fuplinkChunkSize\x18\x1a \x01(\v2..xray.transport.internet.splithttp.RangeConfigR\x0fuplinkChunkSize\x122\n" +
"\x14serverMaxHeaderBytes\x18\x1b \x01(\x05R\x14serverMaxHeaderBytes\x1a:\n" + "\x14serverMaxHeaderBytes\x18\x1b \x01(\x05R\x14serverMaxHeaderBytes\x12&\n" +
"\x0esessionIDTable\x18\x1c \x01(\tR\x0esessionIDTable\x12X\n" +
"\x0fsessionIDLength\x18\x1d \x01(\v2..xray.transport.internet.splithttp.RangeConfigR\x0fsessionIDLength\x1a:\n" +
"\fHeadersEntry\x12\x10\n" + "\fHeadersEntry\x12\x10\n" +
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01B\x85\x01\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01B\x85\x01\n" +
@@ -496,11 +514,12 @@ var file_transport_internet_splithttp_config_proto_depIdxs = []int32{
1, // 10: xray.transport.internet.splithttp.Config.xmux:type_name -> xray.transport.internet.splithttp.XmuxConfig 1, // 10: xray.transport.internet.splithttp.Config.xmux:type_name -> xray.transport.internet.splithttp.XmuxConfig
4, // 11: xray.transport.internet.splithttp.Config.downloadSettings:type_name -> xray.transport.internet.StreamConfig 4, // 11: xray.transport.internet.splithttp.Config.downloadSettings:type_name -> xray.transport.internet.StreamConfig
0, // 12: xray.transport.internet.splithttp.Config.uplinkChunkSize:type_name -> xray.transport.internet.splithttp.RangeConfig 0, // 12: xray.transport.internet.splithttp.Config.uplinkChunkSize:type_name -> xray.transport.internet.splithttp.RangeConfig
13, // [13:13] is the sub-list for method output_type 0, // 13: xray.transport.internet.splithttp.Config.sessionIDLength:type_name -> xray.transport.internet.splithttp.RangeConfig
13, // [13:13] is the sub-list for method input_type 14, // [14:14] is the sub-list for method output_type
13, // [13:13] is the sub-list for extension type_name 14, // [14:14] is the sub-list for method input_type
13, // [13:13] is the sub-list for extension extendee 14, // [14:14] is the sub-list for extension type_name
0, // [0:13] is the sub-list for field type_name 14, // [14:14] is the sub-list for extension extendee
0, // [0:14] is the sub-list for field type_name
} }
func init() { file_transport_internet_splithttp_config_proto_init() } func init() { file_transport_internet_splithttp_config_proto_init() }
@@ -50,4 +50,6 @@ message Config {
string uplinkDataKey = 25; string uplinkDataKey = 25;
RangeConfig uplinkChunkSize = 26; RangeConfig uplinkChunkSize = 26;
int32 serverMaxHeaderBytes = 27; int32 serverMaxHeaderBytes = 27;
string sessionIDTable = 28;
RangeConfig sessionIDLength = 29;
} }
+1 -3
View File
@@ -24,7 +24,6 @@ import (
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/net/cnc" "github.com/xtls/xray-core/common/net/cnc"
"github.com/xtls/xray-core/common/signal/done" "github.com/xtls/xray-core/common/signal/done"
"github.com/xtls/xray-core/common/uuid"
"github.com/xtls/xray-core/transport/internet" "github.com/xtls/xray-core/transport/internet"
"github.com/xtls/xray-core/transport/internet/browser_dialer" "github.com/xtls/xray-core/transport/internet/browser_dialer"
"github.com/xtls/xray-core/transport/internet/hysteria/congestion" "github.com/xtls/xray-core/transport/internet/hysteria/congestion"
@@ -376,8 +375,7 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
sessionId := "" sessionId := ""
if mode != "stream-one" { if mode != "stream-one" {
sessionIdUuid := uuid.New() sessionId = transportConfiguration.GenerateSessionID()
sessionId = sessionIdUuid.String()
} }
errors.LogInfo(ctx, fmt.Sprintf("XHTTP is dialing to %s, mode %s, HTTP version %s, host %s", dest, mode, httpVersion, requestURL.Host)) errors.LogInfo(ctx, fmt.Sprintf("XHTTP is dialing to %s, mode %s, HTTP version %s, host %s", dest, mode, httpVersion, requestURL.Host))
+3 -3
View File
@@ -176,15 +176,15 @@ func ApplyPaddingToQuery(u *url.URL, key, value string) {
u.RawQuery = q.Encode() u.RawQuery = q.Encode()
} }
func (c *Config) GetNormalizedXPaddingBytes() RangeConfig { func (c *Config) GetNormalizedXPaddingBytes() *RangeConfig {
if c.XPaddingBytes == nil || c.XPaddingBytes.To == 0 { if c.XPaddingBytes == nil || c.XPaddingBytes.To == 0 {
return RangeConfig{ return &RangeConfig{
From: 100, From: 100,
To: 1000, To: 1000,
} }
} }
return *c.XPaddingBytes return c.XPaddingBytes
} }
func (c *Config) ApplyXPaddingToHeader(h http.Header, config XPaddingConfig) { func (c *Config) ApplyXPaddingToHeader(h http.Header, config XPaddingConfig) {