Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qhttpprotocolhandler.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2014 BlackBerry Limited. All rights reserved.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include <private/qhttpprotocolhandler_p.h>
6#include <private/qnoncontiguousbytedevice_p.h>
7#include <private/qhttpnetworkconnectionchannel_p.h>
8#include <private/qsocketabstraction_p.h>
9
11
12using namespace Qt::StringLiterals;
13
18
20{
22
23 if (!m_reply) {
24 if (m_socket->bytesAvailable() > 0)
25 qWarning() << "QAbstractProtocolHandler::_q_receiveReply() called without QHttpNetworkReply,"
26 << m_socket->bytesAvailable() << "bytes on socket.";
28 return;
29 }
30
31 // only run when the QHttpNetworkConnection is not currently being destructed, e.g.
32 // this function is called from _q_disconnected which is called because
33 // of ~QHttpNetworkConnectionPrivate
34 if (!qobject_cast<QHttpNetworkConnection*>(m_connection)) {
35 return;
36 }
37
38 // connection might be closed to signal the end of data
40 if (m_socket->bytesAvailable() <= 0) {
42 // finish this reply. this case happens when the server did not send a content length
45 return;
46 } else {
48 return;
49 }
50 } else {
51 // socket not connected but still bytes for reading.. just continue in this function
52 }
53 }
54
55 // read loop for the response
56 qint64 bytes = 0;
57 qint64 lastBytes = bytes;
58 do {
59 lastBytes = bytes;
60
62 switch (state) {
66 }
68 qint64 statusBytes = m_reply->d_func()->readStatus(m_socket);
69 if (statusBytes == -1) {
70 // connection broke while reading status. also handled if later _q_disconnected is called
72 return;
73 }
74 bytes += statusBytes;
76 break;
77 }
79 QHttpNetworkReplyPrivate *replyPrivate = m_reply->d_func();
80 qint64 headerBytes = replyPrivate->readHeader(m_socket);
81 if (headerBytes == -1) {
82 // connection broke while reading headers. also handled if later _q_disconnected is called
84 return;
85 }
86 bytes += headerBytes;
87 // If headers were parsed successfully now it is the ReadingDataState
88 if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState) {
89 if (replyPrivate->isCompressed() && replyPrivate->autoDecompress) {
90 // remove the Content-Length from header
91 replyPrivate->removeAutoDecompressHeader();
92 } else {
93 replyPrivate->autoDecompress = false;
94 }
95 const int statusCode = m_reply->statusCode();
96 if (statusCode == 100 || (102 <= statusCode && statusCode <= 199)) {
97 replyPrivate->clearHttpLayerInformation();
99 break; // ignore
100 }
101 if (replyPrivate->shouldEmitSignals())
103 // After headerChanged had been emitted
104 // we can suddenly have a replyPrivate->userProvidedDownloadBuffer
105 // this is handled in the ReadingDataState however
106
107 if (!replyPrivate->expectContent()) {
108 replyPrivate->state = QHttpNetworkReplyPrivate::AllDoneState;
110 break;
111 }
112 }
113 break;
114 }
116 QHttpNetworkReplyPrivate *replyPrivate = m_reply->d_func();
118 replyPrivate->downstreamLimited && !replyPrivate->responseData.isEmpty() && replyPrivate->shouldEmitSignals()) {
119 // (only do the following when still connected, not when we have already been disconnected and there is still data)
120 // We already have some HTTP body data. We don't read more from the socket until
121 // this is fetched by QHttpNetworkAccessHttpBackend. If we would read more,
122 // we could not limit our read buffer usage.
123 // We only do this when shouldEmitSignals==true because our HTTP parsing
124 // always needs to parse the 401/407 replies. Therefore they don't really obey
125 // to the read buffer maximum size, but we don't care since they should be small.
126 return;
127 }
128
129 if (replyPrivate->userProvidedDownloadBuffer) {
130 // the user provided a direct buffer where we should put all our data in.
131 // this only works when we can tell the user the content length and he/she can allocate
132 // the buffer in that size.
133 // note that this call will read only from the still buffered data
134 qint64 haveRead = replyPrivate->readBodyVeryFast(m_socket, replyPrivate->userProvidedDownloadBuffer + replyPrivate->totalProgress);
135 if (haveRead > 0) {
136 bytes += haveRead;
137 replyPrivate->totalProgress += haveRead;
138 // the user will get notified of it via progress signal
139 emit m_reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
140 } else if (haveRead == 0) {
141 // Happens since this called in a loop. Currently no bytes available.
142 } else if (haveRead < 0) {
144 break;
145 }
146 } else if (!replyPrivate->isChunked() && replyPrivate->bodyLength > 0) {
147 // bulk files like images should fulfill these properties and
148 // we can therefore save on memory copying
149 qint64 haveRead = replyPrivate->readBodyFast(m_socket, &replyPrivate->responseData);
150 bytes += haveRead;
151 replyPrivate->totalProgress += haveRead;
152 if (replyPrivate->shouldEmitSignals()) {
154 emit m_reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
155 }
156 }
157 else
158 {
159 // use the traditional slower reading (for compressed encoding, chunked encoding,
160 // no content-length etc)
161 qint64 haveRead = replyPrivate->readBody(m_socket, &replyPrivate->responseData);
162 if (haveRead > 0) {
163 bytes += haveRead;
164 replyPrivate->totalProgress += haveRead;
165 if (replyPrivate->shouldEmitSignals()) {
167 emit m_reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
168 }
169 } else if (haveRead == -1) {
170 // Some error occurred
171 m_connection->d_func()->emitReplyError(m_socket, m_reply, QNetworkReply::ProtocolFailure);
172 break;
173 }
174 }
175 // still in ReadingDataState? This function will be called again by the socket's readyRead
176 if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState)
177 break;
178
179 // everything done
181 }
185 lastBytes = bytes; // No need to loop more just to call m_channel->allDone again.
186 break;
187 default:
188 break;
189 }
190 } while (bytes != lastBytes && m_reply);
191}
192
194{
196 && m_socket->bytesAvailable() == 0) {
197 // We got a readyRead but no bytes are available..
198 // This happens for the Unbuffered QTcpSocket
199 // Also check if socket is in ConnectedState since
200 // this function may also be invoked via the event loop.
201 char c;
202 qint64 ret = m_socket->peek(&c, 1);
203 if (ret < 0) {
205 // We still need to handle the reply so it emits its signals etc.
206 if (m_reply)
208 return;
209 }
210 }
211
213 if (m_socket->bytesAvailable()) {
214 // We might get a spurious call from readMoreLater()
215 // call of the QHttpNetworkConnection even while the socket is disconnecting.
216 // Therefore check if there is actually bytes available before changing the channel state.
218 }
219 if (m_reply)
221 }
222}
223
225{
227
228 if (!m_reply) {
229 // heh, how should that happen!
230 qWarning("QAbstractProtocolHandler::sendRequest() called without QHttpNetworkReply");
231 return false;
232 }
233
234 switch (m_channel->state) {
235 case QHttpNetworkConnectionChannel::IdleState: { // write the header
236 if (!m_channel->ensureConnection()) {
237 // wait for the connection (and encryption) to be done
238 // sendRequest will be called again from either
239 // _q_connected or _q_encrypted
240 return false;
241 }
246 m_connection->preConnectFinished(); // will only decrease the counter
247 m_reply = nullptr; // so we can reuse this channel
248 return true; // we have a working connection and are done
249 }
250
251 m_channel->written = 0; // excluding the header
253
254 QHttpNetworkReplyPrivate *replyPrivate = m_reply->d_func();
255 replyPrivate->clear();
256 replyPrivate->connection = m_connection;
257 replyPrivate->connectionChannel = m_channel;
258 replyPrivate->autoDecompress = m_channel->request.d->autoDecompress;
259 replyPrivate->pipeliningUsed = false;
260
261 // if the url contains authentication parameters, use the new ones
262 // both channels will use the new authentication parameters
266 if (url.userName() != auth.user()
267 || (!url.password().isEmpty() && url.password() != auth.password())) {
268 auth.setUser(url.userName());
269 auth.setPassword(url.password());
270 m_connection->d_func()->copyCredentials(m_connection->d_func()->indexOf(m_socket), &auth, false);
271 }
272 // clear the userinfo, since we use the same request for resending
273 // userinfo in url can conflict with the one in the authenticator
276 }
277 // Will only be false if Qt WebKit is performing a cross-origin XMLHttpRequest
278 // and withCredentials has not been set to true.
280 m_connection->d_func()->createAuthorization(m_socket, m_channel->request);
281#ifndef QT_NO_NETWORKPROXY
283 (m_connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy));
284#else
286#endif
287
288 // flushing is dangerous (QSslSocket calls transmit which might read or error)
289// m_socket->flush();
291 if (uploadByteDevice) {
292 // connect the signals so this function gets called again
293 QObject::connect(uploadByteDevice, SIGNAL(readyRead()), m_channel, SLOT(_q_uploadDataReadyRead()));
294
296
298 sendRequest(); //recurse
299 } else {
300 // no data to send: just send the HTTP headers
301 m_socket->write(std::exchange(m_header, {}));
304 sendRequest(); //recurse
305 }
306
307 break;
308 }
310 {
311 // write the data
313 if (!uploadByteDevice || m_channel->bytesTotal == m_channel->written) {
314 // the upload device might have no data to send, but we still have to send the headers,
315 // do it now.
316 if (!m_header.isEmpty())
317 m_socket->write(std::exchange(m_header, {}));
318 if (uploadByteDevice)
321 sendRequest(); // recurse
322 break;
323 }
324
325 // only feed the QTcpSocket buffer when there is less than 32 kB in it;
326 // note that the headers do not count towards these limits.
327 const qint64 socketBufferFill = 32*1024;
328 const qint64 socketWriteMaxSize = 16*1024;
329
330 // if it is really an ssl socket, check more than just bytesToWrite()
331#ifndef QT_NO_SSL
332 QSslSocket *sslSocket = qobject_cast<QSslSocket*>(m_socket);
333 const auto encryptedBytesToWrite = [sslSocket]() -> qint64
334 {
335 return sslSocket ? sslSocket->encryptedBytesToWrite() : 0;
336 };
337#else
338 const auto encryptedBytesToWrite = [](){ return qint64(0); };
339#endif
340
341 // throughout this loop, we want to send the data coming from uploadByteDevice.
342 // we also need to send the headers, as we try to coalesce their write with the data.
343 // we won't send more than the limits above, excluding the headers
344 while ((m_socket->bytesToWrite() + encryptedBytesToWrite()) <= socketBufferFill
346 {
347 // get pointer to upload data
348 qint64 currentReadSize = 0;
349 const qint64 desiredReadSize = qMin(socketWriteMaxSize, m_channel->bytesTotal - m_channel->written);
350 const char *readPointer = uploadByteDevice->readPointer(desiredReadSize, currentReadSize);
351
352 if (currentReadSize == -1) {
353 // premature eof happened
355 return false;
356 } else if (readPointer == nullptr || currentReadSize == 0) {
357 // nothing to read currently, break the loop
358 break;
359 } else {
360 if (m_channel->written != uploadByteDevice->pos()) {
361 // Sanity check. This was useful in tracking down an upload corruption.
362 qWarning() << "QHttpProtocolHandler: Internal error in sendRequest. Expected to write at position" << m_channel->written << "but read device is at" << uploadByteDevice->pos();
363 Q_ASSERT(m_channel->written == uploadByteDevice->pos());
364 m_connection->d_func()->emitReplyError(m_socket, m_reply, QNetworkReply::ProtocolFailure);
365 return false;
366 }
367 qint64 currentWriteSize;
368 if (m_header.isEmpty()) {
369 currentWriteSize = m_socket->write(readPointer, currentReadSize);
370 } else {
371 // assemble header and data and send them together
372 const qint64 headerSize = m_header.size();
373 m_header.append(readPointer, currentReadSize);
374 currentWriteSize = m_socket->write(std::exchange(m_header, {}));
375 if (currentWriteSize != -1)
376 currentWriteSize -= headerSize;
378 }
379 if (currentWriteSize == -1 || currentWriteSize != currentReadSize) {
380 // socket broke down
382 return false;
383 } else {
384 m_channel->written += currentWriteSize;
385 uploadByteDevice->advanceReadPointer(currentWriteSize);
386
388
390 // make sure this function is called once again
392 sendRequest();
393 break;
394 }
395 }
396 }
397 }
398 break;
399 }
400
402 {
404 if (uploadByteDevice) {
405 QObject::disconnect(uploadByteDevice, SIGNAL(readyRead()), m_channel, SLOT(_q_uploadDataReadyRead()));
406 }
407
408 // HTTP pipelining
409 //m_connection->d_func()->fillPipeline(m_socket);
410 //m_socket->flush();
411
412 // ensure we try to receive a reply in all cases, even if _q_readyRead_ hat not been called
413 // this is needed if the sends an reply before we have finished sending the request. In that
414 // case receiveReply had been called before but ignored the server reply
417 break;
418 }
420 // ignore _q_bytesWritten in these states
422 default:
423 break;
424 }
425 return true;
426}
427
IOBluetoothL2CAPChannel * channel
QHttpNetworkConnectionChannel * m_channel
QHttpNetworkConnection * m_connection
The QAuthenticator class provides an authentication object.
void setUser(const QString &user)
Sets the user used for authentication.
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:494
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
QByteArray & append(char c)
This is an overloaded member function, provided for convenience. It differs from the above function o...
void _q_error(QAbstractSocket::SocketError)
QHttpNetworkConnection * connection()
void dataSendProgress(qint64 done, qint64 total)
void dataReadProgress(qint64 done, qint64 total)
static QByteArray header(const QHttpNetworkRequest &request, bool throughProxy)
void setUrl(const QUrl &url) override
QUrl url() const override
QNonContiguousByteDevice * uploadByteDevice() const
qint64 contentLength() const override
QHttpProtocolHandler(QHttpNetworkConnectionChannel *channel)
virtual bool sendRequest() override
virtual void _q_receiveReply() override
virtual void _q_readyRead() override
qint64 write(const char *data, qint64 len)
Writes at most maxSize bytes of data from data to the device.
qint64 peek(char *data, qint64 maxlen)
virtual qint64 bytesAvailable() const
Returns the number of bytes that are available for reading.
virtual qint64 bytesToWrite() const
For buffered devices, this function returns the number of bytes waiting to be written.
virtual const char * readPointer(qint64 maximumLength, qint64 &len)=0
Return a byte pointer for at most maximumLength bytes of that device.
virtual bool advanceReadPointer(qint64 amount)=0
will advance the internal read pointer by amount bytes.
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
Definition qobject.cpp:3236
The QSslSocket class provides an SSL encrypted socket for both clients and servers.
Definition qsslsocket.h:29
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
\inmodule QtCore
Definition qurl.h:94
QString userInfo(ComponentFormattingOptions options=PrettyDecoded) const
Returns the user info of the URL, or an empty string if the user info is undefined.
Definition qurl.cpp:2129
QString userName(ComponentFormattingOptions options=FullyDecoded) const
Returns the user name of the URL if it is defined; otherwise an empty string is returned.
Definition qurl.cpp:2199
QString password(ComponentFormattingOptions=FullyDecoded) const
Returns the password of the URL if it is defined; otherwise an empty string is returned.
Definition qurl.cpp:2262
void setUserInfo(const QString &userInfo, ParsingMode mode=TolerantMode)
Sets the user info of the URL to userInfo.
Definition qurl.cpp:2093
else opt state
[0]
QAbstractSocket::SocketError socketError(QIODevice *device)
QAbstractSocket::SocketState socketState(QIODevice *device)
Combined button and popup list for selecting options.
@ QueuedConnection
#define Q_FALLTHROUGH()
static const qint64 headerSize
#define qWarning
Definition qlogging.h:166
return ret
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
#define SLOT(a)
Definition qobjectdefs.h:52
#define SIGNAL(a)
Definition qobjectdefs.h:53
const GLubyte * c
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
long long qint64
Definition qtypes.h:60
QUrl url("example.com")
[constructor-url-reference]
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
\threadsafe This is an overloaded member function, provided for convenience. It differs from the abov...