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); } }
// 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(); } }
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(); }
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(); }
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; } } }