QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetworkRequest &request) { Q_Q(QHttpNetworkConnection); // The reply component of the pair is created initially. QHttpNetworkReply *reply = new QHttpNetworkReply(request.url()); reply->setRequest(request); reply->d_func()->connection = q; reply->d_func()->connectionChannel = &channels[0]; // will have the correct one set later HttpMessagePair pair = qMakePair(request, reply); switch (request.priority()) { case QHttpNetworkRequest::HighPriority: highPriorityQueue.prepend(pair); break; case QHttpNetworkRequest::NormalPriority: case QHttpNetworkRequest::LowPriority: lowPriorityQueue.prepend(pair); break; } // this used to be called via invokeMethod and a QueuedConnection // It is the only place _q_startNextRequest is called directly without going // through the event loop using a QueuedConnection. // This is dangerous because of recursion that might occur when emitting // signals as DirectConnection from this code path. Therefore all signal // emissions that can come out from this code path need to // be QueuedConnection. // We are currently trying to fine-tune this. _q_startNextRequest(); return reply; }
void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors) { if (!socket) return; //QNetworkReply::NetworkError errorCode = QNetworkReply::ProtocolFailure; // Also pause the connection because socket notifiers may fire while an user // dialog is displaying connection->d_func()->pauseConnection(); if (pendingEncrypt && !reply) connection->d_func()->dequeueRequest(socket); if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP) { if (reply) emit reply->sslErrors(errors); } #ifndef QT_NO_SSL else { // SPDY QList<HttpMessagePair> spdyPairs = spdyRequestsToSend.values(); for (int a = 0; a < spdyPairs.count(); ++a) { // emit SSL errors for all replies QHttpNetworkReply *currentReply = spdyPairs.at(a).second; Q_ASSERT(currentReply); emit currentReply->sslErrors(errors); } } #endif // QT_NO_SSL connection->d_func()->resumeConnection(); }
void QHttpNetworkConnectionChannel::emitFinishedWithError(QNetworkReply::NetworkError error, const char *message) { if (reply) emit reply->finishedWithError(error, QHttpNetworkConnectionChannel::tr(message)); QList<HttpMessagePair> spdyPairs = spdyRequestsToSend.values(); for (int a = 0; a < spdyPairs.count(); ++a) { QHttpNetworkReply *currentReply = spdyPairs.at(a).second; Q_ASSERT(currentReply); emit currentReply->finishedWithError(error, QHttpNetworkConnectionChannel::tr(message)); } }
void tst_QHttpNetworkReply::parseHeader() { QFETCH(QByteArray, headers); QFETCH(QStringList, fields); QFETCH(QStringList, values); QHttpNetworkReply reply; reply.parseHeader(headers); for (int i = 0; i < fields.count(); ++i) { //qDebug() << "field" << fields.at(i) << "value" << reply.headerField(fields.at(i)) << "expected" << values.at(i); QString field = reply.headerField(fields.at(i).toLatin1()); QCOMPARE(field, values.at(i)); } }
void QHttpNetworkConnectionChannel::pipelineInto(HttpMessagePair &pair) { // this is only called for simple GET QHttpNetworkRequest &request = pair.first; QHttpNetworkReply *reply = pair.second; reply->d_func()->clear(); reply->d_func()->connection = connection; reply->d_func()->connectionChannel = this; reply->d_func()->autoDecompress = request.d->autoDecompress; reply->d_func()->pipeliningUsed = true; #ifndef QT_NO_NETWORKPROXY QByteArray header = QHttpNetworkRequestPrivate::header(request, (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy)); #else QByteArray header = QHttpNetworkRequestPrivate::header(request, false); #endif socket->write(header); alreadyPipelinedRequests.append(pair); }
void QHttpNetworkConnectionChannel::pipelineInto(HttpMessagePair &pair) { // this is only called for simple GET QHttpNetworkRequest &request = pair.first; QHttpNetworkReply *reply = pair.second; reply->d_func()->clear(); reply->d_func()->connection = connection; reply->d_func()->connectionChannel = this; reply->d_func()->autoDecompress = request.d->autoDecompress; reply->d_func()->pipeliningUsed = true; #ifndef QT_NO_NETWORKPROXY pipeline.append(QHttpNetworkRequestPrivate::header(request, (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy))); #else pipeline.append(QHttpNetworkRequestPrivate::header(request, false)); #endif alreadyPipelinedRequests.append(pair); // pipelineFlush() needs to be called at some point afterwards }
void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair) { QHttpNetworkRequest &request = messagePair.first; QHttpNetworkReply *reply = messagePair.second; // add missing fields for the request QByteArray value; // check if Content-Length is provided QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice(); if (uploadByteDevice) { if (request.contentLength() != -1 && uploadByteDevice->size() != -1) { // both values known, take the smaller one. request.setContentLength(qMin(uploadByteDevice->size(), request.contentLength())); } else if (request.contentLength() == -1 && uploadByteDevice->size() != -1) { // content length not supplied by user, but the upload device knows it request.setContentLength(uploadByteDevice->size()); } else if (request.contentLength() != -1 && uploadByteDevice->size() == -1) { // everything OK, the user supplied us the contentLength } else if (request.contentLength() == -1 && uploadByteDevice->size() == -1) { qFatal("QHttpNetworkConnectionPrivate: Neither content-length nor upload device size were given"); } } // set the Connection/Proxy-Connection: Keep-Alive headers #ifndef QT_NO_NETWORKPROXY if (networkProxy.type() == QNetworkProxy::HttpCachingProxy) { value = request.headerField("proxy-connection"); if (value.isEmpty()) request.setHeaderField("Proxy-Connection", "Keep-Alive"); } else { #endif value = request.headerField("connection"); if (value.isEmpty()) request.setHeaderField("Connection", "Keep-Alive"); #ifndef QT_NO_NETWORKPROXY } #endif // If the request had a accept-encoding set, we better not mess // with it. If it was not set, we announce that we understand gzip // and remember this fact in request.d->autoDecompress so that // we can later decompress the HTTP reply if it has such an // encoding. value = request.headerField("accept-encoding"); if (value.isEmpty()) { #ifndef QT_NO_COMPRESS request.setHeaderField("Accept-Encoding", "gzip"); request.d->autoDecompress = true; #else // if zlib is not available set this to false always request.d->autoDecompress = false; #endif } // some websites mandate an accept-language header and fail // if it is not sent. This is a problem with the website and // not with us, but we work around this by setting // one always. value = request.headerField("accept-language"); if (value.isEmpty()) { QString systemLocale = QLocale::system().name().replace(QChar::fromAscii('_'),QChar::fromAscii('-')); QString acceptLanguage; if (systemLocale == QLatin1String("C")) acceptLanguage = QString::fromAscii("en,*"); else if (systemLocale.startsWith(QLatin1String("en-"))) acceptLanguage = QString::fromAscii("%1,*").arg(systemLocale); else acceptLanguage = QString::fromAscii("%1,en,*").arg(systemLocale); request.setHeaderField("Accept-Language", acceptLanguage.toAscii()); } // set the User Agent value = request.headerField("user-agent"); if (value.isEmpty()) request.setHeaderField("User-Agent", "Mozilla/5.0"); // set the host value = request.headerField("host"); if (value.isEmpty()) { QHostAddress add; QByteArray host; if(add.setAddress(hostName)) { if(add.protocol() == QAbstractSocket::IPv6Protocol) { host = "[" + hostName.toAscii() + "]";//format the ipv6 in the standard way } else { host = QUrl::toAce(hostName); } } else { host = QUrl::toAce(hostName); } int port = request.url().port(); if (port != -1) { host += ':'; host += QByteArray::number(port); } request.setHeaderField("Host", host); } reply->d_func()->requestIsPrepared = true; }
qint64 QHttpNetworkConnectionPrivate::uncompressedBytesAvailableNextBlock(const QHttpNetworkReply &reply) const { return reply.d_func()->responseData.sizeNextBlock(); }
qint64 QHttpNetworkConnectionPrivate::uncompressedBytesAvailable(const QHttpNetworkReply &reply) const { return reply.d_func()->responseData.byteAmount(); }
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) break; 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; case QAbstractSocket::ProxyConnectionClosedError: // try to reconnect/resend before sending an error. if (reconnectAttempts-- > 0) { closeAndResendCurrentRequest(); return; } errorCode = QNetworkReply::ProxyConnectionClosedError; break; case QAbstractSocket::ProxyConnectionTimeoutError: // try to reconnect/resend before sending an error. if (reconnectAttempts-- > 0) { closeAndResendCurrentRequest(); return; } errorCode = QNetworkReply::ProxyTimeoutError; 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 HostLookupPending state the channel should not emit the error. // This will instead be handled by the connection. if (!connection->d_func()->shouldEmitChannelError(socket)) return; // emit error for all waiting replies do { // 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; if (protocolHandler) protocolHandler->setReply(0); } } while (!connection->d_func()->highPriorityQueue.isEmpty() || !connection->d_func()->lowPriorityQueue.isEmpty()); #ifndef QT_NO_SSL if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY) { QList<HttpMessagePair> spdyPairs = spdyRequestsToSend.values(); for (int a = 0; a < spdyPairs.count(); ++a) { // emit error for all replies QHttpNetworkReply *currentReply = spdyPairs.at(a).second; Q_ASSERT(currentReply); emit currentReply->finishedWithError(errorCode, errorString); } } #endif // QT_NO_SSL // send the next request QMetaObject::invokeMethod(that, "_q_startNextRequest", Qt::QueuedConnection); if (that) //signal emission triggered event loop close(); }