1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtNetwork module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qnetworkaccessbackend_p.h"
41#include "qnetworkaccessmanager_p.h"
42#include "qnetworkconfigmanager.h"
43#include "qnetworkrequest.h"
44#include "qnetworkreply.h"
45#include "qnetworkreply_p.h"
46#include "QtCore/qmutex.h"
47#include "QtCore/qstringlist.h"
48#include "QtNetwork/private/qnetworksession_p.h"
49
50#include "qnetworkaccesscachebackend_p.h"
51#include "qabstractnetworkcache.h"
52#include "qhostinfo.h"
53
54#include "private/qnoncontiguousbytedevice_p.h"
55
56QT_BEGIN_NAMESPACE
57
58class QNetworkAccessBackendFactoryData: public QList<QNetworkAccessBackendFactory *>
59{
60public:
61 QNetworkAccessBackendFactoryData()
62 {
63 valid.ref();
64 }
65 ~QNetworkAccessBackendFactoryData()
66 {
67 QMutexLocker locker(&mutex); // why do we need to lock?
68 valid.deref();
69 }
70
71 QRecursiveMutex mutex;
72 //this is used to avoid (re)constructing factory data from destructors of other global classes
73 static QBasicAtomicInt valid;
74};
75Q_GLOBAL_STATIC(QNetworkAccessBackendFactoryData, factoryData)
76QBasicAtomicInt QNetworkAccessBackendFactoryData::valid = Q_BASIC_ATOMIC_INITIALIZER(0);
77
78QNetworkAccessBackendFactory::QNetworkAccessBackendFactory()
79{
80 QMutexLocker locker(&factoryData()->mutex);
81 factoryData()->append(t: this);
82}
83
84QNetworkAccessBackendFactory::~QNetworkAccessBackendFactory()
85{
86 if (QNetworkAccessBackendFactoryData::valid.loadRelaxed()) {
87 QMutexLocker locker(&factoryData()->mutex);
88 factoryData()->removeAll(t: this);
89 }
90}
91
92QNetworkAccessBackend *QNetworkAccessManagerPrivate::findBackend(QNetworkAccessManager::Operation op,
93 const QNetworkRequest &request)
94{
95 if (QNetworkAccessBackendFactoryData::valid.loadRelaxed()) {
96 QMutexLocker locker(&factoryData()->mutex);
97 QNetworkAccessBackendFactoryData::ConstIterator it = factoryData()->constBegin(),
98 end = factoryData()->constEnd();
99 while (it != end) {
100 QNetworkAccessBackend *backend = (*it)->create(op, request);
101 if (backend) {
102 backend->manager = this;
103 return backend; // found a factory that handled our request
104 }
105 ++it;
106 }
107 }
108 return nullptr;
109}
110
111QStringList QNetworkAccessManagerPrivate::backendSupportedSchemes() const
112{
113 if (QNetworkAccessBackendFactoryData::valid.loadRelaxed()) {
114 QMutexLocker locker(&factoryData()->mutex);
115 QNetworkAccessBackendFactoryData::ConstIterator it = factoryData()->constBegin();
116 QNetworkAccessBackendFactoryData::ConstIterator end = factoryData()->constEnd();
117 QStringList schemes;
118 while (it != end) {
119 schemes += (*it)->supportedSchemes();
120 ++it;
121 }
122 return schemes;
123 }
124 return QStringList();
125}
126
127QNonContiguousByteDevice* QNetworkAccessBackend::createUploadByteDevice()
128{
129 if (reply->outgoingDataBuffer)
130 uploadByteDevice = QNonContiguousByteDeviceFactory::createShared(ringBuffer: reply->outgoingDataBuffer);
131 else if (reply->outgoingData) {
132 uploadByteDevice = QNonContiguousByteDeviceFactory::createShared(device: reply->outgoingData);
133 } else {
134 return nullptr;
135 }
136
137 // We want signal emissions only for normal asynchronous uploads
138 if (!isSynchronous())
139 connect(sender: uploadByteDevice.data(), SIGNAL(readProgress(qint64,qint64)), receiver: this, SLOT(emitReplyUploadProgress(qint64,qint64)));
140
141 return uploadByteDevice.data();
142}
143
144// need to have this function since the reply is a private member variable
145// and the special backends need to access this.
146void QNetworkAccessBackend::emitReplyUploadProgress(qint64 bytesSent, qint64 bytesTotal)
147{
148 if (reply->isFinished)
149 return;
150 reply->emitUploadProgress(bytesSent, bytesTotal);
151}
152
153QNetworkAccessBackend::QNetworkAccessBackend()
154 : manager(nullptr)
155 , reply(nullptr)
156 , synchronous(false)
157{
158}
159
160QNetworkAccessBackend::~QNetworkAccessBackend()
161{
162}
163
164void QNetworkAccessBackend::downstreamReadyWrite()
165{
166 // do nothing
167}
168
169void QNetworkAccessBackend::setDownstreamLimited(bool b)
170{
171 Q_UNUSED(b);
172 // do nothing
173}
174
175void QNetworkAccessBackend::copyFinished(QIODevice *)
176{
177 // do nothing
178}
179
180void QNetworkAccessBackend::ignoreSslErrors()
181{
182 // do nothing
183}
184
185void QNetworkAccessBackend::ignoreSslErrors(const QList<QSslError> &errors)
186{
187 Q_UNUSED(errors);
188 // do nothing
189}
190
191void QNetworkAccessBackend::fetchSslConfiguration(QSslConfiguration &) const
192{
193 // do nothing
194}
195
196void QNetworkAccessBackend::setSslConfiguration(const QSslConfiguration &)
197{
198 // do nothing
199}
200
201QNetworkCacheMetaData QNetworkAccessBackend::fetchCacheMetaData(const QNetworkCacheMetaData &) const
202{
203 return QNetworkCacheMetaData();
204}
205
206QNetworkAccessManager::Operation QNetworkAccessBackend::operation() const
207{
208 return reply->operation;
209}
210
211QNetworkRequest QNetworkAccessBackend::request() const
212{
213 return reply->request;
214}
215
216#ifndef QT_NO_NETWORKPROXY
217QList<QNetworkProxy> QNetworkAccessBackend::proxyList() const
218{
219 return reply->proxyList;
220}
221#endif
222
223QAbstractNetworkCache *QNetworkAccessBackend::networkCache() const
224{
225 if (!manager)
226 return nullptr;
227 return manager->networkCache;
228}
229
230void QNetworkAccessBackend::setCachingEnabled(bool enable)
231{
232 reply->setCachingEnabled(enable);
233}
234
235bool QNetworkAccessBackend::isCachingEnabled() const
236{
237 return reply->isCachingEnabled();
238}
239
240qint64 QNetworkAccessBackend::nextDownstreamBlockSize() const
241{
242 return reply->nextDownstreamBlockSize();
243}
244
245void QNetworkAccessBackend::writeDownstreamData(QByteDataBuffer &list)
246{
247 reply->appendDownstreamData(data&: list);
248}
249
250void QNetworkAccessBackend::writeDownstreamData(QIODevice *data)
251{
252 reply->appendDownstreamData(data);
253}
254
255// not actually appending data, it was already written to the user buffer
256void QNetworkAccessBackend::writeDownstreamDataDownloadBuffer(qint64 bytesReceived, qint64 bytesTotal)
257{
258 reply->appendDownstreamDataDownloadBuffer(bytesReceived, bytesTotal);
259}
260
261char* QNetworkAccessBackend::getDownloadBuffer(qint64 size)
262{
263 return reply->getDownloadBuffer(size);
264}
265
266QVariant QNetworkAccessBackend::header(QNetworkRequest::KnownHeaders header) const
267{
268 return reply->q_func()->header(header);
269}
270
271void QNetworkAccessBackend::setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value)
272{
273 reply->setCookedHeader(header, value);
274}
275
276bool QNetworkAccessBackend::hasRawHeader(const QByteArray &headerName) const
277{
278 return reply->q_func()->hasRawHeader(headerName);
279}
280
281QByteArray QNetworkAccessBackend::rawHeader(const QByteArray &headerName) const
282{
283 return reply->q_func()->rawHeader(headerName);
284}
285
286QList<QByteArray> QNetworkAccessBackend::rawHeaderList() const
287{
288 return reply->q_func()->rawHeaderList();
289}
290
291void QNetworkAccessBackend::setRawHeader(const QByteArray &headerName, const QByteArray &headerValue)
292{
293 reply->setRawHeader(key: headerName, value: headerValue);
294}
295
296QVariant QNetworkAccessBackend::attribute(QNetworkRequest::Attribute code) const
297{
298 return reply->q_func()->attribute(code);
299}
300
301void QNetworkAccessBackend::setAttribute(QNetworkRequest::Attribute code, const QVariant &value)
302{
303 if (value.isValid())
304 reply->attributes.insert(akey: code, avalue: value);
305 else
306 reply->attributes.remove(akey: code);
307}
308QUrl QNetworkAccessBackend::url() const
309{
310 return reply->url;
311}
312
313void QNetworkAccessBackend::setUrl(const QUrl &url)
314{
315 reply->url = url;
316}
317
318void QNetworkAccessBackend::finished()
319{
320 reply->finished();
321}
322
323void QNetworkAccessBackend::error(QNetworkReply::NetworkError code, const QString &errorString)
324{
325 reply->error(code, errorString);
326}
327
328#ifndef QT_NO_NETWORKPROXY
329void QNetworkAccessBackend::proxyAuthenticationRequired(const QNetworkProxy &proxy,
330 QAuthenticator *authenticator)
331{
332 manager->proxyAuthenticationRequired(url: QUrl(), proxy, synchronous, authenticator, lastProxyAuthentication: &reply->lastProxyAuthentication);
333}
334#endif
335
336void QNetworkAccessBackend::authenticationRequired(QAuthenticator *authenticator)
337{
338 manager->authenticationRequired(authenticator, reply: reply->q_func(), synchronous, url&: reply->url, urlForLastAuthentication: &reply->urlForLastAuthentication);
339}
340
341void QNetworkAccessBackend::metaDataChanged()
342{
343 reply->metaDataChanged();
344}
345
346void QNetworkAccessBackend::redirectionRequested(const QUrl &target)
347{
348 reply->redirectionRequested(target);
349}
350
351void QNetworkAccessBackend::encrypted()
352{
353#ifndef QT_NO_SSL
354 reply->encrypted();
355#endif
356}
357
358void QNetworkAccessBackend::sslErrors(const QList<QSslError> &errors)
359{
360#ifndef QT_NO_SSL
361 reply->sslErrors(errors);
362#else
363 Q_UNUSED(errors);
364#endif
365}
366
367/*!
368 Starts the backend. Returns \c true if the backend is started. Returns \c false if the backend
369 could not be started due to an unopened or roaming session. The caller should recall this
370 function once the session has been opened or the roaming process has finished.
371*/
372bool QNetworkAccessBackend::start()
373{
374#ifndef QT_NO_BEARERMANAGEMENT // ### Qt6: Remove section
375 // For bearer, check if session start is required
376 QSharedPointer<QNetworkSession> networkSession(manager->getNetworkSession());
377 if (networkSession) {
378 // session required
379 if (networkSession->isOpen() &&
380 networkSession->state() == QNetworkSession::Connected) {
381 // Session is already open and ready to use.
382 // copy network session down to the backend
383 setProperty(name: "_q_networksession", value: QVariant::fromValue(value: networkSession));
384 } else {
385 // Session not ready, but can skip for loopback connections
386
387 // This is not ideal.
388 // Don't need an open session for localhost access.
389 if (!reply->url.isLocalFile()) {
390 const QString host = reply->url.host();
391 if (host != QLatin1String("localhost") && !QHostAddress(host).isLoopback())
392 return false; // need to wait for session to be opened
393 }
394 }
395 }
396#endif
397
398#ifndef QT_NO_NETWORKPROXY
399 reply->proxyList = manager->queryProxy(query: QNetworkProxyQuery(url()));
400#endif
401
402 // now start the request
403 open();
404 return true;
405}
406
407QT_END_NAMESPACE
408

source code of qtbase/src/network/access/qnetworkaccessbackend.cpp