Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions infra/conf/transport_internet.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/hex"
"encoding/json"
"math"
"math/big"
"net/netip"
"net/url"
"os"
Expand Down Expand Up @@ -233,6 +234,8 @@ type SplitHTTPConfig struct {
ScMaxBufferedPosts int64 `json:"scMaxBufferedPosts"`
ScStreamUpServerSecs Int32Range `json:"scStreamUpServerSecs"`
ServerMaxHeaderBytes int32 `json:"serverMaxHeaderBytes"`
SessionIDTable string `json:"sessionIDTable"`
SessionIDLength Int32Range `json:"sessionIDLength"`
Xmux XmuxConfig `json:"xmux"`
DownloadSettings *StreamConfig `json:"downloadSettings"`
Extra json.RawMessage `json:"extra"`
Expand Down Expand Up @@ -378,6 +381,25 @@ func (c *SplitHTTPConfig) Build() (proto.Message, error) {
return nil, errors.New("invalid negative value of maxHeaderBytes")
}

if c.SessionIDTable != "" {
if predefined, ok := splithttp.PredefinedTable[c.SessionIDTable]; ok {
c.SessionIDTable = predefined
}
room := roomSize(len(c.SessionIDTable), c.SessionIDLength.From, c.SessionIDLength.To)
// 2.1B possiblities should be enough
if room.Cmp(big.NewInt(2<<30)) < 0 {
return nil, errors.New("sessionIDTable or sessionIDLength is too small")
}
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 {
return nil, errors.New("maxConnections cannot be specified together with maxConcurrency")
}
Expand Down Expand Up @@ -416,6 +438,8 @@ func (c *SplitHTTPConfig) Build() (proto.Message, error) {
ScMaxBufferedPosts: c.ScMaxBufferedPosts,
ScStreamUpServerSecs: newRangeConfig(c.ScStreamUpServerSecs),
ServerMaxHeaderBytes: c.ServerMaxHeaderBytes,
SessionIDTable: c.SessionIDTable,
SessionIDLength: newRangeConfig(c.SessionIDLength),
Xmux: &splithttp.XmuxConfig{
MaxConcurrency: newRangeConfig(c.Xmux.MaxConcurrency),
MaxConnections: newRangeConfig(c.Xmux.MaxConnections),
Expand All @@ -439,6 +463,17 @@ func (c *SplitHTTPConfig) Build() (proto.Message, error) {
return config, nil
}

func roomSize(tableSize int, min, max int32) *big.Int {
base := big.NewInt(int64(tableSize))
sum := new(big.Int)
term := new(big.Int)
for k := min; k <= max; k++ {
term.Exp(base, big.NewInt(int64(k)), nil)
sum.Add(sum, term)
}
return sum
}

const (
Byte = 1
Kilobyte = 1024 * Byte
Expand Down
96 changes: 66 additions & 30 deletions transport/internet/splithttp/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import (
"encoding/base64"
"fmt"
"io"
"math/rand/v2"
"net/http"
"strings"

"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/crypto"
"github.com/xtls/xray-core/common/utils"
"github.com/xtls/xray-core/common/uuid"
"github.com/xtls/xray-core/transport/internet"
)

Expand Down Expand Up @@ -131,26 +133,26 @@ func (c *Config) GetNormalizedUplinkHTTPMethod() string {
return c.UplinkHTTPMethod
}

func (c *Config) GetNormalizedScMaxEachPostBytes() RangeConfig {
func (c *Config) GetNormalizedScMaxEachPostBytes() *RangeConfig {
if c.ScMaxEachPostBytes == nil || c.ScMaxEachPostBytes.To == 0 {
return RangeConfig{
return &RangeConfig{
From: 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 {
return RangeConfig{
return &RangeConfig{
From: 30,
To: 30,
}
}

return *c.ScMinPostsIntervalMs
return c.ScMinPostsIntervalMs
}

func (c *Config) GetNormalizedScMaxBufferedPosts() int {
Expand All @@ -161,41 +163,41 @@ func (c *Config) GetNormalizedScMaxBufferedPosts() int {
return int(c.ScMaxBufferedPosts)
}

func (c *Config) GetNormalizedScStreamUpServerSecs() RangeConfig {
func (c *Config) GetNormalizedScStreamUpServerSecs() *RangeConfig {
if c.ScStreamUpServerSecs == nil || c.ScStreamUpServerSecs.To == 0 {
return RangeConfig{
return &RangeConfig{
From: 20,
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 {
switch c.UplinkDataPlacement {
case PlacementCookie:
return RangeConfig{
return &RangeConfig{
From: 2 * 1024, // 2 KiB
To: 3 * 1024, // 3 KiB
}
case PlacementHeader:
return RangeConfig{
return &RangeConfig{
From: 3 * 1000, // 3 KB
To: 4 * 1000, // 4 KB
}
default:
return c.GetNormalizedScMaxEachPostBytes()
}
} else if c.UplinkChunkSize.From < 64 {
return RangeConfig{
return &RangeConfig{
From: 64,
To: max(64, c.UplinkChunkSize.To),
}
}

return *c.UplinkChunkSize
return c.UplinkChunkSize
}

func (c *Config) GetNormalizedServerMaxHeaderBytes() int {
Expand Down Expand Up @@ -417,59 +419,59 @@ func (c *Config) ExtractMetaFromRequest(req *http.Request, path string) (session
return sessionId, seqStr
}

func (m *XmuxConfig) GetNormalizedMaxConcurrency() RangeConfig {
func (m *XmuxConfig) GetNormalizedMaxConcurrency() *RangeConfig {
if m.MaxConcurrency == nil {
return RangeConfig{
return &RangeConfig{
From: 0,
To: 0,
}
}

return *m.MaxConcurrency
return m.MaxConcurrency
}

func (m *XmuxConfig) GetNormalizedMaxConnections() RangeConfig {
func (m *XmuxConfig) GetNormalizedMaxConnections() *RangeConfig {
if m.MaxConnections == nil {
return RangeConfig{
return &RangeConfig{
From: 0,
To: 0,
}
}

return *m.MaxConnections
return m.MaxConnections
}

func (m *XmuxConfig) GetNormalizedCMaxReuseTimes() RangeConfig {
func (m *XmuxConfig) GetNormalizedCMaxReuseTimes() *RangeConfig {
if m.CMaxReuseTimes == nil {
return RangeConfig{
return &RangeConfig{
From: 0,
To: 0,
}
}

return *m.CMaxReuseTimes
return m.CMaxReuseTimes
}

func (m *XmuxConfig) GetNormalizedHMaxRequestTimes() RangeConfig {
func (m *XmuxConfig) GetNormalizedHMaxRequestTimes() *RangeConfig {
if m.HMaxRequestTimes == nil {
return RangeConfig{
return &RangeConfig{
From: 0,
To: 0,
}
}

return *m.HMaxRequestTimes
return m.HMaxRequestTimes
}

func (m *XmuxConfig) GetNormalizedHMaxReusableSecs() RangeConfig {
func (m *XmuxConfig) GetNormalizedHMaxReusableSecs() *RangeConfig {
if m.HMaxReusableSecs == nil {
return RangeConfig{
return &RangeConfig{
From: 0,
To: 0,
}
}

return *m.HMaxReusableSecs
return m.HMaxReusableSecs
}

func init() {
Expand All @@ -478,10 +480,44 @@ func init() {
}))
}

func (c RangeConfig) rand() int32 {
func (c *RangeConfig) rand() int32 {
if c == nil {
return 0
}
return int32(crypto.RandBetween(int64(c.From), int64(c.To)))
}

// predefined
var PredefinedTable = map[string]string{
"ALPHABET": "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"Alphabet": "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
"BASE36": "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"Base62": "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
"HEX": "0123456789ABCDEF",
"alphabet": "abcdefghijklmnopqrstuvwxyz",
"base36": "0123456789abcdefghijklmnopqrstuvwxyz",
"hex": "0123456789abcdef",
"number": "0123456789",
}

func (c *Config) GenerateSessionID() string {
length := c.SessionIDLength.rand()
table := c.SessionIDTable
if predefined, ok := PredefinedTable[table]; ok {
table = predefined
}
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 {
if strings.HasSuffix(path, "/") {
return path + value
Expand Down
33 changes: 26 additions & 7 deletions transport/internet/splithttp/config.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions transport/internet/splithttp/config.proto
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,6 @@ message Config {
string uplinkDataKey = 25;
RangeConfig uplinkChunkSize = 26;
int32 serverMaxHeaderBytes = 27;
string sessionIDTable = 28;
RangeConfig sessionIDLength = 29;
}
4 changes: 1 addition & 3 deletions transport/internet/splithttp/dialer.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import (
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/net/cnc"
"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/browser_dialer"
"github.com/xtls/xray-core/transport/internet/hysteria/congestion"
Expand Down Expand Up @@ -376,8 +375,7 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me

sessionId := ""
if mode != "stream-one" {
sessionIdUuid := uuid.New()
sessionId = sessionIdUuid.String()
sessionId = transportConfiguration.GenerateSessionID()
}

errors.LogInfo(ctx, fmt.Sprintf("XHTTP is dialing to %s, mode %s, HTTP version %s, host %s", dest, mode, httpVersion, requestURL.Host))
Expand Down
Loading
Loading