3#include <QtCore/QCoreApplication>
5#include <QtCore/QIODevice>
6#include <QtCore/QJsonDocument>
7#include <QtCore/QUrlQuery>
8#include <QtNetwork/QHttpPart>
9#include <QtNetwork/QNetworkAccessManager>
10#include <QtNetwork/QNetworkInformation>
11#include <QtNetwork/QNetworkProxy>
12#include <QtNetwork/QNetworkProxyFactory>
13#include <QtNetwork/QSslSocket>
18#include <QtBluetooth/QBluetoothLocalDevice>
30 if (statusCode >= 100 && statusCode < 200) {
31 return HttpStatusClass::Informational;
33 if (statusCode >= 200 && statusCode < 300) {
34 return HttpStatusClass::Success;
36 if (statusCode >= 300 && statusCode < 400) {
37 return HttpStatusClass::Redirection;
39 if (statusCode >= 400 && statusCode < 500) {
40 return HttpStatusClass::ClientError;
42 if (statusCode >= 500 && statusCode < 600) {
43 return HttpStatusClass::ServerError;
45 return HttpStatusClass::Unknown;
52 case HttpStatusCode::Continue:
53 return QStringLiteral(
"Continue");
54 case HttpStatusCode::SwitchingProtocols:
55 return QStringLiteral(
"Switching Protocols");
56 case HttpStatusCode::Processing:
57 return QStringLiteral(
"Processing");
59 case HttpStatusCode::Ok:
60 return QStringLiteral(
"OK");
61 case HttpStatusCode::Created:
62 return QStringLiteral(
"Created");
63 case HttpStatusCode::Accepted:
64 return QStringLiteral(
"Accepted");
65 case HttpStatusCode::NonAuthoritativeInformation:
66 return QStringLiteral(
"Non-Authoritative Information");
67 case HttpStatusCode::NoContent:
68 return QStringLiteral(
"No Content");
69 case HttpStatusCode::ResetContent:
70 return QStringLiteral(
"Reset Content");
71 case HttpStatusCode::PartialContent:
72 return QStringLiteral(
"Partial Content");
73 case HttpStatusCode::MultiStatus:
74 return QStringLiteral(
"Multi-Status");
75 case HttpStatusCode::AlreadyReported:
76 return QStringLiteral(
"Already Reported");
77 case HttpStatusCode::IMUsed:
78 return QStringLiteral(
"IM Used");
80 case HttpStatusCode::MultipleChoices:
81 return QStringLiteral(
"Multiple Choices");
82 case HttpStatusCode::MovedPermanently:
83 return QStringLiteral(
"Moved Permanently");
84 case HttpStatusCode::Found:
85 return QStringLiteral(
"Found");
86 case HttpStatusCode::SeeOther:
87 return QStringLiteral(
"See Other");
88 case HttpStatusCode::NotModified:
89 return QStringLiteral(
"Not Modified");
90 case HttpStatusCode::UseProxy:
91 return QStringLiteral(
"Use Proxy");
92 case HttpStatusCode::TemporaryRedirect:
93 return QStringLiteral(
"Temporary Redirect");
94 case HttpStatusCode::PermanentRedirect:
95 return QStringLiteral(
"Permanent Redirect");
97 case HttpStatusCode::BadRequest:
98 return QStringLiteral(
"Bad Request");
99 case HttpStatusCode::Unauthorized:
100 return QStringLiteral(
"Unauthorized");
101 case HttpStatusCode::PaymentRequired:
102 return QStringLiteral(
"Payment Required");
103 case HttpStatusCode::Forbidden:
104 return QStringLiteral(
"Forbidden");
105 case HttpStatusCode::NotFound:
106 return QStringLiteral(
"Not Found");
107 case HttpStatusCode::MethodNotAllowed:
108 return QStringLiteral(
"Method Not Allowed");
109 case HttpStatusCode::NotAcceptable:
110 return QStringLiteral(
"Not Acceptable");
111 case HttpStatusCode::ProxyAuthenticationRequired:
112 return QStringLiteral(
"Proxy Authentication Required");
113 case HttpStatusCode::RequestTimeout:
114 return QStringLiteral(
"Request Timeout");
115 case HttpStatusCode::Conflict:
116 return QStringLiteral(
"Conflict");
117 case HttpStatusCode::Gone:
118 return QStringLiteral(
"Gone");
119 case HttpStatusCode::LengthRequired:
120 return QStringLiteral(
"Length Required");
121 case HttpStatusCode::PreconditionFailed:
122 return QStringLiteral(
"Precondition Failed");
123 case HttpStatusCode::PayloadTooLarge:
124 return QStringLiteral(
"Payload Too Large");
125 case HttpStatusCode::UriTooLong:
126 return QStringLiteral(
"URI Too Long");
127 case HttpStatusCode::UnsupportedMediaType:
128 return QStringLiteral(
"Unsupported Media Type");
129 case HttpStatusCode::RequestRangeNotSatisfiable:
130 return QStringLiteral(
"Range Not Satisfiable");
131 case HttpStatusCode::ExpectationFailed:
132 return QStringLiteral(
"Expectation Failed");
133 case HttpStatusCode::ImATeapot:
134 return QStringLiteral(
"I'm a teapot");
135 case HttpStatusCode::MisdirectedRequest:
136 return QStringLiteral(
"Misdirected Request");
137 case HttpStatusCode::UnprocessableEntity:
138 return QStringLiteral(
"Unprocessable Entity");
139 case HttpStatusCode::Locked:
140 return QStringLiteral(
"Locked");
141 case HttpStatusCode::FailedDependency:
142 return QStringLiteral(
"Failed Dependency");
143 case HttpStatusCode::UpgradeRequired:
144 return QStringLiteral(
"Upgrade Required");
145 case HttpStatusCode::PreconditionRequired:
146 return QStringLiteral(
"Precondition Required");
147 case HttpStatusCode::TooManyRequests:
148 return QStringLiteral(
"Too Many Requests");
149 case HttpStatusCode::RequestHeaderFieldsTooLarge:
150 return QStringLiteral(
"Request Header Fields Too Large");
151 case HttpStatusCode::UnavailableForLegalReasons:
152 return QStringLiteral(
"Unavailable For Legal Reasons");
154 case HttpStatusCode::InternalServerError:
155 return QStringLiteral(
"Internal Server Error");
156 case HttpStatusCode::NotImplemented:
157 return QStringLiteral(
"Not Implemented");
158 case HttpStatusCode::BadGateway:
159 return QStringLiteral(
"Bad Gateway");
160 case HttpStatusCode::ServiceUnavailable:
161 return QStringLiteral(
"Service Unavailable");
162 case HttpStatusCode::GatewayTimeout:
163 return QStringLiteral(
"Gateway Timeout");
164 case HttpStatusCode::HttpVersionNotSupported:
165 return QStringLiteral(
"HTTP Version Not Supported");
166 case HttpStatusCode::VariantAlsoNegotiates:
167 return QStringLiteral(
"Variant Also Negotiates");
168 case HttpStatusCode::InsufficientStorage:
169 return QStringLiteral(
"Insufficient Storage");
170 case HttpStatusCode::LoopDetected:
171 return QStringLiteral(
"Loop Detected");
172 case HttpStatusCode::NotExtended:
173 return QStringLiteral(
"Not Extended");
174 case HttpStatusCode::NetworkAuthenticationRequired:
175 return QStringLiteral(
"Network Authentication Required");
176 case HttpStatusCode::NetworkConnectTimeoutError:
177 return QStringLiteral(
"Network Connect Timeout Error");
179 return QStringLiteral(
"Unknown Status");
186 return QStringLiteral(
"Unknown Status (%1)").arg(statusCode);
199 case HttpMethod::Get:
200 return QStringLiteral(
"GET");
201 case HttpMethod::Post:
202 return QStringLiteral(
"POST");
203 case HttpMethod::Put:
204 return QStringLiteral(
"PUT");
205 case HttpMethod::Delete:
206 return QStringLiteral(
"DELETE");
207 case HttpMethod::Head:
208 return QStringLiteral(
"HEAD");
209 case HttpMethod::Options:
210 return QStringLiteral(
"OPTIONS");
211 case HttpMethod::Patch:
212 return QStringLiteral(
"PATCH");
213 case HttpMethod::Connect:
214 return QStringLiteral(
"CONNECT");
215 case HttpMethod::Trace:
216 return QStringLiteral(
"TRACE");
218 return QStringLiteral(
"GET");
224 const QByteArray upper = methodStr.toUpper().toLatin1();
225 const char* str = upper.constData();
227 if (qstrcmp(str,
"GET") == 0)
228 return HttpMethod::Get;
229 if (qstrcmp(str,
"POST") == 0)
230 return HttpMethod::Post;
231 if (qstrcmp(str,
"PUT") == 0)
232 return HttpMethod::Put;
233 if (qstrcmp(str,
"DELETE") == 0)
234 return HttpMethod::Delete;
235 if (qstrcmp(str,
"HEAD") == 0)
236 return HttpMethod::Head;
237 if (qstrcmp(str,
"OPTIONS") == 0)
238 return HttpMethod::Options;
239 if (qstrcmp(str,
"PATCH") == 0)
240 return HttpMethod::Patch;
241 if (qstrcmp(str,
"CONNECT") == 0)
242 return HttpMethod::Connect;
243 if (qstrcmp(str,
"TRACE") == 0)
244 return HttpMethod::Trace;
246 return HttpMethod::Get;
255 if (!url.isValid()) {
259 const QString scheme = url.scheme().toLower();
260 return scheme == QLatin1String(
"http") || scheme == QLatin1String(
"https") || scheme == QLatin1String(
"file") ||
261 scheme == QLatin1String(
"qrc") || scheme.isEmpty();
266 const QString scheme = url.scheme().toLower();
267 return scheme == QLatin1String(
"http") || scheme == QLatin1String(
"https");
272 return url.scheme().toLower() == QLatin1String(
"https");
277 if (!url.isValid()) {
281 QUrl normalized = url;
284 normalized.setScheme(normalized.scheme().toLower());
285 normalized.setHost(normalized.host().toLower());
288 const int port = normalized.port();
289 const QString scheme = normalized.scheme();
290 if ((scheme == QLatin1String(
"http") && port == 80) || (scheme == QLatin1String(
"https") && port == 443) ||
291 (scheme == QLatin1String(
"ftp") && port == 21)) {
292 normalized.setPort(-1);
296 QString path = normalized.path();
297 if (path.length() > 1 && path.endsWith(QLatin1Char(
'/'))) {
299 normalized.setPath(path);
307 if (!url.isValid()) {
311 if (url.scheme().isEmpty()) {
312 QUrl withScheme = url;
313 withScheme.setScheme(defaultScheme);
320QUrl
buildUrl(
const QString& baseUrl,
const QMap<QString, QString>& params)
323 if (!url.isValid()) {
328 for (
auto it = params.constBegin(); it != params.constEnd(); ++it) {
329 query.addQueryItem(it.key(), it.value());
336QUrl
buildUrl(
const QString& baseUrl,
const QList<QPair<QString, QString>>& params)
339 if (!url.isValid()) {
344 for (
const auto& [key, value] : params) {
345 query.addQueryItem(key, value);
354 const QString path = url.path();
355 const int lastSlash = path.lastIndexOf(QLatin1Char(
'/'));
356 if (lastSlash >= 0 && lastSlash < path.length() - 1) {
357 return path.mid(lastSlash + 1);
365 result.setQuery(QString());
366 result.setFragment(QString());
377 request.setTransferTimeout(config.
timeoutMs);
381 request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
383 request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::ManualRedirectPolicy);
387 request.setAttribute(QNetworkRequest::Http2AllowedAttribute, config.
http2Allowed);
391 request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
392 request.setAttribute(QNetworkRequest::CacheSaveControlAttribute,
true);
394 request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork);
395 request.setAttribute(QNetworkRequest::CacheSaveControlAttribute,
false);
399 request.setAttribute(QNetworkRequest::BackgroundRequestAttribute, config.
backgroundRequest);
403 request.setHeader(QNetworkRequest::UserAgentHeader, userAgent);
405 if (!config.
accept.isEmpty()) {
406 request.setRawHeader(
"Accept", config.
accept.toUtf8());
410 request.setRawHeader(
"Accept-Encoding", config.
acceptEncoding.toUtf8());
414 request.setHeader(QNetworkRequest::ContentTypeHeader, config.
contentType);
418 request.setAttribute(attribute, value);
421 request.setRawHeader(
"Connection",
"keep-alive");
426 QNetworkRequest request(url);
434 request.setHeader(QNetworkRequest::UserAgentHeader, ua);
435 request.setRawHeader(
"Accept",
"*/*");
436 request.setRawHeader(
"Accept-Encoding",
"gzip, deflate");
437 request.setRawHeader(
"Connection",
"keep-alive");
442 request.setRawHeader(
"Accept",
"application/json");
443 request.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral(
"application/json"));
448 request.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral(
"application/x-www-form-urlencoded"));
453 static QString userAgent;
454 if (userAgent.isEmpty()) {
455 userAgent = QStringLiteral(
"%1/%2 (Qt %3)")
456 .arg(QCoreApplication::applicationName())
457 .arg(QCoreApplication::applicationVersion())
458 .arg(QString::fromLatin1(qVersion()));
469 request.setRawHeader(
"Authorization", (
"Basic " + credentials).toUtf8());
472void setBasicAuth(QNetworkRequest& request,
const QString& username,
const QString& password)
479 request.setRawHeader(
"Authorization", (
"Bearer " + token).toUtf8());
484 const QString credentials = username + QLatin1Char(
':') + password;
485 return QString::fromLatin1(credentials.toUtf8().toBase64());
495 part.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral(
"form-data; name=\"%1\"").arg(name));
496 part.setBody(value.toUtf8());
500QHttpPart
createFilePart(
const QString& name,
const QString& fileName,
const QString& contentType, QIODevice* device)
503 part.setHeader(QNetworkRequest::ContentTypeHeader,
contentType);
504 part.setHeader(QNetworkRequest::ContentDispositionHeader,
505 QStringLiteral(
"form-data; name=\"%1\"; filename=\"%2\"").arg(name, fileName));
506 part.setBodyDevice(device);
510QHttpPart
createFilePart(
const QString& name,
const QString& fileName, QIODevice* device)
521 QSslConfiguration config = QSslConfiguration::defaultConfiguration();
522 config.setProtocol(protocol);
528 QSslConfiguration config = QSslConfiguration::defaultConfiguration();
529 config.setPeerVerifyMode(QSslSocket::VerifyNone);
535 request.setSslConfiguration(config);
540 const auto certs = QSslCertificate::fromPath(filePath, QSsl::Pem);
541 if (certs.isEmpty() && errorOut) {
542 *errorOut = QStringLiteral(
"No certificates found in %1").arg(filePath);
548 QSslCertificate& certOut, QSslKey& keyOut,
551 const auto certs = QSslCertificate::fromPath(certPath, QSsl::Pem);
552 if (certs.isEmpty()) {
553 if (errorOut) *errorOut = QStringLiteral(
"No certificate found in %1").arg(certPath);
557 QFile keyFile(keyPath);
558 if (!keyFile.open(QIODevice::ReadOnly)) {
559 if (errorOut) *errorOut = QStringLiteral(
"Cannot open key file %1").arg(keyPath);
563 QSslKey key(&keyFile, QSsl::Rsa);
566 key = QSslKey(&keyFile, QSsl::Ec);
569 if (errorOut) *errorOut = QStringLiteral(
"Invalid key in %1").arg(keyPath);
573 certOut = certs.first();
584 QJsonParseError localError;
585 QJsonParseError* errorPtr = (
error !=
nullptr) ?
error : &localError;
587 QJsonDocument doc = QJsonDocument::fromJson(data, errorPtr);
589 if (errorPtr->error != QJsonParseError::NoError) {
590 qCWarning(QGCNetworkHelperLog) <<
"JSON parse error:" << errorPtr->errorString() <<
"at offset"
600 if (reply ==
nullptr) {
601 if (
error !=
nullptr) {
602 error->error = QJsonParseError::UnterminatedObject;
608 if (reply->error() != QNetworkReply::NoError) {
609 qCWarning(QGCNetworkHelperLog) <<
"Network error before JSON parse:" << reply->errorString();
610 if (
error !=
nullptr) {
611 error->error = QJsonParseError::UnterminatedObject;
622 if (data.isEmpty()) {
627 for (
int i = 0; i < data.size(); ++i) {
628 const char c = data.at(i);
629 if (c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\r') {
632 return c ==
'{' || c ==
'[';
647 const QVariant statusAttr = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
648 return statusAttr.isValid() ? statusAttr.toInt() : -1;
657 const QVariant redirectAttr = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
658 if (redirectAttr.isNull()) {
662 const QUrl redirectTarget = redirectAttr.toUrl();
663 return reply->url().resolved(redirectTarget);
669 return QStringLiteral(
"No reply");
673 if (reply->error() != QNetworkReply::NoError) {
674 return reply->errorString();
680 return QStringLiteral(
"HTTP %1: %2").arg(status).arg(
httpStatusText(status));
692 if (reply->error() != QNetworkReply::NoError) {
715 return reply->header(QNetworkRequest::ContentTypeHeader).toString();
723 const QVariant lenAttr = reply->header(QNetworkRequest::ContentLengthHeader);
724 return lenAttr.isValid() ? lenAttr.toLongLong() : -1;
730 return type.contains(QLatin1String(
"application/json"), Qt::CaseInsensitive) ||
731 type.contains(QLatin1String(
"+json"), Qt::CaseInsensitive);
740 if (!QNetworkInformation::loadDefaultBackend()) {
741 qCDebug(QGCNetworkHelperLog) <<
"Failed to load network information backend";
745 const QNetworkInformation* netInfo = QNetworkInformation::instance();
746 if (netInfo ==
nullptr) {
750 return netInfo->reachability() != QNetworkInformation::Reachability::Disconnected;
755 if (QNetworkInformation::availableBackends().isEmpty()) {
759 if (!QNetworkInformation::loadDefaultBackend()) {
763 if (!QNetworkInformation::loadBackendByFeatures(QNetworkInformation::Feature::Reachability)) {
767 const QNetworkInformation* netInfo = QNetworkInformation::instance();
768 if (netInfo ==
nullptr) {
772 return netInfo->reachability() == QNetworkInformation::Reachability::Online;
777 if (QNetworkInformation::availableBackends().isEmpty()) {
781 if (!QNetworkInformation::loadDefaultBackend()) {
785 const QNetworkInformation* netInfo = QNetworkInformation::instance();
786 if (netInfo ==
nullptr) {
790 return netInfo->transportMedium() == QNetworkInformation::TransportMedium::Ethernet;
795 const QList<QBluetoothHostInfo> devices = QBluetoothLocalDevice::allDevices();
796 return !devices.isEmpty();
801 if (!QNetworkInformation::loadDefaultBackend()) {
802 return ConnectionType::Unknown;
805 const QNetworkInformation* netInfo = QNetworkInformation::instance();
807 return ConnectionType::Unknown;
810 if (netInfo->reachability() == QNetworkInformation::Reachability::Disconnected) {
811 return ConnectionType::None;
814 switch (netInfo->transportMedium()) {
815 case QNetworkInformation::TransportMedium::Ethernet:
816 return ConnectionType::Ethernet;
817 case QNetworkInformation::TransportMedium::WiFi:
818 return ConnectionType::WiFi;
819 case QNetworkInformation::TransportMedium::Cellular:
820 return ConnectionType::Cellular;
821 case QNetworkInformation::TransportMedium::Bluetooth:
822 return ConnectionType::Bluetooth;
824 return ConnectionType::Unknown;
831 case ConnectionType::None:
832 return QStringLiteral(
"None");
833 case ConnectionType::Ethernet:
834 return QStringLiteral(
"Ethernet");
835 case ConnectionType::WiFi:
836 return QStringLiteral(
"WiFi");
837 case ConnectionType::Cellular:
838 return QStringLiteral(
"Cellular");
839 case ConnectionType::Bluetooth:
840 return QStringLiteral(
"Bluetooth");
841 case ConnectionType::Unknown:
843 return QStringLiteral(
"Unknown");
857 QObject::connect(reply, &QNetworkReply::sslErrors, reply, [reply](
const QList<QSslError>& errors) {
858 qCWarning(QGCNetworkHelperLog) <<
"Ignoring SSL errors for" << reply->url();
859 for (
const QSslError&
error : errors) {
860 qCDebug(QGCNetworkHelperLog) <<
" -" <<
error.errorString();
862 reply->ignoreSslErrors();
873 const bool sslLibraryBuildIs1x = ((QSslSocket::sslLibraryBuildVersionNumber() & 0xf0000000) == 0x10000000);
874 const bool sslLibraryIs3x = ((QSslSocket::sslLibraryVersionNumber() & 0xf0000000) == 0x30000000);
875 if (sslLibraryBuildIs1x && sslLibraryIs3x) {
876 qCWarning(QGCNetworkHelperLog) <<
"Ignoring SSL certificate errors due to OpenSSL version mismatch";
877 QList<QSslError> errorsThatCanBeIgnored;
878 errorsThatCanBeIgnored << QSslError(QSslError::NoPeerCertificate);
879 reply->ignoreSslErrors(errorsThatCanBeIgnored);
885 return QSslSocket::supportsSsl();
890 return QSslSocket::sslLibraryVersionString();
899 QNetworkProxyFactory::setUseSystemConfiguration(
true);
904 auto* manager =
new QNetworkAccessManager(parent);
915#if !defined(Q_OS_IOS) && !defined(Q_OS_ANDROID)
916 QNetworkProxy proxy = manager->proxy();
917 proxy.setType(QNetworkProxy::DefaultProxy);
918 manager->setProxy(proxy);
#define QGC_LOGGING_CATEGORY(name, categoryStr)
HttpStatusClass classifyHttpStatus(int statusCode)
Classify an HTTP status code.
bool isSuccess(const QNetworkReply *reply)
Check if reply indicates success (no error and HTTP 2xx)
QHttpServerRequest::Method HttpMethod
HTTP request methods - uses Qt's QHttpServerRequest::Method enum.
HttpStatusClass
HTTP status code ranges.
void applySslConfig(QNetworkRequest &request, const QSslConfiguration &config)
QHttpServerResponder::StatusCode HttpStatusCode
HTTP status codes - uses Qt's QHttpServerResponder::StatusCode enum.
QList< QSslCertificate > loadCaCertificates(const QString &filePath, QString *errorOut)
bool looksLikeJson(const QByteArray &data)
void configureProxy(QNetworkAccessManager *manager)
Set up default proxy configuration on a network manager.
bool isJsonResponse(const QNetworkReply *reply)
Check if response is JSON based on Content-Type.
const QString kContentTypeOctetStream
void initializeProxySupport()
QNetworkAccessManager * createNetworkManager(QObject *parent)
ConnectionType
Network connection types.
QNetworkRequest createRequest(const QUrl &url, const RequestConfig &config)
bool isRedirect(const QNetworkReply *reply)
Check if reply indicates a redirect.
bool isNetworkEthernet()
Check if current network connection is Ethernet.
QString sslVersion()
Get SSL library version string.
bool isHttpRedirect(int statusCode)
Check if HTTP status indicates redirect (3xx)
HttpMethod parseHttpMethod(const QString &methodStr)
bool isHttpUrl(const QUrl &url)
Check if URL uses HTTP or HTTPS scheme.
QHttpPart createFormField(const QString &name, const QString &value)
void setFormHeaders(QNetworkRequest &request)
Set form data content headers.
QString urlFileName(const QUrl &url)
Extract filename from URL path (last path segment)
QUrl redirectUrl(const QNetworkReply *reply)
void ignoreSslErrorsIfNeeded(QNetworkReply *reply)
void setStandardHeaders(QNetworkRequest &request, const QString &userAgent)
Set standard browser-like headers on a request.
bool isBluetoothAvailable()
Check if Bluetooth is available on this device.
QString contentType(const QNetworkReply *reply)
Get Content-Type header from reply.
bool loadClientCertAndKey(const QString &certPath, const QString &keyPath, QSslCertificate &certOut, QSslKey &keyOut, QString *errorOut)
int httpStatusCode(const QNetworkReply *reply)
QSslConfiguration createInsecureSslConfig()
QString defaultUserAgent()
Get the default User-Agent string for QGC.
QUrl buildUrl(const QString &baseUrl, const QMap< QString, QString > ¶ms)
Build URL with query parameters from a map.
bool isNetworkAvailable()
Check if network is available (not disconnected)
void setBasicAuth(QNetworkRequest &request, const QString &credentials)
QString createBasicAuthCredentials(const QString &username, const QString &password)
QString httpStatusText(HttpStatusCode statusCode)
bool isHttpsUrl(const QUrl &url)
Check if URL uses secure HTTPS scheme.
QString httpMethodName(HttpMethod method)
Get string name for an HTTP method (e.g., "GET", "POST")
bool isHttpSuccess(int statusCode)
Check if HTTP status indicates success (2xx)
ConnectionType connectionType()
Get current network connection type.
QJsonDocument parseJson(const QByteArray &data, QJsonParseError *error)
QSslConfiguration createSslConfig(QSsl::SslProtocol protocol)
void setBearerToken(QNetworkRequest &request, const QString &token)
bool isValidUrl(const QUrl &url)
QHttpPart createFilePart(const QString &name, const QString &fileName, const QString &contentType, QIODevice *device)
QJsonDocument parseJsonReply(QNetworkReply *reply, QJsonParseError *error)
QUrl normalizeUrl(const QUrl &url)
Normalize URL (lowercase scheme/host, remove default ports, trailing slashes)
QUrl urlWithoutQuery(const QUrl &url)
Get URL without query string and fragment.
void setJsonHeaders(QNetworkRequest &request)
Set JSON content headers (Accept and Content-Type)
qint64 contentLength(const QNetworkReply *reply)
Get Content-Length header from reply (-1 if not present)
QString connectionTypeName(ConnectionType type)
Get human-readable name for connection type.
QString errorMessage(const QNetworkReply *reply)
QUrl ensureScheme(const QUrl &url, const QString &defaultScheme)
Ensure URL has scheme, defaulting to https:// if missing.
bool isInternetAvailable()
Check if internet is reachable (online state, stricter than isNetworkAvailable)
void ignoreSslErrors(QNetworkReply *reply)
void configureRequest(QNetworkRequest &request, const RequestConfig &config)
bool isSslAvailable()
Check if SSL is available.
Common request configuration options.
QList< QPair< QNetworkRequest::Attribute, QVariant > > requestAttributes