mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-10-14 03:23:58 +00:00
MASQUE client: Support XMUX ("maxConnections": 1 by default); Use Chrome's User-Agent by default (#7121)
https://github.com/XTLS/Xray-core/pull/6810#issuecomment-5842441136 https://github.com/XTLS/Xray-core/pull/6878#issuecomment-6095112915 https://github.com/XTLS/Xray-core/pull/7121#issuecomment-6117636043 --------- Co-authored-by: SVLAVR <skugorov.s@gmail.com>
This commit is contained in:
+68
-11
@@ -16,8 +16,61 @@ import (
|
||||
. "github.com/xtls/xray-core/infra/conf"
|
||||
masqueproxy "github.com/xtls/xray-core/proxy/masque"
|
||||
"github.com/xtls/xray-core/transport/internet/masque"
|
||||
"github.com/xtls/xray-core/transport/internet/splithttp"
|
||||
)
|
||||
|
||||
func defaultMasqueXmux() *splithttp.XmuxConfig {
|
||||
return &splithttp.XmuxConfig{
|
||||
MaxConcurrency: &splithttp.RangeConfig{},
|
||||
MaxConnections: &splithttp.RangeConfig{From: 1, To: 1},
|
||||
CMaxReuseTimes: &splithttp.RangeConfig{},
|
||||
HMaxRequestTimes: &splithttp.RangeConfig{},
|
||||
HMaxReusableSecs: &splithttp.RangeConfig{From: 1800, To: 3000},
|
||||
}
|
||||
}
|
||||
|
||||
func TestMasqueXmuxConfig(t *testing.T) {
|
||||
creator := func() Buildable {
|
||||
return new(MasqueConfig)
|
||||
}
|
||||
|
||||
runMultiTestCase(t, []TestCase{
|
||||
{
|
||||
Input: `{"xmux": {"maxConnections": "2-4", "hMaxReusableSecs": 60, "hKeepAlivePeriod": -1}}`,
|
||||
Parser: loadJSON(creator),
|
||||
Output: &masque.Config{
|
||||
Path: "/.well-known/masque/ip/*/*/",
|
||||
Xmux: &splithttp.XmuxConfig{
|
||||
MaxConcurrency: &splithttp.RangeConfig{},
|
||||
MaxConnections: &splithttp.RangeConfig{From: 2, To: 4},
|
||||
CMaxReuseTimes: &splithttp.RangeConfig{},
|
||||
HMaxRequestTimes: &splithttp.RangeConfig{},
|
||||
HMaxReusableSecs: &splithttp.RangeConfig{From: 60, To: 60},
|
||||
HKeepAlivePeriod: -1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Input: `{"xmux": {"maxConcurrency": 8}}`,
|
||||
Parser: loadJSON(creator),
|
||||
Output: &masque.Config{
|
||||
Path: "/.well-known/masque/ip/*/*/",
|
||||
Xmux: &splithttp.XmuxConfig{
|
||||
MaxConcurrency: &splithttp.RangeConfig{From: 8, To: 8},
|
||||
MaxConnections: &splithttp.RangeConfig{},
|
||||
CMaxReuseTimes: &splithttp.RangeConfig{},
|
||||
HMaxRequestTimes: &splithttp.RangeConfig{},
|
||||
HMaxReusableSecs: &splithttp.RangeConfig{},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if _, err := loadJSON(creator)(`{"xmux": {"maxConnections": 2, "maxConcurrency": 8}}`); err == nil {
|
||||
t.Error("expected an error for maxConnections with maxConcurrency")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMasqueConfig(t *testing.T) {
|
||||
creator := func() Buildable {
|
||||
return new(MasqueConfig)
|
||||
@@ -27,7 +80,7 @@ func TestMasqueConfig(t *testing.T) {
|
||||
{
|
||||
Input: `{}`,
|
||||
Parser: loadJSON(creator),
|
||||
Output: &masque.Config{Path: "/.well-known/masque/ip/*/*/"},
|
||||
Output: &masque.Config{Path: "/.well-known/masque/ip/*/*/", Xmux: defaultMasqueXmux()},
|
||||
},
|
||||
{
|
||||
Input: `{
|
||||
@@ -40,12 +93,13 @@ func TestMasqueConfig(t *testing.T) {
|
||||
Host: "example.com:8443",
|
||||
Path: "/.well-known/masque/ip/*/*/",
|
||||
Headers: map[string]string{"Authorization": "Basic dTpw"},
|
||||
Xmux: defaultMasqueXmux(),
|
||||
},
|
||||
},
|
||||
{
|
||||
Input: `{"path": "/masque/ip{?target,ipproto}"}`,
|
||||
Parser: loadJSON(creator),
|
||||
Output: &masque.Config{Path: "/masque/ip?target=*&ipproto=*"},
|
||||
Output: &masque.Config{Path: "/masque/ip?target=*&ipproto=*", Xmux: defaultMasqueXmux()},
|
||||
},
|
||||
{
|
||||
Input: `{"user": "u", "pass": "p:q", "headers": {"X-Token": "a"}}`,
|
||||
@@ -53,6 +107,7 @@ func TestMasqueConfig(t *testing.T) {
|
||||
Output: &masque.Config{
|
||||
Path: "/.well-known/masque/ip/*/*/",
|
||||
Headers: map[string]string{"Authorization": "Basic dTpwOnE=", "X-Token": "a"},
|
||||
Xmux: defaultMasqueXmux(),
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -122,6 +177,7 @@ func TestMasqueWarpConfig(t *testing.T) {
|
||||
Host: "cloudflareaccess.com",
|
||||
Path: "/",
|
||||
Warp: &masque.Warp{PrivateKey: pkcs8, PublicKey: publicKey, Address: address},
|
||||
Xmux: defaultMasqueXmux(),
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -134,6 +190,7 @@ func TestMasqueWarpConfig(t *testing.T) {
|
||||
Host: "example.com",
|
||||
Path: "/warp",
|
||||
Warp: &masque.Warp{PrivateKey: pkcs8, PublicKey: publicKey, Address: address},
|
||||
Xmux: defaultMasqueXmux(),
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -164,6 +221,7 @@ func TestMasqueWarpConfig(t *testing.T) {
|
||||
Host: "cloudflareaccess.com",
|
||||
Path: "/",
|
||||
Warp: &masque.Warp{PrivateKey: pkcs8, PublicKey: publicKey, Address: []string{"172.16.0.2/32"}},
|
||||
Xmux: defaultMasqueXmux(),
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -199,18 +257,17 @@ func TestMasqueOutboundConfig(t *testing.T) {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := build(`{
|
||||
"protocol": "masque",
|
||||
"settings": {"address": "example.com", "port": 443},
|
||||
"streamSettings": {"network": "masque", "security": "tls"},
|
||||
"mux": {"enabled": false, "concurrency": -1}
|
||||
}`); err != nil {
|
||||
t.Error(err)
|
||||
for _, input := range []string{
|
||||
`{"protocol": "masque", "settings": {"address": "example.com", "port": 443}, "streamSettings": {"network": "masque", "security": "tls"}, "mux": {"enabled": false, "concurrency": -1}}`,
|
||||
`{"protocol": "masque", "settings": {"address": "example.com", "port": 443}, "streamSettings": {"network": "masque", "security": "tls"}, "mux": {"enabled": true}}`,
|
||||
`{"protocol": "masque", "settings": {"address": "example.com", "port": 443}, "streamSettings": {"network": "masque", "security": "tls"}, "mux": {"enabled": true, "concurrency": -1, "xudpConcurrency": 8}}`,
|
||||
} {
|
||||
if err := build(input); err != nil {
|
||||
t.Errorf("%s: %v", input, err)
|
||||
}
|
||||
}
|
||||
for _, input := range []string{
|
||||
`{"protocol": "masque", "settings": {"address": "example.com"}, "streamSettings": {"network": "masque", "security": "tls"}}`,
|
||||
`{"protocol": "masque", "settings": {"address": "example.com", "port": 443}, "streamSettings": {"network": "masque", "security": "tls"}, "mux": {"enabled": true}}`,
|
||||
`{"protocol": "masque", "settings": {"address": "example.com", "port": 443}, "streamSettings": {"network": "masque", "security": "tls"}, "mux": {"enabled": true, "concurrency": -1}}`,
|
||||
`{"protocol": "freedom", "streamSettings": {"network": "masque", "security": "tls"}}`,
|
||||
} {
|
||||
if err := build(input); err == nil {
|
||||
|
||||
@@ -808,6 +808,7 @@ type MasqueConfig struct {
|
||||
Pass string `json:"pass"`
|
||||
Headers map[string]string `json:"headers"`
|
||||
Warp *MasqueWarpConfig `json:"warp"`
|
||||
Xmux XmuxConfig `json:"xmux"`
|
||||
}
|
||||
|
||||
func (c *MasqueConfig) Build() (proto.Message, error) {
|
||||
@@ -878,11 +879,28 @@ func (c *MasqueConfig) Build() (proto.Message, error) {
|
||||
}
|
||||
headers["Authorization"] = "Basic " + base64.StdEncoding.EncodeToString([]byte(c.User+":"+c.Pass))
|
||||
}
|
||||
if c.Xmux.MaxConnections.To > 0 && c.Xmux.MaxConcurrency.To > 0 {
|
||||
return nil, errors.New("maxConnections cannot be specified together with maxConcurrency")
|
||||
}
|
||||
if c.Xmux == (XmuxConfig{}) {
|
||||
c.Xmux.MaxConnections.From = 1
|
||||
c.Xmux.MaxConnections.To = 1
|
||||
c.Xmux.HMaxReusableSecs.From = 1800
|
||||
c.Xmux.HMaxReusableSecs.To = 3000
|
||||
}
|
||||
return &masque.Config{
|
||||
Host: host,
|
||||
Path: path,
|
||||
Headers: headers,
|
||||
Warp: warp,
|
||||
Xmux: &splithttp.XmuxConfig{
|
||||
MaxConcurrency: newRangeConfig(c.Xmux.MaxConcurrency),
|
||||
MaxConnections: newRangeConfig(c.Xmux.MaxConnections),
|
||||
CMaxReuseTimes: newRangeConfig(c.Xmux.CMaxReuseTimes),
|
||||
HMaxRequestTimes: newRangeConfig(c.Xmux.HMaxRequestTimes),
|
||||
HMaxReusableSecs: newRangeConfig(c.Xmux.HMaxReusableSecs),
|
||||
HKeepAlivePeriod: c.Xmux.HKeepAlivePeriod,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
+1
-5
@@ -344,11 +344,7 @@ func (c *OutboundDetourConfig) Build() (*core.OutboundHandlerConfig, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, ok := ts.(*masque.ClientConfig); ok {
|
||||
if ms := senderSettings.MultiplexSettings; ms != nil && ms.Enabled {
|
||||
return nil, errors.New(`masque outbound does not support "mux"`)
|
||||
}
|
||||
} else if senderSettings.StreamSettings != nil && senderSettings.StreamSettings.ProtocolName == "masque" {
|
||||
if _, ok := ts.(*masque.ClientConfig); !ok && senderSettings.StreamSettings != nil && senderSettings.StreamSettings.ProtocolName == "masque" {
|
||||
return nil, errors.New("the masque transport can only be used by the masque outbound")
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user