3#include <QtCore/QCoreApplication>
4#include <QtCore/QIODevice>
5#include <QtCore/QJsonDocument>
6#include <QtCore/QUrlQuery>
7#include <QtNetwork/QHttpPart>
8#include <QtNetwork/QNetworkAccessManager>
9#include <QtNetwork/QNetworkInformation>
10#include <QtNetwork/QNetworkProxy>
11#include <QtNetwork/QNetworkProxyFactory>
12#include <QtNetwork/QSslSocket>
17#include <QtBluetooth/QBluetoothLocalDevice>
29 if (statusCode >= 100 && statusCode < 200) {
30 return HttpStatusClass::Informational;
32 if (statusCode >= 200 && statusCode < 300) {
33 return HttpStatusClass::Success;
35 if (statusCode >= 300 && statusCode < 400) {
36 return HttpStatusClass::Redirection;
38 if (statusCode >= 400 && statusCode < 500) {
39 return HttpStatusClass::ClientError;
41 if (statusCode >= 500 && statusCode < 600) {
42 return HttpStatusClass::ServerError;
44 return HttpStatusClass::Unknown;
51 case HttpStatusCode::Continue:
52 return QStringLiteral(
"Continue");
53 case HttpStatusCode::SwitchingProtocols:
54 return QStringLiteral(
"Switching Protocols");
55 case HttpStatusCode::Processing:
56 return QStringLiteral(
"Processing");
58 case HttpStatusCode::Ok:
59 return QStringLiteral(
"OK");
60 case HttpStatusCode::Created:
61 return QStringLiteral(
"Created");
62 case HttpStatusCode::Accepted:
63 return QStringLiteral(
"Accepted");
64 case HttpStatusCode::NonAuthoritativeInformation:
65 return QStringLiteral(
"Non-Authoritative Information");
66 case HttpStatusCode::NoContent:
67 return QStringLiteral(
"No Content");
68 case HttpStatusCode::ResetContent:
69 return QStringLiteral(
"Reset Content");
70 case HttpStatusCode::PartialContent:
71 return QStringLiteral(
"Partial Content");
72 case HttpStatusCode::MultiStatus:
73 return QStringLiteral(
"Multi-Status");
74 case HttpStatusCode::AlreadyReported:
75 return QStringLiteral(
"Already Reported");
76 case HttpStatusCode::IMUsed:
77 return QStringLiteral(
"IM Used");
79 case HttpStatusCode::MultipleChoices:
80 return QStringLiteral(
"Multiple Choices");
81 case HttpStatusCode::MovedPermanently:
82 return QStringLiteral(
"Moved Permanently");
83 case HttpStatusCode::Found:
84 return QStringLiteral(
"Found");
85 case HttpStatusCode::SeeOther:
86 return QStringLiteral(
"See Other");
87 case HttpStatusCode::NotModified:
88 return QStringLiteral(
"Not Modified");
89 case HttpStatusCode::UseProxy:
90 return QStringLiteral(
"Use Proxy");
91 case HttpStatusCode::TemporaryRedirect:
92 return QStringLiteral(
"Temporary Redirect");
93 case HttpStatusCode::PermanentRedirect:
94 return QStringLiteral(
"Permanent Redirect");
96 case HttpStatusCode::BadRequest:
97 return QStringLiteral(
"Bad Request");
98 case HttpStatusCode::Unauthorized:
99 return QStringLiteral(
"Unauthorized");
100 case HttpStatusCode::PaymentRequired:
101 return QStringLiteral(
"Payment Required");
102 case HttpStatusCode::Forbidden:
103 return QStringLiteral(
"Forbidden");
104 case HttpStatusCode::NotFound:
105 return QStringLiteral(
"Not Found");
106 case HttpStatusCode::MethodNotAllowed:
107 return QStringLiteral(
"Method Not Allowed");
108 case HttpStatusCode::NotAcceptable:
109 return QStringLiteral(
"Not Acceptable");
110 case HttpStatusCode::ProxyAuthenticationRequired:
111 return QStringLiteral(
"Proxy Authentication Required");
112 case HttpStatusCode::RequestTimeout:
113 return QStringLiteral(
"Request Timeout");
114 case HttpStatusCode::Conflict:
115 return QStringLiteral(
"Conflict");
116 case HttpStatusCode::Gone:
117 return QStringLiteral(
"Gone");
118 case HttpStatusCode::LengthRequired:
119 return QStringLiteral(
"Length Required");
120 case HttpStatusCode::PreconditionFailed:
121 return QStringLiteral(
"Precondition Failed");
122 case HttpStatusCode::PayloadTooLarge:
123 return QStringLiteral(
"Payload Too Large");
124 case HttpStatusCode::UriTooLong:
125 return QStringLiteral(
"URI Too Long");
126 case HttpStatusCode::UnsupportedMediaType:
127 return QStringLiteral(
"Unsupported Media Type");
128 case HttpStatusCode::RequestRangeNotSatisfiable:
129 return QStringLiteral(
"Range Not Satisfiable");
130 case HttpStatusCode::ExpectationFailed:
131 return QStringLiteral(
"Expectation Failed");
132 case HttpStatusCode::ImATeapot:
133 return QStringLiteral(
"I'm a teapot");
134 case HttpStatusCode::MisdirectedRequest:
135 return QStringLiteral(
"Misdirected Request");
136 case HttpStatusCode::UnprocessableEntity:
137 return QStringLiteral(
"Unprocessable Entity");
138 case HttpStatusCode::Locked:
139 return QStringLiteral(
"Locked");
140 case HttpStatusCode::FailedDependency:
141 return QStringLiteral(
"Failed Dependency");
142 case HttpStatusCode::UpgradeRequired:
143 return QStringLiteral(
"Upgrade Required");
144 case HttpStatusCode::PreconditionRequired:
145 return QStringLiteral(
"Precondition Required");
146 case HttpStatusCode::TooManyRequests:
147 return QStringLiteral(
"Too Many Requests");
148 case HttpStatusCode::RequestHeaderFieldsTooLarge:
149 return QStringLiteral(
"Request Header Fields Too Large");
150 case HttpStatusCode::UnavailableForLegalReasons:
151 return QStringLiteral(
"Unavailable For Legal Reasons");
153 case HttpStatusCode::InternalServerError:
154 return QStringLiteral(
"Internal Server Error");
155 case HttpStatusCode::NotImplemented:
156 return QStringLiteral(
"Not Implemented");
157 case HttpStatusCode::BadGateway:
158 return QStringLiteral(
"Bad Gateway");
159 case HttpStatusCode::ServiceUnavailable:
160 return QStringLiteral(
"Service Unavailable");
161 case HttpStatusCode::GatewayTimeout:
162 return QStringLiteral(
"Gateway Timeout");
163 case HttpStatusCode::HttpVersionNotSupported:
164 return QStringLiteral(
"HTTP Version Not Supported");
165 case HttpStatusCode::VariantAlsoNegotiates:
166 return QStringLiteral(
"Variant Also Negotiates");
167 case HttpStatusCode::InsufficientStorage:
168 return QStringLiteral(
"Insufficient Storage");
169 case HttpStatusCode::LoopDetected:
170 return QStringLiteral(
"Loop Detected");
171 case HttpStatusCode::NotExtended:
172 return QStringLiteral(
"Not Extended");
173 case HttpStatusCode::NetworkAuthenticationRequired:
174 return QStringLiteral(
"Network Authentication Required");
175 case HttpStatusCode::NetworkConnectTimeoutError:
176 return QStringLiteral(
"Network Connect Timeout Error");
178 return QStringLiteral(
"Unknown Status");
185 return QStringLiteral(
"Unknown Status (%1)").arg(statusCode);
198 case HttpMethod::Get:
199 return QStringLiteral(
"GET");
200 case HttpMethod::Post:
201 return QStringLiteral(
"POST");
202 case HttpMethod::Put:
203 return QStringLiteral(
"PUT");
204 case HttpMethod::Delete:
205 return QStringLiteral(
"DELETE");
206 case HttpMethod::Head:
207 return QStringLiteral(
"HEAD");
208 case HttpMethod::Options:
209 return QStringLiteral(
"OPTIONS");
210 case HttpMethod::Patch:
211 return QStringLiteral(
"PATCH");
212 case HttpMethod::Connect:
213 return QStringLiteral(
"CONNECT");
214 case HttpMethod::Trace:
215 return QStringLiteral(
"TRACE");
217 return QStringLiteral(
"GET");
223 const QByteArray upper = methodStr.toUpper().toLatin1();
224 const char* str = upper.constData();
226 if (qstrcmp(str,
"GET") == 0)
227 return HttpMethod::Get;
228 if (qstrcmp(str,
"POST") == 0)
229 return HttpMethod::Post;
230 if (qstrcmp(str,
"PUT") == 0)
231 return HttpMethod::Put;
232 if (qstrcmp(str,
"DELETE") == 0)
233 return HttpMethod::Delete;
234 if (qstrcmp(str,
"HEAD") == 0)
235 return HttpMethod::Head;
236 if (qstrcmp(str,
"OPTIONS") == 0)
237 return HttpMethod::Options;
238 if (qstrcmp(str,
"PATCH") == 0)
239 return HttpMethod::Patch;
240 if (qstrcmp(str,
"CONNECT") == 0)
241 return HttpMethod::Connect;
242 if (qstrcmp(str,
"TRACE") == 0)
243 return HttpMethod::Trace;
245 return HttpMethod::Get;
254 if (!url.isValid()) {
258 const QString scheme = url.scheme().toLower();
259 return scheme == QLatin1String(
"http") || scheme == QLatin1String(
"https") || scheme == QLatin1String(
"file") ||
260 scheme == QLatin1String(
"qrc") || scheme.isEmpty();
265 const QString scheme = url.scheme().toLower();
266 return scheme == QLatin1String(
"http") || scheme == QLatin1String(
"https");
271 return url.scheme().toLower() == QLatin1String(
"https");
276 if (!url.isValid()) {
280 QUrl normalized = url;
283 normalized.setScheme(normalized.scheme().toLower());
284 normalized.setHost(normalized.host().toLower());
287 const int port = normalized.port();
288 const QString scheme = normalized.scheme();
289 if ((scheme == QLatin1String(
"http") && port == 80) || (scheme == QLatin1String(
"https") && port == 443) ||
290 (scheme == QLatin1String(
"ftp") && port == 21)) {
291 normalized.setPort(-1);
295 QString path = normalized.path();
296 if (path.length() > 1 && path.endsWith(QLatin1Char(
'/'))) {
298 normalized.setPath(path);
306 if (!url.isValid()) {
310 if (url.scheme().isEmpty()) {
311 QUrl withScheme = url;
312 withScheme.setScheme(defaultScheme);
319QUrl
buildUrl(
const QString& baseUrl,
const QMap<QString, QString>& params)
322 if (!url.isValid()) {
327 for (
auto it = params.constBegin(); it != params.constEnd(); ++it) {
328 query.addQueryItem(it.key(), it.value());
335QUrl
buildUrl(
const QString& baseUrl,
const QList<QPair<QString, QString>>& params)
338 if (!url.isValid()) {
343 for (
const auto& [key, value] : params) {
344 query.addQueryItem(key, value);
353 const QString path = url.path();
354 const int lastSlash = path.lastIndexOf(QLatin1Char(
'/'));
355 if (lastSlash >= 0 && lastSlash < path.length() - 1) {
356 return path.mid(lastSlash + 1);
364 result.setQuery(QString());
365 result.setFragment(QString());
376 request.setTransferTimeout(config.
timeoutMs);
380 request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
382 request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::ManualRedirectPolicy);
386 request.setAttribute(QNetworkRequest::Http2AllowedAttribute, config.
http2Allowed);
390 request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
391 request.setAttribute(QNetworkRequest::CacheSaveControlAttribute,
true);
393 request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork);
394 request.setAttribute(QNetworkRequest::CacheSaveControlAttribute,
false);
398 request.setAttribute(QNetworkRequest::BackgroundRequestAttribute, config.
backgroundRequest);
402 request.setHeader(QNetworkRequest::UserAgentHeader, userAgent);
404 if (!config.
accept.isEmpty()) {
405 request.setRawHeader(
"Accept", config.
accept.toUtf8());
409 request.setRawHeader(
"Accept-Encoding", config.
acceptEncoding.toUtf8());
413 request.setHeader(QNetworkRequest::ContentTypeHeader, config.
contentType);
417 request.setAttribute(attribute, value);
420 request.setRawHeader(
"Connection",
"keep-alive");
425 QNetworkRequest request(url);
433 request.setHeader(QNetworkRequest::UserAgentHeader, ua);
434 request.setRawHeader(
"Accept",
"*/*");
435 request.setRawHeader(
"Accept-Encoding",
"gzip, deflate");
436 request.setRawHeader(
"Connection",
"keep-alive");
441 request.setRawHeader(
"Accept",
"application/json");
442 request.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral(
"application/json"));
447 request.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral(
"application/x-www-form-urlencoded"));
452 static QString userAgent;
453 if (userAgent.isEmpty()) {
454 userAgent = QStringLiteral(
"%1/%2 (Qt %3)")
455 .arg(QCoreApplication::applicationName())
456 .arg(QCoreApplication::applicationVersion())
457 .arg(QString::fromLatin1(qVersion()));
468 request.setRawHeader(
"Authorization", (
"Basic " + credentials).toUtf8());
471void setBasicAuth(QNetworkRequest& request,
const QString& username,
const QString& password)
478 request.setRawHeader(
"Authorization", (
"Bearer " + token).toUtf8());
483 const QString credentials = username + QLatin1Char(
':') + password;
484 return QString::fromLatin1(credentials.toUtf8().toBase64());
494 part.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral(
"form-data; name=\"%1\"").arg(name));
495 part.setBody(value.toUtf8());
499QHttpPart
createFilePart(
const QString& name,
const QString& fileName,
const QString& contentType, QIODevice* device)
502 part.setHeader(QNetworkRequest::ContentTypeHeader,
contentType);
503 part.setHeader(QNetworkRequest::ContentDispositionHeader,
504 QStringLiteral(
"form-data; name=\"%1\"; filename=\"%2\"").arg(name, fileName));
505 part.setBodyDevice(device);
509QHttpPart
createFilePart(
const QString& name,
const QString& fileName, QIODevice* device)
520 QSslConfiguration config = QSslConfiguration::defaultConfiguration();
521 config.setProtocol(protocol);
527 QSslConfiguration config = QSslConfiguration::defaultConfiguration();
528 config.setPeerVerifyMode(QSslSocket::VerifyNone);
534 request.setSslConfiguration(config);
543 QJsonParseError localError;
544 QJsonParseError* errorPtr = (
error !=
nullptr) ?
error : &localError;
546 QJsonDocument doc = QJsonDocument::fromJson(data, errorPtr);
548 if (errorPtr->error != QJsonParseError::NoError) {
549 qCWarning(QGCNetworkHelperLog) <<
"JSON parse error:" << errorPtr->errorString() <<
"at offset"
559 if (reply ==
nullptr) {
560 if (
error !=
nullptr) {
561 error->error = QJsonParseError::UnterminatedObject;
567 if (reply->error() != QNetworkReply::NoError) {
568 qCWarning(QGCNetworkHelperLog) <<
"Network error before JSON parse:" << reply->errorString();
569 if (
error !=
nullptr) {
570 error->error = QJsonParseError::UnterminatedObject;
581 if (data.isEmpty()) {
586 for (
int i = 0; i < data.size(); ++i) {
587 const char c = data.at(i);
588 if (c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\r') {
591 return c ==
'{' || c ==
'[';
606 const QVariant statusAttr = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
607 return statusAttr.isValid() ? statusAttr.toInt() : -1;
616 const QVariant redirectAttr = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
617 if (redirectAttr.isNull()) {
621 const QUrl redirectTarget = redirectAttr.toUrl();
622 return reply->url().resolved(redirectTarget);
628 return QStringLiteral(
"No reply");
632 if (reply->error() != QNetworkReply::NoError) {
633 return reply->errorString();
639 return QStringLiteral(
"HTTP %1: %2").arg(status).arg(
httpStatusText(status));
651 if (reply->error() != QNetworkReply::NoError) {
674 return reply->header(QNetworkRequest::ContentTypeHeader).toString();
682 const QVariant lenAttr = reply->header(QNetworkRequest::ContentLengthHeader);
683 return lenAttr.isValid() ? lenAttr.toLongLong() : -1;
689 return type.contains(QLatin1String(
"application/json"), Qt::CaseInsensitive) ||
690 type.contains(QLatin1String(
"+json"), Qt::CaseInsensitive);
699 if (!QNetworkInformation::loadDefaultBackend()) {
700 qCDebug(QGCNetworkHelperLog) <<
"Failed to load network information backend";
704 const QNetworkInformation* netInfo = QNetworkInformation::instance();
705 if (netInfo ==
nullptr) {
709 return netInfo->reachability() != QNetworkInformation::Reachability::Disconnected;
714 if (QNetworkInformation::availableBackends().isEmpty()) {
718 if (!QNetworkInformation::loadDefaultBackend()) {
722 if (!QNetworkInformation::loadBackendByFeatures(QNetworkInformation::Feature::Reachability)) {
726 const QNetworkInformation* netInfo = QNetworkInformation::instance();
727 if (netInfo ==
nullptr) {
731 return netInfo->reachability() == QNetworkInformation::Reachability::Online;
736 if (QNetworkInformation::availableBackends().isEmpty()) {
740 if (!QNetworkInformation::loadDefaultBackend()) {
744 const QNetworkInformation* netInfo = QNetworkInformation::instance();
745 if (netInfo ==
nullptr) {
749 return netInfo->transportMedium() == QNetworkInformation::TransportMedium::Ethernet;
754 const QList<QBluetoothHostInfo> devices = QBluetoothLocalDevice::allDevices();
755 return !devices.isEmpty();
760 if (!QNetworkInformation::loadDefaultBackend()) {
761 return ConnectionType::Unknown;
764 const QNetworkInformation* netInfo = QNetworkInformation::instance();
766 return ConnectionType::Unknown;
769 if (netInfo->reachability() == QNetworkInformation::Reachability::Disconnected) {
770 return ConnectionType::None;
773 switch (netInfo->transportMedium()) {
774 case QNetworkInformation::TransportMedium::Ethernet:
775 return ConnectionType::Ethernet;
776 case QNetworkInformation::TransportMedium::WiFi:
777 return ConnectionType::WiFi;
778 case QNetworkInformation::TransportMedium::Cellular:
779 return ConnectionType::Cellular;
780 case QNetworkInformation::TransportMedium::Bluetooth:
781 return ConnectionType::Bluetooth;
783 return ConnectionType::Unknown;
790 case ConnectionType::None:
791 return QStringLiteral(
"None");
792 case ConnectionType::Ethernet:
793 return QStringLiteral(
"Ethernet");
794 case ConnectionType::WiFi:
795 return QStringLiteral(
"WiFi");
796 case ConnectionType::Cellular:
797 return QStringLiteral(
"Cellular");
798 case ConnectionType::Bluetooth:
799 return QStringLiteral(
"Bluetooth");
800 case ConnectionType::Unknown:
802 return QStringLiteral(
"Unknown");
816 QObject::connect(reply, &QNetworkReply::sslErrors, reply, [reply](
const QList<QSslError>& errors) {
817 qCWarning(QGCNetworkHelperLog) <<
"Ignoring SSL errors for" << reply->url();
818 for (
const QSslError&
error : errors) {
819 qCDebug(QGCNetworkHelperLog) <<
" -" <<
error.errorString();
821 reply->ignoreSslErrors();
832 const bool sslLibraryBuildIs1x = ((QSslSocket::sslLibraryBuildVersionNumber() & 0xf0000000) == 0x10000000);
833 const bool sslLibraryIs3x = ((QSslSocket::sslLibraryVersionNumber() & 0xf0000000) == 0x30000000);
834 if (sslLibraryBuildIs1x && sslLibraryIs3x) {
835 qCWarning(QGCNetworkHelperLog) <<
"Ignoring SSL certificate errors due to OpenSSL version mismatch";
836 QList<QSslError> errorsThatCanBeIgnored;
837 errorsThatCanBeIgnored << QSslError(QSslError::NoPeerCertificate);
838 reply->ignoreSslErrors(errorsThatCanBeIgnored);
844 return QSslSocket::supportsSsl();
849 return QSslSocket::sslLibraryVersionString();
858 QNetworkProxyFactory::setUseSystemConfiguration(
true);
863 auto* manager =
new QNetworkAccessManager(parent);
874#if !defined(Q_OS_IOS) && !defined(Q_OS_ANDROID)
875 QNetworkProxy proxy = manager->proxy();
876 proxy.setType(QNetworkProxy::DefaultProxy);
877 manager->setProxy(proxy);
892 QByteArray jsonData = data;
897 if (jsonData.isEmpty()) {
899 error->error = QJsonParseError::IllegalValue;
906 return QJsonDocument::fromJson(jsonData,
error);
#define QGC_LOGGING_CATEGORY(name, categoryStr)
bool isCompressionFormat(Format format)
Check if format is a compression format (single stream)
QByteArray decompressData(const QByteArray &data, Format format, qint64 maxDecompressedBytes)
Format detectFormatFromData(const QByteArray &data)
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.
bool looksLikeJson(const QByteArray &data)
QJsonDocument parseCompressedJson(const QByteArray &data, QJsonParseError *error)
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.
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 looksLikeCompressedData(const QByteArray &data)
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