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 
56 QT_BEGIN_NAMESPACE
57 
58 class QNetworkAccessBackendFactoryData: public QList<QNetworkAccessBackendFactory *>
59 {
60 public:
QNetworkAccessBackendFactoryData()61     QNetworkAccessBackendFactoryData()
62     {
63         valid.ref();
64     }
~QNetworkAccessBackendFactoryData()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 };
75 Q_GLOBAL_STATIC(QNetworkAccessBackendFactoryData, factoryData)
76 QBasicAtomicInt QNetworkAccessBackendFactoryData::valid = Q_BASIC_ATOMIC_INITIALIZER(0);
77 
QNetworkAccessBackendFactory()78 QNetworkAccessBackendFactory::QNetworkAccessBackendFactory()
79 {
80     QMutexLocker locker(&factoryData()->mutex);
81     factoryData()->append(this);
82 }
83 
~QNetworkAccessBackendFactory()84 QNetworkAccessBackendFactory::~QNetworkAccessBackendFactory()
85 {
86     if (QNetworkAccessBackendFactoryData::valid.loadRelaxed()) {
87         QMutexLocker locker(&factoryData()->mutex);
88         factoryData()->removeAll(this);
89     }
90 }
91 
findBackend(QNetworkAccessManager::Operation op,const QNetworkRequest & request)92 QNetworkAccessBackend *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 
backendSupportedSchemes() const111 QStringList 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 
createUploadByteDevice()127 QNonContiguousByteDevice* QNetworkAccessBackend::createUploadByteDevice()
128 {
129     if (reply->outgoingDataBuffer)
130         uploadByteDevice = QNonContiguousByteDeviceFactory::createShared(reply->outgoingDataBuffer);
131     else if (reply->outgoingData) {
132         uploadByteDevice = QNonContiguousByteDeviceFactory::createShared(reply->outgoingData);
133     } else {
134         return nullptr;
135     }
136 
137     // We want signal emissions only for normal asynchronous uploads
138     if (!isSynchronous())
139         connect(uploadByteDevice.data(), SIGNAL(readProgress(qint64,qint64)), 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.
emitReplyUploadProgress(qint64 bytesSent,qint64 bytesTotal)146 void QNetworkAccessBackend::emitReplyUploadProgress(qint64 bytesSent, qint64 bytesTotal)
147 {
148     if (reply->isFinished)
149         return;
150     reply->emitUploadProgress(bytesSent, bytesTotal);
151 }
152 
QNetworkAccessBackend()153 QNetworkAccessBackend::QNetworkAccessBackend()
154     : manager(nullptr)
155     , reply(nullptr)
156     , synchronous(false)
157 {
158 }
159 
~QNetworkAccessBackend()160 QNetworkAccessBackend::~QNetworkAccessBackend()
161 {
162 }
163 
downstreamReadyWrite()164 void QNetworkAccessBackend::downstreamReadyWrite()
165 {
166     // do nothing
167 }
168 
setDownstreamLimited(bool b)169 void QNetworkAccessBackend::setDownstreamLimited(bool b)
170 {
171     Q_UNUSED(b);
172     // do nothing
173 }
174 
copyFinished(QIODevice *)175 void QNetworkAccessBackend::copyFinished(QIODevice *)
176 {
177     // do nothing
178 }
179 
ignoreSslErrors()180 void QNetworkAccessBackend::ignoreSslErrors()
181 {
182     // do nothing
183 }
184 
ignoreSslErrors(const QList<QSslError> & errors)185 void QNetworkAccessBackend::ignoreSslErrors(const QList<QSslError> &errors)
186 {
187     Q_UNUSED(errors);
188     // do nothing
189 }
190 
fetchSslConfiguration(QSslConfiguration &) const191 void QNetworkAccessBackend::fetchSslConfiguration(QSslConfiguration &) const
192 {
193     // do nothing
194 }
195 
setSslConfiguration(const QSslConfiguration &)196 void QNetworkAccessBackend::setSslConfiguration(const QSslConfiguration &)
197 {
198     // do nothing
199 }
200 
fetchCacheMetaData(const QNetworkCacheMetaData &) const201 QNetworkCacheMetaData QNetworkAccessBackend::fetchCacheMetaData(const QNetworkCacheMetaData &) const
202 {
203     return QNetworkCacheMetaData();
204 }
205 
operation() const206 QNetworkAccessManager::Operation QNetworkAccessBackend::operation() const
207 {
208     return reply->operation;
209 }
210 
request() const211 QNetworkRequest QNetworkAccessBackend::request() const
212 {
213     return reply->request;
214 }
215 
216 #ifndef QT_NO_NETWORKPROXY
proxyList() const217 QList<QNetworkProxy> QNetworkAccessBackend::proxyList() const
218 {
219     return reply->proxyList;
220 }
221 #endif
222 
networkCache() const223 QAbstractNetworkCache *QNetworkAccessBackend::networkCache() const
224 {
225     if (!manager)
226         return nullptr;
227     return manager->networkCache;
228 }
229 
setCachingEnabled(bool enable)230 void QNetworkAccessBackend::setCachingEnabled(bool enable)
231 {
232     reply->setCachingEnabled(enable);
233 }
234 
isCachingEnabled() const235 bool QNetworkAccessBackend::isCachingEnabled() const
236 {
237     return reply->isCachingEnabled();
238 }
239 
nextDownstreamBlockSize() const240 qint64 QNetworkAccessBackend::nextDownstreamBlockSize() const
241 {
242     return reply->nextDownstreamBlockSize();
243 }
244 
writeDownstreamData(QByteDataBuffer & list)245 void QNetworkAccessBackend::writeDownstreamData(QByteDataBuffer &list)
246 {
247     reply->appendDownstreamData(list);
248 }
249 
writeDownstreamData(QIODevice * data)250 void QNetworkAccessBackend::writeDownstreamData(QIODevice *data)
251 {
252     reply->appendDownstreamData(data);
253 }
254 
255 // not actually appending data, it was already written to the user buffer
writeDownstreamDataDownloadBuffer(qint64 bytesReceived,qint64 bytesTotal)256 void QNetworkAccessBackend::writeDownstreamDataDownloadBuffer(qint64 bytesReceived, qint64 bytesTotal)
257 {
258     reply->appendDownstreamDataDownloadBuffer(bytesReceived, bytesTotal);
259 }
260 
getDownloadBuffer(qint64 size)261 char* QNetworkAccessBackend::getDownloadBuffer(qint64 size)
262 {
263     return reply->getDownloadBuffer(size);
264 }
265 
header(QNetworkRequest::KnownHeaders header) const266 QVariant QNetworkAccessBackend::header(QNetworkRequest::KnownHeaders header) const
267 {
268     return reply->q_func()->header(header);
269 }
270 
setHeader(QNetworkRequest::KnownHeaders header,const QVariant & value)271 void QNetworkAccessBackend::setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value)
272 {
273     reply->setCookedHeader(header, value);
274 }
275 
hasRawHeader(const QByteArray & headerName) const276 bool QNetworkAccessBackend::hasRawHeader(const QByteArray &headerName) const
277 {
278     return reply->q_func()->hasRawHeader(headerName);
279 }
280 
rawHeader(const QByteArray & headerName) const281 QByteArray QNetworkAccessBackend::rawHeader(const QByteArray &headerName) const
282 {
283     return reply->q_func()->rawHeader(headerName);
284 }
285 
rawHeaderList() const286 QList<QByteArray> QNetworkAccessBackend::rawHeaderList() const
287 {
288     return reply->q_func()->rawHeaderList();
289 }
290 
setRawHeader(const QByteArray & headerName,const QByteArray & headerValue)291 void QNetworkAccessBackend::setRawHeader(const QByteArray &headerName, const QByteArray &headerValue)
292 {
293     reply->setRawHeader(headerName, headerValue);
294 }
295 
attribute(QNetworkRequest::Attribute code) const296 QVariant QNetworkAccessBackend::attribute(QNetworkRequest::Attribute code) const
297 {
298     return reply->q_func()->attribute(code);
299 }
300 
setAttribute(QNetworkRequest::Attribute code,const QVariant & value)301 void QNetworkAccessBackend::setAttribute(QNetworkRequest::Attribute code, const QVariant &value)
302 {
303     if (value.isValid())
304         reply->attributes.insert(code, value);
305     else
306         reply->attributes.remove(code);
307 }
url() const308 QUrl QNetworkAccessBackend::url() const
309 {
310     return reply->url;
311 }
312 
setUrl(const QUrl & url)313 void QNetworkAccessBackend::setUrl(const QUrl &url)
314 {
315     reply->url = url;
316 }
317 
finished()318 void QNetworkAccessBackend::finished()
319 {
320     reply->finished();
321 }
322 
error(QNetworkReply::NetworkError code,const QString & errorString)323 void QNetworkAccessBackend::error(QNetworkReply::NetworkError code, const QString &errorString)
324 {
325     reply->error(code, errorString);
326 }
327 
328 #ifndef QT_NO_NETWORKPROXY
proxyAuthenticationRequired(const QNetworkProxy & proxy,QAuthenticator * authenticator)329 void QNetworkAccessBackend::proxyAuthenticationRequired(const QNetworkProxy &proxy,
330                                                         QAuthenticator *authenticator)
331 {
332     manager->proxyAuthenticationRequired(QUrl(), proxy, synchronous, authenticator, &reply->lastProxyAuthentication);
333 }
334 #endif
335 
authenticationRequired(QAuthenticator * authenticator)336 void QNetworkAccessBackend::authenticationRequired(QAuthenticator *authenticator)
337 {
338     manager->authenticationRequired(authenticator, reply->q_func(), synchronous, reply->url, &reply->urlForLastAuthentication);
339 }
340 
metaDataChanged()341 void QNetworkAccessBackend::metaDataChanged()
342 {
343     reply->metaDataChanged();
344 }
345 
redirectionRequested(const QUrl & target)346 void QNetworkAccessBackend::redirectionRequested(const QUrl &target)
347 {
348     reply->redirectionRequested(target);
349 }
350 
encrypted()351 void QNetworkAccessBackend::encrypted()
352 {
353 #ifndef QT_NO_SSL
354     reply->encrypted();
355 #endif
356 }
357 
sslErrors(const QList<QSslError> & errors)358 void 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 */
start()372 bool 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("_q_networksession", QVariant::fromValue(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(QNetworkProxyQuery(url()));
400 #endif
401 
402     // now start the request
403     open();
404     return true;
405 }
406 
407 QT_END_NAMESPACE
408