Migrate to QtNetwork for HTTP(s) requests (#344)

* Migrate to QtNetwork for HTTP(s) requests

* Fix

* Cleanup

* Fix linux build
This commit is contained in:
parhelia512 2025-04-07 16:56:09 +08:00 committed by Nova
parent 19cbfe0754
commit c43d11cb7b
8 changed files with 90 additions and 113 deletions

View File

@ -37,7 +37,6 @@ list(APPEND CMAKE_PREFIX_PATH ${NKR_LIBS})
if(WIN32) if(WIN32)
list(APPEND CMAKE_PREFIX_PATH "libs/deps/built/x64-windows-static") list(APPEND CMAKE_PREFIX_PATH "libs/deps/built/x64-windows-static")
endif () endif ()
add_definitions(-DCURL_STATICLIB)
message("[CMAKE_PREFIX_PATH] ${CMAKE_PREFIX_PATH}") message("[CMAKE_PREFIX_PATH] ${CMAKE_PREFIX_PATH}")
@ -66,9 +65,6 @@ list(APPEND NKR_EXTERNAL_TARGETS yaml-cpp)
find_package(ZXing CONFIG REQUIRED) find_package(ZXing CONFIG REQUIRED)
list(APPEND NKR_EXTERNAL_TARGETS ZXing::ZXing) list(APPEND NKR_EXTERNAL_TARGETS ZXing::ZXing)
find_package(cpr REQUIRED)
list(APPEND NKR_EXTERNAL_TARGETS cpr::cpr)
set(BUILD_SHARED_LIBS OFF) set(BUILD_SHARED_LIBS OFF)
list(APPEND NKR_EXTERNAL_TARGETS qhotkey) list(APPEND NKR_EXTERNAL_TARGETS qhotkey)

View File

@ -1,7 +1,5 @@
#pragma once #pragma once
#ifndef NKR_NO_GRPC
#include "core/server/gen/libcore.pb.h" #include "core/server/gen/libcore.pb.h"
#include <QString> #include <QString>
@ -54,4 +52,3 @@ namespace NekoGui_rpc {
inline Client *defaultClient; inline Client *defaultClient;
} // namespace NekoGui_rpc } // namespace NekoGui_rpc
#endif

View File

@ -1,8 +1,5 @@
#pragma once #pragma once
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QObject> #include <QObject>
#include <functional> #include <functional>
@ -26,7 +23,7 @@ namespace NekoGui_network {
static QString GetHeader(const QList<QPair<QByteArray, QByteArray>> &header, const QString &name); static QString GetHeader(const QList<QPair<QByteArray, QByteArray>> &header, const QString &name);
static QString DownloadAsset(const QString &url, const QString &fileName, bool isTemp); static QString DownloadAsset(const QString &url, const QString &fileName);
}; };
} // namespace NekoGui_network } // namespace NekoGui_network

View File

@ -19,7 +19,7 @@ mkdir -p $INSTALL_PREFIX
#### clean #### #### clean ####
clean() { clean() {
rm -rf dl.zip yaml-* zxing-* protobuf curl cpr libpsl* zlib vcpkg rm -rf dl.zip yaml-* zxing-* protobuf
} }
#### ZXing v2.3.0 #### #### ZXing v2.3.0 ####
@ -69,46 +69,5 @@ ninja && ninja install
cd ../.. cd ../..
if [[ "$(uname -s)" == *"NT"* ]]; then
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
export VCPKG_BUILD_TYPE="release"
export VCPKG_LIBRARY_LINKAGE="static"
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install curl:x64-windows-static --x-install-root=$INSTALL_PREFIX
cd ..
git clone https://github.com/libcpr/cpr.git
cd cpr
git checkout bb01c8db702fb41e5497aee9c0559ddf4bf13749
sed -i 's/find_package(CURL COMPONENTS HTTP HTTPS)/find_package(CURL REQUIRED)/g' CMakeLists.txt
mkdir build && cd build
cmake -GNinja .. -DCMAKE_BUILD_TYPE=Release -DCPR_USE_SYSTEM_CURL=ON -DBUILD_SHARED_LIBS=OFF -DCURL_STATICLIB=ON -DCMAKE_OSX_ARCHITECTURES=$1 -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCURL_LIBRARY=../../built/x64-windows-static/lib -DCURL_INCLUDE_DIR=../../built/x64-windows-static/include -DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX
ninja && ninja install
cd ../..
else
git clone https://github.com/curl/curl.git
cd curl
git checkout 7eb8c048470ed2cc14dca75be9c1cdae7ac8498b
mkdir build && cd build
cmake -GNinja .. -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DCMAKE_OSX_ARCHITECTURES=$1 -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX -DCURL_STATICLIB=ON -DUSE_LIBIDN2=OFF
ninja && ninja install
cd ../..
git clone https://github.com/libcpr/cpr.git
cd cpr
git checkout bb01c8db702fb41e5497aee9c0559ddf4bf13749
mkdir build && cd build
cmake -GNinja .. -DCMAKE_BUILD_TYPE=Release -DCPR_USE_SYSTEM_CURL=ON -DBUILD_SHARED_LIBS=OFF -DCURL_STATICLIB=ON -DCMAKE_OSX_ARCHITECTURES=$1 -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX
ninja && ninja install
cd ../..
fi
#### ####
clean clean

