Skip to content

Commit 8a4217f

Browse files
authored
SplitHTTP client: Add minUploadInterval (#3592)
1 parent 7cf5ee8 commit 8a4217f

File tree

6 files changed

+188
-25
lines changed

6 files changed

+188
-25
lines changed

infra/conf/common.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package conf
22

33
import (
44
"encoding/json"
5+
"strconv"
56
"strings"
67

78
"github.com/xtls/xray-core/common/errors"
@@ -242,3 +243,33 @@ func (v *User) Build() *protocol.User {
242243
Level: uint32(v.LevelByte),
243244
}
244245
}
246+
247+
// Int32Range deserializes from "1-2" or 1, so can deserialize from both int and number.
248+
// Negative integers can be passed as sentinel values, but do not parse as ranges.
249+
type Int32Range struct {
250+
From int32
251+
To int32
252+
}
253+
254+
func (v *Int32Range) UnmarshalJSON(data []byte) error {
255+
var stringrange string
256+
var rawint int32
257+
if err := json.Unmarshal(data, &stringrange); err == nil {
258+
pair := strings.SplitN(stringrange, "-", 2)
259+
if len(pair) == 2 {
260+
from, err := strconv.Atoi(pair[0])
261+
to, err2 := strconv.Atoi(pair[1])
262+
if err == nil && err2 == nil {
263+
v.From = int32(from)
264+
v.To = int32(to)
265+
return nil
266+
}
267+
}
268+
} else if err := json.Unmarshal(data, &rawint); err == nil {
269+
v.From = rawint
270+
v.To = rawint
271+
return nil
272+
}
273+
274+
return errors.New("Invalid integer range, expected either string of form \"1-2\" or plain integer.")
275+
}

infra/conf/transport_internet.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ type SplitHTTPConfig struct {
231231
Headers map[string]string `json:"headers"`
232232
MaxConcurrentUploads int32 `json:"maxConcurrentUploads"`
233233
MaxUploadSize int32 `json:"maxUploadSize"`
234+
MinUploadIntervalMs Int32Range `json:"minUploadIntervalMs"`
234235
}
235236

236237
// Build implements Buildable.
@@ -249,6 +250,10 @@ func (c *SplitHTTPConfig) Build() (proto.Message, error) {
249250
Header: c.Headers,
250251
MaxConcurrentUploads: c.MaxConcurrentUploads,
251252
MaxUploadSize: c.MaxUploadSize,
253+
MinUploadIntervalMs: &splithttp.RandRangeConfig{
254+
From: c.MinUploadIntervalMs.From,
255+
To: c.MinUploadIntervalMs.To,
256+
},
252257
}
253258
return config, nil
254259
}

transport/internet/splithttp/config.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package splithttp
22

33
import (
4+
"crypto/rand"
5+
"math/big"
46
"net/http"
57

68
"github.com/xtls/xray-core/common"
@@ -45,8 +47,29 @@ func (c *Config) GetNormalizedMaxUploadSize() int32 {
4547
return c.MaxUploadSize
4648
}
4749

50+
func (c *Config) GetNormalizedMinUploadInterval() RandRangeConfig {
51+
r := c.MinUploadIntervalMs
52+
53+
if r == nil {
54+
r = &RandRangeConfig{
55+
From: 30,
56+
To: 30,
57+
}
58+
}
59+
60+
return *r
61+
}
62+
4863
func init() {
4964
common.Must(internet.RegisterProtocolConfigCreator(protocolName, func() interface{} {
5065
return new(Config)
5166
}))
5267
}
68+
69+
func (c RandRangeConfig) roll() int32 {
70+
if c.From == c.To {
71+
return c.From
72+
}
73+
bigInt, _ := rand.Int(rand.Reader, big.NewInt(int64(c.To-c.From)))
74+
return c.From + int32(bigInt.Int64())
75+
}

transport/internet/splithttp/config.pb.go

Lines changed: 112 additions & 25 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

transport/internet/splithttp/config.proto

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,10 @@ message Config {
1212
map<string, string> header = 3;
1313
int32 maxConcurrentUploads = 4;
1414
int32 maxUploadSize = 5;
15+
RandRangeConfig minUploadIntervalMs = 6;
16+
}
17+
18+
message RandRangeConfig {
19+
int32 from = 1;
20+
int32 to = 2;
1521
}

transport/internet/splithttp/dialer.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
183183

184184
maxConcurrentUploads := transportConfiguration.GetNormalizedMaxConcurrentUploads()
185185
maxUploadSize := transportConfiguration.GetNormalizedMaxUploadSize()
186+
minUploadInterval := transportConfiguration.GetNormalizedMinUploadInterval()
186187

187188
if tlsConfig != nil {
188189
requestURL.Scheme = "https"
@@ -207,6 +208,8 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
207208
requestsLimiter := semaphore.New(int(maxConcurrentUploads))
208209
var requestCounter int64
209210

211+
lastWrite := time.Now()
212+
210213
// by offloading the uploads into a buffered pipe, multiple conn.Write
211214
// calls get automatically batched together into larger POST requests.
212215
// without batching, bandwidth is extremely limited.
@@ -237,6 +240,14 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
237240
}
238241
}()
239242

243+
if minUploadInterval.From > 0 {
244+
roll := time.Duration(minUploadInterval.roll()) * time.Millisecond
245+
if time.Since(lastWrite) < roll {
246+
time.Sleep(roll)
247+
}
248+
249+
lastWrite = time.Now()
250+
}
240251
}
241252
}()
242253

0 commit comments

Comments
 (0)