~ubuntu-branches/ubuntu/wily/qtbase-opensource-src/wily

« back to all changes in this revision

Viewing changes to src/network/access/qhttpnetworkconnectionchannel.cpp

  • Committer: Package Import Robot
  • Author(s): Timo Jyrinki
  • Date: 2013-02-05 12:46:17 UTC
  • Revision ID: package-import@ubuntu.com-20130205124617-c8jouts182j002fx
Tags: upstream-5.0.1+dfsg
ImportĀ upstreamĀ versionĀ 5.0.1+dfsg

Show diffs side-by-side

added added

removed removed

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