View File

@ -53,7 +53,7 @@ patchelf --set-rpath '$ORIGIN/../../lib' ./usr/plugins/platformthemes/libqxdgdes
# fix extra libs... # fix extra libs...
mkdir ./usr/lib2 mkdir ./usr/lib2
ls ./usr/lib/ ls ./usr/lib/
cp ./usr/lib/libQt* ./usr/lib/libxcb-util* ./usr/lib/libicuuc* ./usr/lib/libicui18n* ./usr/lib/libicudata* ./usr/lib/libssl* ./usr/lib/libcrypto* ./usr/lib2 cp ./usr/lib/libQt* ./usr/lib/libxcb-util* ./usr/lib/libicuuc* ./usr/lib/libicui18n* ./usr/lib/libicudata* ./usr/lib2
rm -r ./usr/lib rm -r ./usr/lib
mv ./usr/lib2 ./usr/lib mv ./usr/lib2 ./usr/lib

View File

@ -1,42 +1,67 @@
#include "include/global/HTTPRequestHelper.hpp" #include "include/global/HTTPRequestHelper.hpp"
#include <QByteArray> #include <QNetworkProxy>
#include <QMetaEnum> #include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QTimer> #include <QTimer>
#include <QJsonObject>
#include <QJsonArray>
#include <QFile> #include <QFile>
#include <QDir>
#include <QApplication> #include <QApplication>
#include "cpr/cpr.h"
#include "include/global/NekoGui.hpp" #include "include/global/NekoGui.hpp"
namespace NekoGui_network { namespace NekoGui_network {
NekoHTTPResponse NetworkRequestHelper::HttpGet(const QString &url) { NekoHTTPResponse NetworkRequestHelper::HttpGet(const QString &url) {
cpr::Session session; QNetworkRequest request;
QNetworkAccessManager accessManager;
request.setUrl(url);
if (NekoGui::dataStore->sub_use_proxy || NekoGui::dataStore->spmode_system_proxy) { if (NekoGui::dataStore->sub_use_proxy || NekoGui::dataStore->spmode_system_proxy) {
session.SetProxies({{"http", "127.0.0.1:" + QString(Int2String(NekoGui::dataStore->inbound_socks_port)).toStdString()}, QNetworkProxy p;
{"https", "127.0.0.1:" + QString(Int2String(NekoGui::dataStore->inbound_socks_port)).toStdString()}}); p.setType(QNetworkProxy::HttpProxy);
if (NekoGui::dataStore->started_id < 0 && NekoGui::dataStore->sub_use_proxy) { p.setHostName("127.0.0.1");
p.setPort(NekoGui::dataStore->inbound_socks_port);
accessManager.setProxy(p);
if (NekoGui::dataStore->started_id < 0) {
return NekoHTTPResponse{QObject::tr("Request with proxy but no profile started.")}; return NekoHTTPResponse{QObject::tr("Request with proxy but no profile started.")};
} }
} }
// Set attribute
request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
request.setHeader(QNetworkRequest::KnownHeaders::UserAgentHeader, NekoGui::dataStore->GetUserAgent());
if (NekoGui::dataStore->sub_insecure) { if (NekoGui::dataStore->sub_insecure) {
session.SetVerifySsl(cpr::VerifySsl{false}); QSslConfiguration c;
c.setPeerVerifyMode(QSslSocket::PeerVerifyMode::VerifyNone);
request.setSslConfiguration(c);
} }
session.SetUserAgent(cpr::UserAgent{NekoGui::dataStore->GetUserAgent().toStdString()}); //
session.SetTimeout(cpr::Timeout(10000)); auto _reply = accessManager.get(request);
session.SetUrl(cpr::Url(url.toStdString())); connect(_reply, &QNetworkReply::sslErrors, _reply, [](const QList<QSslError> &errors) {
auto resp = session.Get(); QStringList error_str;
auto headerPairs = QList<QPair<QByteArray, QByteArray>>(); for (const auto &err: errors) {
for (const auto &item: resp.header) { error_str << err.errorString();
headerPairs.append(std::pair<QByteArray, QByteArray>(QByteArray(item.first.c_str()), QByteArray(item.second.c_str()))); }
MW_show_log(QString("SSL Errors: %1 %2").arg(error_str.join(","), NekoGui::dataStore->sub_insecure ? "(Ignored)" : ""));
});
// Wait for response
auto abortTimer = new QTimer;
abortTimer->setSingleShot(true);
abortTimer->setInterval(10000);
connect(abortTimer, &QTimer::timeout, _reply, &QNetworkReply::abort);
abortTimer->start();
{
QEventLoop loop;
connect(_reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
loop.exec();
} }
auto err = resp.error.message.empty() ? (resp.status_code == 200 ? "" : resp.status_line) : resp.error.message; if (abortTimer != nullptr) {
auto result = NekoHTTPResponse{ err.c_str(), abortTimer->stop();
resp.text.c_str(), headerPairs}; abortTimer->deleteLater();
}
//
auto result = NekoHTTPResponse{_reply->error() == QNetworkReply::NetworkError::NoError ? "" : _reply->errorString(),
_reply->readAll(), _reply->rawHeaderPairs()};
_reply->deleteLater();
return result; return result;
} }
@ -47,38 +72,46 @@ namespace NekoGui_network {
return ""; return "";
} }
QString NetworkRequestHelper::DownloadAsset(const QString &url, const QString &fileName, bool isTemp) { QString NetworkRequestHelper::DownloadAsset(const QString &url, const QString &fileName) {
cpr::Session session; QNetworkRequest request;
session.SetUrl(cpr::Url{url.toStdString()}); QNetworkAccessManager accessManager;
request.setUrl(url);
if (NekoGui::dataStore->spmode_system_proxy) { if (NekoGui::dataStore->spmode_system_proxy) {
session.SetProxies({{"http", "127.0.0.1:" + QString(Int2String(NekoGui::dataStore->inbound_socks_port)).toStdString()}, QNetworkProxy p;
{"https", "127.0.0.1:" + QString(Int2String(NekoGui::dataStore->inbound_socks_port)).toStdString()}}); p.setType(QNetworkProxy::HttpProxy);
p.setHostName("127.0.0.1");
p.setPort(NekoGui::dataStore->inbound_socks_port);
accessManager.setProxy(p);
if (NekoGui::dataStore->started_id < 0) {
return QObject::tr("Request with proxy but no profile started.");
}
} }
auto _reply = accessManager.get(request);
connect(_reply, &QNetworkReply::sslErrors, _reply, [](const QList<QSslError> &errors) {
QStringList error_str;
for (const auto &err: errors) {
error_str << err.errorString();
}
MW_show_log(QString("SSL Errors: %1").arg(error_str.join(",")));
});
QEventLoop loop;
connect(_reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
loop.exec();
if(_reply->error() != QNetworkReply::NetworkError::NoError) {
return _reply->errorString();
}
auto filePath = NekoGui::GetBasePath()+ "/" + fileName; auto filePath = NekoGui::GetBasePath()+ "/" + fileName;
auto tempFilePath = QString(filePath); auto file = QFile(filePath);
if(isTemp) tempFilePath += ".1"; if (file.exists()) {
QFile::remove(tempFilePath); file.remove();
std::ofstream fout;
fout.open(tempFilePath.toStdString(), std::ios::trunc | std::ios::out | std::ios::binary);
auto r = session.Download(fout);
fout.close();
auto tmpFile = QFile(tempFilePath);
if (r.status_code != 200) {
tmpFile.remove();
if (r.status_code == 0) {
return "Please check the URL and your network Connectivity";
}
return r.status_line.c_str();
} }
if(isTemp) { if (!file.open(QIODevice::WriteOnly)) {
QFile(filePath).remove(); return QObject::tr("Could not open file.");
if (!tmpFile.rename(filePath)) {
tmpFile.remove();
return tmpFile.errorString();
}
} }
file.write(_reply->readAll());
file.close();
return ""; return "";
} }

View File

@ -19,11 +19,6 @@
#include "include/sys/windows/MiniDump.h" #include "include/sys/windows/MiniDump.h"
#include "include/sys/windows/vcCheck.h" #include "include/sys/windows/vcCheck.h"
#include "include/sys/windows/eventHandler.h" #include "include/sys/windows/eventHandler.h"
#pragma comment (lib, "cpr.lib")
#pragma comment (lib, "libcurl.lib")
#pragma comment (lib, "Ws2_32.lib")
#pragma comment (lib, "Wldap32.lib")
#pragma comment (lib, "Crypt32.lib")
#endif #endif
void signal_handler(int signum) { void signal_handler(int signum) {

View File

@ -2094,14 +2094,14 @@ void MainWindow::DownloadAssets(const QString &geoipUrl, const QString &geositeU
MW_show_log("Start downloading..."); MW_show_log("Start downloading...");
QString errors; QString errors;
if (!geoipUrl.isEmpty()) { if (!geoipUrl.isEmpty()) {
auto resp = NetworkRequestHelper::DownloadAsset(geoipUrl, "geoip.db", true); auto resp = NetworkRequestHelper::DownloadAsset(geoipUrl, "geoip.db");
if (!resp.isEmpty()) { if (!resp.isEmpty()) {
MW_show_log(QString(tr("Failed to download geoip: %1")).arg(resp)); MW_show_log(QString(tr("Failed to download geoip: %1")).arg(resp));
errors += "geoip: " + resp; errors += "geoip: " + resp;
} }
} }
if (!geositeUrl.isEmpty()) { if (!geositeUrl.isEmpty()) {
auto resp = NetworkRequestHelper::DownloadAsset(geositeUrl, "geosite.db", true); auto resp = NetworkRequestHelper::DownloadAsset(geositeUrl, "geosite.db");
if (!resp.isEmpty()) { if (!resp.isEmpty()) {
MW_show_log(QString(tr("Failed to download geosite: %1")).arg(resp)); MW_show_log(QString(tr("Failed to download geosite: %1")).arg(resp));
errors += "\ngeosite: " + resp; errors += "\ngeosite: " + resp;
@ -2193,7 +2193,7 @@ void MainWindow::CheckUpdate() {
}); });
return; return;
} }
auto resp = NetworkRequestHelper::HttpGet("https://api.github.com/repos/Mahdi-zarei/nekoray/releases"); auto resp = NetworkRequestHelper::HttpGet("https://api.github.com/repos/Mahdi-zarei/nekoray/releases");
if (!resp.error.isEmpty()) { if (!resp.error.isEmpty()) {
runOnUiThread([=] { runOnUiThread([=] {
@ -2201,7 +2201,7 @@ void MainWindow::CheckUpdate() {
}); });
return; return;
} }
QString assets_name, release_download_url, release_url, release_note, note_pre_release; QString assets_name, release_download_url, release_url, release_note, note_pre_release;
bool exitFlag = false; bool exitFlag = false;
QJsonArray array = QString2QJsonArray(resp.data); QJsonArray array = QString2QJsonArray(resp.data);
@ -2252,7 +2252,7 @@ void MainWindow::CheckUpdate() {
} }
QString errors; QString errors;
if (!release_download_url.isEmpty()) { if (!release_download_url.isEmpty()) {
auto res = NetworkRequestHelper::DownloadAsset(release_download_url, "nekoray.zip", false); auto res = NetworkRequestHelper::DownloadAsset(release_download_url, "nekoray.zip");
if (!res.isEmpty()) { if (!res.isEmpty()) {
errors += res; errors += res;
} }
@ -2275,4 +2275,4 @@ void MainWindow::CheckUpdate() {
QDesktopServices::openUrl(QUrl(release_url)); QDesktopServices::openUrl(QUrl(release_url));
} }
}); });
} }