refactor: merge hysteria2 into hysteria outbound

This commit is contained in:
parhelia512 2025-11-20 09:26:06 +08:00
parent 0599f8640d
commit b598959742
15 changed files with 294 additions and 512 deletions

View File

@ -158,9 +158,6 @@ set(PROJECT_SOURCES
include/ui/profile/edit_hysteria.h
src/ui/profile/edit_hysteria.cpp
include/ui/profile/edit_hysteria.ui
include/ui/profile/edit_hysteria2.h
src/ui/profile/edit_hysteria2.cpp
include/ui/profile/edit_hysteria2.ui
include/ui/profile/edit_tuic.h
src/ui/profile/edit_tuic.cpp
include/ui/profile/edit_tuic.ui
@ -252,7 +249,6 @@ set(PROJECT_SOURCES
include/configs/outbounds/hysteria.h
include/configs/outbounds/vless.h
include/configs/outbounds/tuic.h
include/configs/outbounds/hysteria2.h
include/configs/outbounds/anyTLS.h
include/configs/outbounds/ssh.h
@ -260,7 +256,6 @@ set(PROJECT_SOURCES
src/configs/outbounds/anyTLS.cpp
src/configs/outbounds/http.cpp
src/configs/outbounds/hysteria.cpp
src/configs/outbounds/hysteria2.cpp
src/configs/outbounds/shadowsocks.cpp
src/configs/outbounds/socks.cpp
src/configs/outbounds/ssh.cpp
@ -292,9 +287,6 @@ set(PROJECT_SOURCES
include/ui/profile/edit_tuic.h
src/ui/profile/edit_tuic.cpp
include/ui/profile/edit_tuic.ui
include/ui/profile/edit_hysteria2.h
src/ui/profile/edit_hysteria2.cpp
include/ui/profile/edit_hysteria2.ui
)
if (NOT APPLE AND Qt6_VERSION VERSION_GREATER_EQUAL 6.9.0)

View File

@ -7,30 +7,44 @@ namespace Configs
class hysteria : public outbound
{
public:
QString protocol_version = "1";
QStringList server_ports;
QString hop_interval;
int up_mbps = 0;
int down_mbps = 0;
QString obfs;
// Hysteria1
QString auth_type;
QString auth;
QString auth_str;
int recv_window_conn = 0;
int recv_window = 0;
bool disable_mtu_discovery = false;
// Hysteria2
QString password;
std::shared_ptr<TLS> tls = std::make_shared<TLS>();
hysteria() : outbound()
{
_add(new configItem("protocol_version", &protocol_version, string));
_add(new configItem("server_ports", &server_ports, stringList));
_add(new configItem("hop_interval", &hop_interval, string));
_add(new configItem("up_mbps", &up_mbps, integer));
_add(new configItem("down_mbps", &down_mbps, integer));
_add(new configItem("obfs", &obfs, string));
// Hysteria1
_add(new configItem("auth_type", &auth_type, string));
_add(new configItem("auth", &auth, string));
_add(new configItem("auth_str", &auth_str, string));
_add(new configItem("recv_window_conn", &recv_window_conn, integer));
_add(new configItem("recv_window", &recv_window, integer));
_add(new configItem("disable_mtu_discovery", &disable_mtu_discovery, boolean));
// Hysteria2
_add(new configItem("password", &password, string));
_add(new configItem("tls", dynamic_cast<JsonStore *>(tls.get()), jsonStore));
}

View File

@ -1,50 +0,0 @@
#pragma once
#include "include/configs/common/Outbound.h"
#include "include/configs/common/TLS.h"
namespace Configs
{
class hysteria2 : public outbound
{
public:
QStringList server_ports;
QString hop_interval;
int up_mbps = 0;
int down_mbps = 0;
QString obfsPassword;
QString password;
std::shared_ptr<TLS> tls = std::make_shared<TLS>();
hysteria2() : outbound()
{
_add(new configItem("server_ports", &server_ports, stringList));
_add(new configItem("hop_interval", &hop_interval, string));
_add(new configItem("up_mbps", &up_mbps, integer));
_add(new configItem("down_mbps", &down_mbps, integer));
_add(new configItem("obfsPassword", &obfsPassword, string));
_add(new configItem("password", &password, string));
_add(new configItem("tls", dynamic_cast<JsonStore *>(tls.get()), jsonStore));
}
bool HasTLS() override {
return true;
}
bool MustTLS() override {
return true;
}
std::shared_ptr<TLS> GetTLS() override {
return tls;
}
// baseConfig overrides
bool ParseFromLink(const QString& link) override;
bool ParseFromJson(const QJsonObject& object) override;
QString ExportToLink() override;
QJsonObject ExportToJson() override;
BuildResult Build() override;
QString DisplayType() override;
};
}

View File

@ -11,7 +11,6 @@
#include "include/configs/outbounds/socks.h"
#include "include/configs/outbounds/http.h"
#include "include/configs/outbounds/hysteria.h"
#include "include/configs/outbounds/hysteria2.h"
#include "include/configs/outbounds/shadowsocks.h"
#include "include/configs/outbounds/ssh.h"
#include "include/configs/outbounds/trojan.h"
@ -80,10 +79,6 @@ namespace Configs {
return dynamic_cast<Configs::hysteria *>(outbound.get());
};
[[nodiscard]] Configs::hysteria2 *Hysteria2() const {
return dynamic_cast<Configs::hysteria2 *>(outbound.get());
};
[[nodiscard]] Configs::ssh *SSH() const {
return dynamic_cast<Configs::ssh *>(outbound.get());
};

View File

@ -21,8 +21,8 @@ public:
bool onEnd() override;
private:
Ui::EditHysteria *ui;
private:
std::shared_ptr<Configs::ProxyEntity> ent;
};

View File

@ -14,103 +14,150 @@
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="3" column="0">
<item row="0" column="0">
<widget class="QLabel" name="protocol_version_l">
<property name="text">
<string>Protocol Version</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="protocol_version">
<item>
<property name="text">
<string notr="true">1</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">2</string>
</property>
</item>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Down Mbps</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLineEdit" name="auth_str"/>
</item>
<item row="5" column="1">
<item row="7" column="1">
<widget class="QLineEdit" name="auth"/>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_8">
<item row="6" column="1">
<widget class="QComboBox" name="auth_type">
<item>
<property name="text">
<string notr="true"/>
</property>
</item>
<item>
<property name="text">
<string notr="true">STRING</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">BASE64</string>
</property>
</item>
</widget>
</item>
<item row="8" column="0">
<widget class="QLabel" name="recv_window_conn_l">
<property name="text">
<string>Recv window conn</string>
</property>
</widget>
</item>
<item row="1" column="0">
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Hop Interval</string>
</property>
</widget>
</item>
<item row="4" column="1">
<item row="5" column="1">
<widget class="QLineEdit" name="obfs"/>
</item>
<item row="0" column="0">
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Server Ports</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_6">
<item row="6" column="0">
<widget class="QLabel" name="auth_type_l">
<property name="text">
<string>Auth</string>
<string>Authentication Type</string>
</property>
</widget>
</item>
<item row="3" column="1">
<item row="4" column="1">
<widget class="QLineEdit" name="down_mbps"/>
</item>
<item row="2" column="0">
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Up Mbps</string>
</property>
</widget>
</item>
<item row="0" column="1">
<item row="1" column="1">
<widget class="QLineEdit" name="server_ports"/>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_7">
<item row="7" column="0">
<widget class="QLabel" name="auth_l">
<property name="text">
<string>Auth Str</string>
<string>Authentication Payload</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="hop_interval"/>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="hop_interval"/>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="up_mbps"/>
</item>
<item row="7" column="1">
<item row="8" column="1">
<widget class="QLineEdit" name="recv_window_conn"/>
</item>
<item row="4" column="0">
<item row="5" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Obfs</string>
<string>Obfuscation Password</string>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QLabel" name="label_9">
<item row="9" column="0">
<widget class="QLabel" name="recv_window_l">
<property name="text">
<string>Recv window</string>
</property>
</widget>
</item>
<item row="8" column="1">
<item row="9" column="1">
<widget class="QLineEdit" name="recv_window"/>
</item>
<item row="9" column="1">
<item row="10" column="1">
<widget class="QCheckBox" name="disable_mtu_discovery">
<property name="text">
<string>Disable Mtu Discovery</string>
</property>
</widget>
</item>
<item row="11" column="0">
<widget class="QLabel" name="password_l">
<property name="text">
<string>Password</string>
</property>
</widget>
</item>
<item row="11" column="1">
<widget class="QLineEdit" name="password"/>
</item>
</layout>
</widget>
<resources/>

View File

@ -1,29 +0,0 @@
#ifndef EDIT_HYSTERIA2_H
#define EDIT_HYSTERIA2_H
#include <QWidget>
#include "profile_editor.h"
#include "ui_edit_hysteria2.h"
namespace Ui {
class EditHysteria2;
}
class EditHysteria2 : public QWidget, public ProfileEditor {
Q_OBJECT
public:
explicit EditHysteria2(QWidget *parent = nullptr);
~EditHysteria2() override;
void onStart(std::shared_ptr<Configs::ProxyEntity> _ent) override;
bool onEnd() override;
private:
Ui::EditHysteria2 *ui;
std::shared_ptr<Configs::ProxyEntity> ent;
};
#endif // EDIT_HYSTERIA2_H

View File

@ -1,81 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EditHysteria2</class>
<widget class="QWidget" name="EditHysteria2">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="1">
<widget class="QLineEdit" name="up_mbps"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Server Ports</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="hop_interval"/>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Password</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Down Mbps</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Obfs Password</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="obfs_password"/>
</item>
<item row="5" column="1">
<widget class="QLineEdit" name="password"/>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="down_mbps"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Hop Interval</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Up Mbps</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="server_ports"/>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -25,13 +25,30 @@ namespace Configs {
outbound::ParseFromLink(link);
if (url.scheme() == "hysteria") {
protocol_version = "1";
if (query.hasQueryItem("obfsParam")) obfs = QUrl::fromPercentEncoding(query.queryItemValue("obfsParam").toUtf8());
if (query.hasQueryItem("auth")) {
auth = query.queryItemValue("auth");
auth_type = "STRING";
}
if (query.hasQueryItem("recv_window_conn")) recv_window_conn = query.queryItemValue("recv_window_conn").toInt();
if (query.hasQueryItem("recv_window")) recv_window = query.queryItemValue("recv_window").toInt();
if (query.hasQueryItem("disable_mtu_discovery")) disable_mtu_discovery = query.queryItemValue("disable_mtu_discovery") == "true";
} else {
protocol_version = "2";
if (url.password().isEmpty()) {
password = url.userName();
} else {
password = url.userName() + ":" + url.password();
}
if (query.hasQueryItem("obfs-password")) {
obfs = QUrl::fromPercentEncoding(query.queryItemValue("obfs-password").toUtf8());
}
}
if (query.hasQueryItem("upmbps")) up_mbps = query.queryItemValue("upmbps").toInt();
if (query.hasQueryItem("downmbps")) down_mbps = query.queryItemValue("downmbps").toInt();
if (query.hasQueryItem("obfsParam")) obfs = QUrl::fromPercentEncoding(query.queryItemValue("obfsParam").toUtf8());
if (query.hasQueryItem("auth")) auth_str = query.queryItemValue("auth");
if (query.hasQueryItem("recv_window_conn")) recv_window_conn = query.queryItemValue("recv_window_conn").toInt();
if (query.hasQueryItem("recv_window")) recv_window = query.queryItemValue("recv_window").toInt();
if (query.hasQueryItem("disable_mtu_discovery")) disable_mtu_discovery = query.queryItemValue("disable_mtu_discovery") == "true";
if (query.hasQueryItem("hop_interval")) hop_interval = query.queryItemValue("hop_interval");
if (query.hasQueryItem("mport")) {
server_ports = query.queryItemValue("mport").split(",");
@ -49,7 +66,14 @@ namespace Configs {
bool hysteria::ParseFromJson(const QJsonObject& object)
{
if (object.isEmpty() || object["type"].toString() != "hysteria") return false;
if (object.isEmpty()) return false;
if (object["type"].toString() == "hysteria") {
protocol_version = "1";
} else if (object["type"].toString() == "hysteria2") {
protocol_version = "2";
} else {
return false;
}
outbound::ParseFromJson(object);
if (object.contains("server_ports")) {
server_ports = QJsonArray2QListString(object["server_ports"].toArray());
@ -57,13 +81,27 @@ namespace Configs {
if (object.contains("hop_interval")) hop_interval = object["hop_interval"].toString();
if (object.contains("up_mbps")) up_mbps = object["up_mbps"].toInt();
if (object.contains("down_mbps")) down_mbps = object["down_mbps"].toInt();
if (object.contains("obfs")) obfs = object["obfs"].toString();
if (object.contains("auth")) auth = object["auth"].toString();
if (object.contains("auth_str")) auth_str = object["auth_str"].toString();
if (object.contains("recv_window_conn")) recv_window_conn = object["recv_window_conn"].toInt();
if (object.contains("recv_window")) recv_window = object["recv_window"].toInt();
if (object.contains("disable_mtu_discovery")) disable_mtu_discovery = object["disable_mtu_discovery"].toBool();
if (object.contains("tls")) tls->ParseFromJson(object["tls"].toObject());
if (protocol_version == "1") {
if (object.contains("obfs")) obfs = object["obfs"].toString();
if (object.contains("auth")) {
auth = object["auth"].toString();
auth_type = "BASE64";
}
if (object.contains("auth_str")) {
auth = object["auth_str"].toString();
auth_type = "STRING";
}
if (object.contains("recv_window_conn")) recv_window_conn = object["recv_window_conn"].toInt();
if (object.contains("recv_window")) recv_window = object["recv_window"].toInt();
if (object.contains("disable_mtu_discovery")) disable_mtu_discovery = object["disable_mtu_discovery"].toBool();
if (object.contains("tls")) tls->ParseFromJson(object["tls"].toObject());
} else {
if (object.contains("obfs")) {
auto obfsObj = object["obfs"].toObject();
if (obfsObj.contains("password")) obfs = obfsObj["password"].toString();
}
if (object.contains("obfsPassword")) obfs = object["obfsPassword"].toString();
}
return true;
}
@ -71,7 +109,7 @@ namespace Configs {
{
QUrl url;
QUrlQuery query;
url.setScheme("hysteria");
url.setScheme(protocol_version == "1" ? "hysteria" : "hysteria2");
url.setHost(server);
url.setPort(0);
@ -89,16 +127,30 @@ namespace Configs {
}
if (!name.isEmpty()) url.setFragment(name);
if (protocol_version == "1") {
if (!obfs.isEmpty()) {
query.addQueryItem("obfsParam", QUrl::toPercentEncoding(obfs));
}
if (auth_type == "STRING" && !auth.isEmpty()) query.addQueryItem("auth", auth);
if (recv_window_conn > 0) query.addQueryItem("recv_window_conn", QString::number(recv_window_conn));
if (recv_window > 0) query.addQueryItem("recv_window", QString::number(recv_window));
if (disable_mtu_discovery) query.addQueryItem("disable_mtu_discovery", "true");
} else {
if (password.contains(":")) {
url.setUserName(SubStrBefore(password, ":"));
url.setPassword(SubStrAfter(password, ":"));
} else {
url.setUserName(password);
}
if (!obfs.isEmpty()) {
query.addQueryItem("obfs-password", QUrl::toPercentEncoding(obfs));
}
}
if (up_mbps > 0) query.addQueryItem("upmbps", QString::number(up_mbps));
if (down_mbps > 0) query.addQueryItem("downmbps", QString::number(down_mbps));
if (!obfs.isEmpty()) {
query.addQueryItem("obfsParam", QUrl::toPercentEncoding(obfs));
}
if (!auth_str.isEmpty()) query.addQueryItem("auth", auth_str);
if (recv_window_conn > 0) query.addQueryItem("recv_window_conn", QString::number(recv_window_conn));
if (recv_window > 0) query.addQueryItem("recv_window", QString::number(recv_window));
if (disable_mtu_discovery) query.addQueryItem("disable_mtu_discovery", "true");
if (!hop_interval.isEmpty()) query.addQueryItem("hop_interval", hop_interval);
mergeUrlQuery(query, tls->ExportToLink());
@ -116,18 +168,30 @@ namespace Configs {
QJsonObject hysteria::ExportToJson()
{
QJsonObject object;
object["type"] = "hysteria";
object["type"] = protocol_version == "1" ? "hysteria" : "hysteria2";
mergeJsonObjects(object, outbound::ExportToJson());
if (!server_ports.isEmpty()) object["server_ports"] = QListStr2QJsonArray(server_ports);
if (!hop_interval.isEmpty()) object["hop_interval"] = hop_interval;
if (up_mbps > 0) object["up_mbps"] = up_mbps;
if (down_mbps > 0) object["down_mbps"] = down_mbps;
if (!obfs.isEmpty()) object["obfs"] = obfs;
if (!auth.isEmpty()) object["auth"] = auth;
if (!auth_str.isEmpty()) object["auth_str"] = auth_str;
if (recv_window_conn > 0) object["recv_window_conn"] = recv_window_conn;
if (recv_window > 0) object["recv_window"] = recv_window;
if (disable_mtu_discovery) object["disable_mtu_discovery"] = disable_mtu_discovery;
if (protocol_version == "1") {
if (!obfs.isEmpty()) object["obfs"] = obfs;
if (!auth.isEmpty()) {
if (auth_type == "BASE64") object["auth"] = auth;
if (auth_type == "STRING") object["auth_str"] = auth;
}
if (recv_window_conn > 0) object["recv_window_conn"] = recv_window_conn;
if (recv_window > 0) object["recv_window"] = recv_window;
if (disable_mtu_discovery) object["disable_mtu_discovery"] = disable_mtu_discovery;
} else {
if (!obfs.isEmpty()) {
QJsonObject obfsObj;
obfsObj["type"] = "salamander";
obfsObj["password"] = obfs;
object["obfs"] = obfsObj;
}
if (!password.isEmpty()) object["password"] = password;
}
if (tls->enabled) object["tls"] = tls->ExportToJson();
return object;
}
@ -135,18 +199,30 @@ namespace Configs {
BuildResult hysteria::Build()
{
QJsonObject object;
object["type"] = "hysteria";
object["type"] = protocol_version == "1" ? "hysteria" : "hysteria2";
mergeJsonObjects(object, outbound::Build().object);
if (!server_ports.isEmpty()) object["server_ports"] = QListStr2QJsonArray(server_ports);
if (!hop_interval.isEmpty()) object["hop_interval"] = hop_interval;
if (up_mbps > 0) object["up_mbps"] = up_mbps;
if (down_mbps > 0) object["down_mbps"] = down_mbps;
if (!obfs.isEmpty()) object["obfs"] = obfs;
if (!auth.isEmpty()) object["auth"] = auth;
if (!auth_str.isEmpty()) object["auth_str"] = auth_str;
if (recv_window_conn > 0) object["recv_window_conn"] = recv_window_conn;
if (recv_window > 0) object["recv_window"] = recv_window;
if (disable_mtu_discovery) object["disable_mtu_discovery"] = disable_mtu_discovery;
if (protocol_version == "1") {
if (!obfs.isEmpty()) object["obfs"] = obfs;
if (!auth.isEmpty()) {
if (auth_type == "BASE64") object["auth"] = auth;
if (auth_type == "STRING") object["auth_str"] = auth;
}
if (recv_window_conn > 0) object["recv_window_conn"] = recv_window_conn;
if (recv_window > 0) object["recv_window"] = recv_window;
if (disable_mtu_discovery) object["disable_mtu_discovery"] = disable_mtu_discovery;
} else {
if (!obfs.isEmpty()) {
QJsonObject obfsObj;
obfsObj["type"] = "salamander";
obfsObj["password"] = obfs;
object["obfs"] = obfsObj;
}
if (!password.isEmpty()) object["password"] = password;
}
if (tls->enabled) object["tls"] = tls->Build().object;
return {object, ""};
}

View File

@ -1,168 +0,0 @@
#include "include/configs/outbounds/hysteria2.h"
#include <QJsonArray>
#include <QUrlQuery>
#include <3rdparty/URLParser/url_parser.h>
#include <include/global/Utils.hpp>
#include "include/configs/common/utils.h"
namespace Configs {
bool hysteria2::ParseFromLink(const QString& link)
{
auto url = QUrl(link);
if (!url.isValid())
{
if(!url.errorString().startsWith("Invalid port"))
return false;
server_port = 0;
server_ports = QString::fromStdString(URLParser::Parse((link.split("?")[0] + "/").toStdString()).port).split(",");
for (auto & serverPort : server_ports) {
serverPort.replace("-", ":");
}
}
auto query = QUrlQuery(url.query(QUrl::ComponentFormattingOption::FullyDecoded));
outbound::ParseFromLink(link);
if (url.password().isEmpty()) {
password = url.userName();
} else {
password = url.userName() + ":" + url.password();
}
if (query.hasQueryItem("obfs-password")) {
obfsPassword = QUrl::fromPercentEncoding(query.queryItemValue("obfs-password").toUtf8());
}
if (query.hasQueryItem("upmbps")) up_mbps = query.queryItemValue("upmbps").toInt();
if (query.hasQueryItem("downmbps")) down_mbps = query.queryItemValue("downmbps").toInt();
if (query.hasQueryItem("mport")) {
QStringList ports = query.queryItemValue("mport").split(",");
for (auto& port : ports) {
port.replace("-", ":");
server_ports.append(port);
}
}
if (query.hasQueryItem("hop_interval")) hop_interval = query.queryItemValue("hop_interval");
tls->ParseFromLink(link);
if (server_port == 0 && server_ports.isEmpty()) server_port = 443;
return true;
}
bool hysteria2::ParseFromJson(const QJsonObject& object)
{
if (object.isEmpty() || object["type"].toString() != "hysteria2") return false;
outbound::ParseFromJson(object);
if (object.contains("server_ports")) {
server_ports = QJsonArray2QListString(object["server_ports"].toArray());
}
if (object.contains("hop_interval")) hop_interval = object["hop_interval"].toString();
if (object.contains("up_mbps")) up_mbps = object["up_mbps"].toInt();
if (object.contains("down_mbps")) down_mbps = object["down_mbps"].toInt();
if (object.contains("obfs")) {
auto obfsObj = object["obfs"].toObject();
if (obfsObj.contains("password")) obfsPassword = obfsObj["password"].toString();
}
if (object.contains("obfsPassword")) obfsPassword = object["obfsPassword"].toString();
if (object.contains("password")) password = object["password"].toString();
if (object.contains("tls")) tls->ParseFromJson(object["tls"].toObject());
return true;
}
QString hysteria2::ExportToLink()
{
QUrl url;
QUrlQuery query;
url.setScheme("hy2");
url.setHost(server);
if (password.contains(":")) {
url.setUserName(SubStrBefore(password, ":"));
url.setPassword(SubStrAfter(password, ":"));
} else {
url.setUserName(password);
}
if (!server_ports.isEmpty()) {
QStringList portList;
for (const auto& port : server_ports) {
QString modified = port;
modified.replace(":", "-");
portList.append(modified);
}
url.setPort(0);
query.addQueryItem("mport", portList.join(","));
} else {
url.setPort(server_port);
}
if (!name.isEmpty()) url.setFragment(name);
if (!obfsPassword.isEmpty()) {
query.addQueryItem("obfs-password", QUrl::toPercentEncoding(obfsPassword));
}
if (up_mbps > 0) query.addQueryItem("upmbps", QString::number(up_mbps));
if (down_mbps > 0) query.addQueryItem("downmbps", QString::number(down_mbps));
if (!hop_interval.isEmpty()) query.addQueryItem("hop_interval", hop_interval);
mergeUrlQuery(query, tls->ExportToLink());
mergeUrlQuery(query, outbound::ExportToLink());
if (!query.isEmpty()) url.setQuery(query);
QString result = url.toString();
if (!server_ports.isEmpty()) {
result = result.replace(":0?", ":" + query.queryItemValue("mport") + "?");
}
return result;
}
QJsonObject hysteria2::ExportToJson()
{
QJsonObject object;
object["type"] = "hysteria2";
mergeJsonObjects(object, outbound::ExportToJson());
if (!server_ports.isEmpty()) object["server_ports"] = QListStr2QJsonArray(server_ports);
if (!hop_interval.isEmpty()) object["hop_interval"] = hop_interval;
if (up_mbps > 0) object["up_mbps"] = up_mbps;
if (down_mbps > 0) object["down_mbps"] = down_mbps;
if (!obfsPassword.isEmpty()) {
QJsonObject obfsObj;
obfsObj["type"] = "salamander";
obfsObj["password"] = obfsPassword;
object["obfs"] = obfsObj;
}
if (!password.isEmpty()) object["password"] = password;
if (tls->enabled) object["tls"] = tls->ExportToJson();
return object;
}
BuildResult hysteria2::Build()
{
QJsonObject object;
object["type"] = "hysteria2";
mergeJsonObjects(object, outbound::Build().object);
if (!server_ports.isEmpty()) object["server_ports"] = QListStr2QJsonArray(server_ports);
if (!hop_interval.isEmpty()) object["hop_interval"] = hop_interval;
if (up_mbps > 0) object["up_mbps"] = up_mbps;
if (down_mbps > 0) object["down_mbps"] = down_mbps;
if (!obfsPassword.isEmpty()) {
QJsonObject obfsObj;
obfsObj["type"] = "salamander";
obfsObj["password"] = obfsPassword;
object["obfs"] = obfsObj;
}
if (!password.isEmpty()) object["password"] = password;
if (tls->enabled) object["tls"] = tls->Build().object;
return {object, ""};
}
QString hysteria2::DisplayType()
{
return "Hysteria2";
}
}

View File

@ -171,20 +171,13 @@ namespace Subscription {
if (!ok) return;
}
// Hysteria1
if (str.startsWith("hysteria://")) {
// Hysteria
if (str.startsWith("hysteria://") || str.startsWith("hysteria2://") || str.startsWith("hy2://")) {
ent = Configs::ProfileManager::NewProxyEntity("hysteria");
auto ok = ent->Hysteria()->ParseFromLink(str);
if (!ok) return;
}
// Hysteria2
if (str.startsWith("hysteria2://") || str.startsWith("hy2://")) {
ent = Configs::ProfileManager::NewProxyEntity("hysteria2");
auto ok = ent->Hysteria2()->ParseFromLink(str);
if (!ok) return;
}
// TUIC
if (str.startsWith("tuic://")) {
ent = Configs::ProfileManager::NewProxyEntity("tuic");
@ -288,20 +281,13 @@ namespace Subscription {
if (!ok) continue;
}
// Hysteria1
// Hysteria
if (out["type"] == "hysteria") {
ent = Configs::ProfileManager::NewProxyEntity("hysteria");
auto ok = ent->Hysteria()->ParseFromJson(out);
if (!ok) continue;
}
// Hysteria2
if (out["type"] == "hysteria2") {
ent = Configs::ProfileManager::NewProxyEntity("hysteria2");
auto ok = ent->Hysteria2()->ParseFromJson(out);
if (!ok) continue;
}
// TUIC
if (out["type"] == "tuic") {
ent = Configs::ProfileManager::NewProxyEntity("tuic");
@ -701,7 +687,7 @@ namespace Subscription {
bean->tls->reality->public_key = Node2QString(reality["public-key"]);
bean->tls->reality->short_id = Node2QString(reality["short-id"]);
}
} else if (type == "hysteria") {
} else if (type == "hysteria" || type == "hysteria2") {
auto bean = ent->Hysteria();
bean->tls->enabled = true;
@ -711,53 +697,32 @@ namespace Subscription {
if (!alpn.isEmpty()) bean->tls->alpn = {alpn[0]};
bean->tls->server_name = Node2QString(proxy["sni"]);
auto auth_str = FIRST_OR_SECOND(Node2QString(proxy["auth_str"]), Node2QString(proxy["auth-str"]));
auto auth = Node2QString(proxy["auth"]);
if (!auth_str.isEmpty()) {
bean->auth_str = auth_str;
}
if (!auth.isEmpty()) {
bean->auth = auth;
}
bean->obfs = Node2QString(proxy["obfs"]);
if (type == "hysteria") {
auto auth_str = FIRST_OR_SECOND(Node2QString(proxy["auth_str"]), Node2QString(proxy["auth-str"]));
auto auth = Node2QString(proxy["auth"]);
if (!auth_str.isEmpty()) {
bean->auth_type = "STRING";
bean->auth = auth_str;
}
if (!auth.isEmpty()) {
bean->auth_type = "BASE64";
bean->auth = auth;
}
bean->obfs = Node2QString(proxy["obfs"]);
if (Node2Bool(proxy["disable_mtu_discovery"]) || Node2Bool(proxy["disable-mtu-discovery"])) bean->disable_mtu_discovery = true;
bean->recv_window = Node2Int(proxy["recv-window"]);
bean->recv_window_conn = Node2Int(proxy["recv-window-conn"]);
if (Node2Bool(proxy["disable_mtu_discovery"]) || Node2Bool(proxy["disable-mtu-discovery"])) bean->disable_mtu_discovery = true;
bean->recv_window = Node2Int(proxy["recv-window"]);
bean->recv_window_conn = Node2Int(proxy["recv-window-conn"]);
} else {
bean->obfs = Node2QString(proxy["obfs-password"]);
bean->password = Node2QString(proxy["password"]);
}
auto upMbps = Node2QString(proxy["up"]).split(" ")[0].toInt();
auto downMbps = Node2QString(proxy["down"]).split(" ")[0].toInt();
if (upMbps > 0) bean->up_mbps = upMbps;
if (downMbps > 0) bean->down_mbps = downMbps;
auto ports = Node2QString(proxy["ports"]);
if (!ports.isEmpty()) {
QStringList serverPorts;
ports.replace("/", ",");
for (const QString& port : ports.split(",", Qt::SkipEmptyParts)) {
if (port.isEmpty()) {
continue;
}
QString modifiedPort = port;
modifiedPort.replace("-", ":");
serverPorts.append(modifiedPort);
}
bean->server_ports = serverPorts;
}
} else if (type == "hysteria2") {
auto bean = ent->Hysteria2();
bean->tls->enabled = true;
bean->tls->insecure = Node2Bool(proxy["skip-cert-verify"]);
bean->tls->certificate = Node2QString(proxy["ca-str"]);
bean->tls->server_name = Node2QString(proxy["sni"]);
bean->obfsPassword = Node2QString(proxy["obfs-password"]);
bean->password = Node2QString(proxy["password"]);
bean->up_mbps = Node2QString(proxy["up"]).split(" ")[0].toInt();
bean->down_mbps = Node2QString(proxy["down"]).split(" ")[0].toInt();
auto ports = Node2QString(proxy["ports"]);
if (!ports.isEmpty()) {
QStringList serverPorts;

View File

@ -256,9 +256,6 @@ namespace Configs {
} else if (type == "hysteria") {
bean = new Configs::QUICBean(Configs::QUICBean::proxy_Hysteria);
outbound = new Configs::hysteria();
} else if (type == "hysteria2") {
bean = new Configs::QUICBean(Configs::QUICBean::proxy_Hysteria2);
outbound = new Configs::hysteria2();
} else if (type == "tuic") {
bean = new Configs::QUICBean(Configs::QUICBean::proxy_TUIC);
outbound = new Configs::tuic();

View File

@ -21,7 +21,6 @@
#include <QInputDialog>
#include "include/ui/profile/edit_hysteria.h"
#include "include/ui/profile/edit_hysteria2.h"
#include "include/ui/profile/edit_socks.h"
#include "include/ui/profile/edit_trojan.h"
#include "include/ui/profile/edit_tuic.h"
@ -139,7 +138,6 @@ DialogEditProfile::DialogEditProfile(const QString &_type, int profileOrGroupId,
LOAD_TYPE("vmess")
LOAD_TYPE("vless")
LOAD_TYPE("hysteria")
LOAD_TYPE("hysteria2")
LOAD_TYPE("tuic")
LOAD_TYPE("anytls")
LOAD_TYPE("wireguard")
@ -216,10 +214,37 @@ void DialogEditProfile::typeSelected(const QString &newType) {
auto _innerWidget = new EditHysteria(this);
innerWidget = _innerWidget;
innerEditor = _innerWidget;
} else if (type == "hysteria2") {
auto _innerWidget = new EditHysteria2(this);
innerWidget = _innerWidget;
innerEditor = _innerWidget;
connect(_innerWidget->ui->protocol_version, &QComboBox::currentTextChanged, _innerWidget, [=,this](const QString &txt)
{
if (txt == "1")
{
_innerWidget->ui->auth_type->setVisible(true);
_innerWidget->ui->auth_type_l->setVisible(true);
_innerWidget->ui->auth->setVisible(true);
_innerWidget->ui->auth_l->setVisible(true);
_innerWidget->ui->recv_window_conn->setVisible(true);
_innerWidget->ui->recv_window_conn_l->setVisible(true);
_innerWidget->ui->recv_window->setVisible(true);
_innerWidget->ui->recv_window_l->setVisible(true);
_innerWidget->ui->disable_mtu_discovery->setVisible(true);
_innerWidget->ui->password->setVisible(false);
_innerWidget->ui->password_l->setVisible(false);
} else
{
_innerWidget->ui->auth_type->setVisible(false);
_innerWidget->ui->auth_type_l->setVisible(false);
_innerWidget->ui->auth->setVisible(false);
_innerWidget->ui->auth_l->setVisible(false);
_innerWidget->ui->recv_window_conn->setVisible(false);
_innerWidget->ui->recv_window_conn_l->setVisible(false);
_innerWidget->ui->recv_window->setVisible(false);
_innerWidget->ui->recv_window_l->setVisible(false);
_innerWidget->ui->disable_mtu_discovery->setVisible(false);
_innerWidget->ui->password->setVisible(true);
_innerWidget->ui->password_l->setVisible(true);
}
ADJUST_SIZE
});
} else if (type == "tuic") {
auto _innerWidget = new EditTuic(this);
innerWidget = _innerWidget;

View File

@ -14,28 +14,62 @@ void EditHysteria::onStart(std::shared_ptr<Configs::ProxyEntity> _ent) {
this->ent = _ent;
auto outbound = _ent->Hysteria();
ui->protocol_version->setCurrentText(outbound->protocol_version);
ui->server_ports->setText(outbound->server_ports.join(","));
ui->hop_interval->setText(outbound->hop_interval);
ui->up_mbps->setText(Int2String(outbound->up_mbps));
ui->down_mbps->setText(Int2String(outbound->down_mbps));
ui->obfs->setText(outbound->obfs);
ui->auth_type->setCurrentText(outbound->auth_type);
ui->auth->setText(outbound->auth);
ui->auth_str->setText(outbound->auth_str);
ui->recv_window->setText(Int2String(outbound->recv_window));
ui->recv_window_conn->setText(Int2String(outbound->recv_window_conn));
ui->disable_mtu_discovery->setChecked(outbound->disable_mtu_discovery);
ui->password->setText(outbound->password);
if (outbound->protocol_version == "1")
{
ui->auth_type->setVisible(true);
ui->auth_type_l->setVisible(true);
ui->auth->setVisible(true);
ui->auth_l->setVisible(true);
ui->recv_window_conn->setVisible(true);
ui->recv_window_conn_l->setVisible(true);
ui->recv_window->setVisible(true);
ui->recv_window_l->setVisible(true);
ui->disable_mtu_discovery->setVisible(true);
ui->password->setVisible(false);
ui->password_l->setVisible(false);
} else
{
ui->auth_type->setVisible(false);
ui->auth_type_l->setVisible(false);
ui->auth->setVisible(false);
ui->auth_l->setVisible(false);
ui->recv_window_conn->setVisible(false);
ui->recv_window_conn_l->setVisible(false);
ui->recv_window->setVisible(false);
ui->recv_window_l->setVisible(false);
ui->disable_mtu_discovery->setVisible(false);
ui->password->setVisible(true);
ui->password_l->setVisible(true);
}
}
bool EditHysteria::onEnd() {
auto outbound = ent->Hysteria();
outbound->protocol_version = ui->protocol_version->currentText();
outbound->server_ports = SplitAndTrim(ui->server_ports->text(), ",");
outbound->hop_interval = ui->hop_interval->text();
outbound->up_mbps = ui->up_mbps->text().toInt();
outbound->down_mbps = ui->down_mbps->text().toInt();
outbound->obfs = ui->obfs->text();
outbound->auth_type = ui->auth_type->currentText();
outbound->auth = ui->auth->text();
outbound->auth_str = ui->auth_str->text();
outbound->recv_window = ui->recv_window->text().toInt();
outbound->recv_window_conn = ui->recv_window_conn->text().toInt();
outbound->disable_mtu_discovery = ui->disable_mtu_discovery->isChecked();
outbound->password = ui->password->text();
return true;
}

View File

@ -1,35 +0,0 @@
#include "include/ui/profile/edit_hysteria2.h"
EditHysteria2::EditHysteria2(QWidget *parent)
: QWidget(parent),
ui(new Ui::EditHysteria2) {
ui->setupUi(this);
}
EditHysteria2::~EditHysteria2() {
delete ui;
}
void EditHysteria2::onStart(std::shared_ptr<Configs::ProxyEntity> _ent) {
this->ent = _ent;
auto outbound = this->ent->Hysteria2();
ui->server_ports->setText(outbound->server_ports.join(","));
ui->hop_interval->setText(outbound->hop_interval);
ui->up_mbps->setText(Int2String(outbound->up_mbps));
ui->down_mbps->setText(Int2String(outbound->down_mbps));
ui->obfs_password->setText(outbound->obfsPassword);
ui->password->setText(outbound->password);
}
bool EditHysteria2::onEnd() {
auto outbound = this->ent->Hysteria2();
outbound->server_ports = SplitAndTrim(ui->server_ports->text(), ",");
outbound->hop_interval = ui->hop_interval->text();
outbound->up_mbps = ui->up_mbps->text().toInt();
outbound->down_mbps = ui->down_mbps->text().toInt();
outbound->obfsPassword = ui->obfs_password->text();
outbound->password = ui->password->text();
return true;
}