feat: Add http headers and method &&

Fix missing reality SID &&
Fix minor UI issues
This commit is contained in:
unknown 2024-08-02 15:54:12 +03:30
parent 5d5cf3b81f
commit 40fbff7826
No known key found for this signature in database
GPG Key ID: C2CA486E4F771093
9 changed files with 214 additions and 147 deletions

View File

@ -19,6 +19,12 @@ namespace NekoGui_fmt {
transport["early_data_header_name"] = "Sec-WebSocket-Protocol";
}
}
bool ok;
auto headerMap = GetHeaderPairs(&ok);
if (!ok) {
MW_show_log("Warning: headers could not be parsed, they will not be used");
}
transport["headers"] = QMapString2QJsonObject(headerMap);
if (ws_early_data_length > 0) {
transport["max_early_data"] = ws_early_data_length;
transport["early_data_header_name"] = ws_early_data_name;
@ -26,22 +32,26 @@ namespace NekoGui_fmt {
} else if (network == "http") {
if (!path.isEmpty()) transport["path"] = path;
if (!host.isEmpty()) transport["host"] = QListStr2QJsonArray(host.split(","));
if (!method.isEmpty()) transport["method"] = method.toUpper();
bool ok;
auto headerMap = GetHeaderPairs(&ok);
if (!ok) {
MW_show_log("Warning: headers could not be parsed, they will not be used");
}
transport["headers"] = QMapString2QJsonObject(headerMap);
} else if (network == "grpc") {
if (!path.isEmpty()) transport["service_name"] = path;
} else if (network == "httpupgrade") {
if (!path.isEmpty()) transport["path"] = path;
if (!host.isEmpty()) transport["host"] = host;
bool ok;
auto headerMap = GetHeaderPairs(&ok);
if (!ok) {
MW_show_log("Warning: headers could not be parsed, they will not be used");
}
transport["headers"] = QMapString2QJsonObject(headerMap);
}
outbound->insert("transport", transport);
} else if (header_type == "http") {
// TCP + headerType
QJsonObject transport{
{"type", "http"},
{"method", "GET"},
{"path", path},
{"headers", QJsonObject{{"Host", QListStr2QJsonArray(host.split(","))}}},
};
outbound->insert("transport", transport);
}
// 对应字段 tls

View File

