Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2021 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 : #include "networkmanager.h"
23 :
24 : #include <QNetworkAccessManager>
25 : #include <QNetworkReply>
26 : #include <QUrl>
27 :
28 : #include "networkerrors.h"
29 :
30 : #include "log.h"
31 :
32 : using namespace muse;
33 : using namespace muse::network;
34 : using namespace muse::async;
35 :
36 0 : static bool openDevice(QIODevicePtr& device, QIODevice::OpenModeFlag flags)
37 : {
38 0 : IF_ASSERT_FAILED(device) {
39 0 : return false;
40 : }
41 :
42 0 : if (device->isOpen()) {
43 0 : device->close();
44 : }
45 :
46 0 : return device->open(flags);
47 : }
48 :
49 0 : static void closeDevice(QIODevicePtr& device)
50 : {
51 0 : if (device && device->isOpen()) {
52 0 : device->close();
53 : }
54 0 : }
55 :
56 0 : static Ret retFromReply(const QNetworkReply* reply)
57 : {
58 0 : if (!reply) {
59 0 : return make_ret(Err::NetworkError);
60 : }
61 :
62 0 : Ret ret = muse::make_ok();
63 :
64 0 : if (reply->error() == QNetworkReply::TimeoutError) {
65 0 : ret = make_ret(Err::Timeout);
66 0 : } else if (reply->error() == QNetworkReply::OperationCanceledError) {
67 0 : ret = make_ret(Err::Abort);
68 0 : } else if (reply->error() != QNetworkReply::NoError) {
69 0 : ret.setCode(static_cast<int>(Err::NetworkError));
70 : }
71 :
72 0 : QString errorString = reply->errorString();
73 0 : if (!errorString.isEmpty()) {
74 0 : ret.setText(errorString.toStdString());
75 : }
76 :
77 0 : QVariant status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
78 0 : if (status.isValid()) {
79 0 : ret.setData("status", status.toInt());
80 : }
81 :
82 0 : QVariant statusText = reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute);
83 0 : if (statusText.isValid()) {
84 0 : ret.setData("statusText", statusText.toString().toStdString());
85 : }
86 :
87 0 : QVariantMap headers;
88 0 : const QList<QNetworkReply::RawHeaderPair> pairs = reply->rawHeaderPairs();
89 0 : for (const QNetworkReply::RawHeaderPair& pair : pairs) {
90 0 : const QString name = QString::fromUtf8(pair.first).toLower();
91 0 : const QString value = QString::fromUtf8(pair.second);
92 0 : if (headers.contains(name)) {
93 0 : headers[name] = headers[name].toString() + QStringLiteral(", ") + value;
94 : } else {
95 0 : headers.insert(name, value);
96 : }
97 0 : }
98 0 : ret.setData("headers", headers);
99 :
100 0 : return ret;
101 0 : }
102 :
103 0 : NetworkManager::NetworkManager(QObject* parent)
104 0 : : QObject(parent)
105 : {
106 0 : m_manager = new QNetworkAccessManager(this);
107 0 : m_manager->setTransferTimeout(); // Use Qt's default timeout (30s)
108 0 : }
109 :
110 0 : RetVal<Progress> NetworkManager::get(const QUrl& url, IncomingDevicePtr incomingData, const RequestHeaders& headers)
111 : {
112 0 : return execRequest(GET_REQUEST, url, incomingData, NoOutgoingDevice(), headers);
113 : }
114 :
115 0 : RetVal<Progress> NetworkManager::head(const QUrl& url, const RequestHeaders& headers)
116 : {
117 0 : return execRequest(HEAD_REQUEST, url, nullptr, NoOutgoingDevice(), headers);
118 : }
119 :
120 0 : RetVal<Progress> NetworkManager::post(const QUrl& url, OutgoingDeviceVar outgoingData, IncomingDevicePtr incomingData,
121 : const RequestHeaders& headers)
122 : {
123 0 : return execRequest(POST_REQUEST, url, incomingData, outgoingData, headers);
124 : }
125 :
126 0 : RetVal<Progress> NetworkManager::put(const QUrl& url, OutgoingDeviceVar outgoingData, IncomingDevicePtr incomingData,
127 : const RequestHeaders& headers)
128 : {
129 0 : return execRequest(PUT_REQUEST, url, incomingData, outgoingData, headers);
130 : }
131 :
132 0 : RetVal<Progress> NetworkManager::patch(const QUrl& url, OutgoingDeviceVar outgoingData, IncomingDevicePtr incomingData,
133 : const RequestHeaders& headers)
134 : {
135 0 : return execRequest(PATCH_REQUEST, url, incomingData, outgoingData, headers);
136 : }
137 :
138 0 : RetVal<Progress> NetworkManager::del(const QUrl& url, IncomingDevicePtr incomingData, const RequestHeaders& headers)
139 : {
140 0 : return execRequest(DELETE_REQUEST, url, incomingData, NoOutgoingDevice(), headers);
141 : }
142 :
143 0 : RetVal<Progress> NetworkManager::execRequest(RequestType requestType, const QUrl& url,
144 : IncomingDevicePtr incomingData,
145 : OutgoingDeviceVar outgoingData,
146 : const RequestHeaders& headers)
147 : {
148 0 : if (std::holds_alternative<QIODevicePtr>(outgoingData)) {
149 0 : if (!openDevice(std::get<QIODevicePtr>(outgoingData), QIODevice::ReadOnly)) {
150 0 : return RetVal<Progress>::make_ret((int)Err::FiledOpenIODeviceRead);
151 : }
152 : }
153 :
154 0 : if (incomingData) {
155 0 : if (!openDevice(incomingData, QIODevice::WriteOnly)) {
156 0 : return RetVal<Progress>::make_ret((int)Err::FiledOpenIODeviceWrite);
157 : }
158 : }
159 :
160 0 : const QNetworkRequest request = prepareRequest(url, headers);
161 0 : QNetworkReply* reply = sendRequest(requestType, request, outgoingData);
162 0 : IF_ASSERT_FAILED(reply) {
163 0 : return RetVal<Progress>::make_ret((int)Err::NetworkError);
164 : }
165 :
166 0 : const size_t requestId = m_lastRequestId++;
167 0 : RequestData& requestData = m_requestDataMap[requestId];
168 0 : requestData.incomingData = incomingData;
169 0 : requestData.outgoingData = outgoingData;
170 0 : requestData.reply = reply;
171 0 : requestData.progress.start();
172 0 : requestData.progress.canceled().onNotify(this, [this, requestId]() {
173 0 : auto it = m_requestDataMap.find(requestId);
174 0 : IF_ASSERT_FAILED(it != m_requestDataMap.end() && it->second.reply) {
175 0 : return;
176 : }
177 :
178 0 : it->second.reply->abort();
179 : });
180 :
181 0 : if (!std::holds_alternative<NoOutgoingDevice>(outgoingData)) {
182 0 : connect(reply, &QNetworkReply::uploadProgress, this, [this, requestId](qint64 curr, qint64 total) {
183 0 : auto it = m_requestDataMap.find(requestId);
184 0 : IF_ASSERT_FAILED(it != m_requestDataMap.end()) {
185 0 : return;
186 : }
187 :
188 0 : it->second.progress.progress(curr, total);
189 : });
190 : }
191 :
192 0 : if (incomingData) {
193 0 : connect(reply, &QNetworkReply::downloadProgress, this, [this, requestId](qint64 curr, qint64 total) {
194 0 : auto it = m_requestDataMap.find(requestId);
195 0 : IF_ASSERT_FAILED(it != m_requestDataMap.end()) {
196 0 : return;
197 : }
198 :
199 0 : it->second.progress.progress(curr, total);
200 : });
201 :
202 0 : connect(reply, &QNetworkReply::readyRead, this, [this, requestId]() {
203 0 : auto it = m_requestDataMap.find(requestId);
204 0 : IF_ASSERT_FAILED(it != m_requestDataMap.end()) {
205 0 : return;
206 : }
207 :
208 0 : RequestData& data = it->second;
209 0 : IF_ASSERT_FAILED(data.incomingData && data.reply) {
210 0 : return;
211 : }
212 :
213 0 : const QByteArray chunk = data.reply->readAll();
214 0 : if (data.incomingData->write(chunk) != chunk.size()) {
215 0 : LOGE() << "failed to store incoming data: " << data.incomingData->errorString();
216 0 : data.reply->abort();
217 : }
218 0 : });
219 : }
220 :
221 0 : connect(reply, &QNetworkReply::finished, this, [this, requestId]() {
222 0 : auto it = m_requestDataMap.find(requestId);
223 0 : IF_ASSERT_FAILED(it != m_requestDataMap.end() && it->second.reply) {
224 0 : return;
225 : }
226 :
227 0 : RequestData& data = it->second;
228 0 : data.progress.canceled().disconnect(this);
229 :
230 0 : if (std::holds_alternative<QIODevicePtr>(data.outgoingData)) {
231 0 : closeDevice(std::get<QIODevicePtr>(data.outgoingData));
232 : }
233 :
234 0 : closeDevice(data.incomingData);
235 :
236 0 : const Ret ret = retFromReply(data.reply);
237 0 : data.reply->deleteLater();
238 :
239 0 : Progress progress = data.progress;
240 0 : m_requestDataMap.erase(it);
241 0 : progress.finish(ret);
242 0 : });
243 :
244 0 : return RetVal<Progress>::make_ok(requestData.progress);
245 0 : }
246 :
247 0 : QNetworkRequest NetworkManager::prepareRequest(const QUrl& url, const RequestHeaders& headers) const
248 : {
249 0 : QNetworkRequest request(url);
250 0 : request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, true);
251 :
252 0 : RequestHeaders _headers = headers;
253 0 : if (_headers.isEmpty()) {
254 0 : _headers = configuration()->defaultHeaders();
255 : }
256 :
257 0 : for (auto it = _headers.knownHeaders.cbegin(); it != _headers.knownHeaders.cend(); ++it) {
258 0 : request.setHeader(it.key(), it.value());
259 : }
260 :
261 0 : for (auto it = _headers.rawHeaders.cbegin(); it != _headers.rawHeaders.cend(); ++it) {
262 0 : request.setRawHeader(it.key(), it.value());
263 : }
264 :
265 0 : return request;
266 0 : }
267 :
268 0 : QNetworkReply* NetworkManager::sendRequest(RequestType type, const QNetworkRequest& request, const OutgoingDeviceVar& device)
269 : {
270 0 : switch (type) {
271 0 : case GET_REQUEST: return m_manager->get(request);
272 0 : case HEAD_REQUEST: return m_manager->head(request);
273 0 : case DELETE_REQUEST: return m_manager->deleteResource(request);
274 0 : case PUT_REQUEST: {
275 0 : if (std::holds_alternative<QIODevicePtr>(device)) {
276 0 : return m_manager->put(request, std::get<QIODevicePtr>(device).get());
277 0 : } else if (std::holds_alternative<QHttpMultiPartPtr>(device)) {
278 0 : return m_manager->put(request, std::get<QHttpMultiPartPtr>(device).get());
279 : }
280 0 : break;
281 : }
282 0 : case PATCH_REQUEST: {
283 0 : if (std::holds_alternative<QIODevicePtr>(device)) {
284 0 : return m_manager->sendCustomRequest(request, "PATCH", std::get<QIODevicePtr>(device).get());
285 0 : } else if (std::holds_alternative<QHttpMultiPartPtr>(device)) {
286 0 : return m_manager->sendCustomRequest(request, "PATCH", std::get<QHttpMultiPartPtr>(device).get());
287 : }
288 0 : break;
289 : }
290 0 : case POST_REQUEST: {
291 0 : if (std::holds_alternative<QIODevicePtr>(device)) {
292 0 : return m_manager->post(request, std::get<QIODevicePtr>(device).get());
293 0 : } else if (std::holds_alternative<QHttpMultiPartPtr>(device)) {
294 0 : return m_manager->post(request, std::get<QHttpMultiPartPtr>(device).get());
295 : }
296 0 : break;
297 : }
298 : }
299 :
300 0 : UNREACHABLE;
301 : return nullptr;
302 : }
|