mirror of
https://github.com/alireza0/x-ui.git
synced 2026-03-14 05:23:09 +00:00
splithttp to xhttp
This commit is contained in:
@@ -221,16 +221,16 @@ func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string {
|
|||||||
headers, _ := httpupgrade["headers"].(map[string]interface{})
|
headers, _ := httpupgrade["headers"].(map[string]interface{})
|
||||||
obj["host"] = searchHost(headers)
|
obj["host"] = searchHost(headers)
|
||||||
}
|
}
|
||||||
case "splithttp":
|
case "xhttp":
|
||||||
splithttp, _ := stream["splithttpSettings"].(map[string]interface{})
|
xhttp, _ := stream["xhttpSettings"].(map[string]interface{})
|
||||||
obj["path"] = splithttp["path"].(string)
|
obj["path"] = xhttp["path"].(string)
|
||||||
if host, ok := splithttp["host"].(string); ok && len(host) > 0 {
|
if host, ok := xhttp["host"].(string); ok && len(host) > 0 {
|
||||||
obj["host"] = host
|
obj["host"] = host
|
||||||
} else {
|
} else {
|
||||||
headers, _ := splithttp["headers"].(map[string]interface{})
|
headers, _ := xhttp["headers"].(map[string]interface{})
|
||||||
obj["host"] = searchHost(headers)
|
obj["host"] = searchHost(headers)
|
||||||
}
|
}
|
||||||
obj["mode"] = splithttp["mode"].(string)
|
obj["mode"] = xhttp["mode"].(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
security, _ := stream["security"].(string)
|
security, _ := stream["security"].(string)
|
||||||
@@ -373,16 +373,16 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
|
|||||||
headers, _ := httpupgrade["headers"].(map[string]interface{})
|
headers, _ := httpupgrade["headers"].(map[string]interface{})
|
||||||
params["host"] = searchHost(headers)
|
params["host"] = searchHost(headers)
|
||||||
}
|
}
|
||||||
case "splithttp":
|
case "xhttp":
|
||||||
splithttp, _ := stream["splithttpSettings"].(map[string]interface{})
|
xhttp, _ := stream["xhttpSettings"].(map[string]interface{})
|
||||||
params["path"] = splithttp["path"].(string)
|
params["path"] = xhttp["path"].(string)
|
||||||
if host, ok := splithttp["host"].(string); ok && len(host) > 0 {
|
if host, ok := xhttp["host"].(string); ok && len(host) > 0 {
|
||||||
params["host"] = host
|
params["host"] = host
|
||||||
} else {
|
} else {
|
||||||
headers, _ := splithttp["headers"].(map[string]interface{})
|
headers, _ := xhttp["headers"].(map[string]interface{})
|
||||||
params["host"] = searchHost(headers)
|
params["host"] = searchHost(headers)
|
||||||
}
|
}
|
||||||
params["mode"] = splithttp["mode"].(string)
|
params["mode"] = xhttp["mode"].(string)
|
||||||
}
|
}
|
||||||
security, _ := stream["security"].(string)
|
security, _ := stream["security"].(string)
|
||||||
if security == "tls" {
|
if security == "tls" {
|
||||||
@@ -571,16 +571,16 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string
|
|||||||
headers, _ := httpupgrade["headers"].(map[string]interface{})
|
headers, _ := httpupgrade["headers"].(map[string]interface{})
|
||||||
params["host"] = searchHost(headers)
|
params["host"] = searchHost(headers)
|
||||||
}
|
}
|
||||||
case "splithttp":
|
case "xhttp":
|
||||||
splithttp, _ := stream["splithttpSettings"].(map[string]interface{})
|
xhttp, _ := stream["xhttpSettings"].(map[string]interface{})
|
||||||
params["path"] = splithttp["path"].(string)
|
params["path"] = xhttp["path"].(string)
|
||||||
if host, ok := splithttp["host"].(string); ok && len(host) > 0 {
|
if host, ok := xhttp["host"].(string); ok && len(host) > 0 {
|
||||||
params["host"] = host
|
params["host"] = host
|
||||||
} else {
|
} else {
|
||||||
headers, _ := splithttp["headers"].(map[string]interface{})
|
headers, _ := xhttp["headers"].(map[string]interface{})
|
||||||
params["host"] = searchHost(headers)
|
params["host"] = searchHost(headers)
|
||||||
}
|
}
|
||||||
params["mode"] = splithttp["mode"].(string)
|
params["mode"] = xhttp["mode"].(string)
|
||||||
}
|
}
|
||||||
security, _ := stream["security"].(string)
|
security, _ := stream["security"].(string)
|
||||||
if security == "tls" {
|
if security == "tls" {
|
||||||
@@ -765,16 +765,16 @@ func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string) st
|
|||||||
headers, _ := httpupgrade["headers"].(map[string]interface{})
|
headers, _ := httpupgrade["headers"].(map[string]interface{})
|
||||||
params["host"] = searchHost(headers)
|
params["host"] = searchHost(headers)
|
||||||
}
|
}
|
||||||
case "splithttp":
|
case "xhttp":
|
||||||
splithttp, _ := stream["splithttpSettings"].(map[string]interface{})
|
xhttp, _ := stream["xhttpSettings"].(map[string]interface{})
|
||||||
params["path"] = splithttp["path"].(string)
|
params["path"] = xhttp["path"].(string)
|
||||||
if host, ok := splithttp["host"].(string); ok && len(host) > 0 {
|
if host, ok := xhttp["host"].(string); ok && len(host) > 0 {
|
||||||
params["host"] = host
|
params["host"] = host
|
||||||
} else {
|
} else {
|
||||||
headers, _ := splithttp["headers"].(map[string]interface{})
|
headers, _ := xhttp["headers"].(map[string]interface{})
|
||||||
params["host"] = searchHost(headers)
|
params["host"] = searchHost(headers)
|
||||||
}
|
}
|
||||||
params["mode"] = splithttp["mode"].(string)
|
params["mode"] = xhttp["mode"].(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
security, _ := stream["security"].(string)
|
security, _ := stream["security"].(string)
|
||||||
|
|||||||
@@ -457,34 +457,25 @@ class HttpUpgradeStreamSettings extends XrayCommonClass {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class SplitHTTPStreamSettings extends XrayCommonClass {
|
class xHTTPStreamSettings extends XrayCommonClass {
|
||||||
constructor(
|
constructor(
|
||||||
path = '/',
|
path = '/',
|
||||||
host = '',
|
host = '',
|
||||||
headers = [],
|
headers = [],
|
||||||
scMaxConcurrentPosts = "100-200",
|
scMaxBufferedPosts = 30,
|
||||||
scMaxEachPostBytes = "1000000-2000000",
|
scMaxEachPostBytes = "1000000",
|
||||||
scMinPostsIntervalMs = "10-50",
|
|
||||||
noSSEHeader = false,
|
noSSEHeader = false,
|
||||||
xPaddingBytes = "100-1000",
|
xPaddingBytes = "100-1000",
|
||||||
xmux = {
|
|
||||||
maxConcurrency: "16-32",
|
|
||||||
maxConnections: 0,
|
|
||||||
cMaxReuseTimes: "64-128",
|
|
||||||
cMaxLifetimeMs: 0
|
|
||||||
},
|
|
||||||
mode = MODE_OPTION.AUTO,
|
mode = MODE_OPTION.AUTO,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this.path = path;
|
this.path = path;
|
||||||
this.host = host;
|
this.host = host;
|
||||||
this.headers = headers;
|
this.headers = headers;
|
||||||
this.scMaxConcurrentPosts = scMaxConcurrentPosts;
|
this.scMaxBufferedPosts = scMaxBufferedPosts;
|
||||||
this.scMaxEachPostBytes = scMaxEachPostBytes;
|
this.scMaxEachPostBytes = scMaxEachPostBytes;
|
||||||
this.scMinPostsIntervalMs = scMinPostsIntervalMs;
|
|
||||||
this.noSSEHeader = noSSEHeader;
|
this.noSSEHeader = noSSEHeader;
|
||||||
this.xPaddingBytes = xPaddingBytes;
|
this.xPaddingBytes = xPaddingBytes;
|
||||||
this.xmux = xmux;
|
|
||||||
this.mode = mode;
|
this.mode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -497,16 +488,14 @@ class SplitHTTPStreamSettings extends XrayCommonClass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json = {}) {
|
static fromJson(json = {}) {
|
||||||
return new SplitHTTPStreamSettings(
|
return new xHTTPStreamSettings(
|
||||||
json.path,
|
json.path,
|
||||||
json.host,
|
json.host,
|
||||||
XrayCommonClass.toHeaders(json.headers),
|
XrayCommonClass.toHeaders(json.headers),
|
||||||
json.scMaxConcurrentPosts,
|
json.scMaxBufferedPosts,
|
||||||
json.scMaxEachPostBytes,
|
json.scMaxEachPostBytes,
|
||||||
json.scMinPostsIntervalMs,
|
|
||||||
json.noSSEHeader,
|
json.noSSEHeader,
|
||||||
json.xPaddingBytes,
|
json.xPaddingBytes,
|
||||||
json.xmux,
|
|
||||||
json.mode,
|
json.mode,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -516,17 +505,10 @@ class SplitHTTPStreamSettings extends XrayCommonClass {
|
|||||||
path: this.path,
|
path: this.path,
|
||||||
host: this.host,
|
host: this.host,
|
||||||
headers: XrayCommonClass.toV2Headers(this.headers, false),
|
headers: XrayCommonClass.toV2Headers(this.headers, false),
|
||||||
scMaxConcurrentPosts: this.scMaxConcurrentPosts,
|
scMaxBufferedPosts: this.scMaxBufferedPosts,
|
||||||
scMaxEachPostBytes: this.scMaxEachPostBytes,
|
scMaxEachPostBytes: this.scMaxEachPostBytes,
|
||||||
scMinPostsIntervalMs: this.scMinPostsIntervalMs,
|
|
||||||
noSSEHeader: this.noSSEHeader,
|
noSSEHeader: this.noSSEHeader,
|
||||||
xPaddingBytes: this.xPaddingBytes,
|
xPaddingBytes: this.xPaddingBytes,
|
||||||
xmux: {
|
|
||||||
maxConcurrency: this.xmux.maxConcurrency,
|
|
||||||
maxConnections: this.xmux.maxConnections,
|
|
||||||
cMaxReuseTimes: this.xmux.cMaxReuseTimes,
|
|
||||||
cMaxLifetimeMs: this.xmux.cMaxLifetimeMs
|
|
||||||
},
|
|
||||||
mode: this.mode,
|
mode: this.mode,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -784,7 +766,7 @@ class StreamSettings extends XrayCommonClass {
|
|||||||
httpSettings=new HttpStreamSettings(),
|
httpSettings=new HttpStreamSettings(),
|
||||||
grpcSettings=new GrpcStreamSettings(),
|
grpcSettings=new GrpcStreamSettings(),
|
||||||
httpupgradeSettings=new HttpUpgradeStreamSettings(),
|
httpupgradeSettings=new HttpUpgradeStreamSettings(),
|
||||||
splithttpSettings=new SplitHTTPStreamSettings(),
|
xhttpSettings=new xHTTPStreamSettings(),
|
||||||
sockopt = undefined,
|
sockopt = undefined,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
@@ -799,7 +781,7 @@ class StreamSettings extends XrayCommonClass {
|
|||||||
this.http = httpSettings;
|
this.http = httpSettings;
|
||||||
this.grpc = grpcSettings;
|
this.grpc = grpcSettings;
|
||||||
this.httpupgrade = httpupgradeSettings;
|
this.httpupgrade = httpupgradeSettings;
|
||||||
this.splithttp = splithttpSettings;
|
this.xhttp = xhttpSettings;
|
||||||
this.sockopt = sockopt;
|
this.sockopt = sockopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -848,7 +830,7 @@ class StreamSettings extends XrayCommonClass {
|
|||||||
HttpStreamSettings.fromJson(json.httpSettings),
|
HttpStreamSettings.fromJson(json.httpSettings),
|
||||||
GrpcStreamSettings.fromJson(json.grpcSettings),
|
GrpcStreamSettings.fromJson(json.grpcSettings),
|
||||||
HttpUpgradeStreamSettings.fromJson(json.httpupgradeSettings),
|
HttpUpgradeStreamSettings.fromJson(json.httpupgradeSettings),
|
||||||
SplitHTTPStreamSettings.fromJson(json.splithttpSettings),
|
xHTTPStreamSettings.fromJson(json.xhttpSettings),
|
||||||
SockoptStreamSettings.fromJson(json.sockopt),
|
SockoptStreamSettings.fromJson(json.sockopt),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -867,7 +849,7 @@ class StreamSettings extends XrayCommonClass {
|
|||||||
httpSettings: network === 'http' ? this.http.toJson() : undefined,
|
httpSettings: network === 'http' ? this.http.toJson() : undefined,
|
||||||
grpcSettings: network === 'grpc' ? this.grpc.toJson() : undefined,
|
grpcSettings: network === 'grpc' ? this.grpc.toJson() : undefined,
|
||||||
httpupgradeSettings: network === 'httpupgrade' ? this.httpupgrade.toJson() : undefined,
|
httpupgradeSettings: network === 'httpupgrade' ? this.httpupgrade.toJson() : undefined,
|
||||||
splithttpSettings: network === 'splithttp' ? this.splithttp.toJson() : undefined,
|
xhttpSettings: network === 'xhttp' ? this.xhttp.toJson() : undefined,
|
||||||
sockopt: this.sockopt != undefined ? this.sockopt.toJson() : undefined,
|
sockopt: this.sockopt != undefined ? this.sockopt.toJson() : undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -980,8 +962,8 @@ class Inbound extends XrayCommonClass {
|
|||||||
return this.network === "httpupgrade";
|
return this.network === "httpupgrade";
|
||||||
}
|
}
|
||||||
|
|
||||||
get isSplithttp() {
|
get isXHTTP() {
|
||||||
return this.network === "splithttp";
|
return this.network === "xhttp";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shadowsocks
|
// Shadowsocks
|
||||||
@@ -1024,8 +1006,8 @@ class Inbound extends XrayCommonClass {
|
|||||||
return this.stream.ws.host?.length>0 ? this.stream.ws.host : this.getHeader(this.stream.ws, 'host');
|
return this.stream.ws.host?.length>0 ? this.stream.ws.host : this.getHeader(this.stream.ws, 'host');
|
||||||
} else if (this.isHttpupgrade) {
|
} else if (this.isHttpupgrade) {
|
||||||
return this.stream.httpupgrade.host?.length>0 ? this.stream.httpupgrade.host : this.getHeader(this.stream.httpupgrade, 'host');
|
return this.stream.httpupgrade.host?.length>0 ? this.stream.httpupgrade.host : this.getHeader(this.stream.httpupgrade, 'host');
|
||||||
} else if (this.isSplithttp) {
|
} else if (this.isXHTTP) {
|
||||||
return this.stream.splithttp.host?.length>0 ? this.stream.splithttp.host : this.getHeader(this.stream.splithttp, 'host');
|
return this.stream.xhttp.host?.length>0 ? this.stream.xhttp.host : this.getHeader(this.stream.xhttp, 'host');
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -1039,8 +1021,8 @@ class Inbound extends XrayCommonClass {
|
|||||||
return this.stream.http.path;
|
return this.stream.http.path;
|
||||||
} else if (this.isHttpupgrade) {
|
} else if (this.isHttpupgrade) {
|
||||||
return this.stream.httpupgrade.path;
|
return this.stream.httpupgrade.path;
|
||||||
} else if (this.isSplithttp) {
|
} else if (this.isXHTTP) {
|
||||||
return this.stream.splithttp.path;
|
return this.stream.xhttp.path;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -1065,7 +1047,7 @@ class Inbound extends XrayCommonClass {
|
|||||||
|
|
||||||
canEnableTls() {
|
canEnableTls() {
|
||||||
if(![Protocols.VMESS, Protocols.VLESS, Protocols.TROJAN, Protocols.SHADOWSOCKS].includes(this.protocol)) return false;
|
if(![Protocols.VMESS, Protocols.VLESS, Protocols.TROJAN, Protocols.SHADOWSOCKS].includes(this.protocol)) return false;
|
||||||
return ["tcp", "ws", "http", "grpc", "httpupgrade" , "splithttp"].includes(this.network);
|
return ["tcp", "ws", "http", "grpc", "httpupgrade" , "xhttp"].includes(this.network);
|
||||||
}
|
}
|
||||||
|
|
||||||
//this is used for xtls-rprx-vision
|
//this is used for xtls-rprx-vision
|
||||||
@@ -1078,7 +1060,7 @@ class Inbound extends XrayCommonClass {
|
|||||||
|
|
||||||
canEnableReality() {
|
canEnableReality() {
|
||||||
if(![Protocols.VLESS, Protocols.TROJAN].includes(this.protocol)) return false;
|
if(![Protocols.VLESS, Protocols.TROJAN].includes(this.protocol)) return false;
|
||||||
return ["tcp", "http", "grpc", "splithttp"].includes(this.network);
|
return ["tcp", "http", "grpc", "xhttp"].includes(this.network);
|
||||||
}
|
}
|
||||||
|
|
||||||
canEnableStream() {
|
canEnableStream() {
|
||||||
@@ -1142,11 +1124,11 @@ class Inbound extends XrayCommonClass {
|
|||||||
const httpupgrade = this.stream.httpupgrade;
|
const httpupgrade = this.stream.httpupgrade;
|
||||||
obj.path = httpupgrade.path;
|
obj.path = httpupgrade.path;
|
||||||
obj.host = httpupgrade.host?.length>0 ? httpupgrade.host : this.getHeader(httpupgrade, 'host');
|
obj.host = httpupgrade.host?.length>0 ? httpupgrade.host : this.getHeader(httpupgrade, 'host');
|
||||||
} else if (network === 'splithttp') {
|
} else if (network === 'xhttp') {
|
||||||
const splithttp = this.stream.splithttp;
|
const xhttp = this.stream.xhttp;
|
||||||
obj.path = splithttp.path;
|
obj.path = xhttp.path;
|
||||||
obj.host = splithttp.host?.length>0 ? splithttp.host : this.getHeader(splithttp, 'host');
|
obj.host = xhttp.host?.length>0 ? xhttp.host : this.getHeader(xhttp, 'host');
|
||||||
obj.mode = splithttp.mode;
|
obj.mode = xhttp.mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (security === 'tls') {
|
if (security === 'tls') {
|
||||||
@@ -1215,11 +1197,11 @@ class Inbound extends XrayCommonClass {
|
|||||||
params.set("path", httpupgrade.path);
|
params.set("path", httpupgrade.path);
|
||||||
params.set("host", httpupgrade.host?.length>0 ? httpupgrade.host : this.getHeader(httpupgrade, 'host'));
|
params.set("host", httpupgrade.host?.length>0 ? httpupgrade.host : this.getHeader(httpupgrade, 'host'));
|
||||||
break;
|
break;
|
||||||
case "splithttp":
|
case "xhttp":
|
||||||
const splithttp = this.stream.splithttp;
|
const xhttp = this.stream.xhttp;
|
||||||
params.set("path", splithttp.path);
|
params.set("path", xhttp.path);
|
||||||
params.set("host", splithttp.host?.length>0 ? splithttp.host : this.getHeader(splithttp, 'host'));
|
params.set("host", xhttp.host?.length>0 ? xhttp.host : this.getHeader(xhttp, 'host'));
|
||||||
params.set("mode", splithttp.mode);
|
params.set("mode", xhttp.mode);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1319,11 +1301,11 @@ class Inbound extends XrayCommonClass {
|
|||||||
params.set("path", httpupgrade.path);
|
params.set("path", httpupgrade.path);
|
||||||
params.set("host", httpupgrade.host?.length>0 ? httpupgrade.host : this.getHeader(httpupgrade, 'host'));
|
params.set("host", httpupgrade.host?.length>0 ? httpupgrade.host : this.getHeader(httpupgrade, 'host'));
|
||||||
break;
|
break;
|
||||||
case "splithttp":
|
case "xhttp":
|
||||||
const splithttp = this.stream.splithttp;
|
const xhttp = this.stream.xhttp;
|
||||||
params.set("path", splithttp.path);
|
params.set("path", xhttp.path);
|
||||||
params.set("host", splithttp.host?.length>0 ? splithttp.host : this.getHeader(splithttp, 'host'));
|
params.set("host", xhttp.host?.length>0 ? xhttp.host : this.getHeader(xhttp, 'host'));
|
||||||
params.set("mode", splithttp.mode);
|
params.set("mode", xhttp.mode);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1402,11 +1384,11 @@ class Inbound extends XrayCommonClass {
|
|||||||
params.set("path", httpupgrade.path);
|
params.set("path", httpupgrade.path);
|
||||||
params.set("host", httpupgrade.host?.length>0 ? httpupgrade.host : this.getHeader(httpupgrade, 'host'));
|
params.set("host", httpupgrade.host?.length>0 ? httpupgrade.host : this.getHeader(httpupgrade, 'host'));
|
||||||
break;
|
break;
|
||||||
case "splithttp":
|
case "xhttp":
|
||||||
const splithttp = this.stream.splithttp;
|
const xhttp = this.stream.xhttp;
|
||||||
params.set("path", splithttp.path);
|
params.set("path", xhttp.path);
|
||||||
params.set("host", splithttp.host?.length>0 ? splithttp.host : this.getHeader(splithttp, 'host'));
|
params.set("host", xhttp.host?.length>0 ? xhttp.host : this.getHeader(xhttp, 'host'));
|
||||||
params.set("mode", splithttp.mode);
|
params.set("mode", xhttp.mode);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -279,19 +279,39 @@ class HttpUpgradeStreamSettings extends CommonClass {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SplitHTTPStreamSettings extends CommonClass {
|
class xHTTPStreamSettings extends CommonClass {
|
||||||
constructor(path='/', host='',mode = '') {
|
constructor(
|
||||||
|
path = '/',
|
||||||
|
host = '',
|
||||||
|
mode = '',
|
||||||
|
noGRPCHeader = false,
|
||||||
|
scMinPostsIntervalMs = "30",
|
||||||
|
xmux = {
|
||||||
|
maxConcurrency: "16-32",
|
||||||
|
maxConnections: 0,
|
||||||
|
cMaxReuseTimes: "64-128",
|
||||||
|
cMaxLifetimeMs: 0,
|
||||||
|
hMaxRequestTimes: "800-900",
|
||||||
|
hKeepAlivePeriod: 0,
|
||||||
|
},
|
||||||
|
) {
|
||||||
super();
|
super();
|
||||||
this.path = path;
|
this.path = path;
|
||||||
this.host = host;
|
this.host = host;
|
||||||
this.mode = mode;
|
this.mode = mode;
|
||||||
|
this.noGRPCHeader = noGRPCHeader;
|
||||||
|
this.scMinPostsIntervalMs = scMinPostsIntervalMs;
|
||||||
|
this.xmux = xmux;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json={}) {
|
static fromJson(json = {}) {
|
||||||
return new SplitHTTPStreamSettings(
|
return new xHTTPStreamSettings(
|
||||||
json.path,
|
json.path,
|
||||||
json.host,
|
json.host,
|
||||||
json.mode,
|
json.mode,
|
||||||
|
json.noGRPCHeader,
|
||||||
|
json.scMinPostsIntervalMs,
|
||||||
|
json.xmux
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,6 +320,16 @@ class SplitHTTPStreamSettings extends CommonClass {
|
|||||||
path: this.path,
|
path: this.path,
|
||||||
host: this.host,
|
host: this.host,
|
||||||
mode: this.mode,
|
mode: this.mode,
|
||||||
|
noGRPCHeader: this.noGRPCHeader,
|
||||||
|
scMinPostsIntervalMs: this.scMinPostsIntervalMs,
|
||||||
|
xmux: {
|
||||||
|
maxConcurrency: this.xmux.maxConcurrency,
|
||||||
|
maxConnections: this.xmux.maxConnections,
|
||||||
|
cMaxReuseTimes: this.xmux.cMaxReuseTimes,
|
||||||
|
cMaxLifetimeMs: this.xmux.cMaxLifetimeMs,
|
||||||
|
hMaxRequestTimes: this.xmux.hMaxRequestTimes,
|
||||||
|
hKeepAlivePeriod: this.xmux.hKeepAlivePeriod,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -404,7 +434,7 @@ class StreamSettings extends CommonClass {
|
|||||||
httpSettings=new HttpStreamSettings(),
|
httpSettings=new HttpStreamSettings(),
|
||||||
grpcSettings=new GrpcStreamSettings(),
|
grpcSettings=new GrpcStreamSettings(),
|
||||||
httpupgradeSettings=new HttpUpgradeStreamSettings(),
|
httpupgradeSettings=new HttpUpgradeStreamSettings(),
|
||||||
splithttpSettings=new SplitHTTPStreamSettings(),
|
xhttpSettings=new xHTTPStreamSettings(),
|
||||||
sockopt = undefined,
|
sockopt = undefined,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
@@ -418,7 +448,7 @@ class StreamSettings extends CommonClass {
|
|||||||
this.http = httpSettings;
|
this.http = httpSettings;
|
||||||
this.grpc = grpcSettings;
|
this.grpc = grpcSettings;
|
||||||
this.httpupgrade = httpupgradeSettings;
|
this.httpupgrade = httpupgradeSettings;
|
||||||
this.splithttp = splithttpSettings;
|
this.xhttp = xhttpSettings;
|
||||||
this.sockopt = sockopt;
|
this.sockopt = sockopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -450,7 +480,7 @@ class StreamSettings extends CommonClass {
|
|||||||
HttpStreamSettings.fromJson(json.httpSettings),
|
HttpStreamSettings.fromJson(json.httpSettings),
|
||||||
GrpcStreamSettings.fromJson(json.grpcSettings),
|
GrpcStreamSettings.fromJson(json.grpcSettings),
|
||||||
HttpUpgradeStreamSettings.fromJson(json.httpupgradeSettings),
|
HttpUpgradeStreamSettings.fromJson(json.httpupgradeSettings),
|
||||||
SplitHTTPStreamSettings.fromJson(json.splithttpSettings),
|
xHTTPStreamSettings.fromJson(json.xhttpSettings),
|
||||||
SockoptStreamSettings.fromJson(json.sockopt),
|
SockoptStreamSettings.fromJson(json.sockopt),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -468,7 +498,7 @@ class StreamSettings extends CommonClass {
|
|||||||
httpSettings: network === 'http' ? this.http.toJson() : undefined,
|
httpSettings: network === 'http' ? this.http.toJson() : undefined,
|
||||||
grpcSettings: network === 'grpc' ? this.grpc.toJson() : undefined,
|
grpcSettings: network === 'grpc' ? this.grpc.toJson() : undefined,
|
||||||
httpupgradeSettings: network === 'httpupgrade' ? this.httpupgrade.toJson() : undefined,
|
httpupgradeSettings: network === 'httpupgrade' ? this.httpupgrade.toJson() : undefined,
|
||||||
splithttpSettings: network === 'splithttp' ? this.splithttp.toJson() : undefined,
|
xhttpSettings: network === 'xhttp' ? this.xhttp.toJson() : undefined,
|
||||||
sockopt: this.sockopt != undefined ? this.sockopt.toJson() : undefined,
|
sockopt: this.sockopt != undefined ? this.sockopt.toJson() : undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -533,7 +563,7 @@ class Outbound extends CommonClass {
|
|||||||
|
|
||||||
canEnableTls() {
|
canEnableTls() {
|
||||||
if (![Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks].includes(this.protocol)) return false;
|
if (![Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks].includes(this.protocol)) return false;
|
||||||
return ["tcp", "ws", "http", "grpc", "httpupgrade", "splithttp"].includes(this.stream.network);
|
return ["tcp", "ws", "http", "grpc", "httpupgrade", "xhttp"].includes(this.stream.network);
|
||||||
}
|
}
|
||||||
|
|
||||||
//this is used for xtls-rprx-vision
|
//this is used for xtls-rprx-vision
|
||||||
@@ -546,7 +576,7 @@ class Outbound extends CommonClass {
|
|||||||
|
|
||||||
canEnableReality() {
|
canEnableReality() {
|
||||||
if (![Protocols.VLESS, Protocols.Trojan].includes(this.protocol)) return false;
|
if (![Protocols.VLESS, Protocols.Trojan].includes(this.protocol)) return false;
|
||||||
return ["tcp", "http", "grpc", "splithttp"].includes(this.stream.network);
|
return ["tcp", "http", "grpc", "xhttp"].includes(this.stream.network);
|
||||||
}
|
}
|
||||||
|
|
||||||
canEnableStream() {
|
canEnableStream() {
|
||||||
@@ -653,8 +683,8 @@ class Outbound extends CommonClass {
|
|||||||
stream.grpc = new GrpcStreamSettings(json.path, json.authority, json.type == 'multi');
|
stream.grpc = new GrpcStreamSettings(json.path, json.authority, json.type == 'multi');
|
||||||
} else if (network === 'httpupgrade') {
|
} else if (network === 'httpupgrade') {
|
||||||
stream.httpupgrade = new HttpUpgradeStreamSettings(json.path,json.host);
|
stream.httpupgrade = new HttpUpgradeStreamSettings(json.path,json.host);
|
||||||
} else if (network === 'splithttp') {
|
} else if (network === 'xhttp') {
|
||||||
stream.splithttp = new SplitHTTPStreamSettings(json.path,json.host,json.mode);
|
stream.xhttp = new xHTTPStreamSettings(json.path,json.host,json.mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(json.tls && json.tls == 'tls'){
|
if(json.tls && json.tls == 'tls'){
|
||||||
@@ -697,8 +727,8 @@ class Outbound extends CommonClass {
|
|||||||
url.searchParams.get('mode') == 'multi');
|
url.searchParams.get('mode') == 'multi');
|
||||||
} else if (type === 'httpupgrade') {
|
} else if (type === 'httpupgrade') {
|
||||||
stream.httpupgrade = new HttpUpgradeStreamSettings(path,host);
|
stream.httpupgrade = new HttpUpgradeStreamSettings(path,host);
|
||||||
} else if (type === 'splithttp') {
|
} else if (type === 'xhttp') {
|
||||||
stream.splithttp = new SplitHTTPStreamSettings(path,host,mode);
|
stream.xhttp = new xHTTPStreamSettings(path,host,mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(security == 'tls'){
|
if(security == 'tls'){
|
||||||
|
|||||||
@@ -262,7 +262,7 @@
|
|||||||
<a-select-option value="http">HTTP</a-select-option>
|
<a-select-option value="http">HTTP</a-select-option>
|
||||||
<a-select-option value="grpc">gRPC</a-select-option>
|
<a-select-option value="grpc">gRPC</a-select-option>
|
||||||
<a-select-option value="httpupgrade">HTTPUpgrade</a-select-option>
|
<a-select-option value="httpupgrade">HTTPUpgrade</a-select-option>
|
||||||
<a-select-option value="splithttp">SplitHTTP</a-select-option>
|
<a-select-option value="xhttp">XHTTP</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<template v-if="outbound.stream.network === 'tcp'">
|
<template v-if="outbound.stream.network === 'tcp'">
|
||||||
@@ -362,21 +362,46 @@
|
|||||||
<a-input v-model.trim="outbound.stream.httpupgrade.path"></a-input>
|
<a-input v-model.trim="outbound.stream.httpupgrade.path"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- splithttp -->
|
<!-- xhttp -->
|
||||||
<template v-if="outbound.stream.network === 'splithttp'">
|
<template v-if="outbound.stream.network === 'xhttp'">
|
||||||
<a-form-item label='{{ i18n "host" }}'>
|
<a-form-item label='{{ i18n "host" }}'>
|
||||||
<a-input v-model="outbound.stream.splithttp.host"></a-input>
|
<a-input v-model="outbound.stream.xhttp.host"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='{{ i18n "path" }}'>
|
<a-form-item label='{{ i18n "path" }}'>
|
||||||
<a-input v-model.trim="outbound.stream.splithttp.path"></a-input>
|
<a-input v-model.trim="outbound.stream.xhttp.path"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='Mode'>
|
<a-form-item label='Mode'>
|
||||||
<a-select v-model="outbound.stream.splithttp.mode" :dropdown-class-name="themeSwitcher.currentTheme">
|
<a-select v-model="outbound.stream.xhttp.mode" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select-option v-for="key in MODE_OPTION" :value="key">[[ key ]]</a-select-option>
|
<a-select-option v-for="key in MODE_OPTION" :value="key">[[ key ]]</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</template>
|
<a-form-item label="No gRPC Header"
|
||||||
|
v-if="outbound.stream.xhttp.mode === 'stream-up' || outbound.stream.xhttp.mode === 'stream-one'">
|
||||||
|
<a-switch v-model="outbound.stream.xhttp.noGRPCHeader"></a-switch>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Min Upload Interval (Ms)" v-if="outbound.stream.xhttp.mode === 'packet-up'">
|
||||||
|
<a-input v-model.trim="outbound.stream.xhttp.scMinPostsIntervalMs"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Max Concurrency" v-if="!outbound.stream.xhttp.xmux.maxConnections">
|
||||||
|
<a-input v-model="outbound.stream.xhttp.xmux.maxConcurrency"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Max Connections" v-if="!outbound.stream.xhttp.xmux.maxConcurrency">
|
||||||
|
<a-input v-model="outbound.stream.xhttp.xmux.maxConnections"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Max Reuse Times">
|
||||||
|
<a-input v-model="outbound.stream.xhttp.xmux.cMaxReuseTimes"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Max Lifetime (ms)">
|
||||||
|
<a-input v-model="outbound.stream.xhttp.xmux.cMaxLifetimeMs"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Max Request Times">
|
||||||
|
<a-input v-model="outbound.stream.xhttp.xmux.hMaxRequestTimes"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label='Keep Alive Period'>
|
||||||
|
<a-input v-model.number="outbound.stream.xhttp.xmux.hKeepAlivePeriod"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- tls settings -->
|
<!-- tls settings -->
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<a-select-option value="http">HTTP</a-select-option>
|
<a-select-option value="http">HTTP</a-select-option>
|
||||||
<a-select-option value="grpc">gRPC</a-select-option>
|
<a-select-option value="grpc">gRPC</a-select-option>
|
||||||
<a-select-option value="httpupgrade">HttpUpgrade</a-select-option>
|
<a-select-option value="httpupgrade">HttpUpgrade</a-select-option>
|
||||||
<a-select-option value="splithttp">SplitHTTP</a-select-option>
|
<a-select-option value="xhttp">XHTTP</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
@@ -45,9 +45,9 @@
|
|||||||
{{template "form/streamHTTPUPGRADE"}}
|
{{template "form/streamHTTPUPGRADE"}}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- splithttp -->
|
<!-- xhttp -->
|
||||||
<template v-if="inbound.stream.network === 'splithttp'">
|
<template v-if="inbound.stream.network === 'xhttp'">
|
||||||
{{template "form/streamSplitHTTP"}}
|
{{template "form/streamXHTTP"}}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- sockopt -->
|
<!-- sockopt -->
|
||||||
|
|||||||
@@ -1,59 +0,0 @@
|
|||||||
{{define "form/streamSplitHTTP"}}
|
|
||||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
|
||||||
<a-form-item label='{{ i18n "host" }}'>
|
|
||||||
<a-input v-model.trim="inbound.stream.splithttp.host"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label='{{ i18n "path" }}'>
|
|
||||||
<a-input v-model.trim="inbound.stream.splithttp.path"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestHeader" }}'>
|
|
||||||
<a-button icon="plus" size="small" @click="inbound.stream.splithttp.addHeader('host', '')"></a-button>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item :wrapper-col="{span:24}">
|
|
||||||
<a-input-group compact v-for="(header, index) in inbound.stream.splithttp.headers">
|
|
||||||
<a-input style="width: 50%" v-model.trim="header.name"
|
|
||||||
placeholder='{{ i18n "pages.inbounds.stream.general.name"}}'>
|
|
||||||
<template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
|
|
||||||
</a-input>
|
|
||||||
<a-input style="width: 50%" v-model.trim="header.value"
|
|
||||||
placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
|
||||||
<a-button slot="addonAfter" size="small"
|
|
||||||
@click="inbound.stream.splithttp.removeHeader(index)">-</a-button>
|
|
||||||
</a-input>
|
|
||||||
</a-input-group>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label='Mode'>
|
|
||||||
<a-select v-model="inbound.stream.splithttp.mode" style="width: 50%"
|
|
||||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
|
||||||
<a-select-option v-for="key in MODE_OPTION" :value="key">[[ key ]]</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label="Max Concurrent Upload">
|
|
||||||
<a-input v-model.trim="inbound.stream.splithttp.scMaxConcurrentPosts"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label="Max Upload Size (Byte)">
|
|
||||||
<a-input v-model.trim="inbound.stream.splithttp.scMaxEachPostBytes"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label="Min Upload Interval (Ms)">
|
|
||||||
<a-input v-model.trim="inbound.stream.splithttp.scMinPostsIntervalMs"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label="Padding Bytes">
|
|
||||||
<a-input v-model.trim="inbound.stream.splithttp.xPaddingBytes"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label="No SSE Header">
|
|
||||||
<a-switch v-model="inbound.stream.splithttp.noSSEHeader"></a-switch>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label="Max Concurrency" v-if="!inbound.stream.splithttp.xmux.maxConnections">
|
|
||||||
<a-input v-model="inbound.stream.splithttp.xmux.maxConcurrency"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label="Max Connections" v-if="!inbound.stream.splithttp.xmux.maxConcurrency">
|
|
||||||
<a-input v-model="inbound.stream.splithttp.xmux.maxConnections"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label="Max Reuse Times">
|
|
||||||
<a-input v-model="inbound.stream.splithttp.xmux.cMaxReuseTimes"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label="Max Lifetime (ms)">
|
|
||||||
<a-input v-model="inbound.stream.splithttp.xmux.cMaxLifetimeMs"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</a-form>
|
|
||||||
{{end}}
|
|
||||||
43
web/html/xui/form/stream/stream_xhttp.html
Normal file
43
web/html/xui/form/stream/stream_xhttp.html
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
{{define "form/streamXHTTP"}}
|
||||||
|
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
|
<a-form-item label='{{ i18n "host" }}'>
|
||||||
|
<a-input v-model.trim="inbound.stream.xhttp.host"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label='{{ i18n "path" }}'>
|
||||||
|
<a-input v-model.trim="inbound.stream.xhttp.path"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestHeader" }}'>
|
||||||
|
<a-button icon="plus" size="small" @click="inbound.stream.xhttp.addHeader('', '')"></a-button>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item :wrapper-col="{span:24}">
|
||||||
|
<a-input-group compact v-for="(header, index) in inbound.stream.xhttp.headers">
|
||||||
|
<a-input style="width: 50%" v-model.trim="header.name"
|
||||||
|
placeholder='{{ i18n "pages.inbounds.stream.general.name"}}'>
|
||||||
|
<template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
|
||||||
|
</a-input>
|
||||||
|
<a-input style="width: 50%" v-model.trim="header.value"
|
||||||
|
placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
||||||
|
<a-button slot="addonAfter" size="small" @click="inbound.stream.xhttp.removeHeader(index)">-</a-button>
|
||||||
|
</a-input>
|
||||||
|
</a-input-group>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label='Mode'>
|
||||||
|
<a-select v-model="inbound.stream.xhttp.mode" style="width: 50%"
|
||||||
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
|
<a-select-option v-for="key in MODE_OPTION" :value="key">[[ key ]]</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Max Buffered Upload" v-if="inbound.stream.xhttp.mode === 'packet-up'">
|
||||||
|
<a-input v-model.trim="inbound.stream.xhttp.scMaxBufferedPosts"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Max Upload Size (Byte)" v-if="inbound.stream.xhttp.mode === 'packet-up'">
|
||||||
|
<a-input v-model.trim="inbound.stream.xhttp.scMaxEachPostBytes"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Padding Bytes">
|
||||||
|
<a-input v-model.trim="inbound.stream.xhttp.xPaddingBytes"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="No SSE Header">
|
||||||
|
<a-switch v-model="inbound.stream.xhttp.noSSEHeader"></a-switch>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
{{end}}
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>{{ i18n "transmission" }}</td><td><a-tag color="blue">[[ inbound.network ]]</a-tag></td>
|
<td>{{ i18n "transmission" }}</td><td><a-tag color="blue">[[ inbound.network ]]</a-tag></td>
|
||||||
</tr>
|
</tr>
|
||||||
<template v-if="inbound.isTcp || inbound.isWs || inbound.isH2 || inbound.isHttpupgrade || inbound.isSplithttp">
|
<template v-if="inbound.isTcp || inbound.isWs || inbound.isH2 || inbound.isHttpupgrade || inbound.isXHTTP">
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ i18n "host" }}</td>
|
<td>{{ i18n "host" }}</td>
|
||||||
<td v-if="inbound.host">
|
<td v-if="inbound.host">
|
||||||
@@ -44,11 +44,11 @@
|
|||||||
<td v-else><a-tag color="orange">{{ i18n "none" }}</a-tag></td>
|
<td v-else><a-tag color="orange">{{ i18n "none" }}</a-tag></td>
|
||||||
</tr>
|
</tr>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="inbound.isSplithttp">
|
<template v-if="inbound.isXHTTP">
|
||||||
<tr>
|
<tr>
|
||||||
<td>Mode</td>
|
<td>Mode</td>
|
||||||
<td>
|
<td>
|
||||||
<a-tag>[[ inbound.stream.splithttp.mode ]]</a-tag>
|
<a-tag>[[ inbound.stream.xhttp.mode ]]</a-tag>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user