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
9QT_BEGIN_NAMESPACE
10
11using namespace Qt::StringLiterals;
12
13QHttpProtocolHandler::QHttpProtocolHandler(QHttpNetworkConnectionChannel *channel)
14 : QAbstractProtocolHandler(channel)
15{
16}
17
18void QHttpProtocolHandler::_q_receiveReply()
19{
20 Q_ASSERT(m_socket);
21
22 if (!m_reply) {
23 if (m_socket->bytesAvailable() > 0)
24 qWarning() << "QAbstractProtocolHandler::_q_receiveReply() called without QHttpNetworkReply,"
25 << m_socket->bytesAvailable() << "bytes on socket.";
26 m_channel->close();
27 return;
28 }
29
30 // only run when the QHttpNetworkConnection is not currently being destructed, e.g.
31 // this function is called from _q_disconnected which is called because
32 // of ~QHttpNetworkConnectionPrivate
33 if (!qobject_cast<QHttpNetworkConnection*>(object: m_connection)) {
34 return;
35 }
36
37 QAbstractSocket::SocketState socketState = m_socket->state();
38
39 // connection might be closed to signal the end of data
40 if (socketState == QAbstractSocket::UnconnectedState) {
41 if (m_socket->bytesAvailable() <= 0) {
42 if (m_reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) {
43 // finish this reply. this case happens when the server did not send a content length
44 m_reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState;
45 m_channel->allDone();
46 return;
47 } else {
48 m_channel->handleUnexpectedEOF();
49 return;
50 }
51 } else {
52 // socket not connected but still bytes for reading.. just continue in this function
53 }
54 }
55
56 // read loop for the response
57 qint64 bytes = 0;
58 qint64 lastBytes = bytes;
59 do {
60 lastBytes = bytes;
61
62 QHttpNetworkReplyPrivate::ReplyState state = m_reply->d_func()->state;
63 switch (state) {
64 case QHttpNetworkReplyPrivate::NothingDoneState: {
65 m_reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState;
66 Q_FALLTHROUGH();
67 }
68 case QHttpNetworkReplyPrivate::ReadingStatusState: {
69 qint64 statusBytes = m_reply->d_func()->readStatus(socket: m_socket);
70 if (statusBytes == -1) {
71 // connection broke while reading status. also handled if later _q_disconnected is called
72 m_channel->handleUnexpectedEOF();
73 return;
74 }
75 bytes += statusBytes;
76 m_channel->lastStatus = m_reply->statusCode();
77 break;
78 }
79 case QHttpNetworkReplyPrivate::ReadingHeaderState: {
80 QHttpNetworkReplyPrivate *replyPrivate = m_reply->d_func();
81 qint64 headerBytes = replyPrivate->readHeader(socket: m_socket);
82 if (headerBytes == -1) {
83 // connection broke while reading headers. also handled if later _q_disconnected is called
84 m_channel->handleUnexpectedEOF();
85 return;
86 }
87 bytes += headerBytes;
88 // If headers were parsed successfully now it is the ReadingDataState
89 if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState) {
90 if (replyPrivate->isCompressed() && replyPrivate->autoDecompress) {
91 // remove the Content-Length from header
92 replyPrivate->removeAutoDecompressHeader();
93 } else {
94 replyPrivate->autoDecompress = false;
95 }
96 if (m_reply->statusCode() == 100) {
97 replyPrivate->clearHttpLayerInformation();
98 replyPrivate->state = QHttpNetworkReplyPrivate::ReadingStatusState;
99 break; // ignore
100 }
101 if (replyPrivate->shouldEmitSignals())
102 emit m_reply->headerChanged();
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;
109 m_channel->allDone();
110 break;
111 }
112 }
113 break;
114 }
115 case QHttpNetworkReplyPrivate::ReadingDataState: {
116 QHttpNetworkReplyPrivate *replyPrivate = m_reply->d_func();
117 if (m_socket->state() == QAbstractSocket::ConnectedState &&
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(socket: m_socket, b: 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(done: replyPrivate->totalProgress, total: replyPrivate->bodyLength);
140 } else if (haveRead == 0) {
141 // Happens since this called in a loop. Currently no bytes available.
142 } else if (haveRead < 0) {
143 m_connection->d_func()->emitReplyError(socket: m_socket, reply: m_reply, errorCode: QNetworkReply::RemoteHostClosedError);
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(socket: m_socket, rb: &replyPrivate->responseData);
150 bytes += haveRead;
151 replyPrivate->totalProgress += haveRead;
152 if (replyPrivate->shouldEmitSignals()) {
153 emit m_reply->readyRead();
154 emit m_reply->dataReadProgress(done: replyPrivate->totalProgress, total: 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(socket: m_socket, out: &replyPrivate->responseData);
162 if (haveRead > 0) {
163 bytes += haveRead;
164 replyPrivate->totalProgress += haveRead;
165 if (replyPrivate->shouldEmitSignals()) {
166 emit m_reply->readyRead();
167 emit m_reply->dataReadProgress(done: replyPrivate->totalProgress, total: replyPrivate->bodyLength);
168 }
169 } else if (haveRead == -1) {
170 // Some error occurred
171 m_connection->d_func()->emitReplyError(socket: m_socket, reply: m_reply, errorCode: 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
180 Q_FALLTHROUGH();
181 }
182 case QHttpNetworkReplyPrivate::AllDoneState:
183 m_channel->allDone();
184 if (state == QHttpNetworkReplyPrivate::AllDoneState)
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
193void QHttpProtocolHandler::_q_readyRead()
194{
195 if (m_socket->state() == QAbstractSocket::ConnectedState && m_socket->bytesAvailable() == 0) {
196 // We got a readyRead but no bytes are available..
197 // This happens for the Unbuffered QTcpSocket
198 // Also check if socket is in ConnectedState since
199 // this function may also be invoked via the event loop.
200 char c;
201 qint64 ret = m_socket->peek(data: &c, maxlen: 1);
202 if (ret < 0) {
203 m_channel->_q_error(m_socket->error());
204 // We still need to handle the reply so it emits its signals etc.
205 if (m_reply)
206 _q_receiveReply();
207 return;
208 }
209 }
210
211 if (m_channel->isSocketWaiting() || m_channel->isSocketReading()) {
212 if (m_socket->bytesAvailable()) {
213 // We might get a spurious call from readMoreLater()
214 // call of the QHttpNetworkConnection even while the socket is disconnecting.
215 // Therefore check if there is actually bytes available before changing the channel state.
216 m_channel->state = QHttpNetworkConnectionChannel::ReadingState;
217 }
218 if (m_reply)
219 _q_receiveReply();
220 }
221}
222
223bool QHttpProtocolHandler::sendRequest()
224{
225 m_reply = m_channel->reply;
226
227 if (!m_reply) {
228 // heh, how should that happen!
229 qWarning(msg: "QAbstractProtocolHandler::sendRequest() called without QHttpNetworkReply");
230 return false;
231 }
232
233 switch (m_channel->state) {
234 case QHttpNetworkConnectionChannel::IdleState: { // write the header
235 if (!m_channel->ensureConnection()) {
236 // wait for the connection (and encryption) to be done
237 // sendRequest will be called again from either
238 // _q_connected or _q_encrypted
239 return false;
240 }
241 QString scheme = m_channel->request.url().scheme();
242 if (scheme == "preconnect-http"_L1 || scheme == "preconnect-https"_L1) {
243 m_channel->state = QHttpNetworkConnectionChannel::IdleState;
244 m_reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState;
245 m_channel->allDone();
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
252 m_channel->bytesTotal = 0;
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
263 if (!m_channel->request.url().userInfo().isEmpty() && m_channel->request.withCredentials()) {
264 QUrl url = m_channel->request.url();
265 QAuthenticator &auth = m_channel->authenticator;
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(fromChannel: m_connection->d_func()->indexOf(socket: m_socket), auth: &auth, isProxy: 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
274 url.setUserInfo(userInfo: QString());
275 m_channel->request.setUrl(url);
276 }
277 // Will only be false if Qt WebKit is performing a cross-origin XMLHttpRequest
278 // and withCredentials has not been set to true.
279 if (m_channel->request.withCredentials())
280 m_connection->d_func()->createAuthorization(socket: m_socket, request&: m_channel->request);
281#ifndef QT_NO_NETWORKPROXY
282 m_header = QHttpNetworkRequestPrivate::header(request: m_channel->request,
283 throughProxy: (m_connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy));
284#else
285 m_header = QHttpNetworkRequestPrivate::header(m_channel->request, false);
286#endif
287
288 // flushing is dangerous (QSslSocket calls transmit which might read or error)
289// m_socket->flush();
290 QNonContiguousByteDevice* uploadByteDevice = m_channel->request.uploadByteDevice();
291 if (uploadByteDevice) {
292 // connect the signals so this function gets called again
293 QObject::connect(sender: uploadByteDevice, SIGNAL(readyRead()), receiver: m_channel, SLOT(_q_uploadDataReadyRead()));
294
295 m_channel->bytesTotal = m_channel->request.contentLength();
296
297 m_channel->state = QHttpNetworkConnectionChannel::WritingState; // start writing data
298 sendRequest(); //recurse
299 } else {
300 // no data to send: just send the HTTP headers
301 m_socket->write(data: std::exchange(obj&: m_header, new_val: {}));
302 QMetaObject::invokeMethod(obj: m_reply, member: "requestSent", c: Qt::QueuedConnection);
303 m_channel->state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
304 sendRequest(); //recurse
305 }
306
307 break;
308 }
309 case QHttpNetworkConnectionChannel::WritingState:
310 {
311 // write the data
312 QNonContiguousByteDevice* uploadByteDevice = m_channel->request.uploadByteDevice();
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(data: std::exchange(obj&: m_header, new_val: {}));
318 if (uploadByteDevice)
319 emit m_reply->dataSendProgress(done: m_channel->written, total: m_channel->bytesTotal);
320 m_channel->state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
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*>(object: 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
345 && m_channel->bytesTotal != m_channel->written)
346 {
347 // get pointer to upload data
348 qint64 currentReadSize = 0;
349 const qint64 desiredReadSize = qMin(a: socketWriteMaxSize, b: m_channel->bytesTotal - m_channel->written);
350 const char *readPointer = uploadByteDevice->readPointer(maximumLength: desiredReadSize, len&: currentReadSize);
351
352 if (currentReadSize == -1) {
353 // premature eof happened
354 m_connection->d_func()->emitReplyError(socket: m_socket, reply: m_reply, errorCode: QNetworkReply::UnknownNetworkError);
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(socket: m_socket, reply: m_reply, errorCode: QNetworkReply::ProtocolFailure);
365 return false;
366 }
367 qint64 currentWriteSize;
368 if (m_header.isEmpty()) {
369 currentWriteSize = m_socket->write(data: readPointer, len: currentReadSize);
370 } else {
371 // assemble header and data and send them together
372 const qint64 headerSize = m_header.size();
373 m_header.append(s: readPointer, len: currentReadSize);
374 currentWriteSize = m_socket->write(data: std::exchange(obj&: m_header, new_val: {}));
375 if (currentWriteSize != -1)
376 currentWriteSize -= headerSize;
377 QMetaObject::invokeMethod(obj: m_reply, member: "requestSent", c: Qt::QueuedConnection);
378 }
379 if (currentWriteSize == -1 || currentWriteSize != currentReadSize) {
380 // socket broke down
381 m_connection->d_func()->emitReplyError(socket: m_socket, reply: m_reply, errorCode: QNetworkReply::UnknownNetworkError);
382 return false;
383 } else {
384 m_channel->written += currentWriteSize;
385 uploadByteDevice->advanceReadPointer(amount: currentWriteSize);
386
387 emit m_reply->dataSendProgress(done: m_channel->written, total: m_channel->bytesTotal);
388
389 if (m_channel->written == m_channel->bytesTotal) {
390 // make sure this function is called once again
391 m_channel->state = QHttpNetworkConnectionChannel::WaitingState;
392 sendRequest();
393 break;
394 }
395 }
396 }
397 }
398 break;
399 }
400
401 case QHttpNetworkConnectionChannel::WaitingState:
402 {
403 QNonContiguousByteDevice* uploadByteDevice = m_channel->request.uploadByteDevice();
404 if (uploadByteDevice) {
405 QObject::disconnect(sender: uploadByteDevice, SIGNAL(readyRead()), receiver: 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
415 if (m_socket->bytesAvailable())
416 QMetaObject::invokeMethod(obj: m_channel, member: "_q_receiveReply", c: Qt::QueuedConnection);
417 break;
418 }
419 case QHttpNetworkConnectionChannel::ReadingState:
420 // ignore _q_bytesWritten in these states
421 Q_FALLTHROUGH();
422 default:
423 break;
424 }
425 return true;
426}
427
428QT_END_NAMESPACE
429

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