1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtNetwork module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qhttpnetworkconnection_p.h"
43 #include "qhttpnetworkconnectionchannel_p.h"
44 #include "private/qnoncontiguousbytedevice_p.h"
45
46 #include <qpair.h>
47 #include <qdebug.h>
48
49 #ifndef QT_NO_HTTP
50
51 #ifndef QT_NO_OPENSSL
52 # include <QtNetwork/qsslkey.h>
53 # include <QtNetwork/qsslcipher.h>
54 # include <QtNetwork/qsslconfiguration.h>
55 #endif
56
57 #ifndef QT_NO_BEARERMANAGEMENT
58 #include "private/qnetworksession_p.h"
59 #endif
60
61 QT_BEGIN_NAMESPACE
62
63 // TODO: Put channel specific stuff here so it does not polute qhttpnetworkconnection.cpp
64
QHttpNetworkConnectionChannel()65 QHttpNetworkConnectionChannel::QHttpNetworkConnectionChannel()
66 : socket(0)
67 , ssl(false)
68 , state(IdleState)
69 , reply(0)
70 , written(0)
71 , bytesTotal(0)
72 , resendCurrent(false)
73 , lastStatus(0)
74 , pendingEncrypt(false)
75 , reconnectAttempts(2)
76 , authMethod(QAuthenticatorPrivate::None)
77 , proxyAuthMethod(QAuthenticatorPrivate::None)
78 , authenticationCredentialsSent(false)
79 , proxyCredentialsSent(false)
80 #ifndef QT_NO_OPENSSL
81 , ignoreAllSslErrors(false)
82 #endif
83 , pipeliningSupported(PipeliningSupportUnknown)
84 , connection(0)
85 {
86 // Inlining this function in the header leads to compiler error on
87 // release-armv5, on at least timebox 9.2 and 10.1.
88 }
89
init()90 void QHttpNetworkConnectionChannel::init()
91 {
92 #ifndef QT_NO_OPENSSL
93 if (connection->d_func()->encrypt)
94 socket = new QSslSocket;
95 else
96 socket = new QTcpSocket;
97 #else
98 socket = new QTcpSocket;
99 #endif
100 #ifndef QT_NO_BEARERMANAGEMENT
101 //push session down to socket
102 if (networkSession)
103 socket->setProperty("_q_networksession", QVariant::fromValue(networkSession));
104 #endif
105 #ifndef QT_NO_NETWORKPROXY
106 // Set by QNAM anyway, but let's be safe here
107 socket->setProxy(QNetworkProxy::NoProxy);
108 #endif
109
110 // We want all signals (except the interactive ones) be connected as QueuedConnection
111 // because else we're falling into cases where we recurse back into the socket code
112 // and mess up the state. Always going to the event loop (and expecting that when reading/writing)
113 // is safer.
114 QObject::connect(socket, SIGNAL(bytesWritten(qint64)),
115 this, SLOT(_q_bytesWritten(qint64)),
116 Qt::QueuedConnection);
117 QObject::connect(socket, SIGNAL(connected()),
118 this, SLOT(_q_connected()),
119 Qt::QueuedConnection);
120 QObject::connect(socket, SIGNAL(readyRead()),
121 this, SLOT(_q_readyRead()),
122 Qt::QueuedConnection);
123
124 // The disconnected() and error() signals may already come
125 // while calling connectToHost().
126 // In case of a cached hostname or an IP this
127 // will then emit a signal to the user of QNetworkReply
128 // but cannot be caught because the user did not have a chance yet
129 // to connect to QNetworkReply's signals.
130 qRegisterMetaType<QAbstractSocket::SocketError>("QAbstractSocket::SocketError");
131 QObject::connect(socket, SIGNAL(disconnected()),
132 this, SLOT(_q_disconnected()),
133 Qt::QueuedConnection);
134 QObject::connect(socket, SIGNAL(error(QAbstractSocket::SocketError)),
135 this, SLOT(_q_error(QAbstractSocket::SocketError)),
136 Qt::QueuedConnection);
137
138
139 #ifndef QT_NO_NETWORKPROXY
140 QObject::connect(socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
141 this, SLOT(_q_proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
142 Qt::DirectConnection);
143 #endif
144
145 #ifndef QT_NO_OPENSSL
146 QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
147 if (sslSocket) {
148 // won't be a sslSocket if encrypt is false
149 QObject::connect(sslSocket, SIGNAL(encrypted()),
150 this, SLOT(_q_encrypted()),
151 Qt::QueuedConnection);
152 QObject::connect(sslSocket, SIGNAL(sslErrors(QList<QSslError>)),
153 this, SLOT(_q_sslErrors(QList<QSslError>)),
154 Qt::DirectConnection);
155 QObject::connect(sslSocket, SIGNAL(encryptedBytesWritten(qint64)),
156 this, SLOT(_q_encryptedBytesWritten(qint64)),
157 Qt::QueuedConnection);
158 }
159 #endif
160 }
161
162
close()163 void QHttpNetworkConnectionChannel::close()
164 {
165 if (socket->state() == QAbstractSocket::UnconnectedState)
166 state = QHttpNetworkConnectionChannel::IdleState;
167 else
168 state = QHttpNetworkConnectionChannel::ClosingState;
169
170 if (socket)
171 socket->close();
172 }
173
174
sendRequest()175 bool QHttpNetworkConnectionChannel::sendRequest()
176 {
177 if (!reply) {
178 // heh, how should that happen!
179 qWarning() << "QHttpNetworkConnectionChannel::sendRequest() called without QHttpNetworkReply";
180 state = QHttpNetworkConnectionChannel::IdleState;
181 return false;
182 }
183
184 switch (state) {
185 case QHttpNetworkConnectionChannel::IdleState: { // write the header
186 if (!ensureConnection()) {
187 // wait for the connection (and encryption) to be done
188 // sendRequest will be called again from either
189 // _q_connected or _q_encrypted
190 return false;
191 }
192 written = 0; // excluding the header
193 bytesTotal = 0;
194
195 QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
196 replyPrivate->clear();
197 replyPrivate->connection = connection;
198 replyPrivate->connectionChannel = this;
199 replyPrivate->autoDecompress = request.d->autoDecompress;
200 replyPrivate->pipeliningUsed = false;
201
202 // if the url contains authentication parameters, use the new ones
203 // both channels will use the new authentication parameters
204 if (!request.url().userInfo().isEmpty() && request.withCredentials()) {
205 QUrl url = request.url();
206 QAuthenticator &auth = authenticator;
207 if (url.userName() != auth.user()
208 || (!url.password().isEmpty() && url.password() != auth.password())) {
209 auth.setUser(url.userName());
210 auth.setPassword(url.password());
211 connection->d_func()->copyCredentials(connection->d_func()->indexOf(socket), &auth, false);
212 }
213 // clear the userinfo, since we use the same request for resending
214 // userinfo in url can conflict with the one in the authenticator
215 url.setUserInfo(QString());
216 request.setUrl(url);
217 }
218 // Will only be false if QtWebKit is performing a cross-origin XMLHttpRequest
219 // and withCredentials has not been set to true.
220 if (request.withCredentials())
221 connection->d_func()->createAuthorization(socket, request);
222 #ifndef QT_NO_NETWORKPROXY
223 QByteArray header = QHttpNetworkRequestPrivate::header(request,
224 (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy));
225 #else
226 QByteArray header = QHttpNetworkRequestPrivate::header(request, false);
227 #endif
228 socket->write(header);
229 // flushing is dangerous (QSslSocket calls transmit which might read or error)
230 // socket->flush();
231 QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
232 if (uploadByteDevice) {
233 // connect the signals so this function gets called again
234 QObject::connect(uploadByteDevice, SIGNAL(readyRead()),this, SLOT(_q_uploadDataReadyRead()));
235
236 bytesTotal = request.contentLength();
237
238 state = QHttpNetworkConnectionChannel::WritingState; // start writing data
239 sendRequest(); //recurse
240 } else {
241 state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
242 sendRequest(); //recurse
243 }
244
245 break;
246 }
247 case QHttpNetworkConnectionChannel::WritingState:
248 {
249 // write the data
250 QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
251 if (!uploadByteDevice || bytesTotal == written) {
252 if (uploadByteDevice)
253 emit reply->dataSendProgress(written, bytesTotal);
254 state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
255 sendRequest(); // recurse
256 break;
257 }
258
259 // only feed the QTcpSocket buffer when there is less than 32 kB in it
260 const qint64 socketBufferFill = 32*1024;
261 const qint64 socketWriteMaxSize = 16*1024;
262
263
264 #ifndef QT_NO_OPENSSL
265 QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
266 // if it is really an ssl socket, check more than just bytesToWrite()
267 while ((socket->bytesToWrite() + (sslSocket ? sslSocket->encryptedBytesToWrite() : 0))
268 <= socketBufferFill && bytesTotal != written)
269 #else
270 while (socket->bytesToWrite() <= socketBufferFill
271 && bytesTotal != written)
272 #endif
273 {
274 // get pointer to upload data
275 qint64 currentReadSize = 0;
276 qint64 desiredReadSize = qMin(socketWriteMaxSize, bytesTotal - written);
277 const char *readPointer = uploadByteDevice->readPointer(desiredReadSize, currentReadSize);
278
279 if (currentReadSize == -1) {
280 // premature eof happened
281 connection->d_func()->emitReplyError(socket, reply, QNetworkReply::UnknownNetworkError);
282 return false;
283 break;
284 } else if (readPointer == 0 || currentReadSize == 0) {
285 // nothing to read currently, break the loop
286 break;
287 } else {
288 if (written != uploadByteDevice->pos()) {
289 // Sanity check. This was useful in tracking down an upload corruption.
290 qWarning() << "QHttpProtocolHandler: Internal error in sendRequest. Expected to write at position" << written << "but read device is at" << uploadByteDevice->pos();
291 Q_ASSERT(written == uploadByteDevice->pos());
292 connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ProtocolFailure);
293 return false;
294 }
295
296 qint64 currentWriteSize = socket->write(readPointer, currentReadSize);
297 if (currentWriteSize == -1 || currentWriteSize != currentReadSize) {
298 // socket broke down
299 connection->d_func()->emitReplyError(socket, reply, QNetworkReply::UnknownNetworkError);
300 return false;
301 } else {
302 written += currentWriteSize;
303 uploadByteDevice->advanceReadPointer(currentWriteSize);
304
305 emit reply->dataSendProgress(written, bytesTotal);
306
307 if (written == bytesTotal) {
308 // make sure this function is called once again
309 state = QHttpNetworkConnectionChannel::WaitingState;
310 sendRequest();
311 break;
312 }
313 }
314 }
315 }
316 break;
317 }
318
319 case QHttpNetworkConnectionChannel::WaitingState:
320 {
321 QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
322 if (uploadByteDevice) {
323 QObject::disconnect(uploadByteDevice, SIGNAL(readyRead()), this, SLOT(_q_uploadDataReadyRead()));
324 }
325
326 // HTTP pipelining
327 //connection->d_func()->fillPipeline(socket);
328 //socket->flush();
329
330 // ensure we try to receive a reply in all cases, even if _q_readyRead_ hat not been called
331 // this is needed if the sends an reply before we have finished sending the request. In that
332 // case receiveReply had been called before but ignored the server reply
333 if (socket->bytesAvailable())
334 QMetaObject::invokeMethod(this, "_q_receiveReply", Qt::QueuedConnection);
335 break;
336 }
337 case QHttpNetworkConnectionChannel::ReadingState:
338 // ignore _q_bytesWritten in these states
339 // fall through
340 default:
341 break;
342 }
343 return true;
344 }
345
346
_q_receiveReply()347 void QHttpNetworkConnectionChannel::_q_receiveReply()
348 {
349 Q_ASSERT(socket);
350
351 if (!reply) {
352 if (socket->bytesAvailable() > 0)
353 qWarning() << "QHttpNetworkConnectionChannel::_q_receiveReply() called without QHttpNetworkReply,"
354 << socket->bytesAvailable() << "bytes on socket.";
355
356 close();
357 return;
358 }
359
360 // only run when the QHttpNetworkConnection is not currently being destructed, e.g.
361 // this function is called from _q_disconnected which is called because
362 // of ~QHttpNetworkConnectionPrivate
363 if (!qobject_cast<QHttpNetworkConnection*>(connection)) {
364 return;
365 }
366
367 QAbstractSocket::SocketState socketState = socket->state();
368
369 // connection might be closed to signal the end of data
370 if (socketState == QAbstractSocket::UnconnectedState) {
371 if (socket->bytesAvailable() <= 0) {
372 if (reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) {
373 // finish this reply. this case happens when the server did not send a content length
374 reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState;
375 allDone();
376 return;
377 } else {
378 handleUnexpectedEOF();
379 return;
380 }
381 } else {
382 // socket not connected but still bytes for reading.. just continue in this function
383 }
384 }
385
386 // read loop for the response
387 qint64 bytes = 0;
388 qint64 lastBytes = bytes;
389 do {
390 lastBytes = bytes;
391
392 QHttpNetworkReplyPrivate::ReplyState state = reply->d_func()->state;
393 switch (state) {
394 case QHttpNetworkReplyPrivate::NothingDoneState: {
395 state = reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState;
396 // fallthrough
397 }
398 case QHttpNetworkReplyPrivate::ReadingStatusState: {
399 qint64 statusBytes = reply->d_func()->readStatus(socket);
400 if (statusBytes == -1) {
401 // connection broke while reading status. also handled if later _q_disconnected is called
402 handleUnexpectedEOF();
403 return;
404 }
405 bytes += statusBytes;
406 lastStatus = reply->d_func()->statusCode;
407 break;
408 }
409 case QHttpNetworkReplyPrivate::ReadingHeaderState: {
410 QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
411 qint64 headerBytes = replyPrivate->readHeader(socket);
412 if (headerBytes == -1) {
413 // connection broke while reading headers. also handled if later _q_disconnected is called
414 handleUnexpectedEOF();
415 return;
416 }
417 bytes += headerBytes;
418 // If headers were parsed successfully now it is the ReadingDataState
419 if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState) {
420 if (replyPrivate->isGzipped() && replyPrivate->autoDecompress) {
421 // remove the Content-Length from header
422 replyPrivate->removeAutoDecompressHeader();
423 } else {
424 replyPrivate->autoDecompress = false;
425 }
426 if (replyPrivate->statusCode == 100) {
427 replyPrivate->clearHttpLayerInformation();
428 replyPrivate->state = QHttpNetworkReplyPrivate::ReadingStatusState;
429 break; // ignore
430 }
431 if (replyPrivate->shouldEmitSignals())
432 emit reply->headerChanged();
433 // After headerChanged had been emitted
434 // we can suddenly have a replyPrivate->userProvidedDownloadBuffer
435 // this is handled in the ReadingDataState however
436
437 if (!replyPrivate->expectContent()) {
438 replyPrivate->state = QHttpNetworkReplyPrivate::AllDoneState;
439 allDone();
440 break;
441 }
442 }
443 break;
444 }
445 case QHttpNetworkReplyPrivate::ReadingDataState: {
446 QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
447 if (socket->state() == QAbstractSocket::ConnectedState &&
448 replyPrivate->downstreamLimited && !replyPrivate->responseData.isEmpty() && replyPrivate->shouldEmitSignals()) {
449 // (only do the following when still connected, not when we have already been disconnected and there is still data)
450 // We already have some HTTP body data. We don't read more from the socket until
451 // this is fetched by QHttpNetworkAccessHttpBackend. If we would read more,
452 // we could not limit our read buffer usage.
453 // We only do this when shouldEmitSignals==true because our HTTP parsing
454 // always needs to parse the 401/407 replies. Therefore they don't really obey
455 // to the read buffer maximum size, but we don't care since they should be small.
456 return;
457 }
458
459 if (replyPrivate->userProvidedDownloadBuffer) {
460 // the user provided a direct buffer where we should put all our data in.
461 // this only works when we can tell the user the content length and he/she can allocate
462 // the buffer in that size.
463 // note that this call will read only from the still buffered data
464 qint64 haveRead = replyPrivate->readBodyVeryFast(socket, replyPrivate->userProvidedDownloadBuffer + replyPrivate->totalProgress);
465 bytes += haveRead;
466 replyPrivate->totalProgress += haveRead;
467
468 // the user will get notified of it via progress signal
469 if (haveRead > 0)
470 emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
471 } else if (!replyPrivate->isChunked() && !replyPrivate->autoDecompress
472 && replyPrivate->bodyLength > 0) {
473 // bulk files like images should fulfill these properties and
474 // we can therefore save on memory copying
475 qint64 haveRead = replyPrivate->readBodyFast(socket, &replyPrivate->responseData);
476 bytes += haveRead;
477 replyPrivate->totalProgress += haveRead;
478 if (replyPrivate->shouldEmitSignals()) {
479 emit reply->readyRead();
480 emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
481 }
482 }
483 else
484 {
485 // use the traditional slower reading (for compressed encoding, chunked encoding,
486 // no content-length etc)
487 QByteDataBuffer byteDatas;
488 qint64 haveRead = replyPrivate->readBody(socket, &byteDatas);
489 if (haveRead) {
490 bytes += haveRead;
491 if (replyPrivate->autoDecompress)
492 replyPrivate->appendCompressedReplyData(byteDatas);
493 else
494 replyPrivate->appendUncompressedReplyData(byteDatas);
495
496 if (!replyPrivate->autoDecompress) {
497 replyPrivate->totalProgress += bytes;
498 if (replyPrivate->shouldEmitSignals()) {
499 // important: At the point of this readyRead(), the byteDatas list must be empty,
500 // else implicit sharing will trigger memcpy when the user is reading data!
501 emit reply->readyRead();
502 emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
503 }
504 }
505 #ifndef QT_NO_COMPRESS
506 else if (!expand(false)) { // expand a chunk if possible
507 // If expand() failed we can just return, it had already called connection->emitReplyError()
508 return;
509 }
510 #endif
511 }
512 }
513 // still in ReadingDataState? This function will be called again by the socket's readyRead
514 if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState)
515 break;
516
517 // everything done, fall through
518 }
519 case QHttpNetworkReplyPrivate::AllDoneState:
520 allDone();
521 break;
522 default:
523 break;
524 }
525 } while (bytes != lastBytes && reply);
526 }
527
528 // called when unexpectedly reading a -1 or when data is expected but socket is closed
handleUnexpectedEOF()529 void QHttpNetworkConnectionChannel::handleUnexpectedEOF()
530 {
531 Q_ASSERT(reply);
532 if (reconnectAttempts <= 0) {
533 // too many errors reading/receiving/parsing the status, close the socket and emit error
534 requeueCurrentlyPipelinedRequests();
535 close();
536 reply->d_func()->errorString = connection->d_func()->errorDetail(QNetworkReply::RemoteHostClosedError, socket);
537 emit reply->finishedWithError(QNetworkReply::RemoteHostClosedError, reply->d_func()->errorString);
538 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
539 } else {
540 reconnectAttempts--;
541 reply->d_func()->clear();
542 reply->d_func()->connection = connection;
543 reply->d_func()->connectionChannel = this;
544 closeAndResendCurrentRequest();
545 }
546 }
547
ensureConnection()548 bool QHttpNetworkConnectionChannel::ensureConnection()
549 {
550 QAbstractSocket::SocketState socketState = socket->state();
551
552 // resend this request after we receive the disconnected signal
553 if (socketState == QAbstractSocket::ClosingState) {
554 if (reply)
555 resendCurrent = true;
556 return false;
557 }
558
559 // already trying to connect?
560 if (socketState == QAbstractSocket::HostLookupState ||
561 socketState == QAbstractSocket::ConnectingState) {
562 return false;
563 }
564
565 // make sure that this socket is in a connected state, if not initiate
566 // connection to the host.
567 if (socketState != QAbstractSocket::ConnectedState) {
568 // connect to the host if not already connected.
569 state = QHttpNetworkConnectionChannel::ConnectingState;
570 pendingEncrypt = ssl;
571
572 // reset state
573 pipeliningSupported = PipeliningSupportUnknown;
574 authenticationCredentialsSent = false;
575 proxyCredentialsSent = false;
576 authenticator.detach();
577 QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(authenticator);
578 priv->hasFailed = false;
579 proxyAuthenticator.detach();
580 priv = QAuthenticatorPrivate::getPrivate(proxyAuthenticator);
581 priv->hasFailed = false;
582
583 // This workaround is needed since we use QAuthenticator for NTLM authentication. The "phase == Done"
584 // is the usual criteria for emitting authentication signals. The "phase" is set to "Done" when the
585 // last header for Authorization is generated by the QAuthenticator. Basic & Digest logic does not
586 // check the "phase" for generating the Authorization header. NTLM authentication is a two stage
587 // process & needs the "phase". To make sure the QAuthenticator uses the current username/password
588 // the phase is reset to Start.
589 priv = QAuthenticatorPrivate::getPrivate(authenticator);
590 if (priv && priv->phase == QAuthenticatorPrivate::Done)
591 priv->phase = QAuthenticatorPrivate::Start;
592 priv = QAuthenticatorPrivate::getPrivate(proxyAuthenticator);
593 if (priv && priv->phase == QAuthenticatorPrivate::Done)
594 priv->phase = QAuthenticatorPrivate::Start;
595
596 QString connectHost = connection->d_func()->hostName;
597 qint16 connectPort = connection->d_func()->port;
598
599 #ifndef QT_NO_NETWORKPROXY
600 // HTTPS always use transparent proxy.
601 if (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy && !ssl) {
602 connectHost = connection->d_func()->networkProxy.hostName();
603 connectPort = connection->d_func()->networkProxy.port();
604 }
605 if (socket->proxy().type() == QNetworkProxy::HttpProxy) {
606 // Make user-agent field available to HTTP proxy socket engine (QTBUG-17223)
607 QByteArray value;
608 // ensureConnection is called before any request has been assigned, but can also be called again if reconnecting
609 if (request.url().isEmpty())
610 value = connection->d_func()->predictNextRequest().headerField("user-agent");
611 else
612 value = request.headerField("user-agent");
613 if (!value.isEmpty())
614 socket->setProperty("_q_user-agent", value);
615 }
616 #endif
617 if (ssl) {
618 #ifndef QT_NO_OPENSSL
619 QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
620 sslSocket->connectToHostEncrypted(connectHost, connectPort);
621 if (ignoreAllSslErrors)
622 sslSocket->ignoreSslErrors();
623 sslSocket->ignoreSslErrors(ignoreSslErrorsList);
624
625 // limit the socket read buffer size. we will read everything into
626 // the QHttpNetworkReply anyway, so let's grow only that and not
627 // here and there.
628 socket->setReadBufferSize(64*1024);
629 #else
630 connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ProtocolUnknownError);
631 #endif
632 } else {
633 // In case of no proxy we can use the Unbuffered QTcpSocket
634 #ifndef QT_NO_NETWORKPROXY
635 if (connection->d_func()->networkProxy.type() == QNetworkProxy::NoProxy
636 && connection->cacheProxy().type() == QNetworkProxy::NoProxy
637 && connection->transparentProxy().type() == QNetworkProxy::NoProxy) {
638 #endif
639 socket->connectToHost(connectHost, connectPort, QIODevice::ReadWrite | QIODevice::Unbuffered);
640 // For an Unbuffered QTcpSocket, the read buffer size has a special meaning.
641 socket->setReadBufferSize(1*1024);
642 #ifndef QT_NO_NETWORKPROXY
643 } else {
644 socket->connectToHost(connectHost, connectPort);
645
646 // limit the socket read buffer size. we will read everything into
647 // the QHttpNetworkReply anyway, so let's grow only that and not
648 // here and there.
649 socket->setReadBufferSize(64*1024);
650 }
651 #endif
652 }
653 return false;
654 }
655
656 // This code path for ConnectedState
657 if (pendingEncrypt) {
658 // Let's only be really connected when we have received the encrypted() signal. Else the state machine seems to mess up
659 // and corrupt the things sent to the server.
660 return false;
661 }
662
663 return true;
664 }
665
666
667 #ifndef QT_NO_COMPRESS
expand(bool dataComplete)668 bool QHttpNetworkConnectionChannel::expand(bool dataComplete)
669 {
670 Q_ASSERT(socket);
671 Q_ASSERT(reply);
672
673 qint64 total = reply->d_func()->compressedData.size();
674 if (total >= CHUNK || dataComplete) {
675 // uncompress the data
676 QByteArray content, inflated;
677 content = reply->d_func()->compressedData;
678 reply->d_func()->compressedData.clear();
679
680 int ret = Z_OK;
681 if (content.size())
682 ret = reply->d_func()->gunzipBodyPartially(content, inflated);
683 if (ret >= Z_OK) {
684 if (dataComplete && ret == Z_OK && !reply->d_func()->streamEnd) {
685 reply->d_func()->gunzipBodyPartiallyEnd();
686 reply->d_func()->streamEnd = true;
687 }
688 if (inflated.size()) {
689 reply->d_func()->totalProgress += inflated.size();
690 reply->d_func()->appendUncompressedReplyData(inflated);
691 if (reply->d_func()->shouldEmitSignals()) {
692 // important: At the point of this readyRead(), inflated must be cleared,
693 // else implicit sharing will trigger memcpy when the user is reading data!
694 emit reply->readyRead();
695 emit reply->dataReadProgress(reply->d_func()->totalProgress, 0);
696 }
697 }
698 } else {
699 connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ProtocolFailure);
700 return false;
701 }
702 }
703 return true;
704 }
705 #endif
706
707
allDone()708 void QHttpNetworkConnectionChannel::allDone()
709 {
710 Q_ASSERT(reply);
711 #ifndef QT_NO_COMPRESS
712 // expand the whole data.
713 if (reply->d_func()->expectContent() && reply->d_func()->autoDecompress && !reply->d_func()->streamEnd) {
714 bool expandResult = expand(true);
715 // If expand() failed we can just return, it had already called connection->emitReplyError()
716 if (!expandResult)
717 return;
718 }
719 #endif
720
721 if (!reply) {
722 qWarning() << "QHttpNetworkConnectionChannel::allDone() called without reply. Please report at http://bugreports.qt-project.org/";
723 return;
724 }
725
726 // while handling 401 & 407, we might reset the status code, so save this.
727 bool emitFinished = reply->d_func()->shouldEmitSignals();
728 bool connectionCloseEnabled = reply->d_func()->isConnectionCloseEnabled();
729 detectPipeliningSupport();
730
731 handleStatus();
732 // handleStatus() might have removed the reply because it already called connection->emitReplyError()
733
734 // queue the finished signal, this is required since we might send new requests from
735 // slot connected to it. The socket will not fire readyRead signal, if we are already
736 // in the slot connected to readyRead
737 if (reply && emitFinished)
738 QMetaObject::invokeMethod(reply, "finished", Qt::QueuedConnection);
739
740
741 // reset the reconnection attempts after we receive a complete reply.
742 // in case of failures, each channel will attempt two reconnects before emitting error.
743 reconnectAttempts = 2;
744
745 // now the channel can be seen as free/idle again, all signal emissions for the reply have been done
746 if (state != QHttpNetworkConnectionChannel::ClosingState)
747 state = QHttpNetworkConnectionChannel::IdleState;
748
749 // if it does not need to be sent again we can set it to 0
750 // the previous code did not do that and we had problems with accidental re-sending of a
751 // finished request.
752 // Note that this may trigger a segfault at some other point. But then we can fix the underlying
753 // problem.
754 if (!resendCurrent) {
755 request = QHttpNetworkRequest();
756 reply = 0;
757 }
758
759 // move next from pipeline to current request
760 if (!alreadyPipelinedRequests.isEmpty()) {
761 if (resendCurrent || connectionCloseEnabled || socket->state() != QAbstractSocket::ConnectedState) {
762 // move the pipelined ones back to the main queue
763 requeueCurrentlyPipelinedRequests();
764 close();
765 } else {
766 // there were requests pipelined in and we can continue
767 HttpMessagePair messagePair = alreadyPipelinedRequests.takeFirst();
768
769 request = messagePair.first;
770 reply = messagePair.second;
771 state = QHttpNetworkConnectionChannel::ReadingState;
772 resendCurrent = false;
773
774 written = 0; // message body, excluding the header, irrelevant here
775 bytesTotal = 0; // message body total, excluding the header, irrelevant here
776
777 // pipeline even more
778 connection->d_func()->fillPipeline(socket);
779
780 // continue reading
781 //_q_receiveReply();
782 // this was wrong, allDone gets called from that function anyway.
783 }
784 } else if (alreadyPipelinedRequests.isEmpty() && socket->bytesAvailable() > 0) {
785 // this is weird. we had nothing pipelined but still bytes available. better close it.
786 close();
787
788 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
789 } else if (alreadyPipelinedRequests.isEmpty()) {
790 if (connectionCloseEnabled)
791 if (socket->state() != QAbstractSocket::UnconnectedState)
792 close();
793 if (qobject_cast<QHttpNetworkConnection*>(connection))
794 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
795 }
796 }
797
detectPipeliningSupport()798 void QHttpNetworkConnectionChannel::detectPipeliningSupport()
799 {
800 Q_ASSERT(reply);
801 // detect HTTP Pipelining support
802 QByteArray serverHeaderField;
803 if (
804 // check for HTTP/1.1
805 (reply->d_func()->majorVersion == 1 && reply->d_func()->minorVersion == 1)
806 // check for not having connection close
807 && (!reply->d_func()->isConnectionCloseEnabled())
808 // check if it is still connected
809 && (socket->state() == QAbstractSocket::ConnectedState)
810 // check for broken servers in server reply header
811 // this is adapted from http://mxr.mozilla.org/firefox/ident?i=SupportsPipelining
812 && (serverHeaderField = reply->headerField("Server"), !serverHeaderField.contains("Microsoft-IIS/4."))
813 && (!serverHeaderField.contains("Microsoft-IIS/5."))
814 && (!serverHeaderField.contains("Netscape-Enterprise/3."))
815 // this is adpoted from the knowledge of the Nokia 7.x browser team (DEF143319)
816 && (!serverHeaderField.contains("WebLogic"))
817 && (!serverHeaderField.startsWith("Rocket")) // a Python Web Server, see Web2py.com
818 ) {
819 pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningProbablySupported;
820 } else {
821 pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown;
822 }
823 }
824
825 // called when the connection broke and we need to queue some pipelined requests again
requeueCurrentlyPipelinedRequests()826 void QHttpNetworkConnectionChannel::requeueCurrentlyPipelinedRequests()
827 {
828 for (int i = 0; i < alreadyPipelinedRequests.length(); i++)
829 connection->d_func()->requeueRequest(alreadyPipelinedRequests.at(i));
830 alreadyPipelinedRequests.clear();
831
832 // only run when the QHttpNetworkConnection is not currently being destructed, e.g.
833 // this function is called from _q_disconnected which is called because
834 // of ~QHttpNetworkConnectionPrivate
835 if (qobject_cast<QHttpNetworkConnection*>(connection))
836 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
837 }
838
handleStatus()839 void QHttpNetworkConnectionChannel::handleStatus()
840 {
841 Q_ASSERT(socket);
842 Q_ASSERT(reply);
843
844 int statusCode = reply->statusCode();
845 bool resend = false;
846
847 switch (statusCode) {
848 case 401: // auth required
849 case 407: // proxy auth required
850 if (connection->d_func()->handleAuthenticateChallenge(socket, reply, (statusCode == 407), resend)) {
851 if (resend) {
852 if (!resetUploadData())
853 break;
854
855 reply->d_func()->eraseData();
856
857 if (alreadyPipelinedRequests.isEmpty()) {
858 // this does a re-send without closing the connection
859 resendCurrent = true;
860 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
861 } else {
862 // we had requests pipelined.. better close the connection in closeAndResendCurrentRequest
863 closeAndResendCurrentRequest();
864 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
865 }
866 } else {
867 //authentication cancelled, close the channel.
868 close();
869 }
870 } else {
871 emit reply->headerChanged();
872 emit reply->readyRead();
873 QNetworkReply::NetworkError errorCode = (statusCode == 407)
874 ? QNetworkReply::ProxyAuthenticationRequiredError
875 : QNetworkReply::AuthenticationRequiredError;
876 reply->d_func()->errorString = connection->d_func()->errorDetail(errorCode, socket);
877 emit reply->finishedWithError(errorCode, reply->d_func()->errorString);
878 }
879 break;
880 default:
881 if (qobject_cast<QHttpNetworkConnection*>(connection))
882 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
883 }
884 }
885
resetUploadData()886 bool QHttpNetworkConnectionChannel::resetUploadData()
887 {
888 if (!reply) {
889 //this happens if server closes connection while QHttpNetworkConnectionPrivate::_q_startNextRequest is pending
890 return false;
891 }
892 QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
893 if (!uploadByteDevice)
894 return true;
895
896 if (uploadByteDevice->reset()) {
897 written = 0;
898 return true;
899 } else {
900 connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ContentReSendError);
901 return false;
902 }
903 }
904
905
pipelineInto(HttpMessagePair & pair)906 void QHttpNetworkConnectionChannel::pipelineInto(HttpMessagePair &pair)
907 {
908 // this is only called for simple GET
909
910 QHttpNetworkRequest &request = pair.first;
911 QHttpNetworkReply *reply = pair.second;
912 reply->d_func()->clear();
913 reply->d_func()->connection = connection;
914 reply->d_func()->connectionChannel = this;
915 reply->d_func()->autoDecompress = request.d->autoDecompress;
916 reply->d_func()->pipeliningUsed = true;
917
918 #ifndef QT_NO_NETWORKPROXY
919 pipeline.append(QHttpNetworkRequestPrivate::header(request,
920 (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy)));
921 #else
922 pipeline.append(QHttpNetworkRequestPrivate::header(request, false));
923 #endif
924
925 alreadyPipelinedRequests.append(pair);
926
927 // pipelineFlush() needs to be called at some point afterwards
928 }
929
pipelineFlush()930 void QHttpNetworkConnectionChannel::pipelineFlush()
931 {
932 if (pipeline.isEmpty())
933 return;
934
935 // The goal of this is so that we have everything in one TCP packet.
936 // For the Unbuffered QTcpSocket this is manually needed, the buffered
937 // QTcpSocket does it automatically.
938 // Also, sometimes the OS does it for us (Nagle's algorithm) but that
939 // happens only sometimes.
940 socket->write(pipeline);
941 pipeline.clear();
942 }
943
944
closeAndResendCurrentRequest()945 void QHttpNetworkConnectionChannel::closeAndResendCurrentRequest()
946 {
947 requeueCurrentlyPipelinedRequests();
948 close();
949 if (reply)
950 resendCurrent = true;
951 if (qobject_cast<QHttpNetworkConnection*>(connection))
952 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
953 }
954
isSocketBusy() const955 bool QHttpNetworkConnectionChannel::isSocketBusy() const
956 {
957 return (state & QHttpNetworkConnectionChannel::BusyState);
958 }
959
isSocketWriting() const960 bool QHttpNetworkConnectionChannel::isSocketWriting() const
961 {
962 return (state & QHttpNetworkConnectionChannel::WritingState);
963 }
964
isSocketWaiting() const965 bool QHttpNetworkConnectionChannel::isSocketWaiting() const
966 {
967 return (state & QHttpNetworkConnectionChannel::WaitingState);
968 }
969
isSocketReading() const970 bool QHttpNetworkConnectionChannel::isSocketReading() const
971 {
972 return (state & QHttpNetworkConnectionChannel::ReadingState);
973 }
974
975 //private slots
_q_readyRead()976 void QHttpNetworkConnectionChannel::_q_readyRead()
977 {
978 if (socket->state() == QAbstractSocket::ConnectedState && socket->bytesAvailable() == 0) {
979 // We got a readyRead but no bytes are available..
980 // This happens for the Unbuffered QTcpSocket
981 // Also check if socket is in ConnectedState since
982 // this function may also be invoked via the event loop.
983 char c;
984 qint64 ret = socket->peek(&c, 1);
985 if (ret < 0) {
986 _q_error(socket->error());
987 // We still need to handle the reply so it emits its signals etc.
988 if (reply)
989 _q_receiveReply();
990 return;
991 }
992 }
993
994 if (isSocketWaiting() || isSocketReading()) {
995 state = QHttpNetworkConnectionChannel::ReadingState;
996 if (reply)
997 _q_receiveReply();
998 }
999 }
1000
_q_bytesWritten(qint64 bytes)1001 void QHttpNetworkConnectionChannel::_q_bytesWritten(qint64 bytes)
1002 {
1003 Q_UNUSED(bytes);
1004
1005 if (ssl) {
1006 // In the SSL case we want to send data from encryptedBytesWritten signal since that one
1007 // is the one going down to the actual network, not only into some SSL buffer.
1008 return;
1009 }
1010
1011 // bytes have been written to the socket. write even more of them :)
1012 if (isSocketWriting())
1013 sendRequest();
1014 // otherwise we do nothing
1015 }
1016
_q_disconnected()1017 void QHttpNetworkConnectionChannel::_q_disconnected()
1018 {
1019 if (state == QHttpNetworkConnectionChannel::ClosingState) {
1020 state = QHttpNetworkConnectionChannel::IdleState;
1021 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
1022 return;
1023 }
1024
1025 // read the available data before closing
1026 if (isSocketWaiting() || isSocketReading()) {
1027 if (reply) {
1028 state = QHttpNetworkConnectionChannel::ReadingState;
1029 _q_receiveReply();
1030 }
1031 } else if (state == QHttpNetworkConnectionChannel::IdleState && resendCurrent) {
1032 // re-sending request because the socket was in ClosingState
1033 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
1034 }
1035 state = QHttpNetworkConnectionChannel::IdleState;
1036
1037 requeueCurrentlyPipelinedRequests();
1038 close();
1039 }
1040
1041
_q_connected()1042 void QHttpNetworkConnectionChannel::_q_connected()
1043 {
1044 // improve performance since we get the request sent by the kernel ASAP
1045 //socket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
1046 // We have this commented out now. It did not have the effect we wanted. If we want to
1047 // do this properly, Qt has to combine multiple HTTP requests into one buffer
1048 // and send this to the kernel in one syscall and then the kernel immediately sends
1049 // it as one TCP packet because of TCP_NODELAY.
1050 // However, this code is currently not in Qt, so we rely on the kernel combining
1051 // the requests into one TCP packet.
1052
1053 // not sure yet if it helps, but it makes sense
1054 socket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
1055
1056 pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown;
1057
1058 // ### FIXME: if the server closes the connection unexpectedly, we shouldn't send the same broken request again!
1059 //channels[i].reconnectAttempts = 2;
1060 if (!pendingEncrypt && !ssl) { // FIXME: Didn't work properly with pendingEncrypt only, we should refactor this into an EncrypingState
1061 state = QHttpNetworkConnectionChannel::IdleState;
1062 if (!reply)
1063 connection->d_func()->dequeueRequest(socket);
1064 if (reply)
1065 sendRequest();
1066 }
1067 }
1068
1069
_q_error(QAbstractSocket::SocketError socketError)1070 void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socketError)
1071 {
1072 if (!socket)
1073 return;
1074 QNetworkReply::NetworkError errorCode = QNetworkReply::UnknownNetworkError;
1075
1076 switch (socketError) {
1077 case QAbstractSocket::HostNotFoundError:
1078 errorCode = QNetworkReply::HostNotFoundError;
1079 break;
1080 case QAbstractSocket::ConnectionRefusedError:
1081 errorCode = QNetworkReply::ConnectionRefusedError;
1082 break;
1083 case QAbstractSocket::RemoteHostClosedError:
1084 // try to reconnect/resend before sending an error.
1085 // while "Reading" the _q_disconnected() will handle this.
1086 if (state != QHttpNetworkConnectionChannel::IdleState && state != QHttpNetworkConnectionChannel::ReadingState) {
1087 if (reconnectAttempts-- > 0) {
1088 closeAndResendCurrentRequest();
1089 return;
1090 } else {
1091 errorCode = QNetworkReply::RemoteHostClosedError;
1092 }
1093 } else if (state == QHttpNetworkConnectionChannel::ReadingState) {
1094 if (!reply)
1095 break;
1096
1097 if (!reply->d_func()->expectContent()) {
1098 // No content expected, this is a valid way to have the connection closed by the server
1099 return;
1100 }
1101 if (reply->contentLength() == -1 && !reply->d_func()->isChunked()) {
1102 // There was no content-length header and it's not chunked encoding,
1103 // so this is a valid way to have the connection closed by the server
1104 return;
1105 }
1106 // ok, we got a disconnect even though we did not expect it
1107 // Try to read everything from the socket before we emit the error.
1108 if (socket->bytesAvailable()) {
1109 // Read everything from the socket into the reply buffer.
1110 // we can ignore the readbuffersize as the data is already
1111 // in memory and we will not recieve more data on the socket.
1112 reply->setReadBufferSize(0);
1113 _q_receiveReply();
1114 #ifndef QT_NO_OPENSSL
1115 if (ssl) {
1116 // QT_NO_OPENSSL. The QSslSocket can still have encrypted bytes in the plainsocket.
1117 // So we need to check this if the socket is a QSslSocket. When the socket is flushed
1118 // it will force a decrypt of the encrypted data in the plainsocket.
1119 QSslSocket *sslSocket = static_cast<QSslSocket*>(socket);
1120 qint64 beforeFlush = sslSocket->encryptedBytesAvailable();
1121 while (sslSocket->encryptedBytesAvailable()) {
1122 sslSocket->flush();
1123 _q_receiveReply();
1124 qint64 afterFlush = sslSocket->encryptedBytesAvailable();
1125 if (afterFlush == beforeFlush)
1126 break;
1127 beforeFlush = afterFlush;
1128 }
1129 }
1130 #endif
1131 }
1132
1133 errorCode = QNetworkReply::RemoteHostClosedError;
1134 } else {
1135 errorCode = QNetworkReply::RemoteHostClosedError;
1136 }
1137 break;
1138 case QAbstractSocket::SocketTimeoutError:
1139 // try to reconnect/resend before sending an error.
1140 if (state == QHttpNetworkConnectionChannel::WritingState && (reconnectAttempts-- > 0)) {
1141 closeAndResendCurrentRequest();
1142 return;
1143 }
1144 errorCode = QNetworkReply::TimeoutError;
1145 break;
1146 case QAbstractSocket::ProxyAuthenticationRequiredError:
1147 errorCode = QNetworkReply::ProxyAuthenticationRequiredError;
1148 break;
1149 case QAbstractSocket::SslHandshakeFailedError:
1150 errorCode = QNetworkReply::SslHandshakeFailedError;
1151 break;
1152 default:
1153 // all other errors are treated as NetworkError
1154 errorCode = QNetworkReply::UnknownNetworkError;
1155 break;
1156 }
1157 QPointer<QHttpNetworkConnection> that = connection;
1158 QString errorString = connection->d_func()->errorDetail(errorCode, socket, socket->errorString());
1159
1160 // Need to dequeu the request so that we can emit the error.
1161 if (!reply)
1162 connection->d_func()->dequeueRequest(socket);
1163 if (reply) {
1164 reply->d_func()->errorString = errorString;
1165 emit reply->finishedWithError(errorCode, errorString);
1166 reply = 0;
1167 }
1168 // send the next request
1169 QMetaObject::invokeMethod(that, "_q_startNextRequest", Qt::QueuedConnection);
1170
1171 if (that) //signal emission triggered event loop
1172 close();
1173 }
1174
1175 #ifndef QT_NO_NETWORKPROXY
_q_proxyAuthenticationRequired(const QNetworkProxy & proxy,QAuthenticator * auth)1176 void QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator* auth)
1177 {
1178 // Need to dequeue the request before we can emit the error.
1179 if (!reply)
1180 connection->d_func()->dequeueRequest(socket);
1181 if (reply)
1182 connection->d_func()->emitProxyAuthenticationRequired(this, proxy, auth);
1183 }
1184 #endif
1185
_q_uploadDataReadyRead()1186 void QHttpNetworkConnectionChannel::_q_uploadDataReadyRead()
1187 {
1188 if (reply && state == QHttpNetworkConnectionChannel::WritingState) {
1189 // There might be timing issues, make sure to only send upload data if really in that state
1190 sendRequest();
1191 }
1192 }
1193
1194 #ifndef QT_NO_OPENSSL
_q_encrypted()1195 void QHttpNetworkConnectionChannel::_q_encrypted()
1196 {
1197 if (!socket)
1198 return; // ### error
1199 state = QHttpNetworkConnectionChannel::IdleState;
1200 pendingEncrypt = false;
1201 if (!reply)
1202 connection->d_func()->dequeueRequest(socket);
1203 if (reply)
1204 sendRequest();
1205 }
1206
_q_sslErrors(const QList<QSslError> & errors)1207 void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors)
1208 {
1209 if (!socket)
1210 return;
1211 //QNetworkReply::NetworkError errorCode = QNetworkReply::ProtocolFailure;
1212 // Also pause the connection because socket notifiers may fire while an user
1213 // dialog is displaying
1214 connection->d_func()->pauseConnection();
1215 if (pendingEncrypt && !reply)
1216 connection->d_func()->dequeueRequest(socket);
1217 if (reply)
1218 emit reply->sslErrors(errors);
1219 connection->d_func()->resumeConnection();
1220 }
1221
_q_encryptedBytesWritten(qint64 bytes)1222 void QHttpNetworkConnectionChannel::_q_encryptedBytesWritten(qint64 bytes)
1223 {
1224 Q_UNUSED(bytes);
1225 // bytes have been written to the socket. write even more of them :)
1226 if (isSocketWriting())
1227 sendRequest();
1228 // otherwise we do nothing
1229 }
1230
1231 #endif
1232
setConnection(QHttpNetworkConnection * c)1233 void QHttpNetworkConnectionChannel::setConnection(QHttpNetworkConnection *c)
1234 {
1235 // Inlining this function in the header leads to compiler error on
1236 // release-armv5, on at least timebox 9.2 and 10.1.
1237 connection = c;
1238 }
1239
1240 QT_END_NAMESPACE
1241
1242 #include "moc_qhttpnetworkconnectionchannel_p.cpp"
1243
1244 #endif // QT_NO_HTTP
1245