1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the QtNetwork module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 3 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL3 included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 3 requirements
24** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25**
26** GNU General Public License Usage
27** Alternatively, this file may be used under the terms of the GNU
28** General Public License version 2.0 or (at your option) the GNU General
29** Public license version 3 or any later version approved by the KDE Free
30** Qt Foundation. The licenses are as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32** included in the packaging of this file. Please review the following
33** information to ensure the GNU General Public License requirements will
34** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35** https://www.gnu.org/licenses/gpl-3.0.html.
36**
37** $QT_END_LICENSE$
38**
39****************************************************************************/
40
41#include <private/qhttpprotocolhandler_p.h>
42#include <private/qnoncontiguousbytedevice_p.h>
43#include <private/qhttpnetworkconnectionchannel_p.h>
44
45QT_BEGIN_NAMESPACE
46
47QHttpProtocolHandler::QHttpProtocolHandler(QHttpNetworkConnectionChannel *channel)
48 : QAbstractProtocolHandler(channel)
49{
50}
51
52void QHttpProtocolHandler::_q_receiveReply()
53{
54 Q_ASSERT(m_socket);
55
56 if (!m_reply) {
57 if (m_socket->bytesAvailable() > 0)
58 qWarning() << "QAbstractProtocolHandler::_q_receiveReply() called without QHttpNetworkReply,"
59 << m_socket->bytesAvailable() << "bytes on socket.";
60 m_channel->close();
61 return;
62 }
63
64 // only run when the QHttpNetworkConnection is not currently being destructed, e.g.
65 // this function is called from _q_disconnected which is called because
66 // of ~QHttpNetworkConnectionPrivate
67 if (!qobject_cast<QHttpNetworkConnection*>(object: m_connection)) {
68 return;
69 }
70
71 QAbstractSocket::SocketState socketState = m_socket->state();
72
73 // connection might be closed to signal the end of data
74 if (socketState == QAbstractSocket::UnconnectedState) {
75 if (m_socket->bytesAvailable() <= 0) {
76 if (m_reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) {
77 // finish this reply. this case happens when the server did not send a content length
78 m_reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState;
79 m_channel->allDone();
80 return;
81 } else {
82 m_channel->handleUnexpectedEOF();
83 return;
84 }
85 } else {
86 // socket not connected but still bytes for reading.. just continue in this function
87 }
88 }
89
90 // read loop for the response
91 qint64 bytes = 0;
92 qint64 lastBytes = bytes;
93 do {
94 lastBytes = bytes;
95
96 QHttpNetworkReplyPrivate::ReplyState state = m_reply->d_func()->state;
97 switch (state) {
98 case QHttpNetworkReplyPrivate::NothingDoneState: {
99 m_reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState;
100 Q_FALLTHROUGH();
101 }
102 case QHttpNetworkReplyPrivate::ReadingStatusState: {
103 qint64 statusBytes = m_reply->d_func()->readStatus(socket: m_socket);
104 if (statusBytes == -1) {
105 // connection broke while reading status. also handled if later _q_disconnected is called
106 m_channel->handleUnexpectedEOF();
107 return;
108 }
109 bytes += statusBytes;
110 m_channel->lastStatus = m_reply->d_func()->statusCode;
111 break;
112 }
113 case QHttpNetworkReplyPrivate::ReadingHeaderState: {
114 QHttpNetworkReplyPrivate *replyPrivate = m_reply->d_func();
115 qint64 headerBytes = replyPrivate->readHeader(socket: m_socket);
116 if (headerBytes == -1) {
117 // connection broke while reading headers. also handled if later _q_disconnected is called
118 m_channel->handleUnexpectedEOF();
119 return;
120 }
121 bytes += headerBytes;
122 // If headers were parsed successfully now it is the ReadingDataState
123 if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState) {
124 if (replyPrivate->isCompressed() && replyPrivate->autoDecompress) {
125 // remove the Content-Length from header
126 replyPrivate->removeAutoDecompressHeader();
127 } else {
128 replyPrivate->autoDecompress = false;
129 }
130 if (replyPrivate->statusCode == 100) {
131 replyPrivate->clearHttpLayerInformation();
132 replyPrivate->state = QHttpNetworkReplyPrivate::ReadingStatusState;
133 break; // ignore
134 }
135 if (replyPrivate->shouldEmitSignals())
136 emit m_reply->headerChanged();
137 // After headerChanged had been emitted
138 // we can suddenly have a replyPrivate->userProvidedDownloadBuffer
139 // this is handled in the ReadingDataState however
140
141 if (!replyPrivate->expectContent()) {
142 replyPrivate->state = QHttpNetworkReplyPrivate::AllDoneState;
143 m_channel->allDone();
144 break;
145 }
146 }
147 break;
148 }
149 case QHttpNetworkReplyPrivate::ReadingDataState: {
150 QHttpNetworkReplyPrivate *replyPrivate = m_reply->d_func();
151 if (m_socket->state() == QAbstractSocket::ConnectedState &&
152 replyPrivate->downstreamLimited && !replyPrivate->responseData.isEmpty() && replyPrivate->shouldEmitSignals()) {
153 // (only do the following when still connected, not when we have already been disconnected and there is still data)
154 // We already have some HTTP body data. We don't read more from the socket until
155 // this is fetched by QHttpNetworkAccessHttpBackend. If we would read more,
156 // we could not limit our read buffer usage.
157 // We only do this when shouldEmitSignals==true because our HTTP parsing
158 // always needs to parse the 401/407 replies. Therefore they don't really obey
159 // to the read buffer maximum size, but we don't care since they should be small.
160 return;
161 }
162
163 if (replyPrivate->userProvidedDownloadBuffer) {
164 // the user provided a direct buffer where we should put all our data in.
165 // this only works when we can tell the user the content length and he/she can allocate
166 // the buffer in that size.
167 // note that this call will read only from the still buffered data
168 qint64 haveRead = replyPrivate->readBodyVeryFast(socket: m_socket, b: replyPrivate->userProvidedDownloadBuffer + replyPrivate->totalProgress);
169 if (haveRead > 0) {
170 bytes += haveRead;
171 replyPrivate->totalProgress += haveRead;
172 // the user will get notified of it via progress signal
173 emit m_reply->dataReadProgress(done: replyPrivate->totalProgress, total: replyPrivate->bodyLength);
174 } else if (haveRead == 0) {
175 // Happens since this called in a loop. Currently no bytes available.
176 } else if (haveRead < 0) {
177 m_connection->d_func()->emitReplyError(socket: m_socket, reply: m_reply, errorCode: QNetworkReply::RemoteHostClosedError);
178 break;
179 }
180 } else if (!replyPrivate->isChunked() && !replyPrivate->autoDecompress
181 && replyPrivate->bodyLength > 0) {
182 // bulk files like images should fulfill these properties and
183 // we can therefore save on memory copying
184 qint64 haveRead = replyPrivate->readBodyFast(socket: m_socket, rb: &replyPrivate->responseData);
185 bytes += haveRead;
186 replyPrivate->totalProgress += haveRead;
187 if (replyPrivate->shouldEmitSignals()) {
188 emit m_reply->readyRead();
189 emit m_reply->dataReadProgress(done: replyPrivate->totalProgress, total: replyPrivate->bodyLength);
190 }
191 }
192 else
193 {
194 // use the traditional slower reading (for compressed encoding, chunked encoding,
195 // no content-length etc)
196 qint64 haveRead = replyPrivate->readBody(socket: m_socket, out: &replyPrivate->responseData);
197 if (haveRead > 0) {
198 bytes += haveRead;
199 replyPrivate->totalProgress += haveRead;
200 if (replyPrivate->shouldEmitSignals()) {
201 emit m_reply->readyRead();
202 emit m_reply->dataReadProgress(done: replyPrivate->totalProgress, total: replyPrivate->bodyLength);
203 }
204 } else if (haveRead == -1) {
205 // Some error occurred
206 m_connection->d_func()->emitReplyError(socket: m_socket, reply: m_reply, errorCode: QNetworkReply::ProtocolFailure);
207 break;
208 }
209 }
210 // still in ReadingDataState? This function will be called again by the socket's readyRead
211 if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState)
212 break;
213
214 // everything done
215 Q_FALLTHROUGH();
216 }
217 case QHttpNetworkReplyPrivate::AllDoneState:
218 m_channel->allDone();
219 break;
220 default:
221 break;
222 }
223 } while (bytes != lastBytes && m_reply);
224}
225
226void QHttpProtocolHandler::_q_readyRead()
227{
228 if (m_socket->state() == QAbstractSocket::ConnectedState && m_socket->bytesAvailable() == 0) {
229 // We got a readyRead but no bytes are available..
230 // This happens for the Unbuffered QTcpSocket
231 // Also check if socket is in ConnectedState since
232 // this function may also be invoked via the event loop.
233 char c;
234 qint64 ret = m_socket->peek(data: &c, maxlen: 1);
235 if (ret < 0) {
236 m_channel->_q_error(m_socket->error());
237 // We still need to handle the reply so it emits its signals etc.
238 if (m_reply)
239 _q_receiveReply();
240 return;
241 }
242 }
243
244 if (m_channel->isSocketWaiting() || m_channel->isSocketReading()) {
245 if (m_socket->bytesAvailable()) {
246 // We might get a spurious call from readMoreLater()
247 // call of the QHttpNetworkConnection even while the socket is disconnecting.
248 // Therefore check if there is actually bytes available before changing the channel state.
249 m_channel->state = QHttpNetworkConnectionChannel::ReadingState;
250 }
251 if (m_reply)
252 _q_receiveReply();
253 }
254}
255
256bool QHttpProtocolHandler::sendRequest()
257{
258 m_reply = m_channel->reply;
259
260 if (!m_reply) {
261 // heh, how should that happen!
262 qWarning(msg: "QAbstractProtocolHandler::sendRequest() called without QHttpNetworkReply");
263 return false;
264 }
265
266 switch (m_channel->state) {
267 case QHttpNetworkConnectionChannel::IdleState: { // write the header
268 if (!m_channel->ensureConnection()) {
269 // wait for the connection (and encryption) to be done
270 // sendRequest will be called again from either
271 // _q_connected or _q_encrypted
272 return false;
273 }
274 QString scheme = m_channel->request.url().scheme();
275 if (scheme == QLatin1String("preconnect-http")
276 || scheme == QLatin1String("preconnect-https")) {
277 m_channel->state = QHttpNetworkConnectionChannel::IdleState;
278 m_reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState;
279 m_channel->allDone();
280 m_connection->preConnectFinished(); // will only decrease the counter
281 m_reply = nullptr; // so we can reuse this channel
282 return true; // we have a working connection and are done
283 }
284
285 m_channel->written = 0; // excluding the header
286 m_channel->bytesTotal = 0;
287
288 QHttpNetworkReplyPrivate *replyPrivate = m_reply->d_func();
289 replyPrivate->clear();
290 replyPrivate->connection = m_connection;
291 replyPrivate->connectionChannel = m_channel;
292 replyPrivate->autoDecompress = m_channel->request.d->autoDecompress;
293 replyPrivate->pipeliningUsed = false;
294
295 // if the url contains authentication parameters, use the new ones
296 // both channels will use the new authentication parameters
297 if (!m_channel->request.url().userInfo().isEmpty() && m_channel->request.withCredentials()) {
298 QUrl url = m_channel->request.url();
299 QAuthenticator &auth = m_channel->authenticator;
300 if (url.userName() != auth.user()
301 || (!url.password().isEmpty() && url.password() != auth.password())) {
302 auth.setUser(url.userName());
303 auth.setPassword(url.password());
304 m_connection->d_func()->copyCredentials(fromChannel: m_connection->d_func()->indexOf(socket: m_socket), auth: &auth, isProxy: false);
305 }
306 // clear the userinfo, since we use the same request for resending
307 // userinfo in url can conflict with the one in the authenticator
308 url.setUserInfo(userInfo: QString());
309 m_channel->request.setUrl(url);
310 }
311 // Will only be false if Qt WebKit is performing a cross-origin XMLHttpRequest
312 // and withCredentials has not been set to true.
313 if (m_channel->request.withCredentials())
314 m_connection->d_func()->createAuthorization(socket: m_socket, request&: m_channel->request);
315#ifndef QT_NO_NETWORKPROXY
316 QByteArray header = QHttpNetworkRequestPrivate::header(request: m_channel->request,
317 throughProxy: (m_connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy));
318#else
319 QByteArray header = QHttpNetworkRequestPrivate::header(m_channel->request, false);
320#endif
321 m_socket->write(data: header);
322 // flushing is dangerous (QSslSocket calls transmit which might read or error)
323// m_socket->flush();
324 QNonContiguousByteDevice* uploadByteDevice = m_channel->request.uploadByteDevice();
325 if (uploadByteDevice) {
326 // connect the signals so this function gets called again
327 QObject::connect(sender: uploadByteDevice, SIGNAL(readyRead()), receiver: m_channel, SLOT(_q_uploadDataReadyRead()));
328
329 m_channel->bytesTotal = m_channel->request.contentLength();
330
331 m_channel->state = QHttpNetworkConnectionChannel::WritingState; // start writing data
332 sendRequest(); //recurse
333 } else {
334 m_channel->state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
335 sendRequest(); //recurse
336 }
337
338 break;
339 }
340 case QHttpNetworkConnectionChannel::WritingState:
341 {
342 // write the data
343 QNonContiguousByteDevice* uploadByteDevice = m_channel->request.uploadByteDevice();
344 if (!uploadByteDevice || m_channel->bytesTotal == m_channel->written) {
345 if (uploadByteDevice)
346 emit m_reply->dataSendProgress(done: m_channel->written, total: m_channel->bytesTotal);
347 m_channel->state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
348 sendRequest(); // recurse
349 break;
350 }
351
352 // only feed the QTcpSocket buffer when there is less than 32 kB in it
353 const qint64 socketBufferFill = 32*1024;
354 const qint64 socketWriteMaxSize = 16*1024;
355
356
357#ifndef QT_NO_SSL
358 QSslSocket *sslSocket = qobject_cast<QSslSocket*>(object: m_socket);
359 // if it is really an ssl socket, check more than just bytesToWrite()
360 while ((m_socket->bytesToWrite() + (sslSocket ? sslSocket->encryptedBytesToWrite() : 0))
361 <= socketBufferFill && m_channel->bytesTotal != m_channel->written)
362#else
363 while (m_socket->bytesToWrite() <= socketBufferFill
364 && m_channel->bytesTotal != m_channel->written)
365#endif
366 {
367 // get pointer to upload data
368 qint64 currentReadSize = 0;
369 qint64 desiredReadSize = qMin(a: socketWriteMaxSize, b: m_channel->bytesTotal - m_channel->written);
370 const char *readPointer = uploadByteDevice->readPointer(maximumLength: desiredReadSize, len&: currentReadSize);
371
372 if (currentReadSize == -1) {
373 // premature eof happened
374 m_connection->d_func()->emitReplyError(socket: m_socket, reply: m_reply, errorCode: QNetworkReply::UnknownNetworkError);
375 return false;
376 } else if (readPointer == nullptr || currentReadSize == 0) {
377 // nothing to read currently, break the loop
378 break;
379 } else {
380 if (m_channel->written != uploadByteDevice->pos()) {
381 // Sanity check. This was useful in tracking down an upload corruption.
382 qWarning() << "QHttpProtocolHandler: Internal error in sendRequest. Expected to write at position" << m_channel->written << "but read device is at" << uploadByteDevice->pos();
383 Q_ASSERT(m_channel->written == uploadByteDevice->pos());
384 m_connection->d_func()->emitReplyError(socket: m_socket, reply: m_reply, errorCode: QNetworkReply::ProtocolFailure);
385 return false;
386 }
387 qint64 currentWriteSize = m_socket->write(data: readPointer, len: currentReadSize);
388 if (currentWriteSize == -1 || currentWriteSize != currentReadSize) {
389 // socket broke down
390 m_connection->d_func()->emitReplyError(socket: m_socket, reply: m_reply, errorCode: QNetworkReply::UnknownNetworkError);
391 return false;
392 } else {
393 m_channel->written += currentWriteSize;
394 uploadByteDevice->advanceReadPointer(amount: currentWriteSize);
395
396 emit m_reply->dataSendProgress(done: m_channel->written, total: m_channel->bytesTotal);
397
398 if (m_channel->written == m_channel->bytesTotal) {
399 // make sure this function is called once again
400 m_channel->state = QHttpNetworkConnectionChannel::WaitingState;
401 sendRequest();
402 break;
403 }
404 }
405 }
406 }
407 break;
408 }
409
410 case QHttpNetworkConnectionChannel::WaitingState:
411 {
412 QNonContiguousByteDevice* uploadByteDevice = m_channel->request.uploadByteDevice();
413 if (uploadByteDevice) {
414 QObject::disconnect(sender: uploadByteDevice, SIGNAL(readyRead()), receiver: m_channel, SLOT(_q_uploadDataReadyRead()));
415 }
416
417 // HTTP pipelining
418 //m_connection->d_func()->fillPipeline(m_socket);
419 //m_socket->flush();
420
421 // ensure we try to receive a reply in all cases, even if _q_readyRead_ hat not been called
422 // this is needed if the sends an reply before we have finished sending the request. In that
423 // case receiveReply had been called before but ignored the server reply
424 if (m_socket->bytesAvailable())
425 QMetaObject::invokeMethod(obj: m_channel, member: "_q_receiveReply", type: Qt::QueuedConnection);
426 break;
427 }
428 case QHttpNetworkConnectionChannel::ReadingState:
429 // ignore _q_bytesWritten in these states
430 Q_FALLTHROUGH();
431 default:
432 break;
433 }
434 return true;
435}
436
437QT_END_NAMESPACE
438

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