src/network/access/qhttpnetworkconnectionchannel.cpp
changeset 7 f7bc934e204c
parent 3 41300fa6a67c
equal deleted inserted replaced
3:41300fa6a67c 7:f7bc934e204c
     1 /****************************************************************************
     1 /****************************************************************************
     2 **
     2 **
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
     4 ** All rights reserved.
     4 ** All rights reserved.
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
     6 **
     6 **
     7 ** This file is part of the QtNetwork module of the Qt Toolkit.
     7 ** This file is part of the QtNetwork module of the Qt Toolkit.
     8 **
     8 **
    55 #endif
    55 #endif
    56 
    56 
    57 QT_BEGIN_NAMESPACE
    57 QT_BEGIN_NAMESPACE
    58 
    58 
    59 // TODO: Put channel specific stuff here so it does not polute qhttpnetworkconnection.cpp
    59 // TODO: Put channel specific stuff here so it does not polute qhttpnetworkconnection.cpp
       
    60 
       
    61 QHttpNetworkConnectionChannel::QHttpNetworkConnectionChannel()
       
    62     : socket(0)
       
    63     , state(IdleState)
       
    64     , reply(0)
       
    65     , written(0)
       
    66     , bytesTotal(0)
       
    67     , resendCurrent(false)
       
    68     , lastStatus(0)
       
    69     , pendingEncrypt(false)
       
    70     , reconnectAttempts(2)
       
    71     , authMehtod(QAuthenticatorPrivate::None)
       
    72     , proxyAuthMehtod(QAuthenticatorPrivate::None)
       
    73 #ifndef QT_NO_OPENSSL
       
    74     , ignoreAllSslErrors(false)
       
    75 #endif
       
    76     , pipeliningSupported(PipeliningSupportUnknown)
       
    77     , connection(0)
       
    78 {
       
    79     // Inlining this function in the header leads to compiler error on
       
    80     // release-armv5, on at least timebox 9.2 and 10.1.
       
    81 }
    60 
    82 
    61 void QHttpNetworkConnectionChannel::init()
    83 void QHttpNetworkConnectionChannel::init()
    62 {
    84 {
    63 #ifndef QT_NO_OPENSSL
    85 #ifndef QT_NO_OPENSSL
    64     if (connection->d_func()->encrypt)
    86     if (connection->d_func()->encrypt)
   122 }
   144 }
   123 
   145 
   124 
   146 
   125 bool QHttpNetworkConnectionChannel::sendRequest()
   147 bool QHttpNetworkConnectionChannel::sendRequest()
   126 {
   148 {
       
   149     if (!reply) {
       
   150         // heh, how should that happen!
       
   151         qWarning() << "QHttpNetworkConnectionChannel::sendRequest() called without QHttpNetworkReply";
       
   152         state = QHttpNetworkConnectionChannel::IdleState;
       
   153         return false;
       
   154     }
       
   155 
   127     switch (state) {
   156     switch (state) {
   128     case QHttpNetworkConnectionChannel::IdleState: { // write the header
   157     case QHttpNetworkConnectionChannel::IdleState: { // write the header
   129         if (!ensureConnection()) {
   158         if (!ensureConnection()) {
   130             // wait for the connection (and encryption) to be done
   159             // wait for the connection (and encryption) to be done
   131             // sendRequest will be called again from either
   160             // sendRequest will be called again from either
   132             // _q_connected or _q_encrypted
   161             // _q_connected or _q_encrypted
   133             return false;
   162             return false;
   134         }
   163         }
   135         written = 0; // excluding the header
   164         written = 0; // excluding the header
   136         bytesTotal = 0;
   165         bytesTotal = 0;
   137         if (reply) {
   166 
   138             reply->d_func()->clear();
   167         reply->d_func()->clear();
   139             reply->d_func()->connection = connection;
   168         reply->d_func()->connection = connection;
   140             reply->d_func()->autoDecompress = request.d->autoDecompress;
   169         reply->d_func()->connectionChannel = this;
   141             reply->d_func()->pipeliningUsed = false;
   170         reply->d_func()->autoDecompress = request.d->autoDecompress;
   142         }
   171         reply->d_func()->pipeliningUsed = false;
   143         state = QHttpNetworkConnectionChannel::WritingState;
   172 
   144         pendingEncrypt = false;
   173         pendingEncrypt = false;
   145         // if the url contains authentication parameters, use the new ones
   174         // if the url contains authentication parameters, use the new ones
   146         // both channels will use the new authentication parameters
   175         // both channels will use the new authentication parameters
   147         if (!request.url().userInfo().isEmpty()) {
   176         if (!request.url().userInfo().isEmpty()) {
   148             QUrl url = request.url();
   177             QUrl url = request.url();
   164             (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy));
   193             (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy));
   165 #else
   194 #else
   166         QByteArray header = QHttpNetworkRequestPrivate::header(request, false);
   195         QByteArray header = QHttpNetworkRequestPrivate::header(request, false);
   167 #endif
   196 #endif
   168         socket->write(header);
   197         socket->write(header);
       
   198         // flushing is dangerous (QSslSocket calls transmit which might read or error)
       
   199 //        socket->flush();
   169         QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
   200         QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
   170         if (uploadByteDevice) {
   201         if (uploadByteDevice) {
   171             // connect the signals so this function gets called again
   202             // connect the signals so this function gets called again
   172             QObject::connect(uploadByteDevice, SIGNAL(readyRead()),this, SLOT(_q_uploadDataReadyRead()));
   203             QObject::connect(uploadByteDevice, SIGNAL(readyRead()),this, SLOT(_q_uploadDataReadyRead()));
   173 
   204 
   174             bytesTotal = request.contentLength();
   205             bytesTotal = request.contentLength();
       
   206 
       
   207             state = QHttpNetworkConnectionChannel::WritingState; // start writing data
       
   208             sendRequest(); //recurse
   175         } else {
   209         } else {
   176             state = QHttpNetworkConnectionChannel::WaitingState;
   210             state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
   177             sendRequest();
   211             sendRequest(); //recurse
   178             break;
   212         }
   179         }
   213 
   180         // write the initial chunk together with the headers
   214         break;
   181         // fall through
       
   182     }
   215     }
   183     case QHttpNetworkConnectionChannel::WritingState:
   216     case QHttpNetworkConnectionChannel::WritingState:
   184     {
   217     {
   185         // write the data
   218         // write the data
   186         QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
   219         QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
   187         if (!uploadByteDevice || bytesTotal == written) {
   220         if (!uploadByteDevice || bytesTotal == written) {
   188             if (uploadByteDevice)
   221             if (uploadByteDevice)
   189                 emit reply->dataSendProgress(written, bytesTotal);
   222                 emit reply->dataSendProgress(written, bytesTotal);
   190             state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
   223             state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
   191             sendRequest();
   224             sendRequest(); // recurse
   192             break;
   225             break;
   193         }
   226         }
   194 
   227 
   195         // only feed the QTcpSocket buffer when there is less than 32 kB in it
   228         // only feed the QTcpSocket buffer when there is less than 32 kB in it
   196         const qint64 socketBufferFill = 32*1024;
   229         const qint64 socketBufferFill = 32*1024;
   256         socket->flush();
   289         socket->flush();
   257 
   290 
   258         // ensure we try to receive a reply in all cases, even if _q_readyRead_ hat not been called
   291         // ensure we try to receive a reply in all cases, even if _q_readyRead_ hat not been called
   259         // this is needed if the sends an reply before we have finished sending the request. In that
   292         // this is needed if the sends an reply before we have finished sending the request. In that
   260         // case receiveReply had been called before but ignored the server reply
   293         // case receiveReply had been called before but ignored the server reply
   261         receiveReply();
   294         QMetaObject::invokeMethod(this, "_q_receiveReply", Qt::QueuedConnection);
   262         break;
   295         break;
   263     }
   296     }
   264     case QHttpNetworkConnectionChannel::ReadingState:
   297     case QHttpNetworkConnectionChannel::ReadingState:
   265     case QHttpNetworkConnectionChannel::Wait4AuthState:
   298     case QHttpNetworkConnectionChannel::Wait4AuthState:
   266         // ignore _q_bytesWritten in these states
   299         // ignore _q_bytesWritten in these states
   270     }
   303     }
   271     return true;
   304     return true;
   272 }
   305 }
   273 
   306 
   274 
   307 
   275 void QHttpNetworkConnectionChannel::receiveReply()
   308 void QHttpNetworkConnectionChannel::_q_receiveReply()
   276 {
   309 {
   277     Q_ASSERT(socket);
   310     Q_ASSERT(socket);
       
   311 
       
   312     if (!reply) {
       
   313         // heh, how should that happen!
       
   314         qWarning() << "QHttpNetworkConnectionChannel::_q_receiveReply() called without QHttpNetworkReply,"
       
   315                 << socket->bytesAvailable() << "bytes on socket.";
       
   316         close();
       
   317         return;
       
   318     }
   278 
   319 
   279     qint64 bytes = 0;
   320     qint64 bytes = 0;
   280     QAbstractSocket::SocketState socketState = socket->state();
   321     QAbstractSocket::SocketState socketState = socket->state();
   281 
   322 
   282     // connection might be closed to signal the end of data
   323     // connection might be closed to signal the end of data
   283     if (socketState == QAbstractSocket::UnconnectedState) {
   324     if (socketState == QAbstractSocket::UnconnectedState) {
   284         if (!socket->bytesAvailable()) {
   325         if (socket->bytesAvailable() <= 0) {
   285             if (reply && reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) {
   326             if (reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) {
       
   327                 // finish this reply. this case happens when the server did not send a content length
   286                 reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState;
   328                 reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState;
   287                 this->state = QHttpNetworkConnectionChannel::IdleState;
       
   288                 allDone();
   329                 allDone();
       
   330                 return;
   289             } else {
   331             } else {
   290                 // try to reconnect/resend before sending an error.
   332                 handleUnexpectedEOF();
   291                 if (reconnectAttempts-- > 0) {
   333                 return;
   292                     closeAndResendCurrentRequest();
   334             }
   293                 } else if (reply) {
   335         } else {
   294                     reply->d_func()->errorString = connection->d_func()->errorDetail(QNetworkReply::RemoteHostClosedError, socket);
   336             // socket not connected but still bytes for reading.. just continue in this function
   295                     emit reply->finishedWithError(QNetworkReply::RemoteHostClosedError, reply->d_func()->errorString);
       
   296                     QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
       
   297                 }
       
   298             }
       
   299         }
   337         }
   300     }
   338     }
   301 
   339 
   302     // read loop for the response
   340     // read loop for the response
   303     while (socket->bytesAvailable()) {
   341     while (socket->bytesAvailable()) {
   304         QHttpNetworkReplyPrivate::ReplyState state = reply ? reply->d_func()->state : QHttpNetworkReplyPrivate::AllDoneState;
   342         QHttpNetworkReplyPrivate::ReplyState state = reply->d_func()->state;
   305         switch (state) {
   343         switch (state) {
   306         case QHttpNetworkReplyPrivate::NothingDoneState:
   344         case QHttpNetworkReplyPrivate::NothingDoneState: {
       
   345             // only eat whitespace on the first call
       
   346             eatWhitespace();
       
   347             state = reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState;
       
   348             // fallthrough
       
   349         }
   307         case QHttpNetworkReplyPrivate::ReadingStatusState: {
   350         case QHttpNetworkReplyPrivate::ReadingStatusState: {
   308             eatWhitespace();
       
   309             qint64 statusBytes = reply->d_func()->readStatus(socket);
   351             qint64 statusBytes = reply->d_func()->readStatus(socket);
   310             if (statusBytes == -1 && reconnectAttempts <= 0) {
   352             if (statusBytes == -1) {
   311                 // too many errors reading/receiving/parsing the status, close the socket and emit error
   353                 // connection broke while reading status. also handled if later _q_disconnected is called
   312                 close();
   354                 handleUnexpectedEOF();
   313                 reply->d_func()->errorString = connection->d_func()->errorDetail(QNetworkReply::ProtocolFailure, socket);
   355                 return;
   314                 emit reply->finishedWithError(QNetworkReply::ProtocolFailure, reply->d_func()->errorString);
       
   315                 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
       
   316                 break;
       
   317             } else if (statusBytes == -1) {
       
   318                 reconnectAttempts--;
       
   319                 reply->d_func()->clear();
       
   320                 closeAndResendCurrentRequest();
       
   321                 break;
       
   322             }
   356             }
   323             bytes += statusBytes;
   357             bytes += statusBytes;
   324             lastStatus = reply->d_func()->statusCode;
   358             lastStatus = reply->d_func()->statusCode;
   325             break;
   359             break;
   326         }
   360         }
   327         case QHttpNetworkReplyPrivate::ReadingHeaderState:
   361         case QHttpNetworkReplyPrivate::ReadingHeaderState: {
   328             bytes += reply->d_func()->readHeader(socket);
   362             QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
   329             if (reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) {
   363             qint64 headerBytes = replyPrivate->readHeader(socket);
   330                 if (reply->d_func()->isGzipped() && reply->d_func()->autoDecompress) {
   364             if (headerBytes == -1) {
       
   365                 // connection broke while reading headers. also handled if later _q_disconnected is called
       
   366                 handleUnexpectedEOF();
       
   367                 return;
       
   368             }
       
   369             bytes += headerBytes;
       
   370             if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState) {
       
   371                 if (replyPrivate->isGzipped() && replyPrivate->autoDecompress) {
   331                     // remove the Content-Length from header
   372                     // remove the Content-Length from header
   332                     reply->d_func()->removeAutoDecompressHeader();
   373                     replyPrivate->removeAutoDecompressHeader();
   333                 } else {
   374                 } else {
   334                     reply->d_func()->autoDecompress = false;
   375                     replyPrivate->autoDecompress = false;
   335                 }
   376                 }
   336                 if (reply && reply->d_func()->statusCode == 100) {
   377                 if (replyPrivate->statusCode == 100) {
   337                     reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState;
   378                     replyPrivate->clearHttpLayerInformation();
       
   379                     replyPrivate->state = QHttpNetworkReplyPrivate::ReadingStatusState;
   338                     break; // ignore
   380                     break; // ignore
   339                 }
   381                 }
   340                 if (reply->d_func()->shouldEmitSignals())
   382                 if (replyPrivate->shouldEmitSignals())
   341                     emit reply->headerChanged();
   383                     emit reply->headerChanged();
   342                 if (!reply->d_func()->expectContent()) {
   384                 if (!replyPrivate->expectContent()) {
   343                     reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState;
   385                     replyPrivate->state = QHttpNetworkReplyPrivate::AllDoneState;
   344                     this->state = QHttpNetworkConnectionChannel::IdleState;
       
   345                     allDone();
   386                     allDone();
   346                     return;
   387                     return;
   347                 }
   388                 }
   348             }
   389             }
   349             break;
   390             break;
       
   391         }
   350         case QHttpNetworkReplyPrivate::ReadingDataState: {
   392         case QHttpNetworkReplyPrivate::ReadingDataState: {
   351             if (!reply->d_func()->isChunked() && !reply->d_func()->autoDecompress
   393            QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
   352                 && reply->d_func()->bodyLength > 0) {
   394            if (replyPrivate->downstreamLimited && !replyPrivate->responseData.isEmpty() && replyPrivate->shouldEmitSignals()) {
       
   395                // We already have some HTTP body data. We don't read more from the socket until
       
   396                // this is fetched by QHttpNetworkAccessHttpBackend. If we would read more,
       
   397                // we could not limit our read buffer usage.
       
   398                // We only do this when shouldEmitSignals==true because our HTTP parsing
       
   399                // always needs to parse the 401/407 replies. Therefore they don't really obey
       
   400                // to the read buffer maximum size, but we don't care since they should be small.
       
   401                return;
       
   402            }
       
   403 
       
   404             if (!replyPrivate->isChunked() && !replyPrivate->autoDecompress
       
   405                 && replyPrivate->bodyLength > 0) {
   353                 // bulk files like images should fulfill these properties and
   406                 // bulk files like images should fulfill these properties and
   354                 // we can therefore save on memory copying
   407                 // we can therefore save on memory copying
   355                 bytes = reply->d_func()->readBodyFast(socket, &reply->d_func()->responseData);
   408                 bytes = replyPrivate->readBodyFast(socket, &replyPrivate->responseData);
   356                 reply->d_func()->totalProgress += bytes;
   409                 replyPrivate->totalProgress += bytes;
   357                 if (reply->d_func()->shouldEmitSignals()) {
   410                 if (replyPrivate->shouldEmitSignals()) {
   358                     QPointer<QHttpNetworkReply> replyPointer = reply;
   411                     QPointer<QHttpNetworkReply> replyPointer = reply;
   359                     emit reply->readyRead();
   412                     emit reply->readyRead();
   360                     // make sure that the reply is valid
   413                     // make sure that the reply is valid
   361                     if (replyPointer.isNull())
   414                     if (replyPointer.isNull())
   362                         return;
   415                         return;
   363                     emit reply->dataReadProgress(reply->d_func()->totalProgress, reply->d_func()->bodyLength);
   416                     emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
   364                     // make sure that the reply is valid
   417                     // make sure that the reply is valid
   365                     if (replyPointer.isNull())
   418                     if (replyPointer.isNull())
   366                         return;
   419                         return;
   367                 }
   420                 }
   368             }
   421             }
   369             else
   422             else
   370             {
   423             {
   371                 // use the traditional slower reading (for compressed encoding, chunked encoding,
   424                 // use the traditional slower reading (for compressed encoding, chunked encoding,
   372                 // no content-length etc)
   425                 // no content-length etc)
   373                 QByteDataBuffer byteDatas;
   426                 QByteDataBuffer byteDatas;
   374                 bytes = reply->d_func()->readBody(socket, &byteDatas);
   427                 bytes = replyPrivate->readBody(socket, &byteDatas);
   375                 if (bytes) {
   428                 if (bytes) {
   376                     if (reply->d_func()->autoDecompress)
   429                     if (replyPrivate->autoDecompress)
   377                         reply->d_func()->appendCompressedReplyData(byteDatas);
   430                         replyPrivate->appendCompressedReplyData(byteDatas);
   378                     else
   431                     else
   379                         reply->d_func()->appendUncompressedReplyData(byteDatas);
   432                         replyPrivate->appendUncompressedReplyData(byteDatas);
   380 
   433 
   381                     if (!reply->d_func()->autoDecompress) {
   434                     if (!replyPrivate->autoDecompress) {
   382                         reply->d_func()->totalProgress += bytes;
   435                         replyPrivate->totalProgress += bytes;
   383                         if (reply->d_func()->shouldEmitSignals()) {
   436                         if (replyPrivate->shouldEmitSignals()) {
   384                             QPointer<QHttpNetworkReply> replyPointer = reply;
   437                             QPointer<QHttpNetworkReply> replyPointer = reply;
   385                             // important: At the point of this readyRead(), the byteDatas list must be empty,
   438                             // important: At the point of this readyRead(), the byteDatas list must be empty,
   386                             // else implicit sharing will trigger memcpy when the user is reading data!
   439                             // else implicit sharing will trigger memcpy when the user is reading data!
   387                             emit reply->readyRead();
   440                             emit reply->readyRead();
   388                             // make sure that the reply is valid
   441                             // make sure that the reply is valid
   389                             if (replyPointer.isNull())
   442                             if (replyPointer.isNull())
   390                                 return;
   443                                 return;
   391                             emit reply->dataReadProgress(reply->d_func()->totalProgress, reply->d_func()->bodyLength);
   444                             emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
   392                             // make sure that the reply is valid
   445                             // make sure that the reply is valid
   393                            if (replyPointer.isNull())
   446                            if (replyPointer.isNull())
   394                                 return;
   447                                 return;
   395                         }
   448                         }
   396                     }
   449                     }
   399                         return; // ### expand failed
   452                         return; // ### expand failed
   400                     }
   453                     }
   401 #endif
   454 #endif
   402                 }
   455                 }
   403             }
   456             }
   404             if (reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState)
   457             // still in ReadingDataState? This function will be called again by the socket's readyRead
       
   458             if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState)
   405                 break;
   459                 break;
       
   460 
   406             // everything done, fall through
   461             // everything done, fall through
   407             }
   462             }
   408       case QHttpNetworkReplyPrivate::AllDoneState:
   463       case QHttpNetworkReplyPrivate::AllDoneState:
   409             this->state = QHttpNetworkConnectionChannel::IdleState;
       
   410             allDone();
   464             allDone();
   411             break;
   465             break;
   412         default:
   466         default:
   413             break;
   467             break;
   414         }
   468         }
   415     }
   469     }
   416 }
   470 }
   417 
   471 
       
   472 // called when unexpectedly reading a -1 or when data is expected but socket is closed
       
   473 void QHttpNetworkConnectionChannel::handleUnexpectedEOF()
       
   474 {
       
   475     if (reconnectAttempts <= 0) {
       
   476         // too many errors reading/receiving/parsing the status, close the socket and emit error
       
   477         requeueCurrentlyPipelinedRequests();
       
   478         close();
       
   479         reply->d_func()->errorString = connection->d_func()->errorDetail(QNetworkReply::RemoteHostClosedError, socket);
       
   480         emit reply->finishedWithError(QNetworkReply::RemoteHostClosedError, reply->d_func()->errorString);
       
   481         QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
       
   482     } else {
       
   483         reconnectAttempts--;
       
   484         reply->d_func()->clear();
       
   485         reply->d_func()->connection = connection;
       
   486         reply->d_func()->connectionChannel = this;
       
   487         closeAndResendCurrentRequest();
       
   488     }
       
   489 }
       
   490 
   418 bool QHttpNetworkConnectionChannel::ensureConnection()
   491 bool QHttpNetworkConnectionChannel::ensureConnection()
   419 {
   492 {
       
   493     QAbstractSocket::SocketState socketState = socket->state();
       
   494 
       
   495     // resend this request after we receive the disconnected signal
       
   496     if (socketState == QAbstractSocket::ClosingState) {
       
   497         resendCurrent = true;
       
   498         return false;
       
   499     }
       
   500 
       
   501     // already trying to connect?
       
   502     if (socketState == QAbstractSocket::HostLookupState ||
       
   503         socketState == QAbstractSocket::ConnectingState) {
       
   504         return false;
       
   505     }
       
   506 
   420     // make sure that this socket is in a connected state, if not initiate
   507     // make sure that this socket is in a connected state, if not initiate
   421     // connection to the host.
   508     // connection to the host.
   422     if (socket->state() != QAbstractSocket::ConnectedState) {
   509     if (socketState != QAbstractSocket::ConnectedState) {
   423         // connect to the host if not already connected.
   510         // connect to the host if not already connected.
   424         // resend this request after we receive the disconnected signal
       
   425         if (socket->state() == QAbstractSocket::ClosingState) {
       
   426             resendCurrent = true;
       
   427             return false;
       
   428         }
       
   429         state = QHttpNetworkConnectionChannel::ConnectingState;
   511         state = QHttpNetworkConnectionChannel::ConnectingState;
   430         pendingEncrypt = connection->d_func()->encrypt;
   512         pendingEncrypt = connection->d_func()->encrypt;
   431 
   513 
   432         // reset state
   514         // reset state
   433         pipeliningSupported = PipeliningSupportUnknown;
   515         pipeliningSupported = PipeliningSupportUnknown;
   530     // while handling 401 & 407, we might reset the status code, so save this.
   612     // while handling 401 & 407, we might reset the status code, so save this.
   531     bool emitFinished = reply->d_func()->shouldEmitSignals();
   613     bool emitFinished = reply->d_func()->shouldEmitSignals();
   532     handleStatus();
   614     handleStatus();
   533     // ### at this point there should be no more data on the socket
   615     // ### at this point there should be no more data on the socket
   534     // close if server requested
   616     // close if server requested
   535     if (reply->d_func()->isConnectionCloseEnabled())
   617     bool connectionCloseEnabled = reply->d_func()->isConnectionCloseEnabled();
       
   618     if (connectionCloseEnabled)
   536         close();
   619         close();
   537     // queue the finished signal, this is required since we might send new requests from
   620     // queue the finished signal, this is required since we might send new requests from
   538     // slot connected to it. The socket will not fire readyRead signal, if we are already
   621     // slot connected to it. The socket will not fire readyRead signal, if we are already
   539     // in the slot connected to readyRead
   622     // in the slot connected to readyRead
   540     if (emitFinished)
   623     if (emitFinished)
   543     // in case of failures, each channel will attempt two reconnects before emitting error.
   626     // in case of failures, each channel will attempt two reconnects before emitting error.
   544     reconnectAttempts = 2;
   627     reconnectAttempts = 2;
   545 
   628 
   546     detectPipeliningSupport();
   629     detectPipeliningSupport();
   547 
   630 
       
   631     // now the channel can be seen as free/idle again, all signal emissions for the reply have been done
       
   632     this->state = QHttpNetworkConnectionChannel::IdleState;
       
   633 
       
   634     // if it does not need to be sent again we can set it to 0
       
   635     // the previous code did not do that and we had problems with accidental re-sending of a
       
   636     // finished request.
       
   637     // Note that this may trigger a segfault at some other point. But then we can fix the underlying
       
   638     // problem.
       
   639     if (!resendCurrent)
       
   640         reply = 0;
       
   641 
   548     // move next from pipeline to current request
   642     // move next from pipeline to current request
   549     if (!alreadyPipelinedRequests.isEmpty()) {
   643     if (!alreadyPipelinedRequests.isEmpty()) {
   550         if (resendCurrent || reply->d_func()->isConnectionCloseEnabled() || socket->state() != QAbstractSocket::ConnectedState) {
   644         if (resendCurrent || connectionCloseEnabled || socket->state() != QAbstractSocket::ConnectedState) {
   551             // move the pipelined ones back to the main queue
   645             // move the pipelined ones back to the main queue
   552             requeueCurrentlyPipelinedRequests();
   646             requeueCurrentlyPipelinedRequests();
   553             close();
   647             close();
   554         } else {
   648         } else {
   555             // there were requests pipelined in and we can continue
   649             // there were requests pipelined in and we can continue
   565 
   659 
   566             // pipeline even more
   660             // pipeline even more
   567             connection->d_func()->fillPipeline(socket);
   661             connection->d_func()->fillPipeline(socket);
   568 
   662 
   569             // continue reading
   663             // continue reading
   570             receiveReply();
   664             _q_receiveReply();
   571         }
   665         }
   572     } else if (alreadyPipelinedRequests.isEmpty() && socket->bytesAvailable() > 0) {
   666     } else if (alreadyPipelinedRequests.isEmpty() && socket->bytesAvailable() > 0) {
   573         eatWhitespace();
   667         eatWhitespace();
   574         // this is weird. we had nothing pipelined but still bytes available. better close it.
   668         // this is weird. we had nothing pipelined but still bytes available. better close it.
   575         if (socket->bytesAvailable() > 0)
   669         if (socket->bytesAvailable() > 0)
   581 }
   675 }
   582 
   676 
   583 void QHttpNetworkConnectionChannel::detectPipeliningSupport()
   677 void QHttpNetworkConnectionChannel::detectPipeliningSupport()
   584 {
   678 {
   585     // detect HTTP Pipelining support
   679     // detect HTTP Pipelining support
   586     QByteArray serverHeaderField = reply->headerField("Server");
   680     QByteArray serverHeaderField;
   587     if (
   681     if (
   588             // check for broken servers in server reply header
       
   589             // this is adapted from http://mxr.mozilla.org/firefox/ident?i=SupportsPipelining
       
   590             (!serverHeaderField.contains("Microsoft-IIS/4."))
       
   591             && (!serverHeaderField.contains("Microsoft-IIS/5."))
       
   592             && (!serverHeaderField.contains("Netscape-Enterprise/3."))
       
   593             // check for HTTP/1.1
   682             // check for HTTP/1.1
   594             && (reply->d_func()->majorVersion == 1 && reply->d_func()->minorVersion == 1)
   683             (reply->d_func()->majorVersion == 1 && reply->d_func()->minorVersion == 1)
   595             // check for not having connection close
   684             // check for not having connection close
   596             && (!reply->d_func()->isConnectionCloseEnabled())
   685             && (!reply->d_func()->isConnectionCloseEnabled())
   597             // check if it is still connected
   686             // check if it is still connected
   598             && (socket->state() == QAbstractSocket::ConnectedState)
   687             && (socket->state() == QAbstractSocket::ConnectedState)
       
   688             // check for broken servers in server reply header
       
   689             // this is adapted from http://mxr.mozilla.org/firefox/ident?i=SupportsPipelining
       
   690             && (serverHeaderField = reply->headerField("Server"), !serverHeaderField.contains("Microsoft-IIS/4."))
       
   691             && (!serverHeaderField.contains("Microsoft-IIS/5."))
       
   692             && (!serverHeaderField.contains("Netscape-Enterprise/3."))
   599             ) {
   693             ) {
   600         pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningProbablySupported;
   694         pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningProbablySupported;
   601     } else {
   695     } else {
   602         pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown;
   696         pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown;
   603     }
   697     }
   608 {
   702 {
   609     for (int i = 0; i < alreadyPipelinedRequests.length(); i++)
   703     for (int i = 0; i < alreadyPipelinedRequests.length(); i++)
   610         connection->d_func()->requeueRequest(alreadyPipelinedRequests.at(i));
   704         connection->d_func()->requeueRequest(alreadyPipelinedRequests.at(i));
   611     alreadyPipelinedRequests.clear();
   705     alreadyPipelinedRequests.clear();
   612 
   706 
   613     QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
   707     // only run when the QHttpNetworkConnection is not currently being destructed, e.g.
       
   708     // this function is called from _q_disconnected which is called because
       
   709     // of ~QHttpNetworkConnectionPrivate
       
   710     if (qobject_cast<QHttpNetworkConnection*>(connection))
       
   711         QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
   614 }
   712 }
   615 
   713 
   616 void QHttpNetworkConnectionChannel::eatWhitespace()
   714 void QHttpNetworkConnectionChannel::eatWhitespace()
   617 {
   715 {
   618     char c;
   716     char c;
   619     while (socket->bytesAvailable()) {
   717     do {
   620         if (socket->peek(&c, 1) != 1)
   718         qint64 ret = socket->peek(&c, 1);
       
   719 
       
   720         // nothing read, fine.
       
   721         if (ret == 0)
   621             return;
   722             return;
       
   723 
       
   724         // EOF from socket?
       
   725         if (ret == -1)
       
   726             return; // FIXME, we need to stop processing. however the next stuff done will also do that.
   622 
   727 
   623         // read all whitespace and line endings
   728         // read all whitespace and line endings
   624         if (c == 11 || c == '\n' || c == '\r' || c == ' ' || c == 31) {
   729         if (c == 11 || c == '\n' || c == '\r' || c == ' ' || c == 31) {
   625             socket->read(&c, 1);
   730             socket->read(&c, 1);
   626             continue;
   731             continue;
   627         } else {
   732         } else {
   628             break;
   733             break;
   629         }
   734         }
   630     }
   735     } while(true);
   631 }
   736 }
   632 
   737 
   633 void QHttpNetworkConnectionChannel::handleStatus()
   738 void QHttpNetworkConnectionChannel::handleStatus()
   634 {
   739 {
   635     Q_ASSERT(socket);
   740     Q_ASSERT(socket);
   641     switch (statusCode) {
   746     switch (statusCode) {
   642     case 401: // auth required
   747     case 401: // auth required
   643     case 407: // proxy auth required
   748     case 407: // proxy auth required
   644         if (connection->d_func()->handleAuthenticateChallenge(socket, reply, (statusCode == 407), resend)) {
   749         if (connection->d_func()->handleAuthenticateChallenge(socket, reply, (statusCode == 407), resend)) {
   645             if (resend) {
   750             if (resend) {
   646                 QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
   751                 if (!resetUploadData())
   647                 if (uploadByteDevice) {
   752                     break;
   648                     if (uploadByteDevice->reset()) {
       
   649                         written = 0;
       
   650                     } else {
       
   651                         connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ContentReSendError);
       
   652                         break;
       
   653                     }
       
   654                 }
       
   655 
   753 
   656                 reply->d_func()->eraseData();
   754                 reply->d_func()->eraseData();
   657 
   755 
   658                 if (alreadyPipelinedRequests.isEmpty()) {
   756                 if (alreadyPipelinedRequests.isEmpty()) {
   659                     // this does a re-send without closing the connection
   757                     // this does a re-send without closing the connection
   679     default:
   777     default:
   680         QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
   778         QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
   681     }
   779     }
   682 }
   780 }
   683 
   781 
       
   782 bool QHttpNetworkConnectionChannel::resetUploadData()
       
   783 {
       
   784     QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
       
   785     if (!uploadByteDevice)
       
   786         return true;
       
   787 
       
   788     if (uploadByteDevice->reset()) {
       
   789         written = 0;
       
   790         return true;
       
   791     } else {
       
   792         connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ContentReSendError);
       
   793         return false;
       
   794     }
       
   795 }
       
   796 
       
   797 
   684 void  QHttpNetworkConnectionChannel::pipelineInto(HttpMessagePair &pair)
   798 void  QHttpNetworkConnectionChannel::pipelineInto(HttpMessagePair &pair)
   685 {
   799 {
   686     // this is only called for simple GET
   800     // this is only called for simple GET
   687 
   801 
   688     QHttpNetworkRequest &request = pair.first;
   802     QHttpNetworkRequest &request = pair.first;
   689     QHttpNetworkReply *reply = pair.second;
   803     QHttpNetworkReply *reply = pair.second;
   690     if (reply) {
   804     reply->d_func()->clear();
   691         reply->d_func()->clear();
   805     reply->d_func()->connection = connection;
   692         reply->d_func()->connection = connection;
   806     reply->d_func()->connectionChannel = this;
   693         reply->d_func()->autoDecompress = request.d->autoDecompress;
   807     reply->d_func()->autoDecompress = request.d->autoDecompress;
   694         reply->d_func()->pipeliningUsed = true;
   808     reply->d_func()->pipeliningUsed = true;
   695     }
       
   696 
   809 
   697 #ifndef QT_NO_NETWORKPROXY
   810 #ifndef QT_NO_NETWORKPROXY
   698     QByteArray header = QHttpNetworkRequestPrivate::header(request,
   811     QByteArray header = QHttpNetworkRequestPrivate::header(request,
   699                                                            (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy));
   812                                                            (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy));
   700 #else
   813 #else
   737 void QHttpNetworkConnectionChannel::_q_readyRead()
   850 void QHttpNetworkConnectionChannel::_q_readyRead()
   738 {
   851 {
   739     if (isSocketWaiting() || isSocketReading()) {
   852     if (isSocketWaiting() || isSocketReading()) {
   740         state = QHttpNetworkConnectionChannel::ReadingState;
   853         state = QHttpNetworkConnectionChannel::ReadingState;
   741         if (reply)
   854         if (reply)
   742             receiveReply();
   855             _q_receiveReply();
   743     }
   856     }
   744 }
   857 }
   745 
   858 
   746 void QHttpNetworkConnectionChannel::_q_bytesWritten(qint64 bytes)
   859 void QHttpNetworkConnectionChannel::_q_bytesWritten(qint64 bytes)
   747 {
   860 {
   754 
   867 
   755 void QHttpNetworkConnectionChannel::_q_disconnected()
   868 void QHttpNetworkConnectionChannel::_q_disconnected()
   756 {
   869 {
   757     // read the available data before closing
   870     // read the available data before closing
   758     if (isSocketWaiting() || isSocketReading()) {
   871     if (isSocketWaiting() || isSocketReading()) {
   759         state = QHttpNetworkConnectionChannel::ReadingState;
   872         if (reply) {
   760         if (reply)
   873             state = QHttpNetworkConnectionChannel::ReadingState;
   761             receiveReply();
   874             _q_receiveReply();
       
   875         }
   762     } else if (state == QHttpNetworkConnectionChannel::IdleState && resendCurrent) {
   876     } else if (state == QHttpNetworkConnectionChannel::IdleState && resendCurrent) {
   763         // re-sending request because the socket was in ClosingState
   877         // re-sending request because the socket was in ClosingState
   764         QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
   878         QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
   765     }
   879     }
   766     state = QHttpNetworkConnectionChannel::IdleState;
   880     state = QHttpNetworkConnectionChannel::IdleState;
   771 
   885 
   772 
   886 
   773 void QHttpNetworkConnectionChannel::_q_connected()
   887 void QHttpNetworkConnectionChannel::_q_connected()
   774 {
   888 {
   775     // improve performance since we get the request sent by the kernel ASAP
   889     // improve performance since we get the request sent by the kernel ASAP
   776     socket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
   890     //socket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
       
   891     // We have this commented out now. It did not have the effect we wanted. If we want to
       
   892     // do this properly, Qt has to combine multiple HTTP requests into one buffer
       
   893     // and send this to the kernel in one syscall and then the kernel immediately sends
       
   894     // it as one TCP packet because of TCP_NODELAY.
       
   895     // However, this code is currently not in Qt, so we rely on the kernel combining
       
   896     // the requests into one TCP packet.
       
   897 
   777     // not sure yet if it helps, but it makes sense
   898     // not sure yet if it helps, but it makes sense
   778     socket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
   899     socket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
   779 
   900 
   780     pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown;
   901     pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown;
   781 
   902 
   838     default:
   959     default:
   839         // all other errors are treated as NetworkError
   960         // all other errors are treated as NetworkError
   840         errorCode = QNetworkReply::UnknownNetworkError;
   961         errorCode = QNetworkReply::UnknownNetworkError;
   841         break;
   962         break;
   842     }
   963     }
   843     QPointer<QObject> that = connection;
   964     QPointer<QHttpNetworkConnection> that = connection;
   844     QString errorString = connection->d_func()->errorDetail(errorCode, socket);
   965     QString errorString = connection->d_func()->errorDetail(errorCode, socket, socket->errorString());
   845     if (send2Reply) {
   966     if (send2Reply) {
   846         if (reply) {
   967         if (reply) {
   847             reply->d_func()->errorString = errorString;
   968             reply->d_func()->errorString = errorString;
   848             // this error matters only to this reply
   969             // this error matters only to this reply
   849             emit reply->finishedWithError(errorCode, errorString);
   970             emit reply->finishedWithError(errorCode, errorString);
   893     // bytes have been written to the socket. write even more of them :)
  1014     // bytes have been written to the socket. write even more of them :)
   894     if (isSocketWriting())
  1015     if (isSocketWriting())
   895         sendRequest();
  1016         sendRequest();
   896     // otherwise we do nothing
  1017     // otherwise we do nothing
   897 }
  1018 }
   898 #endif
  1019 
       
  1020 #endif
       
  1021 
       
  1022 void QHttpNetworkConnectionChannel::setConnection(QHttpNetworkConnection *c)
       
  1023 {
       
  1024     // Inlining this function in the header leads to compiler error on
       
  1025     // release-armv5, on at least timebox 9.2 and 10.1.
       
  1026     connection = c;
       
  1027 }
   899 
  1028 
   900 QT_END_NAMESPACE
  1029 QT_END_NAMESPACE
   901 
  1030 
   902 #include "moc_qhttpnetworkconnectionchannel_p.cpp"
  1031 #include "moc_qhttpnetworkconnectionchannel_p.cpp"
   903 
  1032