add Xray-specific keys

This commit is contained in:
dranik
2026-03-24 11:54:51 +03:00
parent dc58e1832c
commit a028349dbb
3 changed files with 443 additions and 93 deletions

View File

@@ -3,86 +3,274 @@
#include <QJsonDocument>
#include <QJsonArray>
#include "../../../core/utils/protocolEnum.h"
#include "../../../core/protocols/protocolUtils.h"
#include "../../../core/utils/constants/configKeys.h"
#include "../../../core/utils/constants/protocolConstants.h"
#include "core/utils/protocolEnum.h"
#include "core/protocols/protocolUtils.h"
#include "core/utils/constants/configKeys.h"
#include "core/utils/constants/protocolConstants.h"
using namespace amnezia;
using namespace ProtocolUtils;
namespace amnezia
{
// ═════════════════════════════════════════════════════════════════════════════
// XrayXPaddingConfig
// ═════════════════════════════════════════════════════════════════════════════
QJsonObject XrayXPaddingConfig::toJson() const
{
QJsonObject obj;
if (!bytesMin.isEmpty()) obj[configKey::xPaddingBytesMin] = bytesMin;
if (!bytesMax.isEmpty()) obj[configKey::xPaddingBytesMax] = bytesMax;
obj[configKey::xPaddingObfsMode] = obfsMode;
if (!key.isEmpty()) obj[configKey::xPaddingKey] = key;
if (!header.isEmpty()) obj[configKey::xPaddingHeader] = header;
if (!placement.isEmpty()) obj[configKey::xPaddingPlacement] = placement;
if (!method.isEmpty()) obj[configKey::xPaddingMethod] = method;
return obj;
}
XrayXPaddingConfig XrayXPaddingConfig::fromJson(const QJsonObject &json)
{
XrayXPaddingConfig c;
c.bytesMin = json.value(configKey::xPaddingBytesMin).toString();
c.bytesMax = json.value(configKey::xPaddingBytesMax).toString();
c.obfsMode = json.value(configKey::xPaddingObfsMode).toBool(true);
c.key = json.value(configKey::xPaddingKey).toString("www.googletagmanager.com");
c.header = json.value(configKey::xPaddingHeader).toString();
c.placement = json.value(configKey::xPaddingPlacement).toString("Cookie");
c.method = json.value(configKey::xPaddingMethod).toString("Repeat-x");
return c;
}
// ═════════════════════════════════════════════════════════════════════════════
// XrayXmuxConfig
// ═════════════════════════════════════════════════════════════════════════════
QJsonObject XrayXmuxConfig::toJson() const
{
QJsonObject obj;
obj[configKey::xmuxEnabled] = enabled;
if (!maxConcurrencyMin.isEmpty()) obj[configKey::xmuxMaxConcurrencyMin] = maxConcurrencyMin;
if (!maxConcurrencyMax.isEmpty()) obj[configKey::xmuxMaxConcurrencyMax] = maxConcurrencyMax;
if (!maxConnectionsMin.isEmpty()) obj[configKey::xmuxMaxConnectionsMin] = maxConnectionsMin;
if (!maxConnectionsMax.isEmpty()) obj[configKey::xmuxMaxConnectionsMax] = maxConnectionsMax;
if (!cMaxReuseTimesMin.isEmpty()) obj[configKey::xmuxCMaxReuseTimesMin] = cMaxReuseTimesMin;
if (!cMaxReuseTimesMax.isEmpty()) obj[configKey::xmuxCMaxReuseTimesMax] = cMaxReuseTimesMax;
if (!hMaxRequestTimesMin.isEmpty()) obj[configKey::xmuxHMaxRequestTimesMin] = hMaxRequestTimesMin;
if (!hMaxRequestTimesMax.isEmpty()) obj[configKey::xmuxHMaxRequestTimesMax] = hMaxRequestTimesMax;
if (!hMaxReusableSecsMin.isEmpty()) obj[configKey::xmuxHMaxReusableSecsMin] = hMaxReusableSecsMin;
if (!hMaxReusableSecsMax.isEmpty()) obj[configKey::xmuxHMaxReusableSecsMax] = hMaxReusableSecsMax;
if (!hKeepAlivePeriod.isEmpty()) obj[configKey::xmuxHKeepAlivePeriod] = hKeepAlivePeriod;
return obj;
}
XrayXmuxConfig XrayXmuxConfig::fromJson(const QJsonObject &json)
{
XrayXmuxConfig c;
c.enabled = json.value(configKey::xmuxEnabled).toBool(true);
c.maxConcurrencyMin = json.value(configKey::xmuxMaxConcurrencyMin).toString("0");
c.maxConcurrencyMax = json.value(configKey::xmuxMaxConcurrencyMax).toString("0");
c.maxConnectionsMin = json.value(configKey::xmuxMaxConnectionsMin).toString("0");
c.maxConnectionsMax = json.value(configKey::xmuxMaxConnectionsMax).toString("0");
c.cMaxReuseTimesMin = json.value(configKey::xmuxCMaxReuseTimesMin).toString("0");
c.cMaxReuseTimesMax = json.value(configKey::xmuxCMaxReuseTimesMax).toString("0");
c.hMaxRequestTimesMin = json.value(configKey::xmuxHMaxRequestTimesMin).toString("0");
c.hMaxRequestTimesMax = json.value(configKey::xmuxHMaxRequestTimesMax).toString("0");
c.hMaxReusableSecsMin = json.value(configKey::xmuxHMaxReusableSecsMin).toString("0");
c.hMaxReusableSecsMax = json.value(configKey::xmuxHMaxReusableSecsMax).toString("0");
c.hKeepAlivePeriod = json.value(configKey::xmuxHKeepAlivePeriod).toString();
return c;
}
// ═════════════════════════════════════════════════════════════════════════════
// XrayXhttpConfig
// ═════════════════════════════════════════════════════════════════════════════
QJsonObject XrayXhttpConfig::toJson() const
{
QJsonObject obj;
if (!mode.isEmpty()) obj[configKey::xhttpMode] = mode;
if (!host.isEmpty()) obj[configKey::xhttpHost] = host;
if (!path.isEmpty()) obj[configKey::xhttpPath] = path;
if (!headersTemplate.isEmpty()) obj[configKey::xhttpHeadersTemplate] = headersTemplate;
if (!uplinkMethod.isEmpty()) obj[configKey::xhttpUplinkMethod] = uplinkMethod;
obj[configKey::xhttpDisableGrpc] = disableGrpc;
obj[configKey::xhttpDisableSse] = disableSse;
if (!sessionPlacement.isEmpty()) obj[configKey::xhttpSessionPlacement] = sessionPlacement;
if (!sessionKey.isEmpty()) obj[configKey::xhttpSessionKey] = sessionKey;
if (!seqPlacement.isEmpty()) obj[configKey::xhttpSeqPlacement] = seqPlacement;
if (!seqKey.isEmpty()) obj[configKey::xhttpSeqKey] = seqKey;
if (!uplinkDataPlacement.isEmpty()) obj[configKey::xhttpUplinkDataPlacement] = uplinkDataPlacement;
if (!uplinkDataKey.isEmpty()) obj[configKey::xhttpUplinkDataKey] = uplinkDataKey;
if (!uplinkChunkSize.isEmpty()) obj[configKey::xhttpUplinkChunkSize] = uplinkChunkSize;
if (!scMaxBufferedPosts.isEmpty()) obj[configKey::xhttpScMaxBufferedPosts] = scMaxBufferedPosts;
if (!scMaxEachPostBytesMin.isEmpty()) obj[configKey::xhttpScMaxEachPostBytesMin] = scMaxEachPostBytesMin;
if (!scMaxEachPostBytesMax.isEmpty()) obj[configKey::xhttpScMaxEachPostBytesMax] = scMaxEachPostBytesMax;
if (!scMinPostsIntervalMsMin.isEmpty()) obj[configKey::xhttpScMinPostsIntervalMsMin] = scMinPostsIntervalMsMin;
if (!scMinPostsIntervalMsMax.isEmpty()) obj[configKey::xhttpScMinPostsIntervalMsMax] = scMinPostsIntervalMsMax;
if (!scStreamUpServerSecsMin.isEmpty()) obj[configKey::xhttpScStreamUpServerSecsMin] = scStreamUpServerSecsMin;
if (!scStreamUpServerSecsMax.isEmpty()) obj[configKey::xhttpScStreamUpServerSecsMax] = scStreamUpServerSecsMax;
obj["xPadding"] = xPadding.toJson();
obj["xmux"] = xmux.toJson();
return obj;
}
XrayXhttpConfig XrayXhttpConfig::fromJson(const QJsonObject &json)
{
XrayXhttpConfig c;
c.mode = json.value(configKey::xhttpMode).toString("Auto");
c.host = json.value(configKey::xhttpHost).toString("www.googletagmanager.com");
c.path = json.value(configKey::xhttpPath).toString();
c.headersTemplate = json.value(configKey::xhttpHeadersTemplate).toString("HTTP");
c.uplinkMethod = json.value(configKey::xhttpUplinkMethod).toString("POST");
c.disableGrpc = json.value(configKey::xhttpDisableGrpc).toBool(true);
c.disableSse = json.value(configKey::xhttpDisableSse).toBool(true);
c.sessionPlacement = json.value(configKey::xhttpSessionPlacement).toString("Path");
c.sessionKey = json.value(configKey::xhttpSessionKey).toString("Path");
c.seqPlacement = json.value(configKey::xhttpSeqPlacement).toString("Path");
c.seqKey = json.value(configKey::xhttpSeqKey).toString();
c.uplinkDataPlacement = json.value(configKey::xhttpUplinkDataPlacement).toString("Body");
c.uplinkDataKey = json.value(configKey::xhttpUplinkDataKey).toString();
c.uplinkChunkSize = json.value(configKey::xhttpUplinkChunkSize).toString("0");
c.scMaxBufferedPosts = json.value(configKey::xhttpScMaxBufferedPosts).toString();
c.scMaxEachPostBytesMin = json.value(configKey::xhttpScMaxEachPostBytesMin).toString("1");
c.scMaxEachPostBytesMax = json.value(configKey::xhttpScMaxEachPostBytesMax).toString("100");
c.scMinPostsIntervalMsMin = json.value(configKey::xhttpScMinPostsIntervalMsMin).toString("100");
c.scMinPostsIntervalMsMax = json.value(configKey::xhttpScMinPostsIntervalMsMax).toString("800");
c.scStreamUpServerSecsMin = json.value(configKey::xhttpScStreamUpServerSecsMin).toString("1");
c.scStreamUpServerSecsMax = json.value(configKey::xhttpScStreamUpServerSecsMax).toString("100");
c.xPadding = XrayXPaddingConfig::fromJson(json.value("xPadding").toObject());
c.xmux = XrayXmuxConfig::fromJson(json.value("xmux").toObject());
return c;
}
// ═════════════════════════════════════════════════════════════════════════════
// XrayMkcpConfig
// ═════════════════════════════════════════════════════════════════════════════
QJsonObject XrayMkcpConfig::toJson() const
{
QJsonObject obj;
if (!tti.isEmpty()) obj[configKey::mkcpTti] = tti;
if (!uplinkCapacity.isEmpty()) obj[configKey::mkcpUplinkCapacity] = uplinkCapacity;
if (!downlinkCapacity.isEmpty()) obj[configKey::mkcpDownlinkCapacity] = downlinkCapacity;
if (!readBufferSize.isEmpty()) obj[configKey::mkcpReadBufferSize] = readBufferSize;
if (!writeBufferSize.isEmpty()) obj[configKey::mkcpWriteBufferSize] = writeBufferSize;
obj[configKey::mkcpCongestion] = congestion;
return obj;
}
XrayMkcpConfig XrayMkcpConfig::fromJson(const QJsonObject &json)
{
XrayMkcpConfig c;
c.tti = json.value(configKey::mkcpTti).toString();
c.uplinkCapacity = json.value(configKey::mkcpUplinkCapacity).toString();
c.downlinkCapacity = json.value(configKey::mkcpDownlinkCapacity).toString();
c.readBufferSize = json.value(configKey::mkcpReadBufferSize).toString();
c.writeBufferSize = json.value(configKey::mkcpWriteBufferSize).toString();
c.congestion = json.value(configKey::mkcpCongestion).toBool(true);
return c;
}
// ═════════════════════════════════════════════════════════════════════════════
// XrayServerConfig
// ═════════════════════════════════════════════════════════════════════════════
QJsonObject XrayServerConfig::toJson() const
{
QJsonObject obj;
if (!port.isEmpty()) {
obj[configKey::port] = port;
}
if (!transportProto.isEmpty()) {
obj[configKey::transportProto] = transportProto;
}
if (!subnetAddress.isEmpty()) {
obj[configKey::subnetAddress] = subnetAddress;
}
if (!site.isEmpty()) {
obj[configKey::site] = site;
}
// Existing fields
if (!port.isEmpty()) obj[configKey::port] = port;
if (!transportProto.isEmpty()) obj[configKey::transportProto] = transportProto;
if (!subnetAddress.isEmpty()) obj[configKey::subnetAddress] = subnetAddress;
if (!site.isEmpty()) obj[configKey::site] = site;
if (isThirdPartyConfig) obj[configKey::isThirdPartyConfig] = isThirdPartyConfig;
if (isThirdPartyConfig) {
obj[configKey::isThirdPartyConfig] = isThirdPartyConfig;
}
// New: Security
if (!security.isEmpty()) obj[configKey::xraySecurity] = security;
if (!flow.isEmpty()) obj[configKey::xrayFlow] = flow;
if (!fingerprint.isEmpty()) obj[configKey::xrayFingerprint] = fingerprint;
if (!sni.isEmpty()) obj[configKey::xraySni] = sni;
if (!alpn.isEmpty()) obj[configKey::xrayAlpn] = alpn;
// New: Transport
if (!transport.isEmpty()) obj[configKey::xrayTransport] = transport;
obj["xhttp"] = xhttp.toJson();
obj["mkcp"] = mkcp.toJson();
return obj;
}
XrayServerConfig XrayServerConfig::fromJson(const QJsonObject& json)
XrayServerConfig XrayServerConfig::fromJson(const QJsonObject &json)
{
XrayServerConfig config;
XrayServerConfig c;
config.port = json.value(configKey::port).toString();
config.transportProto = json.value(configKey::transportProto).toString();
config.subnetAddress = json.value(configKey::subnetAddress).toString();
config.site = json.value(configKey::site).toString();
// Existing fields
c.port = json.value(configKey::port).toString();
c.transportProto = json.value(configKey::transportProto).toString();
c.subnetAddress = json.value(configKey::subnetAddress).toString();
c.site = json.value(configKey::site).toString();
c.isThirdPartyConfig = json.value(configKey::isThirdPartyConfig).toBool(false);
config.isThirdPartyConfig = json.value(configKey::isThirdPartyConfig).toBool(false);
// New: Security
c.security = json.value(configKey::xraySecurity).toString("reality");
c.flow = json.value(configKey::xrayFlow).toString("xtls-rprx-vision");
c.fingerprint = json.value(configKey::xrayFingerprint).toString("Mozilla/5.0");
c.sni = json.value(configKey::xraySni).toString("cdn.example.com");
c.alpn = json.value(configKey::xrayAlpn).toString("HTTP/2");
return config;
// New: Transport
c.transport = json.value(configKey::xrayTransport).toString("raw");
c.xhttp = XrayXhttpConfig::fromJson(json.value("xhttp").toObject());
c.mkcp = XrayMkcpConfig::fromJson(json.value("mkcp").toObject());
return c;
}
bool XrayServerConfig::hasEqualServerSettings(const XrayServerConfig& other) const
bool XrayServerConfig::hasEqualServerSettings(const XrayServerConfig &other) const
{
return port == other.port && site == other.site;
return port == other.port
&& site == other.site
&& security == other.security
&& flow == other.flow
&& transport == other.transport
&& fingerprint == other.fingerprint
&& sni == other.sni;
}
// ═════════════════════════════════════════════════════════════════════════════
// XrayClientConfig (unchanged logic, kept as-is)
// ═════════════════════════════════════════════════════════════════════════════
QJsonObject XrayClientConfig::toJson() const
{
QJsonObject obj;
if (!nativeConfig.isEmpty()) {
obj[configKey::config] = nativeConfig;
}
if (!localPort.isEmpty()) {
obj[configKey::localPort] = localPort;
}
if (!id.isEmpty()) {
obj[configKey::clientId] = id;
}
if (!nativeConfig.isEmpty()) obj[configKey::config] = nativeConfig;
if (!localPort.isEmpty()) obj[configKey::localPort] = localPort;
if (!id.isEmpty()) obj[configKey::clientId] = id;
return obj;
}
XrayClientConfig XrayClientConfig::fromJson(const QJsonObject& json)
XrayClientConfig XrayClientConfig::fromJson(const QJsonObject &json)
{
XrayClientConfig config;
XrayClientConfig c;
c.nativeConfig = json.value(configKey::config).toString();
c.localPort = json.value(configKey::localPort).toString();
c.id = json.value(configKey::clientId).toString();
config.nativeConfig = json.value(configKey::config).toString();
config.localPort = json.value(configKey::localPort).toString();
config.id = json.value(configKey::clientId).toString();
if (config.id.isEmpty() && !config.nativeConfig.isEmpty()) {
QJsonDocument doc = QJsonDocument::fromJson(config.nativeConfig.toUtf8());
if (c.id.isEmpty() && !c.nativeConfig.isEmpty()) {
QJsonDocument doc = QJsonDocument::fromJson(c.nativeConfig.toUtf8());
if (!doc.isNull() && doc.isObject()) {
QJsonObject configObj = doc.object();
if (configObj.contains(protocols::xray::outbounds)) {
@@ -98,10 +286,7 @@ XrayClientConfig XrayClientConfig::fromJson(const QJsonObject& json)
if (vnextObj.contains(protocols::xray::users)) {
QJsonArray users = vnextObj[protocols::xray::users].toArray();
if (!users.isEmpty()) {
QJsonObject user = users[0].toObject();
if (user.contains(protocols::xray::id)) {
config.id = user[protocols::xray::id].toString();
}
c.id = users[0].toObject().value(protocols::xray::id).toString();
}
}
}
@@ -112,15 +297,18 @@ XrayClientConfig XrayClientConfig::fromJson(const QJsonObject& json)
}
}
return config;
return c;
}
// ═════════════════════════════════════════════════════════════════════════════
// XrayProtocolConfig (unchanged logic, kept as-is)
// ═════════════════════════════════════════════════════════════════════════════
QJsonObject XrayProtocolConfig::toJson() const
{
QJsonObject obj = serverConfig.toJson();
if (clientConfig.has_value()) {
// Third-party import: nativeConfig is raw Xray JSON (inbounds/outbounds)
QJsonDocument doc = QJsonDocument::fromJson(clientConfig->nativeConfig.toUtf8());
if (!doc.isNull() && doc.isObject() && doc.object().contains(protocols::xray::outbounds)
&& !doc.object().contains(configKey::config)) {
@@ -134,38 +322,34 @@ QJsonObject XrayProtocolConfig::toJson() const
return obj;
}
XrayProtocolConfig XrayProtocolConfig::fromJson(const QJsonObject& json)
XrayProtocolConfig XrayProtocolConfig::fromJson(const QJsonObject &json)
{
XrayProtocolConfig config;
config.serverConfig = XrayServerConfig::fromJson(json);
XrayProtocolConfig c;
c.serverConfig = XrayServerConfig::fromJson(json);
QString lastConfigStr = json.value(configKey::lastConfig).toString();
if (!lastConfigStr.isEmpty()) {
QJsonDocument doc = QJsonDocument::fromJson(lastConfigStr.toUtf8());
if (doc.isObject()) {
QJsonObject parsed = doc.object();
// Third-party import stores raw Xray config (inbounds/outbounds) directly
if (parsed.contains(protocols::xray::outbounds) && !parsed.contains(configKey::config)) {
XrayClientConfig clientCfg;
clientCfg.nativeConfig = lastConfigStr;
if (parsed.contains(protocols::xray::inbounds)) {
QJsonArray inbounds = parsed.value(protocols::xray::inbounds).toArray();
if (!inbounds.isEmpty()) {
QJsonObject inbound = inbounds[0].toObject();
if (inbound.contains(protocols::xray::port)) {
clientCfg.localPort = QString::number(inbound.value(protocols::xray::port).toInt());
clientCfg.localPort = QString::number(
inbounds[0].toObject().value(protocols::xray::port).toInt());
}
}
}
config.clientConfig = clientCfg;
c.clientConfig = clientCfg;
} else {
config.clientConfig = XrayClientConfig::fromJson(parsed);
c.clientConfig = XrayClientConfig::fromJson(parsed);
}
}
}
return config;
return c;
}
bool XrayProtocolConfig::hasClientConfig() const
@@ -173,7 +357,7 @@ bool XrayProtocolConfig::hasClientConfig() const
return clientConfig.has_value();
}
void XrayProtocolConfig::setClientConfig(const XrayClientConfig& config)
void XrayProtocolConfig::setClientConfig(const XrayClientConfig &config)
{
clientConfig = config;
}
@@ -184,4 +368,3 @@ void XrayProtocolConfig::clearClientConfig()
}
} // namespace amnezia

View File

@@ -8,41 +8,138 @@
namespace amnezia
{
// ── xPadding ─────────────────────────────────────────────────────────────────
struct XrayXPaddingConfig {
QString bytesMin; // xPaddingBytes min
QString bytesMax; // xPaddingBytes max
bool obfsMode = true; // xPaddingObfsMode
QString key; // xPaddingKey
QString header; // xPaddingHeader
QString placement = "Cookie"; // xPaddingPlacement: Cookie|Header|Query|Body
QString method = "Repeat-x"; // xPaddingMethod: Repeat-x|Random|Zero
QJsonObject toJson() const;
static XrayXPaddingConfig fromJson(const QJsonObject &json);
};
// ── xmux ─────────────────────────────────────────────────────────────────────
struct XrayXmuxConfig {
bool enabled = true;
QString maxConcurrencyMin = "0";
QString maxConcurrencyMax = "0";
QString maxConnectionsMin = "0";
QString maxConnectionsMax = "0";
QString cMaxReuseTimesMin = "0";
QString cMaxReuseTimesMax = "0";
QString hMaxRequestTimesMin = "0";
QString hMaxRequestTimesMax = "0";
QString hMaxReusableSecsMin = "0";
QString hMaxReusableSecsMax = "0";
QString hKeepAlivePeriod;
QJsonObject toJson() const;
static XrayXmuxConfig fromJson(const QJsonObject &json);
};
// ── XHTTP transport ───────────────────────────────────────────────────────────
struct XrayXhttpConfig {
QString mode = "Auto"; // Auto|Packet-up|Stream-up|Stream-one
QString host = "www.googletagmanager.com";
QString path;
QString headersTemplate = "HTTP"; // HTTP|None
QString uplinkMethod = "POST"; // POST|PUT|PATCH
bool disableGrpc = true;
bool disableSse = true;
// Session & Sequence
QString sessionPlacement = "Path"; // Path|Header|Cookie|None
QString sessionKey = "Path";
QString seqPlacement = "Path";
QString seqKey;
QString uplinkDataPlacement = "Body"; // Body|Query
QString uplinkDataKey;
// Traffic Shaping
QString uplinkChunkSize = "0";
QString scMaxBufferedPosts;
QString scMaxEachPostBytesMin = "1";
QString scMaxEachPostBytesMax = "100";
QString scMinPostsIntervalMsMin = "100";
QString scMinPostsIntervalMsMax = "800";
QString scStreamUpServerSecsMin = "1";
QString scStreamUpServerSecsMax = "100";
XrayXPaddingConfig xPadding;
XrayXmuxConfig xmux;
QJsonObject toJson() const;
static XrayXhttpConfig fromJson(const QJsonObject &json);
};
// ── mKCP transport ────────────────────────────────────────────────────────────
struct XrayMkcpConfig {
QString tti;
QString uplinkCapacity;
QString downlinkCapacity;
QString readBufferSize;
QString writeBufferSize;
bool congestion = true;
QJsonObject toJson() const;
static XrayMkcpConfig fromJson(const QJsonObject &json);
};
// ── Server config (settings editable by user) ─────────────────────────────────
struct XrayServerConfig {
// Existing fields
QString port;
QString transportProto;
QString subnetAddress;
QString site;
bool isThirdPartyConfig = false;
QJsonObject toJson() const;
static XrayServerConfig fromJson(const QJsonObject& json);
// New: Security
QString security = "reality"; // none|tls|reality
QString flow = "xtls-rprx-vision"; // ""|xtls-rprx-vision|xtls-rprx-vision-udp443
QString fingerprint = "Mozilla/5.0";
QString sni = "cdn.example.com";
QString alpn = "HTTP/2"; // TLS only: HTTP/2|HTTP/1.1|HTTP/2,HTTP/1.1
bool hasEqualServerSettings(const XrayServerConfig& other) const;
// New: Transport
QString transport = "raw"; // raw|xhttp|mkcp
XrayXhttpConfig xhttp;
XrayMkcpConfig mkcp;
QJsonObject toJson() const;
static XrayServerConfig fromJson(const QJsonObject &json);
bool hasEqualServerSettings(const XrayServerConfig &other) const;
};
// ── Client config (generated, not edited by user) ─────────────────────────────
struct XrayClientConfig {
QString nativeConfig;
QString localPort;
QString id;
QJsonObject toJson() const;
static XrayClientConfig fromJson(const QJsonObject& json);
static XrayClientConfig fromJson(const QJsonObject &json);
};
// ── Top-level protocol config ──────────────────────────────────────────────────
struct XrayProtocolConfig {
XrayServerConfig serverConfig;
std::optional<XrayClientConfig> clientConfig;
QJsonObject toJson() const;
static XrayProtocolConfig fromJson(const QJsonObject& json);
static XrayProtocolConfig fromJson(const QJsonObject &json);
bool hasClientConfig() const;
void setClientConfig(const XrayClientConfig& config);
void setClientConfig(const XrayClientConfig &config);
void clearClientConfig();
};
} // namespace amnezia
#endif // XRAYPROTOCOLCONFIG_H

View File

@@ -121,6 +121,76 @@ namespace amnezia
constexpr QLatin1String latestHandshake("latestHandshake");
constexpr QLatin1String dataReceived("dataReceived");
constexpr QLatin1String dataSent("dataSent");
// ── Xray-specific keys ────────────────────────────────────────
// Security
constexpr QLatin1String xraySecurity("xray_security"); // none | tls | reality
constexpr QLatin1String xrayFlow("xray_flow"); // "" | xtls-rprx-vision | xtls-rprx-vision-udp443
constexpr QLatin1String xrayFingerprint("xray_fingerprint"); // Mozilla/5.0 | chrome | firefox | ...
constexpr QLatin1String xraySni("xray_sni"); // Server Name (SNI)
constexpr QLatin1String xrayAlpn("xray_alpn"); // HTTP/2 | HTTP/1.1 | HTTP/2,HTTP/1.1
// Transport — common
constexpr QLatin1String xrayTransport("xray_transport"); // raw | xhttp | mkcp
// Transport — XHTTP
constexpr QLatin1String xhttpMode("xhttp_mode"); // Auto | Packet-up | Stream-up | Stream-one
constexpr QLatin1String xhttpHost("xhttp_host");
constexpr QLatin1String xhttpPath("xhttp_path");
constexpr QLatin1String xhttpHeadersTemplate("xhttp_headers_template"); // HTTP | None
constexpr QLatin1String xhttpUplinkMethod("xhttp_uplink_method"); // POST | PUT | PATCH
constexpr QLatin1String xhttpDisableGrpc("xhttp_disable_grpc"); // bool
constexpr QLatin1String xhttpDisableSse("xhttp_disable_sse"); // bool
// Transport — XHTTP Session & Sequence
constexpr QLatin1String xhttpSessionPlacement("xhttp_session_placement"); // Path | Header | Cookie | None
constexpr QLatin1String xhttpSessionKey("xhttp_session_key");
constexpr QLatin1String xhttpSeqPlacement("xhttp_seq_placement");
constexpr QLatin1String xhttpSeqKey("xhttp_seq_key");
constexpr QLatin1String xhttpUplinkDataPlacement("xhttp_uplink_data_placement"); // Body | Query
constexpr QLatin1String xhttpUplinkDataKey("xhttp_uplink_data_key");
// Transport — XHTTP Traffic Shaping
constexpr QLatin1String xhttpUplinkChunkSize("xhttp_uplink_chunk_size");
constexpr QLatin1String xhttpScMaxBufferedPosts("xhttp_sc_max_buffered_posts");
constexpr QLatin1String xhttpScMaxEachPostBytesMin("xhttp_sc_max_each_post_bytes_min");
constexpr QLatin1String xhttpScMaxEachPostBytesMax("xhttp_sc_max_each_post_bytes_max");
constexpr QLatin1String xhttpScMinPostsIntervalMsMin("xhttp_sc_min_posts_interval_ms_min");
constexpr QLatin1String xhttpScMinPostsIntervalMsMax("xhttp_sc_min_posts_interval_ms_max");
constexpr QLatin1String xhttpScStreamUpServerSecsMin("xhttp_sc_stream_up_server_secs_min");
constexpr QLatin1String xhttpScStreamUpServerSecsMax("xhttp_sc_stream_up_server_secs_max");
// Transport — mKCP
constexpr QLatin1String mkcpTti("mkcp_tti");
constexpr QLatin1String mkcpUplinkCapacity("mkcp_uplink_capacity");
constexpr QLatin1String mkcpDownlinkCapacity("mkcp_downlink_capacity");
constexpr QLatin1String mkcpReadBufferSize("mkcp_read_buffer_size");
constexpr QLatin1String mkcpWriteBufferSize("mkcp_write_buffer_size");
constexpr QLatin1String mkcpCongestion("mkcp_congestion"); // bool
// xPadding
constexpr QLatin1String xPaddingBytesMin("xpadding_bytes_min");
constexpr QLatin1String xPaddingBytesMax("xpadding_bytes_max");
constexpr QLatin1String xPaddingObfsMode("xpadding_obfs_mode"); // bool
constexpr QLatin1String xPaddingKey("xpadding_key");
constexpr QLatin1String xPaddingHeader("xpadding_header");
constexpr QLatin1String xPaddingPlacement("xpadding_placement"); // Cookie | Header | Query | Body
constexpr QLatin1String xPaddingMethod("xpadding_method"); // Repeat-x | Random | Zero
// xmux
constexpr QLatin1String xmuxEnabled("xmux_enabled"); // bool
constexpr QLatin1String xmuxMaxConcurrencyMin("xmux_max_concurrency_min");
constexpr QLatin1String xmuxMaxConcurrencyMax("xmux_max_concurrency_max");
constexpr QLatin1String xmuxMaxConnectionsMin("xmux_max_connections_min");
constexpr QLatin1String xmuxMaxConnectionsMax("xmux_max_connections_max");
constexpr QLatin1String xmuxCMaxReuseTimesMin("xmux_c_max_reuse_times_min");
constexpr QLatin1String xmuxCMaxReuseTimesMax("xmux_c_max_reuse_times_max");
constexpr QLatin1String xmuxHMaxRequestTimesMin("xmux_h_max_request_times_min");
constexpr QLatin1String xmuxHMaxRequestTimesMax("xmux_h_max_request_times_max");
constexpr QLatin1String xmuxHMaxReusableSecsMin("xmux_h_max_reusable_secs_min");
constexpr QLatin1String xmuxHMaxReusableSecsMax("xmux_h_max_reusable_secs_max");
constexpr QLatin1String xmuxHKeepAlivePeriod("xmux_h_keep_alive_period");
}
}