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#ifndef QHTTPTHREADDELEGATE_H
41#define QHTTPTHREADDELEGATE_H
42
43
44//
45// W A R N I N G
46// -------------
47//
48// This file is not part of the Qt API. It exists for the convenience
49// of the Network Access API. This header file may change from
50// version to version without notice, or even be removed.
51//
52// We mean it.
53//
54
55#include <QtNetwork/private/qtnetworkglobal_p.h>
56#include <QObject>
57#include <QThreadStorage>
58#include <QNetworkProxy>
59#include <QSslConfiguration>
60#include <QSslError>
61#include <QList>
62#include <QNetworkReply>
63#include "qhttpnetworkrequest_p.h"
64#include "qhttpnetworkconnection_p.h"
65#include "qhttp2configuration.h"
66#include <QSharedPointer>
67#include <QScopedPointer>
68#include "private/qnoncontiguousbytedevice_p.h"
69#include "qnetworkaccessauthenticationmanager_p.h"
70#include <QtNetwork/private/http2protocol_p.h>
71
72QT_REQUIRE_CONFIG(http);
73
74QT_BEGIN_NAMESPACE
75
76class QAuthenticator;
77class QHttpNetworkReply;
78class QEventLoop;
79class QNetworkAccessCache;
80class QNetworkAccessCachedHttpConnection;
81
82class QHttpThreadDelegate : public QObject
83{
84 Q_OBJECT
85public:
86 explicit QHttpThreadDelegate(QObject *parent = nullptr);
87
88 ~QHttpThreadDelegate();
89
90 // incoming
91 bool ssl;
92#ifndef QT_NO_SSL
93 QScopedPointer<QSslConfiguration> incomingSslConfiguration;
94#endif
95 QHttpNetworkRequest httpRequest;
96 qint64 downloadBufferMaximumSize;
97 qint64 readBufferMaxSize;
98 qint64 bytesEmitted;
99 // From backend, modified by us for signal compression
100 QSharedPointer<QAtomicInt> pendingDownloadData;
101 QSharedPointer<QAtomicInt> pendingDownloadProgress;
102#ifndef QT_NO_NETWORKPROXY
103 QNetworkProxy cacheProxy;
104 QNetworkProxy transparentProxy;
105#endif
106 QSharedPointer<QNetworkAccessAuthenticationManager> authenticationManager;
107 bool synchronous;
108
109 // outgoing, Retrieved in the synchronous HTTP case
110 QByteArray synchronousDownloadData;
111 QList<QPair<QByteArray,QByteArray> > incomingHeaders;
112 int incomingStatusCode;
113 QString incomingReasonPhrase;
114 bool isPipeliningUsed;
115 bool isHttp2Used;
116 qint64 incomingContentLength;
117 qint64 removedContentLength;
118 QNetworkReply::NetworkError incomingErrorCode;
119 QString incomingErrorDetail;
120 QHttp2Configuration http2Parameters;
121
122protected:
123 // The zerocopy download buffer, if used:
124 QSharedPointer<char> downloadBuffer;
125 // The QHttpNetworkConnection that is used
126 QNetworkAccessCachedHttpConnection *httpConnection;
127 QByteArray cacheKey;
128 QHttpNetworkReply *httpReply;
129
130 // Used for implementing the synchronous HTTP, see startRequestSynchronously()
131 QEventLoop *synchronousRequestLoop;
132
133signals:
134 void authenticationRequired(const QHttpNetworkRequest &request, QAuthenticator *);
135#ifndef QT_NO_NETWORKPROXY
136 void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *);
137#endif
138#ifndef QT_NO_SSL
139 void encrypted();
140 void sslErrors(const QList<QSslError> &, bool *, QList<QSslError> *);
141 void sslConfigurationChanged(const QSslConfiguration &);
142 void preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *);
143#endif
144 void downloadMetaData(const QList<QPair<QByteArray,QByteArray> > &, int, const QString &, bool,
145 QSharedPointer<char>, qint64, qint64, bool);
146 void downloadProgress(qint64, qint64);
147 void downloadData(const QByteArray &);
148 void error(QNetworkReply::NetworkError, const QString &);
149 void downloadFinished();
150 void redirected(const QUrl &url, int httpStatus, int maxRedirectsRemainig);
151
152public slots:
153 // This are called via QueuedConnection from user thread
154 void startRequest();
155 void abortRequest();
156 void readBufferSizeChanged(qint64 size);
157 void readBufferFreed(qint64 size);
158
159 // This is called with a BlockingQueuedConnection from user thread
160 void startRequestSynchronously();
161protected slots:
162 // From QHttp*
163 void readyReadSlot();
164 void finishedSlot();
165 void finishedWithErrorSlot(QNetworkReply::NetworkError errorCode, const QString &detail = QString());
166 void synchronousFinishedSlot();
167 void synchronousFinishedWithErrorSlot(QNetworkReply::NetworkError errorCode, const QString &detail = QString());
168 void headerChangedSlot();
169 void synchronousHeaderChangedSlot();
170 void dataReadProgressSlot(qint64 done, qint64 total);
171 void cacheCredentialsSlot(const QHttpNetworkRequest &request, QAuthenticator *authenticator);
172#ifndef QT_NO_SSL
173 void encryptedSlot();
174 void sslErrorsSlot(const QList<QSslError> &errors);
175 void preSharedKeyAuthenticationRequiredSlot(QSslPreSharedKeyAuthenticator *authenticator);
176#endif
177
178 void synchronousAuthenticationRequiredSlot(const QHttpNetworkRequest &request, QAuthenticator *);
179#ifndef QT_NO_NETWORKPROXY
180 void synchronousProxyAuthenticationRequiredSlot(const QNetworkProxy &, QAuthenticator *);
181#endif
182
183protected:
184 // Cache for all the QHttpNetworkConnection objects.
185 // This is per thread.
186 static QThreadStorage<QNetworkAccessCache *> connections;
187
188};
189
190// This QNonContiguousByteDevice is connected to the QNetworkAccessHttpBackend
191// and represents the PUT/POST data.
192class QNonContiguousByteDeviceThreadForwardImpl : public QNonContiguousByteDevice
193{
194 Q_OBJECT
195protected:
196 bool wantDataPending;
197 qint64 m_amount;
198 char *m_data;
199 QByteArray m_dataArray;
200 bool m_atEnd;
201 qint64 m_size;
202 qint64 m_pos; // to match calls of haveDataSlot with the expected position
203public:
204 QNonContiguousByteDeviceThreadForwardImpl(bool aE, qint64 s)
205 : QNonContiguousByteDevice(),
206 wantDataPending(false),
207 m_amount(0),
208 m_data(nullptr),
209 m_atEnd(aE),
210 m_size(s),
211 m_pos(0)
212 {
213 }
214
215 ~QNonContiguousByteDeviceThreadForwardImpl()
216 {
217 }
218
219 qint64 pos() const override
220 {
221 return m_pos;
222 }
223
224 const char* readPointer(qint64 maximumLength, qint64 &len) override
225 {
226 if (m_amount > 0) {
227 len = m_amount;
228 return m_data;
229 }
230
231 if (m_atEnd) {
232 len = -1;
233 } else if (!wantDataPending) {
234 len = 0;
235 wantDataPending = true;
236 emit wantData(maximumLength);
237 } else {
238 // Do nothing, we already sent a wantData signal and wait for results
239 len = 0;
240 }
241 return nullptr;
242 }
243
244 bool advanceReadPointer(qint64 a) override
245 {
246 if (m_data == nullptr)
247 return false;
248
249 m_amount -= a;
250 m_data += a;
251 m_pos += a;
252
253 // To main thread to inform about our state. The m_pos will be sent as a sanity check.
254 emit processedData(m_pos, a);
255
256 return true;
257 }
258
259 bool atEnd() const override
260 {
261 if (m_amount > 0)
262 return false;
263 else
264 return m_atEnd;
265 }
266
267 bool reset() override
268 {
269 m_amount = 0;
270 m_data = nullptr;
271 m_dataArray.clear();
272
273 if (wantDataPending) {
274 // had requested the user thread to send some data (only 1 in-flight at any moment)
275 wantDataPending = false;
276 }
277
278 // Communicate as BlockingQueuedConnection
279 bool b = false;
280 emit resetData(&b);
281 if (b) {
282 // the reset succeeded, we're at pos 0 again
283 m_pos = 0;
284 // the HTTP code will anyway abort the request if !b.
285 }
286 return b;
287 }
288
289 qint64 size() const override
290 {
291 return m_size;
292 }
293
294public slots:
295 // From user thread:
296 void haveDataSlot(qint64 pos, const QByteArray &dataArray, bool dataAtEnd, qint64 dataSize)
297 {
298 if (pos != m_pos) {
299 // Sometimes when re-sending a request in the qhttpnetwork* layer there is a pending haveData from the
300 // user thread on the way to us. We need to ignore it since it is the data for the wrong(later) chunk.
301 return;
302 }
303 wantDataPending = false;
304
305 m_dataArray = dataArray;
306 m_data = const_cast<char*>(m_dataArray.constData());
307 m_amount = dataArray.size();
308
309 m_atEnd = dataAtEnd;
310 m_size = dataSize;
311
312 // This will tell the HTTP code (QHttpNetworkConnectionChannel) that we have data available now
313 emit readyRead();
314 }
315
316signals:
317 // void readyRead(); in parent class
318 // void readProgress(qint64 current, qint64 total); happens in the main thread with the real bytedevice
319
320 // to main thread:
321 void wantData(qint64);
322 void processedData(qint64 pos, qint64 amount);
323 void resetData(bool *b);
324};
325
326QT_END_NAMESPACE
327
328#endif // QHTTPTHREADDELEGATE_H
329