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*>(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(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(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(m_socket, 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(replyPrivate->totalProgress, 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(m_socket, m_reply, 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(m_socket, &replyPrivate->responseData);
185 bytes += haveRead;
186 replyPrivate->totalProgress += haveRead;
187 if (replyPrivate->shouldEmitSignals()) {
188 emit m_reply->readyRead();
189 emit m_reply->dataReadProgress(replyPrivate->totalProgress, 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(m_socket, &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(replyPrivate->totalProgress, replyPrivate->bodyLength);
203 }
204 } else if (haveRead == -1) {
205 // Some error occurred
206 m_connection->d_func()->emitReplyError(m_socket, m_reply, 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(&c, 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("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(m_connection->d_func()->indexOf(m_socket), &auth, 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(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(m_socket, m_channel->request);
315#ifndef QT_NO_NETWORKPROXY
316 m_header = QHttpNetworkRequestPrivate::header(m_channel->request,
317 (m_connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy));
318#else
319 m_header = QHttpNetworkRequestPrivate::header(m_channel->request, false);
320#endif
321 // flushing is dangerous (QSslSocket calls transmit which might read or error)
322// m_socket->flush();
323 QNonContiguousByteDevice* uploadByteDevice = m_channel->request.uploadByteDevice();
324 if (uploadByteDevice) {
325 // connect the signals so this function gets called again
326 QObject::connect(uploadByteDevice, SIGNAL(readyRead()), m_channel, SLOT(_q_uploadDataReadyRead()));
327
328 m_channel->bytesTotal = m_channel->request.contentLength();
329
330 m_channel->state = QHttpNetworkConnectionChannel::WritingState; // start writing data
331 sendRequest(); //recurse
332 } else {
333 // no data to send: just send the HTTP headers
334 m_socket->write(qExchange(m_header, {}));
335 m_channel->state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
336 sendRequest(); //recurse
337 }
338
339 break;
340 }
341 case QHttpNetworkConnectionChannel::WritingState:
342 {
343 // write the data
344 QNonContiguousByteDevice* uploadByteDevice = m_channel->request.uploadByteDevice();
345 if (!uploadByteDevice || m_channel->bytesTotal == m_channel->written) {
346 // the upload device might have no data to send, but we still have to send the headers,
347 // do it now.
348 if (!m_header.isEmpty())
349 m_socket->write(qExchange(m_header, {}));
350 if (uploadByteDevice)
351 emit m_reply->dataSendProgress(m_channel->written, m_channel->bytesTotal);
352 m_channel->state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
353 sendRequest(); // recurse
354 break;
355 }
356
357 // only feed the QTcpSocket buffer when there is less than 32 kB in it;
358 // note that the headers do not count towards these limits.
359 const qint64 socketBufferFill = 32*1024;
360 const qint64 socketWriteMaxSize = 16*1024;
361
362 // if it is really an ssl socket, check more than just bytesToWrite()
363#ifndef QT_NO_SSL
364 QSslSocket *sslSocket = qobject_cast<QSslSocket*>(m_socket);
365 const auto encryptedBytesToWrite = [sslSocket]() -> qint64
366 {
367 return sslSocket ? sslSocket->encryptedBytesToWrite() : 0;
368 };
369#else
370 const auto encryptedBytesToWrite = [](){ return qint64(0); };
371#endif
372
373 // throughout this loop, we want to send the data coming from uploadByteDevice.
374 // we also need to send the headers, as we try to coalesce their write with the data.
375 // we won't send more than the limits above, excluding the headers
376 while ((m_socket->bytesToWrite() + encryptedBytesToWrite()) <= socketBufferFill
377 && m_channel->bytesTotal != m_channel->written)
378 {
379 // get pointer to upload data
380 qint64 currentReadSize = 0;
381 const qint64 desiredReadSize = qMin(socketWriteMaxSize, m_channel->bytesTotal - m_channel->written);
382 const char *readPointer = uploadByteDevice->readPointer(desiredReadSize, currentReadSize);
383
384 if (currentReadSize == -1) {
385 // premature eof happened
386 m_connection->d_func()->emitReplyError(m_socket, m_reply, QNetworkReply::UnknownNetworkError);
387 return false;
388 } else if (readPointer == nullptr || currentReadSize == 0) {
389 // nothing to read currently, break the loop
390 break;
391 } else {
392 if (m_channel->written != uploadByteDevice->pos()) {
393 // Sanity check. This was useful in tracking down an upload corruption.
394 qWarning() << "QHttpProtocolHandler: Internal error in sendRequest. Expected to write at position" << m_channel->written << "but read device is at" << uploadByteDevice->pos();
395 Q_ASSERT(m_channel->written == uploadByteDevice->pos());
396 m_connection->d_func()->emitReplyError(m_socket, m_reply, QNetworkReply::ProtocolFailure);
397 return false;
398 }
399 qint64 currentWriteSize;
400 if (m_header.isEmpty()) {
401 currentWriteSize = m_socket->write(readPointer, currentReadSize);
402 } else {
403 // assemble header and data and send them together
404 const qint64 headerSize = m_header.size();
405 m_header.append(readPointer, currentReadSize);
406 currentWriteSize = m_socket->write(qExchange(m_header, {}));
407 if (currentWriteSize != -1)
408 currentWriteSize -= headerSize;
409 }
410 if (currentWriteSize == -1 || currentWriteSize != currentReadSize) {
411 // socket broke down
412 m_connection->d_func()->emitReplyError(m_socket, m_reply, QNetworkReply::UnknownNetworkError);
413 return false;
414 } else {
415 m_channel->written += currentWriteSize;
416 uploadByteDevice->advanceReadPointer(currentWriteSize);
417
418 emit m_reply->dataSendProgress(m_channel->written, m_channel->bytesTotal);
419
420 if (m_channel->written == m_channel->bytesTotal) {
421 // make sure this function is called once again
422 m_channel->state = QHttpNetworkConnectionChannel::WaitingState;
423 sendRequest();
424 break;
425 }
426 }
427 }
428 }
429 break;
430 }
431
432 case QHttpNetworkConnectionChannel::WaitingState:
433 {
434 QNonContiguousByteDevice* uploadByteDevice = m_channel->request.uploadByteDevice();
435 if (uploadByteDevice) {
436 QObject::disconnect(uploadByteDevice, SIGNAL(readyRead()), m_channel, SLOT(_q_uploadDataReadyRead()));
437 }
438
439 // HTTP pipelining
440 //m_connection->d_func()->fillPipeline(m_socket);
441 //m_socket->flush();
442
443 // ensure we try to receive a reply in all cases, even if _q_readyRead_ hat not been called
444 // this is needed if the sends an reply before we have finished sending the request. In that
445 // case receiveReply had been called before but ignored the server reply
446 if (m_socket->bytesAvailable())
447 QMetaObject::invokeMethod(m_channel, "_q_receiveReply", Qt::QueuedConnection);
448 break;
449 }
450 case QHttpNetworkConnectionChannel::ReadingState:
451 // ignore _q_bytesWritten in these states
452 Q_FALLTHROUGH();
453 default:
454 break;
455 }
456 return true;
457}
458
459QT_END_NAMESPACE
460