void tst_QHttpNetworkReply::parseEndOfHeader()
{
    QFETCH(QByteArray, headers);
    QFETCH(qint64, lengths);

    TestHeaderSocket socket(headers);

    TestHeaderReply reply;

    QHttpNetworkReplyPrivate *replyPrivate = reply.replyPrivate();
    qint64 headerBytes = replyPrivate->readHeader(&socket);
    QCOMPARE(headerBytes, lengths);
}
Beispiel #2
0
void QHttpNetworkConnectionChannel::_q_receiveReply()
{
    Q_ASSERT(socket);

    if (!reply) {
        // heh, how should that happen!
        qWarning() << "QHttpNetworkConnectionChannel::_q_receiveReply() called without QHttpNetworkReply,"
                << socket->bytesAvailable() << "bytes on socket.";
        close();
        return;
    }

    // only run when the QHttpNetworkConnection is not currently being destructed, e.g.
    // this function is called from _q_disconnected which is called because
    // of ~QHttpNetworkConnectionPrivate
    if (!qobject_cast<QHttpNetworkConnection*>(connection)) {
        return;
    }

    QAbstractSocket::SocketState socketState = socket->state();

    // connection might be closed to signal the end of data
    if (socketState == QAbstractSocket::UnconnectedState) {
        if (socket->bytesAvailable() <= 0) {
            if (reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) {
                // finish this reply. this case happens when the server did not send a content length
                reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState;
                allDone();
                return;
            } else {
                handleUnexpectedEOF();
                return;
            }
        } else {
            // socket not connected but still bytes for reading.. just continue in this function
        }
    }

    // read loop for the response
    qint64 bytes = 0;
    qint64 lastBytes = bytes;
    do {
        lastBytes = bytes;

        QHttpNetworkReplyPrivate::ReplyState state = reply->d_func()->state;
        switch (state) {
        case QHttpNetworkReplyPrivate::NothingDoneState: {
            state = reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState;
            // fallthrough
        }
        case QHttpNetworkReplyPrivate::ReadingStatusState: {
            qint64 statusBytes = reply->d_func()->readStatus(socket);
            if (statusBytes == -1) {
                // connection broke while reading status. also handled if later _q_disconnected is called
                handleUnexpectedEOF();
                return;
            }
            bytes += statusBytes;
            lastStatus = reply->d_func()->statusCode;
            break;
        }
        case QHttpNetworkReplyPrivate::ReadingHeaderState: {
            QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
            qint64 headerBytes = replyPrivate->readHeader(socket);
            if (headerBytes == -1) {
                // connection broke while reading headers. also handled if later _q_disconnected is called
                handleUnexpectedEOF();
                return;
            }
            bytes += headerBytes;
            // If headers were parsed successfully now it is the ReadingDataState
            if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState) {
                if (replyPrivate->isCompressed() && replyPrivate->autoDecompress) {
                    // remove the Content-Length from header
                    replyPrivate->removeAutoDecompressHeader();
                } else {
                    replyPrivate->autoDecompress = false;
                }
                if (replyPrivate->statusCode == 100) {
                    replyPrivate->clearHttpLayerInformation();
                    replyPrivate->state = QHttpNetworkReplyPrivate::ReadingStatusState;
                    break; // ignore
                }
                if (replyPrivate->shouldEmitSignals())
                    emit reply->headerChanged();
                // After headerChanged had been emitted
                // we can suddenly have a replyPrivate->userProvidedDownloadBuffer
                // this is handled in the ReadingDataState however

                if (!replyPrivate->expectContent()) {
                    replyPrivate->state = QHttpNetworkReplyPrivate::AllDoneState;
                    allDone();
                    break;
                }
            }
            break;
        }
        case QHttpNetworkReplyPrivate::ReadingDataState: {
           QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
           if (socket->state() == QAbstractSocket::ConnectedState &&
               replyPrivate->downstreamLimited && !replyPrivate->responseData.isEmpty() && replyPrivate->shouldEmitSignals()) {
               // (only do the following when still connected, not when we have already been disconnected and there is still data)
               // We already have some HTTP body data. We don't read more from the socket until
               // this is fetched by QHttpNetworkAccessHttpBackend. If we would read more,
               // we could not limit our read buffer usage.
               // We only do this when shouldEmitSignals==true because our HTTP parsing
               // always needs to parse the 401/407 replies. Therefore they don't really obey
               // to the read buffer maximum size, but we don't care since they should be small.
               return;
           }

           if (replyPrivate->userProvidedDownloadBuffer) {
               // the user provided a direct buffer where we should put all our data in.
               // this only works when we can tell the user the content length and he/she can allocate
               // the buffer in that size.
               // note that this call will read only from the still buffered data
               qint64 haveRead = replyPrivate->readBodyVeryFast(socket, replyPrivate->userProvidedDownloadBuffer + replyPrivate->totalProgress);
               if (haveRead > 0) {
                   bytes += haveRead;
                   replyPrivate->totalProgress += haveRead;
                   // the user will get notified of it via progress signal
                   emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
               } else if (haveRead == 0) {
                   // Happens since this called in a loop. Currently no bytes available.
               } else if (haveRead < 0) {
                   connection->d_func()->emitReplyError(socket, reply, QNetworkReply::RemoteHostClosedError);
                   break;
               }
           } else if (!replyPrivate->isChunked() && !replyPrivate->autoDecompress
                 && replyPrivate->bodyLength > 0) {
                 // bulk files like images should fulfill these properties and
                 // we can therefore save on memory copying
                qint64 haveRead = replyPrivate->readBodyFast(socket, &replyPrivate->responseData);
                bytes += haveRead;
                replyPrivate->totalProgress += haveRead;
                if (replyPrivate->shouldEmitSignals()) {
                    emit reply->readyRead();
                    emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
                }
            }
            else
            {
                // use the traditional slower reading (for compressed encoding, chunked encoding,
                // no content-length etc)
                qint64 haveRead = replyPrivate->readBody(socket, &replyPrivate->responseData);
                if (haveRead > 0) {
                    bytes += haveRead;
                    replyPrivate->totalProgress += haveRead;
                    if (replyPrivate->shouldEmitSignals()) {
                        emit reply->readyRead();
                        emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
                    }
                } else if (haveRead == -1) {
                    // Some error occured
                    connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ProtocolFailure);
                    break;
                }
            }
            // still in ReadingDataState? This function will be called again by the socket's readyRead
            if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState)
                break;

            // everything done, fall through
            }
      case QHttpNetworkReplyPrivate::AllDoneState:
            allDone();
            break;
        default:
            break;
        }
    } while (bytes != lastBytes && reply);
}
Beispiel #3
0
void QHttpNetworkConnectionChannel::_q_receiveReply()
{
    Q_ASSERT(socket);

    if (!reply) {
        // heh, how should that happen!
        qWarning() << "QHttpNetworkConnectionChannel::_q_receiveReply() called without QHttpNetworkReply,"
                << socket->bytesAvailable() << "bytes on socket.";
        close();
        return;
    }

    // only run when the QHttpNetworkConnection is not currently being destructed, e.g.
    // this function is called from _q_disconnected which is called because
    // of ~QHttpNetworkConnectionPrivate
    if (!qobject_cast<QHttpNetworkConnection*>(connection)) {
        return;
    }

    qint64 bytes = 0;
    QAbstractSocket::SocketState socketState = socket->state();

    // connection might be closed to signal the end of data
    if (socketState == QAbstractSocket::UnconnectedState) {
        if (socket->bytesAvailable() <= 0) {
            if (reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) {
                // finish this reply. this case happens when the server did not send a content length
                reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState;
                allDone();
                return;
            } else {
                handleUnexpectedEOF();
                return;
            }
        } else {
            // socket not connected but still bytes for reading.. just continue in this function
        }
    }

    // read loop for the response
    while (socket->bytesAvailable()) {
        QHttpNetworkReplyPrivate::ReplyState state = reply->d_func()->state;
        switch (state) {
        case QHttpNetworkReplyPrivate::NothingDoneState: {
            // only eat whitespace on the first call
            eatWhitespace();
            state = reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState;
            // fallthrough
        }
        case QHttpNetworkReplyPrivate::ReadingStatusState: {
            qint64 statusBytes = reply->d_func()->readStatus(socket);
            if (statusBytes == -1) {
                // connection broke while reading status. also handled if later _q_disconnected is called
                handleUnexpectedEOF();
                return;
            }
            bytes += statusBytes;
            lastStatus = reply->d_func()->statusCode;
            break;
        }
        case QHttpNetworkReplyPrivate::ReadingHeaderState: {
            QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
            qint64 headerBytes = replyPrivate->readHeader(socket);
            if (headerBytes == -1) {
                // connection broke while reading headers. also handled if later _q_disconnected is called
                handleUnexpectedEOF();
                return;
            }
            bytes += headerBytes;
            if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState) {
                if (replyPrivate->isGzipped() && replyPrivate->autoDecompress) {
                    // remove the Content-Length from header
                    replyPrivate->removeAutoDecompressHeader();
                } else {
                    replyPrivate->autoDecompress = false;
                }
                if (replyPrivate->statusCode == 100) {
                    replyPrivate->clearHttpLayerInformation();
                    replyPrivate->state = QHttpNetworkReplyPrivate::ReadingStatusState;
                    break; // ignore
                }
                if (replyPrivate->shouldEmitSignals())
                    emit reply->headerChanged();
                if (!replyPrivate->expectContent()) {
                    replyPrivate->state = QHttpNetworkReplyPrivate::AllDoneState;
                    allDone();
                    break;
                }
            }
            break;
        }
        case QHttpNetworkReplyPrivate::ReadingDataState: {
           QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
           if (replyPrivate->downstreamLimited && !replyPrivate->responseData.isEmpty() && replyPrivate->shouldEmitSignals()) {
               // We already have some HTTP body data. We don't read more from the socket until
               // this is fetched by QHttpNetworkAccessHttpBackend. If we would read more,
               // we could not limit our read buffer usage.
               // We only do this when shouldEmitSignals==true because our HTTP parsing
               // always needs to parse the 401/407 replies. Therefore they don't really obey
               // to the read buffer maximum size, but we don't care since they should be small.
               return;
           }

            if (!replyPrivate->isChunked() && !replyPrivate->autoDecompress
                && replyPrivate->bodyLength > 0) {
                // bulk files like images should fulfill these properties and
                // we can therefore save on memory copying
                bytes = replyPrivate->readBodyFast(socket, &replyPrivate->responseData);
                replyPrivate->totalProgress += bytes;
                if (replyPrivate->shouldEmitSignals()) {
                    QPointer<QHttpNetworkReply> replyPointer = reply;
                    emit reply->readyRead();
                    // make sure that the reply is valid
                    if (replyPointer.isNull())
                        return;
                    emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
                    // make sure that the reply is valid
                    if (replyPointer.isNull())
                        return;
                }
            }
            else
            {
                // use the traditional slower reading (for compressed encoding, chunked encoding,
                // no content-length etc)
                QByteDataBuffer byteDatas;
                bytes = replyPrivate->readBody(socket, &byteDatas);
                if (bytes) {
                    if (replyPrivate->autoDecompress)
                        replyPrivate->appendCompressedReplyData(byteDatas);
                    else
                        replyPrivate->appendUncompressedReplyData(byteDatas);

                    if (!replyPrivate->autoDecompress) {
                        replyPrivate->totalProgress += bytes;
                        if (replyPrivate->shouldEmitSignals()) {
                            QPointer<QHttpNetworkReply> replyPointer = reply;
                            // important: At the point of this readyRead(), the byteDatas list must be empty,
                            // else implicit sharing will trigger memcpy when the user is reading data!
                            emit reply->readyRead();
                            // make sure that the reply is valid
                            if (replyPointer.isNull())
                                return;
                            emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
                            // make sure that the reply is valid
                           if (replyPointer.isNull())
                                return;
                        }
                    }
#ifndef QT_NO_COMPRESS
                    else if (!expand(false)) { // expand a chunk if possible
                        return; // ### expand failed
                    }
#endif
                }
            }
            // still in ReadingDataState? This function will be called again by the socket's readyRead
            if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState)
                break;

            // everything done, fall through
            }
      case QHttpNetworkReplyPrivate::AllDoneState:
            allDone();
            break;
        default:
            break;
        }
    }
}
void QHttpNetworkConnectionChannel::_q_receiveReply()
{
    Q_ASSERT(socket);

    qint64 bytes = 0;
    QAbstractSocket::SocketState socketState = socket->state();

    // connection might be closed to signal the end of data
    if (socketState == QAbstractSocket::UnconnectedState) {
        if (!socket->bytesAvailable()) {
            if (reply && reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) {
                reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState;
                this->state = QHttpNetworkConnectionChannel::IdleState;
                allDone();
            } else {
                // try to reconnect/resend before sending an error.
                if (reconnectAttempts-- > 0) {
                    closeAndResendCurrentRequest();
                } else if (reply) {
                    reply->d_func()->errorString = connection->d_func()->errorDetail(QNetworkReply::RemoteHostClosedError, socket);
                    emit reply->finishedWithError(QNetworkReply::RemoteHostClosedError, reply->d_func()->errorString);
                    QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
                }
            }
        }
    }

    // read loop for the response
    while (socket->bytesAvailable()) {
        QHttpNetworkReplyPrivate::ReplyState state = reply ? reply->d_func()->state : QHttpNetworkReplyPrivate::AllDoneState;
        switch (state) {
        case QHttpNetworkReplyPrivate::NothingDoneState:
        case QHttpNetworkReplyPrivate::ReadingStatusState: {
            eatWhitespace();
            qint64 statusBytes = reply->d_func()->readStatus(socket);
            if (statusBytes == -1 && reconnectAttempts <= 0) {
                // too many errors reading/receiving/parsing the status, close the socket and emit error
                close();
                reply->d_func()->errorString = connection->d_func()->errorDetail(QNetworkReply::ProtocolFailure, socket);
                emit reply->finishedWithError(QNetworkReply::ProtocolFailure, reply->d_func()->errorString);
                QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
                break;
            } else if (statusBytes == -1) {
                reconnectAttempts--;
                reply->d_func()->clear();
                closeAndResendCurrentRequest();
                break;
            }
            bytes += statusBytes;
            lastStatus = reply->d_func()->statusCode;
            break;
        }
        case QHttpNetworkReplyPrivate::ReadingHeaderState: {
            QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
            bytes += replyPrivate->readHeader(socket);
            if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState) {
                if (replyPrivate->isGzipped() && replyPrivate->autoDecompress) {
                    // remove the Content-Length from header
                    replyPrivate->removeAutoDecompressHeader();
                } else {
                    replyPrivate->autoDecompress = false;
                }
                if (replyPrivate->statusCode == 100) {
                    replyPrivate->state = QHttpNetworkReplyPrivate::ReadingStatusState;
                    break; // ignore
                }
                if (replyPrivate->shouldEmitSignals())
                    emit reply->headerChanged();
                if (!replyPrivate->expectContent()) {
                    replyPrivate->state = QHttpNetworkReplyPrivate::AllDoneState;
                    this->state = QHttpNetworkConnectionChannel::IdleState;
                    allDone();
                    return;
                }
            }
            break;
        }
        case QHttpNetworkReplyPrivate::ReadingDataState: {
           QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
           if (replyPrivate->downstreamLimited && !replyPrivate->responseData.isEmpty() && replyPrivate->shouldEmitSignals()) {
               // We already have some HTTP body data. We don't read more from the socket until
               // this is fetched by QHttpNetworkAccessHttpBackend. If we would read more,
               // we could not limit our read buffer usage.
               // We only do this when shouldEmitSignals==true because our HTTP parsing
               // always needs to parse the 401/407 replies. Therefore they don't really obey
               // to the read buffer maximum size, but we don't care since they should be small.
               return;
           }

            if (!replyPrivate->isChunked() && !replyPrivate->autoDecompress
                && replyPrivate->bodyLength > 0) {
                // bulk files like images should fulfill these properties and
                // we can therefore save on memory copying
                bytes = replyPrivate->readBodyFast(socket, &replyPrivate->responseData);
                replyPrivate->totalProgress += bytes;
                if (replyPrivate->shouldEmitSignals()) {
                    QPointer<QHttpNetworkReply> replyPointer = reply;
                    emit reply->readyRead();
                    // make sure that the reply is valid
                    if (replyPointer.isNull())
                        return;
                    emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
                    // make sure that the reply is valid
                    if (replyPointer.isNull())
                        return;
                }
            }
            else
            {
                // use the traditional slower reading (for compressed encoding, chunked encoding,
                // no content-length etc)
                QByteDataBuffer byteDatas;
                bytes = replyPrivate->readBody(socket, &byteDatas);
                if (bytes) {
                    if (replyPrivate->autoDecompress)
                        replyPrivate->appendCompressedReplyData(byteDatas);
                    else
                        replyPrivate->appendUncompressedReplyData(byteDatas);

                    if (!replyPrivate->autoDecompress) {
                        replyPrivate->totalProgress += bytes;
                        if (replyPrivate->shouldEmitSignals()) {
                            QPointer<QHttpNetworkReply> replyPointer = reply;
                            // important: At the point of this readyRead(), the byteDatas list must be empty,
                            // else implicit sharing will trigger memcpy when the user is reading data!
                            emit reply->readyRead();
                            // make sure that the reply is valid
                            if (replyPointer.isNull())
                                return;
                            emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
                            // make sure that the reply is valid
                           if (replyPointer.isNull())
                                return;
                        }
                    }
#ifndef QT_NO_COMPRESS
                    else if (!expand(false)) { // expand a chunk if possible
                        return; // ### expand failed
                    }
#endif
                }
            }
            if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState)
                break;
            // everything done, fall through
            }
      case QHttpNetworkReplyPrivate::AllDoneState:
            this->state = QHttpNetworkConnectionChannel::IdleState;
            allDone();
            break;
        default:
            break;
        }
    }
}