3#include <QtBluetooth/QBluetoothLocalDevice>
5#include <QtCore/QIODevice>
6#include <QtCore/QJsonDocument>
7#include <QtCore/QUrlQuery>
8#include <QtNetwork/QHostAddress>
9#include <QtNetwork/QHttpHeaders>
10#include <QtNetwork/QHttpPart>
11#include <QtNetwork/QNetworkAccessManager>
12#include <QtNetwork/QNetworkInformation>
13#include <QtNetwork/QNetworkProxyFactory>
14#include <QtNetwork/QSslSocket>
31 if (code >= HttpStatusCode::Continue && code < HttpStatusCode::Ok) {
32 return HttpStatusClass::Informational;
34 if (code >= HttpStatusCode::Ok && code < HttpStatusCode::MultipleChoices) {
35 return HttpStatusClass::Success;
37 if (code >= HttpStatusCode::MultipleChoices && code < HttpStatusCode::BadRequest) {
38 return HttpStatusClass::Redirection;
40 if (code >= HttpStatusCode::BadRequest && code < HttpStatusCode::InternalServerError) {
41 return HttpStatusClass::ClientError;
43 if (code >= HttpStatusCode::InternalServerError && code <= HttpStatusCode::NetworkConnectTimeoutError) {
44 return HttpStatusClass::ServerError;
46 return HttpStatusClass::Unknown;
53 case HttpStatusCode::Continue:
54 return QStringLiteral(
"Continue");
55 case HttpStatusCode::SwitchingProtocols:
56 return QStringLiteral(
"Switching Protocols");
57 case HttpStatusCode::Processing:
58 return QStringLiteral(
"Processing");
60 case HttpStatusCode::Ok:
61 return QStringLiteral(
"OK");
62 case HttpStatusCode::Created:
63 return QStringLiteral(
"Created");
64 case HttpStatusCode::Accepted:
65 return QStringLiteral(
"Accepted");
66 case HttpStatusCode::NonAuthoritativeInformation:
67 return QStringLiteral(
"Non-Authoritative Information");
68 case HttpStatusCode::NoContent:
69 return QStringLiteral(
"No Content");
70 case HttpStatusCode::ResetContent:
71 return QStringLiteral(
"Reset Content");
72 case HttpStatusCode::PartialContent:
73 return QStringLiteral(
"Partial Content");
74 case HttpStatusCode::MultiStatus:
75 return QStringLiteral(
"Multi-Status");
76 case HttpStatusCode::AlreadyReported:
77 return QStringLiteral(
"Already Reported");
78 case HttpStatusCode::IMUsed:
79 return QStringLiteral(
"IM Used");
81 case HttpStatusCode::MultipleChoices:
82 return QStringLiteral(
"Multiple Choices");
83 case HttpStatusCode::MovedPermanently:
84 return QStringLiteral(
"Moved Permanently");
85 case HttpStatusCode::Found:
86 return QStringLiteral(
"Found");
87 case HttpStatusCode::SeeOther:
88 return QStringLiteral(
"See Other");
89 case HttpStatusCode::NotModified:
90 return QStringLiteral(
"Not Modified");
91 case HttpStatusCode::UseProxy:
92 return QStringLiteral(
"Use Proxy");
93 case HttpStatusCode::TemporaryRedirect:
94 return QStringLiteral(
"Temporary Redirect");
95 case HttpStatusCode::PermanentRedirect:
96 return QStringLiteral(
"Permanent Redirect");
98 case HttpStatusCode::BadRequest:
99 return QStringLiteral(
"Bad Request");
100 case HttpStatusCode::Unauthorized:
101 return QStringLiteral(
"Unauthorized");
102 case HttpStatusCode::PaymentRequired:
103 return QStringLiteral(
"Payment Required");
104 case HttpStatusCode::Forbidden:
105 return QStringLiteral(
"Forbidden");
106 case HttpStatusCode::NotFound:
107 return QStringLiteral(
"Not Found");
108 case HttpStatusCode::MethodNotAllowed:
109 return QStringLiteral(
"Method Not Allowed");
110 case HttpStatusCode::NotAcceptable:
111 return QStringLiteral(
"Not Acceptable");
112 case HttpStatusCode::ProxyAuthenticationRequired:
113 return QStringLiteral(
"Proxy Authentication Required");
114 case HttpStatusCode::RequestTimeout:
115 return QStringLiteral(
"Request Timeout");
116 case HttpStatusCode::Conflict:
117 return QStringLiteral(
"Conflict");
118 case HttpStatusCode::Gone:
119 return QStringLiteral(
"Gone");
120 case HttpStatusCode::LengthRequired:
121 return QStringLiteral(
"Length Required");
122 case HttpStatusCode::PreconditionFailed:
123 return QStringLiteral(
"Precondition Failed");
124 case HttpStatusCode::PayloadTooLarge:
125 return QStringLiteral(
"Payload Too Large");
126 case HttpStatusCode::UriTooLong:
127 return QStringLiteral(
"URI Too Long");
128 case HttpStatusCode::UnsupportedMediaType:
129 return QStringLiteral(
"Unsupported Media Type");
130 case HttpStatusCode::RequestRangeNotSatisfiable:
131 return QStringLiteral(
"Range Not Satisfiable");
132 case HttpStatusCode::ExpectationFailed:
133 return QStringLiteral(
"Expectation Failed");
134 case HttpStatusCode::ImATeapot:
135 return QStringLiteral(
"I'm a teapot");
136 case HttpStatusCode::MisdirectedRequest:
137 return QStringLiteral(
"Misdirected Request");
138 case HttpStatusCode::UnprocessableEntity:
139 return QStringLiteral(
"Unprocessable Entity");
140 case HttpStatusCode::Locked:
141 return QStringLiteral(
"Locked");
142 case HttpStatusCode::FailedDependency:
143 return QStringLiteral(
"Failed Dependency");
144 case HttpStatusCode::UpgradeRequired:
145 return QStringLiteral(
"Upgrade Required");
146 case HttpStatusCode::PreconditionRequired:
147 return QStringLiteral(
"Precondition Required");
148 case HttpStatusCode::TooManyRequests:
149 return QStringLiteral(
"Too Many Requests");
150 case HttpStatusCode::RequestHeaderFieldsTooLarge:
151 return QStringLiteral(
"Request Header Fields Too Large");
152 case HttpStatusCode::UnavailableForLegalReasons:
153 return QStringLiteral(
"Unavailable For Legal Reasons");
155 case HttpStatusCode::InternalServerError:
156 return QStringLiteral(
"Internal Server Error");
157 case HttpStatusCode::NotImplemented:
158 return QStringLiteral(
"Not Implemented");
159 case HttpStatusCode::BadGateway:
160 return QStringLiteral(
"Bad Gateway");
161 case HttpStatusCode::ServiceUnavailable:
162 return QStringLiteral(
"Service Unavailable");
163 case HttpStatusCode::GatewayTimeout:
164 return QStringLiteral(
"Gateway Timeout");
165 case HttpStatusCode::HttpVersionNotSupported:
166 return QStringLiteral(
"HTTP Version Not Supported");
167 case HttpStatusCode::VariantAlsoNegotiates:
168 return QStringLiteral(
"Variant Also Negotiates");
169 case HttpStatusCode::InsufficientStorage:
170 return QStringLiteral(
"Insufficient Storage");
171 case HttpStatusCode::LoopDetected:
172 return QStringLiteral(
"Loop Detected");
173 case HttpStatusCode::NotExtended:
174 return QStringLiteral(
"Not Extended");
175 case HttpStatusCode::NetworkAuthenticationRequired:
176 return QStringLiteral(
"Network Authentication Required");
177 case HttpStatusCode::NetworkConnectTimeoutError:
178 return QStringLiteral(
"Network Connect Timeout Error");
180 return QStringLiteral(
"Unknown Status (%1)").arg(
static_cast<int>(statusCode));
196 case HttpMethod::Get:
197 return QStringLiteral(
"GET");
198 case HttpMethod::Post:
199 return QStringLiteral(
"POST");
200 case HttpMethod::Put:
201 return QStringLiteral(
"PUT");
202 case HttpMethod::Delete:
203 return QStringLiteral(
"DELETE");
204 case HttpMethod::Head:
205 return QStringLiteral(
"HEAD");
206 case HttpMethod::Options:
207 return QStringLiteral(
"OPTIONS");
208 case HttpMethod::Patch:
209 return QStringLiteral(
"PATCH");
210 case HttpMethod::Connect:
211 return QStringLiteral(
"CONNECT");
212 case HttpMethod::Trace:
213 return QStringLiteral(
"TRACE");
215 return QStringLiteral(
"GET");
221 const QByteArray upper = methodStr.toUpper().toLatin1();
222 const char* str = upper.constData();
224 if (qstrcmp(str,
"GET") == 0)
225 return HttpMethod::Get;
226 if (qstrcmp(str,
"POST") == 0)
227 return HttpMethod::Post;
228 if (qstrcmp(str,
"PUT") == 0)
229 return HttpMethod::Put;
230 if (qstrcmp(str,
"DELETE") == 0)
231 return HttpMethod::Delete;
232 if (qstrcmp(str,
"HEAD") == 0)
233 return HttpMethod::Head;
234 if (qstrcmp(str,
"OPTIONS") == 0)
235 return HttpMethod::Options;
236 if (qstrcmp(str,
"PATCH") == 0)
237 return HttpMethod::Patch;
238 if (qstrcmp(str,
"CONNECT") == 0)
239 return HttpMethod::Connect;
240 if (qstrcmp(str,
"TRACE") == 0)
241 return HttpMethod::Trace;
243 return HttpMethod::Get;
252 if (!url.isValid()) {
256 const QString scheme = url.scheme().toLower();
257 return scheme == QLatin1String(
"http") || scheme == QLatin1String(
"https") || scheme == QLatin1String(
"file") ||
258 scheme == QLatin1String(
"qrc") || scheme.isEmpty();
263 const QString scheme = url.scheme().toLower();
264 return scheme == QLatin1String(
"http") || scheme == QLatin1String(
"https");
269 return url.scheme().toLower() == QLatin1String(
"https");
274 if (!url.isValid()) {
278 QUrl normalized = url.adjusted(QUrl::NormalizePathSegments | QUrl::StripTrailingSlash);
279 normalized.setScheme(normalized.scheme().toLower());
280 normalized.setHost(normalized.host().toLower());
282 const int port = normalized.port();
283 const QString scheme = normalized.scheme();
284 if ((scheme == QLatin1String(
"http") && port == 80) || (scheme == QLatin1String(
"https") && port == 443) ||
285 (scheme == QLatin1String(
"ftp") && port == 21)) {
286 normalized.setPort(-1);
294 if (!url.isValid()) {
298 if (url.scheme().isEmpty()) {
299 QUrl withScheme = url;
300 withScheme.setScheme(defaultScheme);
307QUrl
buildUrl(
const QString& baseUrl,
const QMap<QString, QString>& params)
310 if (!url.isValid()) {
315 for (
auto it = params.constBegin(); it != params.constEnd(); ++it) {
316 query.addQueryItem(it.key(), it.value());
323QUrl
buildUrl(
const QString& baseUrl,
const QList<QPair<QString, QString>>& params)
326 if (!url.isValid()) {
331 for (
const auto& [key, value] : params) {
332 query.addQueryItem(key, value);
341 return url.adjusted(QUrl::RemoveQuery | QUrl::RemoveFragment);
345enum class HostPortClassification
352bool isAsciiAlphaNumeric(
char character)
354 return ((character >=
'a') && (character <=
'z')) || ((character >=
'A') && (character <=
'Z')) ||
355 ((character >=
'0') && (character <=
'9'));
358bool isValidHostname(QString hostname)
360 if (hostname.endsWith(QLatin1Char(
'.'))) {
364 const QByteArray aceHostname = QUrl::toAce(hostname);
365 if (aceHostname.isEmpty() || (aceHostname.size() > 253)) {
369 const QList<QByteArray> labels = aceHostname.split(
'.');
370 for (
const QByteArray& label : labels) {
371 if (label.isEmpty() || (label.size() > 63) || !isAsciiAlphaNumeric(label.front()) ||
372 !isAsciiAlphaNumeric(label.back())) {
375 for (
const char character : label) {
376 if (!isAsciiAlphaNumeric(character) && (character !=
'-')) {
384HostPortClassification classifyHostPort(
const QString& value)
386 if (value.contains(QStringLiteral(
"://"))) {
387 return HostPortClassification::NotHostPort;
390 const qsizetype separator = value.lastIndexOf(QLatin1Char(
':'));
391 if ((separator <= 0) || (separator == (value.size() - 1))) {
392 return HostPortClassification::NotHostPort;
395 const QString portText = value.sliced(separator + 1);
396 for (
const QChar character : portText) {
397 if (!character.isDigit()) {
398 return HostPortClassification::NotHostPort;
403 const int port = portText.toInt(&portOk);
404 if (!portOk || (port < 1) || (port > 65535)) {
405 return HostPortClassification::Invalid;
408 const QUrl authority(QStringLiteral(
"qgc://") + value, QUrl::StrictMode);
409 if (!authority.isValid() || authority.host().isEmpty() || !authority.userInfo().isEmpty() ||
410 !authority.path().isEmpty() || authority.hasQuery() || authority.hasFragment() ||
411 (authority.port(-1) != port)) {
412 return HostPortClassification::Invalid;
415 QHostAddress address;
416 const bool validHost = address.setAddress(authority.host()) || isValidHostname(authority.host());
417 return validHost ? HostPortClassification::Valid : HostPortClassification::Invalid;
424 return QStringLiteral(
"<empty-url>");
427 const QString sourceText = url.isValid() ? url.toString(QUrl::FullyEncoded) : url.path();
428 const HostPortClassification hostPort = classifyHostPort(sourceText);
429 if (hostPort == HostPortClassification::Valid) {
432 if (hostPort == HostPortClassification::Invalid) {
433 return QStringLiteral(
"<invalid-url length=%1>").arg(sourceText.size());
435 if (!url.isValid()) {
436 return QStringLiteral(
"<invalid-url>");
439 QUrl redactedUrl = url.adjusted(QUrl::RemoveUserInfo);
440 if (redactedUrl.hasQuery()) {
441 const auto queryItems = QUrlQuery(redactedUrl).queryItems(QUrl::FullyDecoded);
442 QUrlQuery redactedQuery;
443 for (
const auto& queryItem : queryItems) {
444 redactedQuery.addQueryItem(queryItem.first, QStringLiteral(
"REDACTED"));
446 if (queryItems.isEmpty()) {
447 redactedUrl.setQuery(QString());
449 redactedUrl.setQuery(redactedQuery);
452 if (redactedUrl.hasFragment()) {
453 redactedUrl.setFragment(QStringLiteral(
"REDACTED"));
456 return redactedUrl.toDisplayString(QUrl::FullyEncoded);
461 const HostPortClassification hostPort = classifyHostPort(url);
462 if (hostPort == HostPortClassification::Valid) {
465 if (hostPort == HostPortClassification::Invalid) {
466 return QStringLiteral(
"<invalid-url length=%1>").arg(url.size());
468 const QUrl parsedUrl(url);
469 if (!url.isEmpty() && !parsedUrl.isValid()) {
470 return QStringLiteral(
"<invalid-url length=%1>").arg(url.size());
482 request.setTransferTimeout(
config.timeoutMs);
485 if (
config.allowRedirects) {
486 request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
488 request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::ManualRedirectPolicy);
492 request.setAttribute(QNetworkRequest::Http2AllowedAttribute,
config.http2Allowed);
495 if (
config.cacheEnabled) {
496 request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
497 request.setAttribute(QNetworkRequest::CacheSaveControlAttribute,
true);
499 request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork);
500 request.setAttribute(QNetworkRequest::CacheSaveControlAttribute,
false);
504 request.setAttribute(QNetworkRequest::BackgroundRequestAttribute,
config.backgroundRequest);
506 using WK = QHttpHeaders::WellKnownHeader;
507 QHttpHeaders headers = request.headers();
509 if (!
config.accept.isEmpty()) {
510 headers.replaceOrAppend(WK::Accept,
config.accept);
512 if (!
config.acceptEncoding.isEmpty()) {
513 headers.replaceOrAppend(WK::AcceptEncoding,
config.acceptEncoding);
515 if (!
config.contentType.isEmpty()) {
516 headers.replaceOrAppend(WK::ContentType,
config.contentType);
518 headers.replaceOrAppend(WK::Connection,
"keep-alive");
519 request.setHeaders(headers);
521 for (
const auto& [attribute, value] :
config.requestAttributes) {
522 request.setAttribute(attribute, value);
525 if (
config.connectionCacheExpirySecs >= 0) {
526 request.setAttribute(QNetworkRequest::ConnectionCacheExpiryTimeoutSecondsAttribute,
527 config.connectionCacheExpirySecs);
529 if (
config.tcpKeepAliveIdleSecs >= 0) {
530 request.setTcpKeepAliveIdleTimeBeforeProbes(std::chrono::seconds(
config.tcpKeepAliveIdleSecs));
532 if (
config.tcpKeepAliveIntervalSecs >= 0) {
533 request.setTcpKeepAliveIntervalBetweenProbes(std::chrono::seconds(
config.tcpKeepAliveIntervalSecs));
535 if (
config.tcpKeepAliveProbeCount >= 0) {
536 request.setTcpKeepAliveProbeCount(
config.tcpKeepAliveProbeCount);
542 QNetworkRequest request(url);
549 using WK = QHttpHeaders::WellKnownHeader;
550 QHttpHeaders headers = request.headers();
551 headers.replaceOrAppend(WK::UserAgent, userAgent.isEmpty() ?
defaultUserAgent() : userAgent);
552 headers.replaceOrAppend(WK::Accept,
"*/*");
553 headers.replaceOrAppend(WK::AcceptEncoding,
"gzip, deflate");
554 headers.replaceOrAppend(WK::Connection,
"keep-alive");
555 request.setHeaders(headers);
560 using WK = QHttpHeaders::WellKnownHeader;
561 QHttpHeaders headers = request.headers();
562 headers.replaceOrAppend(WK::Accept,
"application/json");
563 headers.replaceOrAppend(WK::ContentType,
"application/json");
564 request.setHeaders(headers);
569 request.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral(
"application/x-www-form-urlencoded"));
579 part.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral(
"form-data; name=\"%1\"").arg(name));
580 part.setBody(value.toUtf8());
584QHttpPart
createFilePart(
const QString& name,
const QString& fileName,
const QString& contentType, QIODevice* device)
587 part.setHeader(QNetworkRequest::ContentTypeHeader,
contentType);
588 part.setHeader(QNetworkRequest::ContentDispositionHeader,
589 QStringLiteral(
"form-data; name=\"%1\"; filename=\"%2\"").arg(name, fileName));
590 part.setBodyDevice(device);
594QHttpPart
createFilePart(
const QString& name,
const QString& fileName, QIODevice* device)
605 QSslConfiguration
config = QSslConfiguration::defaultConfiguration();
606 config.setPeerVerifyMode(QSslSocket::VerifyNone);
612 request.setSslConfiguration(
config);
617 const auto certs = QSslCertificate::fromPath(filePath, QSsl::Pem);
618 if (certs.isEmpty() && errorOut) {
619 *errorOut = QStringLiteral(
"No certificates found in %1").arg(filePath);
624bool loadClientCertAndKey(
const QString& certPath,
const QString& keyPath, QSslCertificate& certOut, QSslKey& keyOut,
627 const auto certs = QSslCertificate::fromPath(certPath, QSsl::Pem);
628 if (certs.isEmpty()) {
630 *errorOut = QStringLiteral(
"No certificate found in %1").arg(certPath);
634 QFile keyFile(keyPath);
635 if (!keyFile.open(QIODevice::ReadOnly)) {
637 *errorOut = QStringLiteral(
"Cannot open key file %1").arg(keyPath);
641 QSslKey key(&keyFile, QSsl::Rsa);
644 key = QSslKey(&keyFile, QSsl::Ec);
648 *errorOut = QStringLiteral(
"Invalid key in %1").arg(keyPath);
652 certOut = certs.first();
663 QJsonParseError localError;
664 QJsonParseError* errorPtr = (
error !=
nullptr) ?
error : &localError;
666 QJsonDocument doc = QJsonDocument::fromJson(data, errorPtr);
668 if (errorPtr->error != QJsonParseError::NoError) {
669 qCWarning(QGCNetworkHelperLog) <<
"JSON parse error:" << errorPtr->errorString() <<
"at offset"
679 if (reply ==
nullptr) {
680 if (
error !=
nullptr) {
681 error->error = QJsonParseError::UnterminatedObject;
687 if (reply->error() != QNetworkReply::NoError) {
688 qCWarning(QGCNetworkHelperLog) <<
"Network error before JSON parse:" << reply->errorString();
689 if (
error !=
nullptr) {
690 error->error = QJsonParseError::UnterminatedObject;
701 if (data.isEmpty()) {
706 for (
int i = 0; i < data.size(); ++i) {
707 const char c = data.at(i);
708 if (c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\r') {
711 return c ==
'{' || c ==
'[';
726 const QVariant statusAttr = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
727 return statusAttr.isValid() ? statusAttr.toInt() : -1;
736 const QVariant redirectAttr = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
737 if (redirectAttr.isNull()) {
741 const QUrl redirectTarget = redirectAttr.toUrl();
742 return reply->url().resolved(redirectTarget);
748 return QStringLiteral(
"No reply");
752 if (reply->error() != QNetworkReply::NoError) {
753 return reply->errorString();
759 return QStringLiteral(
"HTTP %1: %2").arg(status).arg(
httpStatusText(status));
771 if (reply->error() != QNetworkReply::NoError) {
794 return reply->header(QNetworkRequest::ContentTypeHeader).toString();
802 const QVariant lenAttr = reply->header(QNetworkRequest::ContentLengthHeader);
803 return lenAttr.isValid() ? lenAttr.toLongLong() : -1;
809 return type.contains(QLatin1String(
"application/json"), Qt::CaseInsensitive) ||
810 type.contains(QLatin1String(
"+json"), Qt::CaseInsensitive);
819 if (!QNetworkInformation::loadDefaultBackend()) {
820 qCDebug(QGCNetworkHelperLog) <<
"Failed to load network information backend";
822 if (!QNetworkInformation::loadBackendByFeatures(QNetworkInformation::Feature::Reachability)) {
823 qCDebug(QGCNetworkHelperLog) <<
"Network information backend does not provide reachability";
825 return QNetworkInformation::instance();
831 return !netInfo || netInfo->reachability() != QNetworkInformation::Reachability::Disconnected;
837 return netInfo && netInfo->reachability() == QNetworkInformation::Reachability::Online;
843 return netInfo && netInfo->transportMedium() == QNetworkInformation::TransportMedium::Ethernet;
848 const QList<QBluetoothHostInfo> devices = QBluetoothLocalDevice::allDevices();
849 return !devices.isEmpty();
856 return ConnectionType::Unknown;
859 if (netInfo->reachability() == QNetworkInformation::Reachability::Disconnected) {
860 return ConnectionType::None;
863 switch (netInfo->transportMedium()) {
864 case QNetworkInformation::TransportMedium::Ethernet:
865 return ConnectionType::Ethernet;
866 case QNetworkInformation::TransportMedium::WiFi:
867 return ConnectionType::WiFi;
868 case QNetworkInformation::TransportMedium::Cellular:
869 return ConnectionType::Cellular;
870 case QNetworkInformation::TransportMedium::Bluetooth:
871 return ConnectionType::Bluetooth;
873 return ConnectionType::Unknown;
880 case ConnectionType::None:
881 return QStringLiteral(
"None");
882 case ConnectionType::Ethernet:
883 return QStringLiteral(
"Ethernet");
884 case ConnectionType::WiFi:
885 return QStringLiteral(
"WiFi");
886 case ConnectionType::Cellular:
887 return QStringLiteral(
"Cellular");
888 case ConnectionType::Bluetooth:
889 return QStringLiteral(
"Bluetooth");
890 case ConnectionType::Unknown:
892 return QStringLiteral(
"Unknown");
906 QObject::connect(reply, &QNetworkReply::sslErrors, reply, [reply](
const QList<QSslError>& errors) {
907 qCWarning(QGCNetworkHelperLog) <<
"Ignoring SSL errors for" << reply->url();
908 for (
const QSslError&
error : errors) {
909 qCDebug(QGCNetworkHelperLog) <<
" -" <<
error.errorString();
911 reply->ignoreSslErrors();
922 const bool sslLibraryBuildIs1x = ((QSslSocket::sslLibraryBuildVersionNumber() & 0xf0000000) == 0x10000000);
923 const bool sslLibraryIs3x = ((QSslSocket::sslLibraryVersionNumber() & 0xf0000000) == 0x30000000);
924 if (sslLibraryBuildIs1x && sslLibraryIs3x) {
925 qCWarning(QGCNetworkHelperLog) <<
"Ignoring SSL certificate errors due to OpenSSL version mismatch";
926 QList<QSslError> errorsThatCanBeIgnored;
927 errorsThatCanBeIgnored << QSslError(QSslError::NoPeerCertificate);
928 reply->ignoreSslErrors(errorsThatCanBeIgnored);
934 return QSslSocket::supportsSsl();
939 return QSslSocket::sslLibraryVersionString();
948 QNetworkProxyFactory::setUseSystemConfiguration(
true);
#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.
QNetworkInformation * networkInformation()
The platform's network information, preferring a backend that reports reachability; null without a ba...
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)
bool isJsonResponse(const QNetworkReply *reply)
Check if response is JSON based on Content-Type.
const QString kContentTypeOctetStream
void initializeProxySupport()
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.
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)
QString redactedUrlForLogging(const QUrl &url)
QSslConfiguration createInsecureSslConfig()
QString defaultUserAgent()
"<application>/<version> (Qt <version>)".
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); assumed available without a backend.
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)
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.
GPSCommandSequence port(QByteArrayView port, unsigned baud)
Clears the SBF output of port, keeps a serial port at the link's baud, and selects SBF and the WGS84 ...
Common request configuration options.