SplitHTTP: More range options, change defaults, enforce maxUploadSize, fix querystring behavior (#3603)

* maxUploadSize and maxConcurrentUploads can now be ranges on the client
* maxUploadSize is now enforced on the server
* the default of maxUploadSize is 2MB on the server, and 1MB on the client
* the default of maxConcurrentUploads is 200 on the server, and 100 on the client
* ranges on the server are treated as a single number. if server is configured as `"1-2"`, server will enforce `2`
* querystrings in `path` are now handled correctly
This commit is contained in:
mmmray 2024-07-29 06:35:17 +02:00 committed by GitHub
parent 4cb2a128db
commit 59f6685774
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 223 additions and 82 deletions

View File

@ -229,8 +229,8 @@ type SplitHTTPConfig struct {
Host string `json:"host"` Host string `json:"host"`
Path string `json:"path"` Path string `json:"path"`
Headers map[string]string `json:"headers"` Headers map[string]string `json:"headers"`
MaxConcurrentUploads int32 `json:"maxConcurrentUploads"` MaxConcurrentUploads Int32Range `json:"maxConcurrentUploads"`
MaxUploadSize int32 `json:"maxUploadSize"` MaxUploadSize Int32Range `json:"maxUploadSize"`
MinUploadIntervalMs Int32Range `json:"minUploadIntervalMs"` MinUploadIntervalMs Int32Range `json:"minUploadIntervalMs"`
} }
@ -245,11 +245,17 @@ func (c *SplitHTTPConfig) Build() (proto.Message, error) {
c.Host = c.Headers["Host"] c.Host = c.Headers["Host"]
} }
config := &splithttp.Config{ config := &splithttp.Config{
Path: c.Path, Path: c.Path,
Host: c.Host, Host: c.Host,
Header: c.Headers, Header: c.Headers,
MaxConcurrentUploads: c.MaxConcurrentUploads, MaxConcurrentUploads: &splithttp.RandRangeConfig{
MaxUploadSize: c.MaxUploadSize, From: c.MaxConcurrentUploads.From,
To: c.MaxConcurrentUploads.To,
},
MaxUploadSize: &splithttp.RandRangeConfig{
From: c.MaxUploadSize.From,
To: c.MaxUploadSize.To,
},
MinUploadIntervalMs: &splithttp.RandRangeConfig{ MinUploadIntervalMs: &splithttp.RandRangeConfig{
From: c.MinUploadIntervalMs.From, From: c.MinUploadIntervalMs.From,
To: c.MinUploadIntervalMs.To, To: c.MinUploadIntervalMs.To,

View File

@ -4,23 +4,28 @@ import (
"crypto/rand" "crypto/rand"
"math/big" "math/big"
"net/http" "net/http"
"strings"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/transport/internet" "github.com/xtls/xray-core/transport/internet"
) )
func (c *Config) GetNormalizedPath() string { func (c *Config) GetNormalizedPath(addPath string, addQuery bool) string {
path := c.Path pathAndQuery := strings.SplitN(c.Path, "?", 2)
if path == "" { path := pathAndQuery[0]
path = "/" query := ""
if len(pathAndQuery) > 1 && addQuery {
query = "?" + pathAndQuery[1]
} }
if path[0] != '/' {
if path == "" || path[0] != '/' {
path = "/" + path path = "/" + path
} }
if path[len(path)-1] != '/' { if path[len(path)-1] != '/' {
path = path + "/" path = path + "/"
} }
return path
return path + addPath + query
} }
func (c *Config) GetRequestHeader() http.Header { func (c *Config) GetRequestHeader() http.Header {
@ -31,33 +36,51 @@ func (c *Config) GetRequestHeader() http.Header {
return header return header
} }
func (c *Config) GetNormalizedMaxConcurrentUploads() int32 { func (c *Config) GetNormalizedMaxConcurrentUploads(isServer bool) RandRangeConfig {
if c.MaxConcurrentUploads == 0 { if c.MaxConcurrentUploads == nil {
return 10 if isServer {
return RandRangeConfig{
From: 200,
To: 200,
}
} else {
return RandRangeConfig{
From: 100,
To: 100,
}
}
} }
return c.MaxConcurrentUploads return *c.MaxConcurrentUploads
} }
func (c *Config) GetNormalizedMaxUploadSize() int32 { func (c *Config) GetNormalizedMaxUploadSize(isServer bool) RandRangeConfig {
if c.MaxUploadSize == 0 { if c.MaxUploadSize == nil {
return 1000000 if isServer {
return RandRangeConfig{
From: 2000000,
To: 2000000,
}
} else {
return RandRangeConfig{
From: 1000000,
To: 1000000,
}
}
} }
return c.MaxUploadSize return *c.MaxUploadSize
} }
func (c *Config) GetNormalizedMinUploadInterval() RandRangeConfig { func (c *Config) GetNormalizedMinUploadInterval() RandRangeConfig {
r := c.MinUploadIntervalMs if c.MinUploadIntervalMs == nil {
return RandRangeConfig{
if r == nil {
r = &RandRangeConfig{
From: 30, From: 30,
To: 30, To: 30,
} }
} }
return *r return *c.MinUploadIntervalMs
} }
func init() { func init() {

View File

@ -28,8 +28,8 @@ type Config struct {
Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"` Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"`
Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
Header map[string]string `protobuf:"bytes,3,rep,name=header,proto3" json:"header,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` Header map[string]string `protobuf:"bytes,3,rep,name=header,proto3" json:"header,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
MaxConcurrentUploads int32 `protobuf:"varint,4,opt,name=maxConcurrentUploads,proto3" json:"maxConcurrentUploads,omitempty"` MaxConcurrentUploads *RandRangeConfig `protobuf:"bytes,4,opt,name=maxConcurrentUploads,proto3" json:"maxConcurrentUploads,omitempty"`
MaxUploadSize int32 `protobuf:"varint,5,opt,name=maxUploadSize,proto3" json:"maxUploadSize,omitempty"` MaxUploadSize *RandRangeConfig `protobuf:"bytes,5,opt,name=maxUploadSize,proto3" json:"maxUploadSize,omitempty"`
MinUploadIntervalMs *RandRangeConfig `protobuf:"bytes,6,opt,name=minUploadIntervalMs,proto3" json:"minUploadIntervalMs,omitempty"` MinUploadIntervalMs *RandRangeConfig `protobuf:"bytes,6,opt,name=minUploadIntervalMs,proto3" json:"minUploadIntervalMs,omitempty"`
} }
@ -86,18 +86,18 @@ func (x *Config) GetHeader() map[string]string {
return nil return nil
} }
func (x *Config) GetMaxConcurrentUploads() int32 { func (x *Config) GetMaxConcurrentUploads() *RandRangeConfig {
if x != nil { if x != nil {
return x.MaxConcurrentUploads return x.MaxConcurrentUploads
} }
return 0 return nil
} }
func (x *Config) GetMaxUploadSize() int32 { func (x *Config) GetMaxUploadSize() *RandRangeConfig {
if x != nil { if x != nil {
return x.MaxUploadSize return x.MaxUploadSize
} }
return 0 return nil
} }
func (x *Config) GetMinUploadIntervalMs() *RandRangeConfig { func (x *Config) GetMinUploadIntervalMs() *RandRangeConfig {
@ -169,8 +169,8 @@ var file_transport_internet_splithttp_config_proto_rawDesc = []byte{
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x63, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x63,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x21, 0x78, 0x72, 0x61, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x21, 0x78, 0x72, 0x61,
0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x22, 0xfa, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x22, 0xe2,
0x02, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x03, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73,
0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a,
0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74,
0x68, 0x12, 0x4d, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28, 0x68, 0x12, 0x4d, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28,
@ -178,35 +178,41 @@ var file_transport_internet_splithttp_config_proto_rawDesc = []byte{
0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69,
0x74, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x48, 0x65, 0x61, 0x74, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x48, 0x65, 0x61,
0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72,
0x12, 0x32, 0x0a, 0x14, 0x6d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x12, 0x66, 0x0a, 0x14, 0x6d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e,
0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x14, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32,
0x6d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x55, 0x70, 0x6c, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e,
0x6f, 0x61, 0x64, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74,
0x64, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x74, 0x70, 0x2e, 0x52, 0x61, 0x6e, 0x64, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66,
0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x64, 0x0a, 0x13, 0x6d, 0x69, 0x69, 0x67, 0x52, 0x14, 0x6d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e,
0x6e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x12, 0x58, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x55,
0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x32, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x52, 0x61, 0x6e, 0x64, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68,
0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x13, 0x6d, 0x69, 0x6e, 0x74, 0x74, 0x70, 0x2e, 0x52, 0x61, 0x6e, 0x64, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e,
0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x73, 0x66, 0x69, 0x67, 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x69,
0x1a, 0x39, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x7a, 0x65, 0x12, 0x64, 0x0a, 0x13, 0x6d, 0x69, 0x6e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x49,
0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x32, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x35, 0x0a, 0x0f, 0x52, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68,
0x61, 0x6e, 0x64, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x74, 0x74, 0x70, 0x2e, 0x52, 0x61, 0x6e, 0x64, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e,
0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x66, 0x72, 0x66, 0x69, 0x67, 0x52, 0x13, 0x6d, 0x69, 0x6e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x6e,
0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x64,
0x74, 0x6f, 0x42, 0x85, 0x01, 0x0a, 0x25, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01,
0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c,
0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x50, 0x01, 0x5a, 0x36, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a,
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x02, 0x38, 0x01, 0x22, 0x35, 0x0a, 0x0f, 0x52, 0x61, 0x6e, 0x64, 0x52, 0x61, 0x6e, 0x67, 0x65,
0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01,
0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x73, 0x70, 0x6c, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f,
0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0xaa, 0x02, 0x21, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x74, 0x6f, 0x42, 0x85, 0x01, 0x0a, 0x25, 0x63,
0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72,
0x2e, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x48, 0x74, 0x74, 0x70, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74,
0x6f, 0x33, 0x68, 0x74, 0x74, 0x70, 0x50, 0x01, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72,
0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0xaa, 0x02,
0x21, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e,
0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x48, 0x74,
0x74, 0x70, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (
@ -229,12 +235,14 @@ var file_transport_internet_splithttp_config_proto_goTypes = []interface{}{
} }
var file_transport_internet_splithttp_config_proto_depIdxs = []int32{ var file_transport_internet_splithttp_config_proto_depIdxs = []int32{
2, // 0: xray.transport.internet.splithttp.Config.header:type_name -> xray.transport.internet.splithttp.Config.HeaderEntry 2, // 0: xray.transport.internet.splithttp.Config.header:type_name -> xray.transport.internet.splithttp.Config.HeaderEntry
1, // 1: xray.transport.internet.splithttp.Config.minUploadIntervalMs:type_name -> xray.transport.internet.splithttp.RandRangeConfig 1, // 1: xray.transport.internet.splithttp.Config.maxConcurrentUploads:type_name -> xray.transport.internet.splithttp.RandRangeConfig
2, // [2:2] is the sub-list for method output_type 1, // 2: xray.transport.internet.splithttp.Config.maxUploadSize:type_name -> xray.transport.internet.splithttp.RandRangeConfig
2, // [2:2] is the sub-list for method input_type 1, // 3: xray.transport.internet.splithttp.Config.minUploadIntervalMs:type_name -> xray.transport.internet.splithttp.RandRangeConfig
2, // [2:2] is the sub-list for extension type_name 4, // [4:4] is the sub-list for method output_type
2, // [2:2] is the sub-list for extension extendee 4, // [4:4] is the sub-list for method input_type
0, // [0:2] is the sub-list for field type_name 4, // [4:4] is the sub-list for extension type_name
4, // [4:4] is the sub-list for extension extendee
0, // [0:4] 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() }

View File

@ -10,8 +10,8 @@ message Config {
string host = 1; string host = 1;
string path = 2; string path = 2;
map<string, string> header = 3; map<string, string> header = 3;
int32 maxConcurrentUploads = 4; RandRangeConfig maxConcurrentUploads = 4;
int32 maxUploadSize = 5; RandRangeConfig maxUploadSize = 5;
RandRangeConfig minUploadIntervalMs = 6; RandRangeConfig minUploadIntervalMs = 6;
} }

View File

@ -0,0 +1,51 @@
package splithttp_test
import (
"testing"
. "github.com/xtls/xray-core/transport/internet/splithttp"
)
func Test_GetNormalizedPath(t *testing.T) {
c := Config{
Path: "/?world",
}
path := c.GetNormalizedPath("hello", true)
if path != "/hello?world" {
t.Error("Unexpected: ", path)
}
}
func Test_GetNormalizedPath2(t *testing.T) {
c := Config{
Path: "?world",
}
path := c.GetNormalizedPath("hello", true)
if path != "/hello?world" {
t.Error("Unexpected: ", path)
}
}
func Test_GetNormalizedPath3(t *testing.T) {
c := Config{
Path: "hello?world",
}
path := c.GetNormalizedPath("", true)
if path != "/hello/?world" {
t.Error("Unexpected: ", path)
}
}
func Test_GetNormalizedPath4(t *testing.T) {
c := Config{
Path: "hello?world",
}
path := c.GetNormalizedPath("", false)
if path != "/hello/" {
t.Error("Unexpected: ", path)
}
}

View File

@ -181,8 +181,8 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
transportConfiguration := streamSettings.ProtocolSettings.(*Config) transportConfiguration := streamSettings.ProtocolSettings.(*Config)
tlsConfig := tls.ConfigFromStreamSettings(streamSettings) tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
maxConcurrentUploads := transportConfiguration.GetNormalizedMaxConcurrentUploads() maxConcurrentUploads := transportConfiguration.GetNormalizedMaxConcurrentUploads(false)
maxUploadSize := transportConfiguration.GetNormalizedMaxUploadSize() maxUploadSize := transportConfiguration.GetNormalizedMaxUploadSize(false)
minUploadInterval := transportConfiguration.GetNormalizedMinUploadInterval() minUploadInterval := transportConfiguration.GetNormalizedMinUploadInterval()
if tlsConfig != nil { if tlsConfig != nil {
@ -194,18 +194,17 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
if requestURL.Host == "" { if requestURL.Host == "" {
requestURL.Host = dest.NetAddr() requestURL.Host = dest.NetAddr()
} }
requestURL.Path = transportConfiguration.GetNormalizedPath()
sessionIdUuid := uuid.New()
requestURL.Path = transportConfiguration.GetNormalizedPath(sessionIdUuid.String(), true)
baseURL := requestURL.String()
httpClient := getHTTPClient(ctx, dest, streamSettings) httpClient := getHTTPClient(ctx, dest, streamSettings)
sessionIdUuid := uuid.New() uploadPipeReader, uploadPipeWriter := pipe.New(pipe.WithSizeLimit(maxUploadSize.roll()))
sessionId := sessionIdUuid.String()
baseURL := requestURL.String() + sessionId
uploadPipeReader, uploadPipeWriter := pipe.New(pipe.WithSizeLimit(maxUploadSize))
go func() { go func() {
requestsLimiter := semaphore.New(int(maxConcurrentUploads)) requestsLimiter := semaphore.New(int(maxConcurrentUploads.roll()))
var requestCounter int64 var requestCounter int64
lastWrite := time.Now() lastWrite := time.Now()

View File

@ -75,7 +75,7 @@ func (h *requestHandler) upsertSession(sessionId string) *httpSession {
} }
s := &httpSession{ s := &httpSession{
uploadQueue: NewUploadQueue(int(2 * h.ln.config.GetNormalizedMaxConcurrentUploads())), uploadQueue: NewUploadQueue(int(h.ln.config.GetNormalizedMaxConcurrentUploads(true).To)),
isFullyConnected: done.New(), isFullyConnected: done.New(),
} }
@ -122,6 +122,7 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
} }
currentSession := h.upsertSession(sessionId) currentSession := h.upsertSession(sessionId)
maxUploadSize := int(h.ln.config.GetNormalizedMaxUploadSize(true).To)
if request.Method == "POST" { if request.Method == "POST" {
seq := "" seq := ""
@ -136,6 +137,13 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
} }
payload, err := io.ReadAll(request.Body) payload, err := io.ReadAll(request.Body)
if len(payload) > maxUploadSize {
errors.LogInfo(context.Background(), "Too large upload. maxUploadSize is set to", maxUploadSize, "but request had size", len(payload), ". Adjust maxUploadSize on the server to be at least as large as client.")
writer.WriteHeader(http.StatusRequestEntityTooLarge)
return
}
if err != nil { if err != nil {
errors.LogInfoInner(context.Background(), err, "failed to upload") errors.LogInfoInner(context.Background(), err, "failed to upload")
writer.WriteHeader(http.StatusInternalServerError) writer.WriteHeader(http.StatusInternalServerError)
@ -260,7 +268,7 @@ func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSet
var localAddr = gonet.TCPAddr{} var localAddr = gonet.TCPAddr{}
handler := &requestHandler{ handler := &requestHandler{
host: shSettings.Host, host: shSettings.Host,
path: shSettings.GetNormalizedPath(), path: shSettings.GetNormalizedPath("", false),
ln: l, ln: l,
sessionMu: &sync.Mutex{}, sessionMu: &sync.Mutex{},
sessions: sync.Map{}, sessions: sync.Map{},

View File

@ -360,3 +360,49 @@ func Test_listenSHAndDial_Unix(t *testing.T) {
common.Must(listen.Close()) common.Must(listen.Close())
} }
func Test_queryString(t *testing.T) {
listenPort := tcp.PickPort()
listen, err := ListenSH(context.Background(), net.LocalHostIP, listenPort, &internet.MemoryStreamConfig{
ProtocolName: "splithttp",
ProtocolSettings: &Config{
// this querystring does not have any effect, but sometimes people blindly copy it from websocket config. make sure the outbound doesn't break
Path: "/sh?ed=2048",
},
}, func(conn stat.Connection) {
go func(c stat.Connection) {
defer c.Close()
var b [1024]byte
c.SetReadDeadline(time.Now().Add(2 * time.Second))
_, err := c.Read(b[:])
if err != nil {
return
}
common.Must2(c.Write([]byte("Response")))
}(conn)
})
common.Must(err)
ctx := context.Background()
streamSettings := &internet.MemoryStreamConfig{
ProtocolName: "splithttp",
ProtocolSettings: &Config{Path: "sh"},
}
conn, err := Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings)
common.Must(err)
_, err = conn.Write([]byte("Test connection 1"))
common.Must(err)
var b [1024]byte
fmt.Println("test2")
n, _ := conn.Read(b[:])
fmt.Println("string is", n)
if string(b[:n]) != "Response" {
t.Error("response: ", string(b[:n]))
}
common.Must(conn.Close())
common.Must(listen.Close())
}