[fix] support self-hosted server URLs with paths

This commit is contained in:
dijunkun
2026-10-09 15:34:12 +08:00
parent d3ecbe2cc9
commit a06ed5806a
14 changed files with 101 additions and 51 deletions
@@ -10,6 +10,7 @@
#include "SimpleIni.h"
#include "autostart.h"
#include "rd_log.h"
#include "signal_server_address.h"
namespace crossdesk {
@@ -290,10 +291,23 @@ int ConfigCenter::SetTurn(bool enable_turn, bool force_relay) {
}
int ConfigCenter::SetServerHost(const std::string& host) {
if (host.empty() || host.size() > 253 || host.find_first_of("/\\@?#") != std::string::npos ||
std::any_of(host.begin(), host.end(), [](unsigned char c) { return c <= 32 || c == 127; })) return -1;
if (StoreValues({{"signal_server_host", host}}) != 0) return -1;
signal_server_host_ = host;
const auto address = minirtc::ParseSignalServerAddress(host);
if (!address) return -1;
if (address->port) return SetServerEndpoint(host, *address->port);
if (StoreValues({{"signal_server_host", address->Address()}}) != 0) return -1;
signal_server_host_ = address->Address();
return 0;
}
int ConfigCenter::SetServerEndpoint(const std::string& host, int port) {
const auto address = minirtc::ParseSignalServerAddress(host);
if (!address) return -1;
port = address->port.value_or(port);
if (port < 1 || port > 65535) return -1;
if (StoreValues({{"signal_server_host", address->Address()},
{"signal_server_port", std::to_string(port)}}) != 0) return -1;
signal_server_host_ = address->Address();
signal_server_port_ = port;
return 0;
}
@@ -42,6 +42,7 @@ class ConfigCenter {
int SetTurn(bool enable_turn, bool force_relay);
int SetServerHost(const std::string& signal_server_host);
int SetServerPort(int signal_server_port);
int SetServerEndpoint(const std::string& address, int port);
int SetSelfHosted(bool enable_self_hosted);
int SetAutostart(bool enable_autostart);
int SetDaemon(bool enable_daemon);
@@ -63,6 +63,7 @@
#include "rd_log.h"
#include "server_window_state.h"
#include "signal_server_settings.h"
#include "signal_server_address.h"
#include "ui/announcement_text.h"
#include "ui/ui_localization.h"
#include "update_checker.h"
@@ -1285,6 +1286,7 @@ void GuiApplication::ResetSettingsUi() {
ui_->main->set_file_save_path(file_transfer_save_path_buf_);
ui_->main->set_server_host(signal_server_ip_self_);
ui_->main->set_server_port(signal_server_port_self_);
ui_->main->set_self_host_settings_error("");
}
void GuiApplication::BindMainCallbacks() {
@@ -1419,29 +1421,14 @@ void GuiApplication::BindMainCallbacks() {
ui_->main->set_file_save_path(folder);
}
});
main->on_save_settings([this] { SaveSettingsFromUi(); });
main->on_save_settings([this] { return SaveSettingsFromUi(); });
main->on_cancel_settings([this] { ResetSettingsUi(); });
main->on_save_self_hosted_settings([this] {
const std::string host(ui_->main->get_server_host());
const auto signal_port = ParsePort(ui_->main->get_server_port());
if (!host.empty()) {
config_center_->SetServerHost(host);
std::memset(signal_server_ip_self_, 0, sizeof(signal_server_ip_self_));
std::strncpy(signal_server_ip_self_, host.c_str(),
sizeof(signal_server_ip_self_) - 1);
std::memset(signal_server_ip_, 0, sizeof(signal_server_ip_));
std::strncpy(signal_server_ip_, host.c_str(),
sizeof(signal_server_ip_) - 1);
}
if (signal_port) {
config_center_->SetServerPort(*signal_port);
std::snprintf(signal_server_port_self_, sizeof(signal_server_port_self_),
"%d", *signal_port);
std::snprintf(signal_server_port_, sizeof(signal_server_port_), "%d",
*signal_port);
}
// Stage the endpoint until the surrounding settings dialog is confirmed.
return ValidateSelfHostedSettingsFromUi();
});
main->on_cancel_self_hosted_settings([this] {
ui_->main->set_self_host_settings_error("");
const std::string host = config_center_->GetSignalServerHost();
ui_->main->set_server_host(UiText(host));
const int signal_port = config_center_->GetSignalServerPort();
@@ -3496,9 +3483,45 @@ void GuiApplication::SyncServerWindow() {
PositionServerWindows();
}
void GuiApplication::SaveSettingsFromUi() {
bool GuiApplication::ValidateSelfHostedSettingsFromUi() {
auto& main = ui_->main;
const auto address = minirtc::ParseSignalServerAddress(
std::string(main->get_server_host()));
if (!address) {
main->set_self_host_settings_error(UiText(
localization::self_hosted_address_invalid[localization_language_index_]));
return false;
}
const auto port = address->port ? address->port : ParsePort(main->get_server_port());
if (!port) {
main->set_self_host_settings_error(UiText(
localization::self_hosted_port_invalid[localization_language_index_]));
return false;
}
main->set_server_host(UiText(address->Address()));
main->set_server_port(UiText(std::to_string(*port)));
main->set_self_host_settings_error("");
return true;
}
bool GuiApplication::SaveSettingsFromUi() {
const auto previous_server = GetSignalServerSettings(*config_center_);
auto& main = ui_->main;
const bool has_endpoint = main->get_self_hosted_enabled() ||
!std::string(main->get_server_host()).empty() ||
!std::string(main->get_server_port()).empty();
if (has_endpoint && !ValidateSelfHostedSettingsFromUi()) {
main->set_self_host_settings_open(true);
return false;
}
if ((has_endpoint && config_center_->SetServerEndpoint(
std::string(main->get_server_host()), *ParsePort(main->get_server_port())) != 0) ||
config_center_->SetSelfHosted(main->get_self_hosted_enabled()) != 0) {
main->set_self_host_settings_error(UiText(
localization::self_hosted_save_failed[localization_language_index_]));
main->set_self_host_settings_open(true);
return false;
}
language_button_value_ =
localization::detail::ClampLanguageIndex(main->get_language_index());
video_encode_format_button_value_ = std::clamp(main->get_codec_index(), 0, 1);
@@ -3564,17 +3587,9 @@ void GuiApplication::SaveSettingsFromUi() {
sizeof(file_transfer_save_path_buf_) - 1);
config_center_->SetFileTransferSavePath(path);
const std::string host(main->get_server_host());
if (!host.empty()) {
config_center_->SetServerHost(host);
}
if (const auto port = ParsePort(main->get_server_port())) {
config_center_->SetServerPort(*port);
}
config_center_->SetSelfHosted(enable_self_hosted_);
ApplySettingsFromConfig(previous_server !=
GetSignalServerSettings(*config_center_));
return true;
}
void GuiApplication::ApplySettingsFromConfig(bool reconnect) {
@@ -76,7 +76,8 @@ private:
#endif
void UpdateLocalization();
void ResetSettingsUi();
void SaveSettingsFromUi();
bool ValidateSelfHostedSettingsFromUi();
bool SaveSettingsFromUi();
void ApplySettingsFromConfig(bool reconnect);
#if _WIN32 && CROSSDESK_PORTABLE
void CheckPortableWindowsService();
@@ -240,6 +240,9 @@ struct TranslationRow {
"Server Address:", u8"Адрес сервера:") \
X(self_hosted_server_port, u8"信令服务端口:", \
"Signal Service Port:", u8"Порт сигнального сервиса:") \
X(self_hosted_address_invalid, u8"请输入有效的主机名、IP 或 HTTPS/WSS 信令地址,可包含路径。", "Enter a valid host, IP, or HTTPS/WSS signaling URL. Paths are supported.", u8"Введите имя хоста, IP или HTTPS/WSS-адрес сигнального сервиса. Путь допустим.") \
X(self_hosted_port_invalid, u8"请输入 1–65535 范围内的信令端口。", "Enter a signaling port between 1 and 65535.", u8"Введите порт сигнального сервиса от 1 до 65535.") \
X(self_hosted_save_failed, u8"配置保存失败,请检查配置目录是否可写后重试。", "Could not save settings. Check that the configuration directory is writable and retry.", u8"Не удалось сохранить настройки. Проверьте доступ на запись в каталог конфигурации.") \
X(ok, u8"确认", "OK", u8"ОК") \
X(cancel, u8"取消", "Cancel", u8"Отмена") \
X(new_password, u8"请输入六位密码:", \
@@ -8,6 +8,8 @@
#include <set>
#include <system_error>
#include "signal_server_address.h"
#ifdef _WIN32
#include <windows.h>
#endif
@@ -51,9 +53,12 @@ void AnnouncementInbox::Configure(std::string host, int port,
std::lock_guard<std::mutex> lock(mutex_);
if (directory_.empty() || host.empty() || port <= 0 || device_id.empty())
return;
for (auto& ch : host)
auto address = minirtc::ParseSignalServerAddress(host);
if (!address) return;
for (auto& ch : address->host)
if (ch >= 'A' && ch <= 'Z') ch += 'a' - 'A';
if (host.back() == '.') host.pop_back();
if (address->host.back() == '.') address->host.pop_back();
host = address->Address();
const auto scope = json::array({host, port, device_id}).dump();
if (scope_ == scope) return;
scope_ = scope;
+16 -7
View File
@@ -379,6 +379,7 @@ export component MainWindow inherits Window {
in-out property <string> file-save-path: "";
in-out property <string> server-host: "";
in-out property <string> server-port: "";
in-out property <string> self-host-settings-error: "";
in property <bool> settings-session-active: false;
// Keep the setting visible on every platform. Unsupported builds expose it
// as disabled so the fixed settings layout never turns into a blank row.
@@ -393,9 +394,9 @@ export component MainWindow inherits Window {
callback recent-connect(string);
callback recent-edit-alias(string, string);
callback recent-delete(string);
callback save-settings;
callback save-settings() -> bool;
callback cancel-settings;
callback save-self-hosted-settings;
callback save-self-hosted-settings() -> bool;
callback cancel-self-hosted-settings;
callback browse-save-path;
callback open-download;
@@ -418,6 +419,9 @@ export component MainWindow inherits Window {
in-out property <bool> settings-open: false;
private property <length> settings-scroll-y: 0px;
in-out property <bool> self-host-settings-open: false;
changed self-host-settings-open => {
if !root.self-host-settings-open { root.self-host-settings-error = ""; }
}
in-out property <bool> about-open: false;
in-out property <bool> update-open: false;
in-out property <bool> reset-password-open: false;
@@ -1809,7 +1813,7 @@ export component MainWindow inherits Window {
}
Rectangle { x: 7px; y: 286px; width: parent.width - 14px; height: 1px; background: ImGuiLineStyle.separator; }
CompactButton { x: root.compact-language ? 63px : 89px; y: 297px; primary: true; text: UiStrings.ok; clicked => { root.save-settings(); root.settings-open = false; root.self-host-settings-open = false; } }
CompactButton { x: root.compact-language ? 63px : 89px; y: 297px; primary: true; text: UiStrings.ok; clicked => { if root.save-settings() { root.settings-open = false; root.self-host-settings-open = false; } } }
CompactButton { x: root.compact-language ? 104px : 130px; y: 297px; width: root.compact-language ? 34px : 56px; text: UiStrings.cancel; clicked => { root.cancel-settings(); root.settings-open = false; root.self-host-settings-open = false; } }
}
}
@@ -1822,7 +1826,7 @@ export component MainWindow inherits Window {
x: root.compact-language ? 190px : 140px;
y: 105px;
width: root.compact-language ? 260px : 360px;
height: 138px;
height: root.self-host-settings-error == "" ? 138px : 202px;
border-radius: 7px;
Rectangle {
@@ -1846,7 +1850,7 @@ export component MainWindow inherits Window {
border-width: 1px;
border-color: ImGuiLineStyle.border;
border-radius: 4px;
LineEdit { x: 1px; y: 1.5px; width: parent.width - 2px; height: parent.height - 2px; text <=> root.server-host; font-size: ImGuiFontStyle.body; }
LineEdit { x: 1px; y: 1.5px; width: parent.width - 2px; height: parent.height - 2px; text <=> root.server-host; input-type: InputType.text; font-size: ImGuiFontStyle.body; }
LineEditFocusUnderlineMask { x: 5px; y: parent.height - 3px; width: parent.width - 10px; }
}
Text { x: 8px; y: 61px; width: root.compact-language ? 88px : 165px; height: 24px; text: UiStrings.server-port; font-size: ImGuiFontStyle.body; overflow: elide; vertical-alignment: center; }
@@ -1862,8 +1866,13 @@ export component MainWindow inherits Window {
LineEdit { x: 1px; y: 1.5px; width: parent.width - 2px; height: parent.height - 2px; text <=> root.server-port; input-type: InputType.number; font-size: ImGuiFontStyle.body; }
LineEditFocusUnderlineMask { x: 5px; y: parent.height - 3px; width: parent.width - 10px; }
}
CompactButton { x: root.compact-language ? 93px : 135px; y: 100px; primary: true; text: UiStrings.ok; clicked => { root.save-self-hosted-settings(); root.self-host-settings-open = false; } }
CompactButton { x: root.compact-language ? 134px : 176px; y: 100px; width: root.compact-language ? 34px : 56px; text: UiStrings.cancel; clicked => { root.cancel-self-hosted-settings(); root.self-host-settings-open = false; } }
if root.self-host-settings-error != "": Text {
x: 8px; y: 89px; width: parent.width - 16px; height: 64px;
text: root.self-host-settings-error; color: #b3261e;
font-size: ImGuiFontStyle.body; wrap: word-wrap;
}
CompactButton { x: root.compact-language ? 93px : 135px; y: parent.height - 38px; primary: true; text: UiStrings.ok; clicked => { if root.save-self-hosted-settings() { root.self-host-settings-open = false; } } }
CompactButton { x: root.compact-language ? 134px : 176px; y: parent.height - 38px; width: root.compact-language ? 34px : 56px; text: UiStrings.cancel; clicked => { root.cancel-self-hosted-settings(); root.self-host-settings-open = false; } }
}
}
@@ -10,6 +10,7 @@
#include <optional>
#include "config_center.h"
#include "signal_server_address.h"
#include "signal_server_settings.h"
namespace crossdesk {
@@ -83,9 +84,7 @@ SettingResult ApplyConsoleSetting(ConfigCenter& config, const std::string& key,
canonical = value;
save = [&config, mode] { return config.SetTurnMode(static_cast<ConfigCenter::TURN_MODE>(mode)); };
} else if (key == "server_host") {
if (input.empty() || input.size() > 253 ||
input.find_first_of("/\\@?#") != std::string::npos ||
std::any_of(input.begin(), input.end(), [](unsigned char c) { return c <= 32 || c == 127; })) return result;
if (!minirtc::ParseSignalServerAddress(input)) return result;
save = [&] { return config.SetServerHost(input); };
} else if (key == "server_port") {
int port = 0;