@ -51,17 +51,15 @@ namespace NekoGui_fmt {
// type
query.addQueryItem("type", stream->network);
if (stream->network == "ws" || stream->network == "http" || stream->network == "httpupgrade") {
if (stream->network == "ws" || stream->network == "httpupgrade") {
if (!stream->path.isEmpty()) query.addQueryItem("path", stream->path);
if (!stream->host.isEmpty()) query.addQueryItem("host", stream->host);
} else if (stream->network == "http" ) {
if (!stream->path.isEmpty()) query.addQueryItem("path", stream->path);
if (!stream->host.isEmpty()) query.addQueryItem("host", stream->host);
if (!stream->method.isEmpty()) query.addQueryItem("method", stream->method);
} else if (stream->network == "grpc") {
if (!stream->path.isEmpty()) query.addQueryItem("serviceName", stream->path);
} else if (stream->network == "tcp") {
if (stream->header_type == "http") {
if (!stream->path.isEmpty()) query.addQueryItem("path", stream->path);
query.addQueryItem("headerType", "http");
query.addQueryItem("host", stream->host);
}
}
// mux
@ -158,11 +156,6 @@ namespace NekoGui_fmt {
if (!stream->host.isEmpty()) query.addQueryItem("host", stream->host);
} else if (stream->network == "grpc") {
if (!stream->path.isEmpty()) query.addQueryItem("serviceName", stream->path);
} else if (stream->network == "tcp") {
if (stream->header_type == "http") {
query.addQueryItem("headerType", "http");
query.addQueryItem("host", stream->host);
}
}
// mux

View File

@ -87,17 +87,12 @@ namespace NekoGui_fmt {
} else if (stream->network == "http") {
stream->path = GetQueryValue(query, "path", "");
stream->host = GetQueryValue(query, "host", "").replace("|", ",");
stream->method = GetQueryValue(query, "method", "");
} else if (stream->network == "httpupgrade") {
stream->path = GetQueryValue(query, "path", "");
stream->host = GetQueryValue(query, "host", "");
} else if (stream->network == "grpc") {
stream->path = GetQueryValue(query, "serviceName", "");
} else if (stream->network == "tcp") {
if (GetQueryValue(query, "headerType") == "http") {
stream->header_type = "http";
stream->host = GetQueryValue(query, "host", "");
stream->path = GetQueryValue(query, "path", "");
}
}
// mux
@ -176,7 +171,6 @@ namespace NekoGui_fmt {
stream->host = objN["host"].toString();
stream->path = objN["path"].toString();
stream->sni = objN["sni"].toString();
stream->header_type = objN["type"].toString();
auto net = objN["net"].toString();
if (!net.isEmpty()) {
if (net == "h2") {
@ -245,12 +239,6 @@ namespace NekoGui_fmt {
stream->host = GetQueryValue(query, "host", "");
} else if (stream->network == "grpc") {
stream->path = GetQueryValue(query, "serviceName", "");
} else if (stream->network == "tcp") {
if (GetQueryValue(query, "headerType") == "http") {
stream->header_type = "http";
stream->path = GetQueryValue(query, "path", "");
stream->host = GetQueryValue(query, "host", "");
}
}
return !(uuid.isEmpty() || serverAddress.isEmpty());
}

View File

@ -8,12 +8,12 @@ namespace NekoGui_fmt {
QString network = "tcp";
QString security = "";
QString packet_encoding = "";
// ws/http/grpc/tcp-http/httpupgrade
QString path = "";
QString host = "";
// kcp/quic/tcp-http
QString header_type = "";
// tls
QString method = "";
QString headers = "";
QString sni = "";
QString alpn = "";
QString certificate = "";
@ -37,7 +37,8 @@ namespace NekoGui_fmt {
_add(new configItem("alpn", &alpn, itemType::string));
_add(new configItem("cert", &certificate, itemType::string));
_add(new configItem("insecure", &allow_insecure, itemType::boolean));
_add(new configItem("h_type", &header_type, itemType::string));
_add(new configItem("headers", &headers, itemType::string));
_add(new configItem("method", &method, itemType::string));
_add(new configItem("ed_name", &ws_early_data_name, itemType::string));
_add(new configItem("ed_len", &ws_early_data_length, itemType::integer));
_add(new configItem("utls", &utlsFingerprint, itemType::string));
@ -47,6 +48,56 @@ namespace NekoGui_fmt {
}
void BuildStreamSettingsSingBox(QJsonObject *outbound);
QMap<QString, QString> GetHeaderPairs(bool* ok) {
bool inQuote = false;
QString curr;
QStringList list;
for (const auto &ch: headers) {
if (inQuote) {
if (ch == '"') {
inQuote = false;
list << curr;
curr = "";
continue;
} else {
curr += ch;
continue;
}
}
if (ch == '"') {
inQuote = true;
continue;
}
if (ch == ' ') {
if (!curr.isEmpty()) {
list << curr;
curr = "";
}
continue;
}
if (ch == '=') {
if (!curr.isEmpty()) {
list << curr;
curr = "";
}
continue;
}
curr+=ch;
}
if (!curr.isEmpty()) list<<curr;
if (list.size()%2 == 1) {
*ok = false;
return {};
}
QMap<QString,QString> res;
for (int i = 0; i < list.size(); i+=2) {
res[list[i]] = list[i + 1];
}
*ok = true;
return res;
}
};
inline V2rayStreamSettings *GetStreamSettings(AbstractBean *bean) {

View File

@ -147,6 +147,15 @@ QJsonArray QString2QJsonArray(const QString& str) {
return {};
}
QJsonObject QMapString2QJsonObject(const QMap<QString,QString> &mp) {
QJsonObject res;
for (const auto &key: mp.keys()) {
res.insert(key, mp[key]);
}
return res;
}
QByteArray ReadFile(const QString &path) {
QFile file(path);
file.open(QFile::ReadOnly);

View File

@ -89,6 +89,8 @@ QJsonArray QListStr2QJsonArray(const QList<QString> &list);
QList<int> QJsonArray2QListInt(const QJsonArray &arr);
QJsonObject QMapString2QJsonObject(const QMap<QString,QString> &mp);
#define QJSONARRAY_ADD(arr, add) \
for (const auto &a: (add)) { \
(arr) += a; \

View File

@ -451,24 +451,6 @@ namespace NekoGui_sub {
}
bean->stream->path = Node2QString(h2["path"]);
}
auto tcp_http = NodeChild(proxy, {"http-opts", "http-opt"});
if (tcp_http.IsMap()) {
bean->stream->network = "tcp";
bean->stream->header_type = "http";
auto headers = tcp_http["headers"];
for (auto header: headers) {
if (Node2QString(header.first).toLower() == "host") {
bean->stream->host = Node2QString(header.second[0]);
}
break;
}
auto paths = tcp_http["path"];
for (auto path: paths) {
bean->stream->path = Node2QString(path);
break;
}
}
} else if (type == "hysteria") {
auto bean = ent->QUICBean();

View File

@ -31,36 +31,43 @@ DialogEditProfile::DialogEditProfile(const QString &_type, int profileOrGroupId,
network_title_base = ui->network_box->title();
connect(ui->network, &QComboBox::currentTextChanged, this, [=](const QString &txt) {
ui->network_box->setTitle(network_title_base.arg(txt));
if (txt == "tcp") {
ui->header_type->setVisible(true);
ui->header_type_l->setVisible(true);
ui->path->setVisible(true);
ui->path_l->setVisible(true);
ui->host->setVisible(true);
ui->host_l->setVisible(true);
} else if (txt == "grpc") {
ui->header_type->setVisible(false);
ui->header_type_l->setVisible(false);
if (txt == "grpc") {
ui->headers->setVisible(false);
ui->headers_l->setVisible(false);
ui->method->setVisible(false);
ui->method_l->setVisible(false);
ui->path->setVisible(true);
ui->path_l->setVisible(true);
ui->host->setVisible(false);
ui->host_l->setVisible(false);
} else if (txt == "ws" || txt == "http" || txt == "httpupgrade") {
ui->header_type->setVisible(false);
ui->header_type_l->setVisible(false);
} else if (txt == "ws" || txt == "httpupgrade") {
ui->headers->setVisible(true);
ui->headers_l->setVisible(true);
ui->method->setVisible(false);
ui->method_l->setVisible(false);
ui->path->setVisible(true);
ui->path_l->setVisible(true);
ui->host->setVisible(true);
ui->host_l->setVisible(true);
} else if (txt == "http") {
ui->headers->setVisible(true);
ui->headers_l->setVisible(true);
ui->method->setVisible(true);
ui->method_l->setVisible(true);
ui->path->setVisible(true);
ui->path_l->setVisible(true);
ui->host->setVisible(true);
ui->host_l->setVisible(true);
} else {
ui->header_type->setVisible(false);
ui->header_type_l->setVisible(false);
ui->headers->setVisible(false);
ui->headers_l->setVisible(false);
ui->method->setVisible(false);
ui->method_l->setVisible(false);
ui->path->setVisible(false);
ui->path_l->setVisible(false);
ui->host->setVisible(false);
ui->host_l->setVisible(false);
}
// 传输设置 ED
if (txt == "ws") {
ui->ws_early_data_length->setVisible(true);
ui->ws_early_data_length_l->setVisible(true);
@ -73,7 +80,6 @@ DialogEditProfile::DialogEditProfile(const QString &_type, int profileOrGroupId,
ui->ws_early_data_name_l->setVisible(false);
}
if (!ui->utlsFingerprint->count()) ui->utlsFingerprint->addItems(Preset::SingBox::UtlsFingerPrint);
// 传输设置 是否可见
int networkBoxVisible = 0;
for (auto label: ui->network_box->findChildren<QLabel *>()) {
if (!label->isHidden()) networkBoxVisible++;
@ -88,6 +94,17 @@ DialogEditProfile::DialogEditProfile(const QString &_type, int profileOrGroupId,
if (txt == "tls") {
ui->security_box->setVisible(true);
ui->tls_camouflage_box->setVisible(true);
ui->reality_pbk->setVisible(false);
ui->reality_pbk_l->setVisible(false);
ui->reality_sid->setVisible(false);
ui->reality_sid_l->setVisible(false);
} else if (txt == "reality") {
ui->security_box->setVisible(true);
ui->tls_camouflage_box->setVisible(true);
ui->reality_pbk->setVisible(true);
ui->reality_pbk_l->setVisible(true);
ui->reality_sid->setVisible(true);
ui->reality_sid_l->setVisible(true);
} else {
ui->security_box->setVisible(false);
ui->tls_camouflage_box->setVisible(false);
@ -213,12 +230,14 @@ void DialogEditProfile::typeSelected(const QString &newType) {
// 右边 stream
auto stream = GetStreamSettings(ent->bean.get());
if (stream != nullptr) {
ui->network_box->setVisible(stream->network != "tcp");
ui->right_all_w->setVisible(true);
ui->network->setCurrentText(stream->network);
ui->security->setCurrentText(stream->security);
ui->packet_encoding->setCurrentText(stream->packet_encoding);
ui->path->setText(stream->path);
ui->host->setText(stream->host);
ui->method->setText(stream->method);
ui->sni->setText(stream->sni);
ui->alpn->setText(stream->alpn);
if (newEnt) {
@ -227,10 +246,11 @@ void DialogEditProfile::typeSelected(const QString &newType) {
ui->utlsFingerprint->setCurrentText(stream->utlsFingerprint);
}
ui->insecure->setChecked(stream->allow_insecure);
ui->header_type->setCurrentText(stream->header_type);
ui->headers->setText(stream->headers);
ui->ws_early_data_name->setText(stream->ws_early_data_name);
ui->ws_early_data_length->setText(Int2String(stream->ws_early_data_length));
ui->reality_pbk->setText(stream->reality_pbk);
ui->reality_sid->setText(stream->reality_sid);
ui->multiplex->setCurrentIndex(ent->bean->mux_state);
ui->brutal_enable->setCheckState(ent->bean->enable_brutal ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
ui->brutal_speed->setText(Int2String(ent->bean->brutal_speed));
@ -292,11 +312,9 @@ void DialogEditProfile::typeSelected(const QString &newType) {
if (type == "vmess" || type == "vless" || type == "trojan") {
ui->network_l->setVisible(true);
ui->network->setVisible(true);
ui->network_box->setVisible(true);
} else {
ui->network_l->setVisible(false);
ui->network->setVisible(false);
ui->network_box->setVisible(false);
}
if (type == "vmess" || type == "vless" || type == "trojan" || type == "http") {
ui->security->setVisible(true);
@ -314,14 +332,12 @@ void DialogEditProfile::typeSelected(const QString &newType) {
ui->multiplex_l->setVisible(false);
ui->brutal_box->setVisible(false);
}
// 设置 是否可见
int streamBoxVisible = 0;
for (auto label: ui->stream_box->findChildren<QLabel *>()) {
if (!label->isHidden()) streamBoxVisible++;
if (!label->isHidden() && label->parent() == ui->stream_box) streamBoxVisible++;
}
ui->stream_box->setVisible(streamBoxVisible);
// 载入 type 之后,有些类型没有右边的设置
auto rightNoBox = (ui->stream_box->isHidden() && ui->network_box->isHidden() && ui->security_box->isHidden());
if (rightNoBox && !ui->right_all_w->isHidden()) {
ui->right_all_w->setVisible(false);
@ -359,14 +375,23 @@ bool DialogEditProfile::onEnd() {
stream->alpn = ui->alpn->text();
stream->utlsFingerprint = ui->utlsFingerprint->currentText();
stream->allow_insecure = ui->insecure->isChecked();
stream->header_type = ui->header_type->currentText();
stream->headers = ui->headers->text();
stream->method = ui->method->text();
stream->ws_early_data_name = ui->ws_early_data_name->text();
stream->ws_early_data_length = ui->ws_early_data_length->text().toInt();
stream->reality_pbk = ui->reality_pbk->text();
stream->reality_sid = ui->reality_sid->text();
ent->bean->mux_state = ui->multiplex->currentIndex();
ent->bean->enable_brutal = ui->brutal_enable->isChecked();
ent->bean->brutal_speed = ui->brutal_speed->text().toInt();
stream->certificate = CACHE.certificate;
bool validHeaders;
stream->GetHeaderPairs(&validHeaders);
if (!validHeaders) {
MW_show_log("Headers are not valid");
return false;
}
}
// cached custom

View File

@ -36,7 +36,7 @@
</property>
<layout class="QVBoxLayout" name="left">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
<enum>QLayout::SizeConstraint::SetDefaultConstraint</enum>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
@ -130,7 +130,7 @@
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
<enum>Qt::Orientation::Vertical</enum>
</property>
</widget>
</item>
@ -270,10 +270,10 @@
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
</property>
</widget>
</item>
@ -301,7 +301,7 @@
</property>
<layout class="QVBoxLayout" name="right_layout">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
<enum>QLayout::SizeConstraint::SetDefaultConstraint</enum>
</property>
<item>
<widget class="QGroupBox" name="stream_box">
@ -429,6 +429,11 @@
<string notr="true">tls</string>
</property>
</item>
<item>
<property name="text">
<string>reality</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0">
@ -478,7 +483,7 @@
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
<enum>Qt::Orientation::Vertical</enum>
</property>
</widget>
</item>
@ -520,7 +525,37 @@
<string>Network Settings (%1)</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<item row="3" column="1">
<widget class="MyLineEdit" name="host"/>
</item>
<item row="4" column="1">
<widget class="MyLineEdit" name="ws_early_data_length"/>
</item>
<item row="5" column="1">
<widget class="MyLineEdit" name="ws_early_data_name"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="host_l">
<property name="toolTip">
<string notr="true">http host (ws/http/伪装http)
security (QUIC)</string>
</property>
<property name="text">
<string notr="true">Host</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="headers"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="ws_early_data_length_l">
<property name="text">
<string notr="true">EarlyData Length</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="path_l">
<property name="toolTip">
<string notr="true">http path (ws/http/伪装http)
@ -532,22 +567,18 @@ key (QUIC)</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="MyLineEdit" name="host"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="host_l">
<property name="toolTip">
<string notr="true">http host (ws/http/伪装http)
security (QUIC)</string>
</property>
<item row="5" column="0">
<widget class="QLabel" name="ws_early_data_name_l">
<property name="text">
<string notr="true">Host</string>
<string notr="true">EarlyData Name</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="MyLineEdit" name="path"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="header_type_l">
<widget class="QLabel" name="headers_l">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
@ -557,56 +588,23 @@ security (QUIC)</string>
<property name="toolTip">
<string notr="true">伪装头部类型 (tcp/quic)</string>
</property>
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Headers in format:&lt;/p&gt;&lt;p&gt;key1=val1 key2=&amp;quot;multi word val2&amp;quot; &amp;quot;multi word key3&amp;quot;=val3&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string notr="true">header</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="method_l">
<property name="text">
<string>Method</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="MyLineEdit" name="path"/>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="header_type">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="editable">
<bool>true</bool>
</property>
<item>
<property name="text">
<string notr="true"/>
</property>
</item>
<item>
<property name="text">
<string notr="true">http</string>
</property>
</item>
</widget>
</item>
<item row="3" column="1">
<widget class="MyLineEdit" name="ws_early_data_length"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="ws_early_data_length_l">
<property name="text">
<string notr="true">EarlyData Length</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="MyLineEdit" name="ws_early_data_name"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="ws_early_data_name_l">
<property name="text">
<string notr="true">EarlyData Name</string>
</property>
</widget>
<widget class="QLineEdit" name="method"/>
</item>
</layout>
</widget>
@ -617,6 +615,9 @@ security (QUIC)</string>
<string>TLS Camouflage Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="1" column="1">
<widget class="MyLineEdit" name="reality_pbk"/>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="utlsFingerprint">
<property name="editable">
@ -624,9 +625,6 @@ security (QUIC)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="MyLineEdit" name="reality_pbk"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="reality_pbk_l">
<property name="toolTip">
@ -637,6 +635,13 @@ security (QUIC)</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="reality_sid_l">
<property name="text">
<string>Reality SID</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
@ -650,6 +655,9 @@ security (QUIC)</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="reality_sid"/>
</item>
</layout>
</widget>
</item>
@ -677,7 +685,6 @@ security (QUIC)</string>
<tabstop>security</tabstop>
<tabstop>packet_encoding</tabstop>
<tabstop>multiplex</tabstop>
<tabstop>header_type</tabstop>
<tabstop>path</tabstop>
<tabstop>host</tabstop>
<tabstop>ws_early_data_length</tabstop>