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

source code of qtbase/src/network/access/qhttpthreaddelegate_p.h