File indexing completed on 2024-04-28 16:44:07
0001 /* 0002 SPDX-FileCopyrightText: 2019 Harald Sitter <sitter@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0005 */ 0006 0007 #include "apijob.h" 0008 0009 #include <QMetaMethod> 0010 #include <QTimer> 0011 0012 #include <KIO/TransferJob> 0013 0014 #include "bugzilla_debug.h" 0015 #include "exceptions.h" 0016 0017 namespace Bugzilla 0018 { 0019 TransferAPIJob::TransferAPIJob(KIO::TransferJob *transferJob, QObject *parent) 0020 : APIJob(parent) 0021 , m_transferJob(transferJob) 0022 { 0023 // Required for every request type. 0024 addMetaData(QStringLiteral("content-type"), QStringLiteral("application/json")); 0025 addMetaData(QStringLiteral("accept"), QStringLiteral("application/json")); 0026 addMetaData(QStringLiteral("UserAgent"), QStringLiteral("DrKonqi")); 0027 // We don't want HTML blobs but proper job errors + text! 0028 addMetaData(QStringLiteral("errorPage"), QStringLiteral("false")); 0029 // Disable automatic cookie injection. We don't need cookies but they 0030 // can mess up requests (supposedly by being unexpected or invalid or outdated ...) 0031 // https://bugs.kde.org/show_bug.cgi?id=419646 0032 addMetaData(QStringLiteral("cookies"), QStringLiteral("none")); 0033 0034 connect(m_transferJob, &KIO::TransferJob::data, this, [this](KIO::Job *, const QByteArray &data) { 0035 m_data += data; 0036 }); 0037 0038 connect(m_transferJob, &KIO::TransferJob::finished, this, [this](KJob *job) { 0039 // Set errors, they are read by document() when the consumer reads 0040 // the data and possibly raised as exception. 0041 setError(job->error()); 0042 setErrorText(job->errorText()); 0043 0044 Q_ASSERT(!((KIO::TransferJob *)job)->isErrorPage()); 0045 0046 // Force a delay on all API actions if configured. This allows 0047 // simulation of slow connections. 0048 static int delay = qEnvironmentVariableIntValue("DRKONQI_HTTP_DELAY_MS"); 0049 if (delay > 0) { 0050 QTimer::singleShot(delay, this, [this] { 0051 emitResult(); 0052 }); 0053 return; 0054 } 0055 0056 emitResult(); 0057 }); 0058 } 0059 0060 void TransferAPIJob::addMetaData(const QString &key, const QString &value) 0061 { 0062 m_transferJob->addMetaData(key, value); 0063 } 0064 0065 void TransferAPIJob::setPutData(const QByteArray &data) 0066 { 0067 m_putData = data; 0068 0069 // This is really awkward, does it need to be this way? Why can't we just 0070 // push the entire array in? 0071 0072 // dataReq says we shouldn't send data >1mb, so segment the incoming data 0073 // accordingly and generate QBAs wrapping the raw data (zero-copy). 0074 int segmentSize = 1024 * 1024; // 1 mb per segment maximum 0075 int segments = qMax(data.size() / segmentSize, 1); 0076 m_dataSegments.reserve(segments); 0077 for (int i = 0; i < segments; ++i) { 0078 int offset = i * segmentSize; 0079 const char *buf = data.constData() + offset; 0080 int segmentLength = qMin(offset + segmentSize, data.size()); 0081 m_dataSegments.append(QByteArray::fromRawData(buf, segmentLength)); 0082 } 0083 0084 // TODO: throw away, only here to make sure I don't mess up the 0085 // segmentation. 0086 int allLengths = 0; 0087 for (const auto &a : qAsConst(m_dataSegments)) { 0088 allLengths += a.size(); 0089 } 0090 Q_ASSERT(allLengths == data.size()); 0091 0092 connect(m_transferJob, &KIO::TransferJob::dataReq, this, [this](KIO::Job *, QByteArray &dataForSending) { 0093 if (m_dataSegments.isEmpty()) { 0094 return; 0095 } 0096 dataForSending = m_dataSegments.takeFirst(); 0097 }); 0098 } 0099 0100 QJsonDocument APIJob::document() const 0101 { 0102 ProtocolException::maybeThrow(this); 0103 Q_ASSERT(error() == KJob::NoError); 0104 0105 auto document = QJsonDocument::fromJson(data()); 0106 APIException::maybeThrow(document); 0107 return document; 0108 } 0109 0110 QJsonObject APIJob::object() const 0111 { 0112 return document().object(); 0113 } 0114 0115 void APIJob::setAutoStart(bool start) 0116 { 0117 m_autostart = start; 0118 } 0119 0120 void APIJob::connectNotify(const QMetaMethod &signal) 0121 { 0122 if (m_autostart && signal == QMetaMethod::fromSignal(&KJob::finished)) { 0123 qCDebug(BUGZILLA_LOG) << "auto starting"; 0124 start(); 0125 } 0126 KJob::connectNotify(signal); 0127 } 0128 0129 } // namespace Bugzilla