void QHttpNetworkConnectionChannel::handleStatus()
{
    Q_ASSERT(socket);
    Q_ASSERT(reply);

    int statusCode = reply->statusCode();
    bool resend = false;

    switch (statusCode) {
    case 401: // auth required
    case 407: // proxy auth required
        if (connection->d_func()->handleAuthenticateChallenge(socket, reply, (statusCode == 407), resend)) {
            if (resend) {
                QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
                if (uploadByteDevice) {
                    if (uploadByteDevice->reset()) {
                        written = 0;
                    } else {
                        connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ContentReSendError);
                        break;
                    }
                }

                reply->d_func()->eraseData();

                if (alreadyPipelinedRequests.isEmpty()) {
                    // this does a re-send without closing the connection
                    resendCurrent = true;
                    QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
                } else {
                    // we had requests pipelined.. better close the connection in closeAndResendCurrentRequest
                    closeAndResendCurrentRequest();
                    QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
                }
            }
        } else {
            emit reply->headerChanged();
            emit reply->readyRead();
            QNetworkReply::NetworkError errorCode = (statusCode == 407)
                ? QNetworkReply::ProxyAuthenticationRequiredError
                : QNetworkReply::AuthenticationRequiredError;
            reply->d_func()->errorString = connection->d_func()->errorDetail(errorCode, socket);
            emit connection->error(errorCode, reply->d_func()->errorString);
            emit reply->finished();
        }
        break;
    default:
        QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
    }
}
コード例 #2
0
// called when unexpectedly reading a -1 or when data is expected but socket is closed
void QHttpNetworkConnectionChannel::handleUnexpectedEOF()
{
    if (reconnectAttempts <= 0) {
        // too many errors reading/receiving/parsing the status, close the socket and emit error
        requeueCurrentlyPipelinedRequests();
        close();
        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);
    } else {
        reconnectAttempts--;
        reply->d_func()->clear();
        reply->d_func()->connection = connection;
        reply->d_func()->connectionChannel = this;
        closeAndResendCurrentRequest();
    }
}
コード例 #3
0
void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socketError)
{
    if (!socket)
        return;
    QNetworkReply::NetworkError errorCode = QNetworkReply::UnknownNetworkError;

    switch (socketError) {
    case QAbstractSocket::HostNotFoundError:
        errorCode = QNetworkReply::HostNotFoundError;
        break;
    case QAbstractSocket::ConnectionRefusedError:
        errorCode = QNetworkReply::ConnectionRefusedError;
        break;
    case QAbstractSocket::RemoteHostClosedError:
        // try to reconnect/resend before sending an error.
        // while "Reading" the _q_disconnected() will handle this.
        if (state != QHttpNetworkConnectionChannel::IdleState && state != QHttpNetworkConnectionChannel::ReadingState) {
            if (reconnectAttempts-- > 0) {
                closeAndResendCurrentRequest();
                return;
            } else {
                errorCode = QNetworkReply::RemoteHostClosedError;
            }
        } else if (state == QHttpNetworkConnectionChannel::ReadingState) {
            if (!reply->d_func()->expectContent()) {
                // No content expected, this is a valid way to have the connection closed by the server
                return;
            }
            if (reply->contentLength() == -1 && !reply->d_func()->isChunked()) {
                // There was no content-length header and it's not chunked encoding,
                // so this is a valid way to have the connection closed by the server
                return;
            }
            // ok, we got a disconnect even though we did not expect it
            errorCode = QNetworkReply::RemoteHostClosedError;
        } else {
            errorCode = QNetworkReply::RemoteHostClosedError;
        }
        break;
    case QAbstractSocket::SocketTimeoutError:
        // try to reconnect/resend before sending an error.
        if (state == QHttpNetworkConnectionChannel::WritingState && (reconnectAttempts-- > 0)) {
            closeAndResendCurrentRequest();
            return;
        }
        errorCode = QNetworkReply::TimeoutError;
        break;
    case QAbstractSocket::ProxyAuthenticationRequiredError:
        errorCode = QNetworkReply::ProxyAuthenticationRequiredError;
        break;
    case QAbstractSocket::SslHandshakeFailedError:
        errorCode = QNetworkReply::SslHandshakeFailedError;
        break;
    default:
        // all other errors are treated as NetworkError
        errorCode = QNetworkReply::UnknownNetworkError;
        break;
    }
    QPointer<QHttpNetworkConnection> that = connection;
    QString errorString = connection->d_func()->errorDetail(errorCode, socket, socket->errorString());

    // In the InProgress state the channel should not emit the error.
    // This will instead be handled by the connection.
    if (!connection->d_func()->shouldEmitChannelError(socket))
        return;

    // Need to dequeu the request so that we can emit the error.
    if (!reply)
        connection->d_func()->dequeueRequest(socket);
    if (reply) {
        reply->d_func()->errorString = errorString;
        emit reply->finishedWithError(errorCode, errorString);
        reply = 0;
    }
    // send the next request
    QMetaObject::invokeMethod(that, "_q_startNextRequest", Qt::QueuedConnection);

    if (that) //signal emission triggered event loop
        close();
}
コード例 #4
0
void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socketError)
{
    if (!socket)
        return;
    QNetworkReply::NetworkError errorCode = QNetworkReply::UnknownNetworkError;

    switch (socketError) {
    case QAbstractSocket::HostNotFoundError:
        errorCode = QNetworkReply::HostNotFoundError;
        break;
    case QAbstractSocket::ConnectionRefusedError:
        errorCode = QNetworkReply::ConnectionRefusedError;
        break;
    case QAbstractSocket::RemoteHostClosedError:
        // try to reconnect/resend before sending an error.
        // while "Reading" the _q_disconnected() will handle this.
        if (state != QHttpNetworkConnectionChannel::IdleState && state != QHttpNetworkConnectionChannel::ReadingState) {
            if (reconnectAttempts-- > 0) {
                closeAndResendCurrentRequest();
                return;
            } else {
                errorCode = QNetworkReply::RemoteHostClosedError;
            }
        } else if (state == QHttpNetworkConnectionChannel::ReadingState) {
            if (!reply->d_func()->expectContent()) {
                // No content expected, this is a valid way to have the connection closed by the server
                return;
            }
            if (reply->contentLength() == -1 && !reply->d_func()->isChunked()) {
                // There was no content-length header and it's not chunked encoding,
                // so this is a valid way to have the connection closed by the server
                return;
            }
            // ok, we got a disconnect even though we did not expect it
            // Try to read everything from the socket before we emit the error.
            if (socket->bytesAvailable()) {
                // Read everything from the socket into the reply buffer.
                // we can ignore the readbuffersize as the data is already
                // in memory and we will not receive more data on the socket.
                reply->setReadBufferSize(0);
                _q_receiveReply();
#ifndef QT_NO_SSL
                if (ssl) {
                    // QT_NO_OPENSSL. The QSslSocket can still have encrypted bytes in the plainsocket.
                    // So we need to check this if the socket is a QSslSocket. When the socket is flushed
                    // it will force a decrypt of the encrypted data in the plainsocket.
                    QSslSocket *sslSocket = static_cast<QSslSocket*>(socket);
                    qint64 beforeFlush = sslSocket->encryptedBytesAvailable();
                    while (sslSocket->encryptedBytesAvailable()) {
                        sslSocket->flush();
                        _q_receiveReply();
                        qint64 afterFlush = sslSocket->encryptedBytesAvailable();
                        if (afterFlush == beforeFlush)
                            break;
                        beforeFlush = afterFlush;
                    }
                }
#endif
            }

            errorCode = QNetworkReply::RemoteHostClosedError;
        } else {
            errorCode = QNetworkReply::RemoteHostClosedError;
        }
        break;
    case QAbstractSocket::SocketTimeoutError:
        // try to reconnect/resend before sending an error.
        if (state == QHttpNetworkConnectionChannel::WritingState && (reconnectAttempts-- > 0)) {
            closeAndResendCurrentRequest();
            return;
        }
        errorCode = QNetworkReply::TimeoutError;
        break;
    case QAbstractSocket::ProxyAuthenticationRequiredError:
        errorCode = QNetworkReply::ProxyAuthenticationRequiredError;
        break;
    case QAbstractSocket::SslHandshakeFailedError:
        errorCode = QNetworkReply::SslHandshakeFailedError;
        break;
    default:
        // all other errors are treated as NetworkError
        errorCode = QNetworkReply::UnknownNetworkError;
        break;
    }
    QPointer<QHttpNetworkConnection> that = connection;
    QString errorString = connection->d_func()->errorDetail(errorCode, socket, socket->errorString());

    // In the InProgress state the channel should not emit the error.
    // This will instead be handled by the connection.
    if (!connection->d_func()->shouldEmitChannelError(socket))
        return;

    // Need to dequeu the request so that we can emit the error.
    if (!reply)
        connection->d_func()->dequeueRequest(socket);
    if (reply) {
        reply->d_func()->errorString = errorString;
        emit reply->finishedWithError(errorCode, errorString);
        reply = 0;
    }
    // send the next request
    QMetaObject::invokeMethod(that, "_q_startNextRequest", Qt::QueuedConnection);

    if (that) //signal emission triggered event loop
        close();
}
コード例 #5
0
void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socketError)
{
    if (!socket)
        return;
    bool send2Reply = false;
    QNetworkReply::NetworkError errorCode = QNetworkReply::UnknownNetworkError;

    switch (socketError) {
    case QAbstractSocket::HostNotFoundError:
        errorCode = QNetworkReply::HostNotFoundError;
        break;
    case QAbstractSocket::ConnectionRefusedError:
        errorCode = QNetworkReply::ConnectionRefusedError;
        break;
    case QAbstractSocket::RemoteHostClosedError:
        // try to reconnect/resend before sending an error.
        // while "Reading" the _q_disconnected() will handle this.
        if (state != QHttpNetworkConnectionChannel::IdleState && state != QHttpNetworkConnectionChannel::ReadingState) {
            if (reconnectAttempts-- > 0) {
                closeAndResendCurrentRequest();
                return;
            } else {
                send2Reply = true;
                errorCode = QNetworkReply::RemoteHostClosedError;
            }
        } else {
            return;
        }
        break;
    case QAbstractSocket::SocketTimeoutError:
        // try to reconnect/resend before sending an error.
        if (state == QHttpNetworkConnectionChannel::WritingState && (reconnectAttempts-- > 0)) {
            closeAndResendCurrentRequest();
            return;
        }
        send2Reply = true;
        errorCode = QNetworkReply::TimeoutError;
        break;
    case QAbstractSocket::ProxyAuthenticationRequiredError:
        errorCode = QNetworkReply::ProxyAuthenticationRequiredError;
        break;
    case QAbstractSocket::SslHandshakeFailedError:
        errorCode = QNetworkReply::SslHandshakeFailedError;
        break;
    default:
        // all other errors are treated as NetworkError
        errorCode = QNetworkReply::UnknownNetworkError;
        break;
    }
    QPointer<QHttpNetworkConnection> that = connection;
    QString errorString = connection->d_func()->errorDetail(errorCode, socket, socket->errorString());
    if (send2Reply) {
        if (reply) {
            reply->d_func()->errorString = errorString;
            // this error matters only to this reply
            emit reply->finishedWithError(errorCode, errorString);
        }
        // send the next request
        QMetaObject::invokeMethod(that, "_q_startNextRequest", Qt::QueuedConnection);
    } else {
        // the failure affects all requests.
        emit connection->error(errorCode, errorString);
    }
    if (that) //signal emission triggered event loop
        close();
}
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;
        }
    }
}