3#include <QtCore/QDateTime>
4#include <QtNetwork/QNetworkAccessManager>
5#include <QtNetwork/QNetworkReply>
6#include <QtNetwork/QNetworkRequest>
7#include <QtNetwork/QSslError>
16bool isSelfSignedOnly(
const QList<QSslError>& errors)
18 if (errors.isEmpty()) {
21 for (
const QSslError&
error : errors) {
22 switch (
error.error()) {
23 case QSslError::SelfSignedCertificate:
24 case QSslError::SelfSignedCertificateInChain:
26 case QSslError::UnableToGetLocalIssuerCertificate:
27 case QSslError::UnableToVerifyFirstCertificate:
28 case QSslError::CertificateUntrusted:
29 if (
error.certificate().isNull() || !
error.certificate().isSelfSigned()) {
61 const QString cacheKey =
config.casterIdentity();
69 if (_model->
count() > 0 && _fetchedAtMs > 0 && cacheKey == _lastFetchKey) {
70 const qint64 age = QDateTime::currentMSecsSinceEpoch() - _fetchedAtMs;
72 qCDebug(NTRIPSourceTableControllerLog) <<
"Source table cache hit, age:" << age <<
"ms";
81 if (
const QString invalid =
config.validationError(); !invalid.isEmpty()) {
82 _onFetchError(invalid);
88 _sortCoord = sortCoord;
89 _lastFetchKey = cacheKey;
95 url.setScheme(
config.useTls ? QStringLiteral(
"https") : QStringLiteral(
"http"));
98 url.setPath(QStringLiteral(
"/"));
102 reqCfg.
userAgent = QStringLiteral(
"QGC-NTRIP");
107 request.setRawHeader(
"Ntrip-Version",
"Ntrip/2.0");
108 if (!
config.username.isEmpty() || !
config.password.isEmpty()) {
112 _replyTooLarge =
false;
113 _reply = _networkManager->get(request);
114 QNetworkReply*
const reply = _reply;
115 connect(reply, &QNetworkReply::sslErrors,
this,
116 [reply, allowSelfSigned =
config.allowSelfSignedCerts](
const QList<QSslError>& errors) {
117 if (allowSelfSigned && isSelfSignedOnly(errors)) {
118 reply->ignoreSslErrors(errors);
122 connect(_reply, &QNetworkReply::downloadProgress,
this, [
this](qint64 received, qint64) {
123 if (received >= kMaxSourceTableBytes && _reply) {
124 _replyTooLarge =
true;
128 connect(_reply, &QNetworkReply::finished,
this, &NTRIPSourceTableController::_onReplyFinished);
131void NTRIPSourceTableController::_onReplyFinished()
133 if (_replyTooLarge) {
135 _onFetchError(tr(
"Source table too large (exceeds %1 MB)").arg(
kMaxSourceTableBytes / (1024 * 1024)));
139 const bool networkError = _reply->error() != QNetworkReply::NoError;
141 const QString body = networkError ? QString() : QString::fromUtf8(_reply->readAll());
145 _onFetchError(networkErrorMsg);
149 if (!body.contains(QStringLiteral(
"ENDSOURCETABLE"))) {
150 _onFetchError(tr(
"Response does not contain a valid source table"));
154 _onSourceTableReceived(body);
157void NTRIPSourceTableController::_onSourceTableReceived(
const QString& table)
160 _fetchedAtMs = QDateTime::currentMSecsSinceEpoch();
162 if (_sortCoord.isValid()) {
171void NTRIPSourceTableController::_onFetchError(
const QString&
error)
183void NTRIPSourceTableController::_abortReply()
186 disconnect(_reply,
nullptr,
this,
nullptr);
187 if (_reply->isRunning()) {
190 _reply->deleteLater();
195void NTRIPSourceTableController::injectSourceTableForTest(
const QString& table)
198 _onSourceTableReceived(table);
201void NTRIPSourceTableController::injectFetchErrorForTest(
const QString&
error)
204 _onFetchError(
error);
#define QGC_LOGGING_CATEGORY(name, categoryStr)
void mountpointModelChanged()
Q_INVOKABLE void selectMountpoint(const QString &mountpoint)
~NTRIPSourceTableController() override
static constexpr int kCacheTtlMs
NTRIPSourceTableController(QObject *parent=nullptr)
void fetch(const NTRIPTransportConfig &config, const QGeoCoordinate &sortCoord={})
void fetchStatusChanged()
void mountpointSelected(const QString &mountpoint)
QAbstractListModel * mountpointModel() const
static constexpr int kFetchTimeoutMs
static constexpr qint64 kMaxSourceTableBytes
void updateDistances(const QGeoCoordinate &from)
void parseSourceTable(const QString &raw)
QNetworkRequest createRequest(const QUrl &url, const RequestConfig &config)
void setBasicAuth(QNetworkRequest &request, const QString &credentials)
QString errorMessage(const QNetworkReply *reply)
Common request configuration options.