Commit 2cf247f4 authored by Dawit Alemayehu's avatar Dawit Alemayehu
Browse files

Revert commit 462a06ea as it causes many regressions. Use KIO::synchronousRun

to fulfill synchrounous XmlHttpRequest instead.

Unofrtunately this means that the fix for the crash reported in bug# 287778
is reverted until we can find a solution for the side effects of using nested
event loops.

BUG: 297954
BUG: 299590
CCBUG: 299710
CCBUG: 287778
FIXED-IN: 4.8.4
parent c4c0ee29
/*
* This file is part of the KDE project.
*
* Copyright (C) 2009,2010 Dawit Alemayehu <adawit @ kde.org>
* Copyright (C) 2009 - 2012 Dawit Alemayehu <adawit @ kde.org>
* Copyright (C) 2008 - 2009 Urs Wolfer <uwolfer @ kde.org>
* Copyright (C) 2007 Trolltech ASA
*
......@@ -28,6 +28,7 @@
#include "job.h"
#include "scheduler.h"
#include "jobuidelegate.h"
#include "netaccess.h"
#include <kdebug.h>
#include <kconfiggroup.h>
......@@ -36,8 +37,8 @@
#include <klocalizedstring.h>
#include <QtCore/QUrl>
#include <QtGui/QWidget>
#include <QtCore/QWeakPointer>
#include <QtGui/QWidget>
#include <QtDBus/QDBusInterface>
#include <QtDBus/QDBusConnection>
#include <QtDBus/QDBusReply>
......@@ -47,7 +48,6 @@
#include <QtNetwork/QSslCertificate>
#include <QtNetwork/QSslConfiguration>
#define QL1S(x) QLatin1String(x)
#define QL1C(x) QLatin1Char(x)
......@@ -57,6 +57,9 @@ static QNetworkRequest::Attribute gSynchronousNetworkRequestAttribute = QNetwork
static QNetworkRequest::Attribute gSynchronousNetworkRequestAttribute = static_cast<QNetworkRequest::Attribute>(QNetworkRequest::HttpPipeliningWasUsedAttribute + 7);
#endif
static qint64 sizeFromRequest(const QNetworkRequest& req)
{
const QVariant size = req.header(QNetworkRequest::ContentLengthHeader);
......@@ -209,30 +212,13 @@ void AccessManager::setEmitReadyReadOnMetaDataChange(bool enable)
QNetworkReply *AccessManager::createRequest(Operation op, const QNetworkRequest &req, QIODevice *outgoingData)
{
/*
* WORKAROUND: Since there is no way to do a synchronous KIO request
* without creating a nested event loop and creating such a loop here is
* the surest way to encounter the inherent flaws of creating such loops,
* i.e. crashes (bug# 287778), we let Qt's networking layer handle such
* requests because it can block without the need for nested event loops.
*
* The only consequence of doing this is the HTTP response will never be
* cached since it won't be handled by kio_http.
*/
if (req.attribute(gSynchronousNetworkRequestAttribute).toBool()) {
return QNetworkAccessManager::createRequest(op, req, outgoingData);
}
KIO::SimpleJob *kioJob = 0;
const KUrl reqUrl (req.url());
if (!d->externalContentAllowed &&
!KDEPrivate::AccessManagerReply::isLocalRequest(reqUrl) &&
reqUrl.scheme() != QL1S("data")) {
kDebug( 7044 ) << "Blocked: " << reqUrl;
KDEPrivate::AccessManagerReply* reply = new KDEPrivate::AccessManagerReply(op, req, kioJob, d->emitReadyReadOnMetaDataChange, this);
reply->setStatus(i18n("Blocked request."),QNetworkReply::ContentAccessDenied);
return reply;
return new KDEPrivate::AccessManagerReply(op, req, QNetworkReply::ContentAccessDenied, i18n("Blocked request."), this);
}
// Check if the internal ignore content disposition header is set.
......@@ -242,6 +228,8 @@ QNetworkReply *AccessManager::createRequest(Operation op, const QNetworkRequest
KIO::MetaData metaData;
d->setMetaDataForRequest(req, metaData);
KIO::SimpleJob *kioJob = 0;
switch (op) {
case HeadOperation: {
//kDebug( 7044 ) << "HeadOperation:" << reqUrl;
......@@ -292,9 +280,7 @@ QNetworkReply *AccessManager::createRequest(Operation op, const QNetworkRequest
//kDebug(7044) << "CustomOperation:" << reqUrl << "method:" << method << "outgoing data:" << outgoingData;
if (method.isEmpty()) {
KDEPrivate::AccessManagerReply* reply = new KDEPrivate::AccessManagerReply(op, req, kioJob, d->emitReadyReadOnMetaDataChange, this);
reply->setStatus(i18n("Unknown HTTP verb."), QNetworkReply::ProtocolUnknownError);
return reply;
return new KDEPrivate::AccessManagerReply(op, req, QNetworkReply::ProtocolUnknownError, i18n("Unknown HTTP verb."), this);
}
if (outgoingData)
......@@ -311,14 +297,6 @@ QNetworkReply *AccessManager::createRequest(Operation op, const QNetworkRequest
}
}
// Set the window on the the KIO ui delegate
if (d->window) {
kioJob->ui()->setWindow(d->window);
}
// Disable internal automatic redirection handling
kioJob->setRedirectionHandlingEnabled(false);
// Set the job priority
switch (req.priority()) {
case QNetworkRequest::HighPriority:
......@@ -331,13 +309,61 @@ QNetworkReply *AccessManager::createRequest(Operation op, const QNetworkRequest
break;
}
// Set the meta data for this job...
kioJob->setMetaData(metaData);
KDEPrivate::AccessManagerReply *reply;
// Create the reply...
KDEPrivate::AccessManagerReply *reply = new KDEPrivate::AccessManagerReply(op, req, kioJob, d->emitReadyReadOnMetaDataChange, this);
/*
NOTE: Here we attempt to handle synchronous XHR requests. Unfortunately,
due to the fact that QNAM is both synchronous and multi-thread while KIO
is completely the opposite (asynchronous and not thread safe), the code
below might cause crashes like the one reported in bug# 287778 (nested
event loops are inherently dangerous).
Unfortunately, all attempts to address the crash has so far failed due to
the many regressions they caused, e.g. bug# 231932 and 297954. Hence, until
a solution is found, we have to live with the side effects of creating
nested event loops.
*/
if (req.attribute(gSynchronousNetworkRequestAttribute).toBool()) {
KUrl finalURL;
QByteArray data;
if (KIO::NetAccess::synchronousRun(kioJob, d->window, &data, &finalURL, &metaData)) {
reply = new KDEPrivate::AccessManagerReply(op, req, data, finalURL, metaData, this);
kDebug(7044) << "Synchronous XHR:" << reply << reqUrl;
} else {
kWarning(7044) << "Failed to create a synchronous XHR for" << reqUrl;
reply = 0;
}
} else {
// Set the window on the the KIO ui delegate
if (d->window) {
kioJob->ui()->setWindow(d->window);
}
// Disable internal automatic redirection handling
kioJob->setRedirectionHandlingEnabled(false);
// Set the job priority
switch (req.priority()) {
case QNetworkRequest::HighPriority:
KIO::Scheduler::setJobPriority(kioJob, -5);
break;
case QNetworkRequest::LowPriority:
KIO::Scheduler::setJobPriority(kioJob, 5);
break;
default:
break;
}
// Set the meta data for this job...
kioJob->setMetaData(metaData);
// Create the reply...
reply = new KDEPrivate::AccessManagerReply(op, req, kioJob, d->emitReadyReadOnMetaDataChange, this);
kDebug(7044) << reply << reqUrl;
}
if (ignoreContentDisposition) {
if (ignoreContentDisposition && reply) {
kDebug(7044) << "Content-Disposition WILL BE IGNORED!";
reply->setIgnoreContentDisposition(ignoreContentDisposition);
}
......
......@@ -2,6 +2,7 @@
* This file is part of the KDE project.
*
* Copyright (C) 2008 - 2009 Urs Wolfer <uwolfer @ kde.org>
* Copyright (C) 2009 - 2012 Dawit Alemayehu <adawit @ kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
......
......@@ -3,7 +3,7 @@
*
* Copyright (C) 2008 Alex Merry <alex.merry @ kdemail.net>
* Copyright (C) 2008 - 2009 Urs Wolfer <uwolfer @ kde.org>
* Copyright (C) 2009 - 2010 Dawit Alemayehu <adawit @ kde.org>
* Copyright (C) 2009 - 2012 Dawit Alemayehu <adawit @ kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
......@@ -39,16 +39,16 @@
namespace KDEPrivate {
AccessManagerReply::AccessManagerReply(const QNetworkAccessManager::Operation &op,
AccessManagerReply::AccessManagerReply(const QNetworkAccessManager::Operation op,
const QNetworkRequest &request,
KIO::SimpleJob *kioJob,
bool emitReadyReadOnMetaDataChange,
QObject *parent)
:QNetworkReply(parent),
m_metaDataRead(false),
m_ignoreContentDisposition(false),
m_emitReadyReadOnMetaDataChange(emitReadyReadOnMetaDataChange),
m_kioJob(kioJob)
:QNetworkReply(parent),
m_metaDataRead(false),
m_ignoreContentDisposition(false),
m_emitReadyReadOnMetaDataChange(emitReadyReadOnMetaDataChange),
m_kioJob(kioJob)
{
setRequest(request);
......@@ -60,33 +60,73 @@ AccessManagerReply::AccessManagerReply(const QNetworkAccessManager::Operation &o
if (!request.sslConfiguration().isNull())
setSslConfiguration(request.sslConfiguration());
if (kioJob) {
connect(kioJob, SIGNAL(redirection(KIO::Job*,KUrl)), SLOT(slotRedirection(KIO::Job*,KUrl)));
connect(kioJob, SIGNAL(percent(KJob*,ulong)), SLOT(slotPercent(KJob*,ulong)));
if (qobject_cast<KIO::StatJob*>(kioJob)) {
connect(kioJob, SIGNAL(result(KJob*)), SLOT(slotStatResult(KJob*)));
} else {
connect(kioJob, SIGNAL(result(KJob*)), SLOT(slotResult(KJob*)));
connect(kioJob, SIGNAL(data(KIO::Job*,QByteArray)),
SLOT(slotData(KIO::Job*,QByteArray)));
connect(kioJob, SIGNAL(mimetype(KIO::Job*,QString)),
SLOT(slotMimeType(KIO::Job*,QString)));
}
connect(kioJob, SIGNAL(redirection(KIO::Job*,KUrl)), SLOT(slotRedirection(KIO::Job*,KUrl)));
connect(kioJob, SIGNAL(percent(KJob*,ulong)), SLOT(slotPercent(KJob*,ulong)));
if (qobject_cast<KIO::StatJob*>(kioJob)) {
connect(kioJob, SIGNAL(result(KJob*)), SLOT(slotStatResult(KJob*)));
} else {
connect(kioJob, SIGNAL(result(KJob*)), SLOT(slotResult(KJob*)));
connect(kioJob, SIGNAL(data(KIO::Job*,QByteArray)),
SLOT(slotData(KIO::Job*,QByteArray)));
connect(kioJob, SIGNAL(mimetype(KIO::Job*,QString)),
SLOT(slotMimeType(KIO::Job*,QString)));
}
}
AccessManagerReply::~AccessManagerReply()
AccessManagerReply::AccessManagerReply (const QNetworkAccessManager::Operation op,
const QNetworkRequest& request,
const QByteArray& data,
const QUrl& url,
const KIO::MetaData& metaData,
QObject* parent)
:QNetworkReply (parent),
m_data(data),
m_ignoreContentDisposition(false),
m_emitReadyReadOnMetaDataChange(false)
{
setRequest(request);
setOpenMode(QIODevice::ReadOnly);
setUrl((url.isValid() ? url : request.url()));
setOperation(op);
setHeaderFromMetaData(metaData);
if (!request.sslConfiguration().isNull())
setSslConfiguration(request.sslConfiguration());
setError(NoError, QString());
setFinished(true);
}
void AccessManagerReply::abort()
AccessManagerReply::AccessManagerReply (const QNetworkAccessManager::Operation op,
const QNetworkRequest& request,
QNetworkReply::NetworkError errorCode,
const QString& errorMessage,
QObject* parent)
:QNetworkReply (parent)
{
if (m_kioJob) {
m_kioJob.data()->kill();
m_kioJob.clear();
setRequest(request);
setOpenMode(QIODevice::ReadOnly);
setUrl(request.url());
setOperation(op);
setError(static_cast<QNetworkReply::NetworkError>(errorCode), errorMessage);
setFinished(true);
if (error() != QNetworkReply::NoError) {
QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, Q_ARG(QNetworkReply::NetworkError, error()));
}
QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
}
AccessManagerReply::~AccessManagerReply()
{
}
void AccessManagerReply::abort()
{
if (!m_kioJob)
kDebug(7044) << this;
m_kioJob.clear();
m_data.clear();
m_metaDataRead = false;
}
......@@ -108,13 +148,13 @@ qint64 AccessManagerReply::readData(char *data, qint64 maxSize)
return length;
}
bool AccessManagerReply::ignoreContentDisposition (KIO::Job* job)
bool AccessManagerReply::ignoreContentDisposition (const KIO::MetaData& metaData)
{
if (m_ignoreContentDisposition) {
return true;
}
if (job->queryMetaData(QL1S("content-disposition-type")).isEmpty()) {
if (!metaData.contains(QL1S("content-disposition-type"))) {
return true;
}
......@@ -127,56 +167,14 @@ bool AccessManagerReply::ignoreContentDisposition (KIO::Job* job)
return false;
}
void AccessManagerReply::setIgnoreContentDisposition(bool on)
{
// kDebug(7044) << on;
m_ignoreContentDisposition = on;
}
void AccessManagerReply::setStatus(const QString& message, QNetworkReply::NetworkError code)
{
setError(code, message);
if (code != QNetworkReply::NoError) {
QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, Q_ARG(QNetworkReply::NetworkError, code));
}
QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
}
void AccessManagerReply::putOnHold()
{
if (!m_kioJob || isFinished())
return;
// kDebug(7044) << m_kioJob << m_data;
m_kioJob.data()->disconnect(this);
m_kioJob.data()->putOnHold();
m_kioJob.clear();
KIO::Scheduler::publishSlaveOnHold();
}
bool AccessManagerReply::isLocalRequest (const KUrl& url)
{
const QString scheme (url.protocol());
return (KProtocolInfo::isKnownProtocol(scheme) &&
KProtocolInfo::protocolClass(scheme).compare(QL1S(":local"), Qt::CaseInsensitive) == 0);
}
void AccessManagerReply::readHttpResponseHeaders(KIO::Job *job)
void AccessManagerReply::setHeaderFromMetaData (const KIO::MetaData& _metaData)
{
if (!job || m_metaDataRead)
return;
KIO::MetaData metaData (job->metaData());
if (metaData.isEmpty()) {
// Allow handling of local resources such as man pages and file url...
if (isLocalRequest(url())) {
setHeader(QNetworkRequest::ContentLengthHeader, job->totalAmount(KJob::Bytes));
setAttribute(QNetworkRequest::HttpStatusCodeAttribute, "200");
emit metaDataChanged();
}
if (_metaData.isEmpty()) {
return;
}
KIO::MetaData metaData(_metaData);
// Set the encryption attribute and values...
QSslConfiguration sslConfig;
const bool isEncrypted = KIO::Integration::sslConfigFromMetaData(metaData, sslConfig);
......@@ -187,13 +185,13 @@ void AccessManagerReply::readHttpResponseHeaders(KIO::Job *job)
// Set the raw header information...
const QStringList httpHeaders (metaData.value(QL1S("HTTP-Headers")).split(QL1C('\n'), QString::SkipEmptyParts));
if (httpHeaders.isEmpty()) {
if (metaData.contains(QL1S("charset"))) {
QString mimeType = header(QNetworkRequest::ContentTypeHeader).toString();
mimeType += QL1S(" ; charset=");
mimeType += metaData.value(QL1S("charset"));
kDebug(7044) << "changed content-type to" << mimeType;
setHeader(QNetworkRequest::ContentTypeHeader, mimeType.toUtf8());
}
if (metaData.contains(QL1S("charset"))) {
QString mimeType = header(QNetworkRequest::ContentTypeHeader).toString();
mimeType += QL1S(" ; charset=");
mimeType += metaData.value(QL1S("charset"));
// kDebug(7044) << "changed content-type to" << mimeType;
setHeader(QNetworkRequest::ContentTypeHeader, mimeType.toUtf8());
}
} else {
Q_FOREACH(const QString& httpHeader, httpHeaders) {
int index = httpHeader.indexOf(QL1C(':'));
......@@ -226,7 +224,7 @@ void AccessManagerReply::readHttpResponseHeaders(KIO::Job *job)
}
if (headerName.startsWith(QL1S("content-disposition"), Qt::CaseInsensitive) &&
ignoreContentDisposition(job)) {
ignoreContentDisposition(metaData)) {
continue;
}
......@@ -267,7 +265,51 @@ void AccessManagerReply::readHttpResponseHeaders(KIO::Job *job)
// Set the returned meta data as attribute...
setAttribute(static_cast<QNetworkRequest::Attribute>(KIO::AccessManager::MetaData), metaData.toVariant());
}
void AccessManagerReply::setIgnoreContentDisposition(bool on)
{
// kDebug(7044) << on;
m_ignoreContentDisposition = on;
}
void AccessManagerReply::putOnHold()
{
if (!m_kioJob || isFinished())
return;
// kDebug(7044) << m_kioJob << m_data;
m_kioJob.data()->disconnect(this);
m_kioJob.data()->putOnHold();
m_kioJob.clear();
KIO::Scheduler::publishSlaveOnHold();
}
bool AccessManagerReply::isLocalRequest (const KUrl& url)
{
const QString scheme (url.protocol());
return (KProtocolInfo::isKnownProtocol(scheme) &&
KProtocolInfo::protocolClass(scheme).compare(QL1S(":local"), Qt::CaseInsensitive) == 0);
}
void AccessManagerReply::readHttpResponseHeaders(KIO::Job *job)
{
if (!job || m_metaDataRead)
return;
KIO::MetaData metaData (job->metaData());
if (metaData.isEmpty()) {
// Allow handling of local resources such as man pages and file url...
if (isLocalRequest(url())) {
setHeader(QNetworkRequest::ContentLengthHeader, job->totalAmount(KJob::Bytes));
setAttribute(QNetworkRequest::HttpStatusCodeAttribute, "200");
emit metaDataChanged();
}
return;
}
setHeaderFromMetaData(metaData);
m_metaDataRead = true;
emit metaDataChanged();
}
......@@ -383,6 +425,7 @@ void AccessManagerReply::slotResult(KJob *kJob)
readHttpResponseHeaders(qobject_cast<KIO::Job*>(kJob));
}
setFinished(true);
emit finished();
}
......@@ -390,6 +433,7 @@ void AccessManagerReply::slotStatResult(KJob* kJob)
{
if (jobError(kJob)) {
emit error (error());
setFinished(true);
emit finished();
return;
}
......@@ -405,6 +449,7 @@ void AccessManagerReply::slotStatResult(KJob* kJob)
if (!mimeType.isEmpty())
setHeader(QNetworkRequest::ContentTypeHeader, mimeType.toUtf8());
setFinished(true);
emit finished();
}
......
......@@ -2,7 +2,7 @@
* This file is part of the KDE project.
*
* Copyright (C) 2008 - 2009 Urs Wolfer <uwolfer @ kde.org>
* Copyright (C) 2009 - 2010 Dawit Alemayehu <adawit @ kde.org>
* Copyright (C) 2009 - 2012 Dawit Alemayehu <adawit @ kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
......@@ -31,6 +31,7 @@ namespace KIO
{
class Job;
class SimpleJob;
class MetaData;
}
class KJob;
class KUrl;
......@@ -48,27 +49,40 @@ class AccessManagerReply : public QNetworkReply
{
Q_OBJECT
public:
AccessManagerReply(const QNetworkAccessManager::Operation &op,
const QNetworkRequest &request,
KIO::SimpleJob *kioJob,
bool emitReadyReadOnMetaDataChange = false,
QObject *parent = 0);
explicit AccessManagerReply(const QNetworkAccessManager::Operation op,
const QNetworkRequest &request,
KIO::SimpleJob *kioJob,
bool emitReadyReadOnMetaDataChange = false,
QObject *parent = 0);
explicit AccessManagerReply(const QNetworkAccessManager::Operation op,
const QNetworkRequest &request,
const QByteArray& data,
const QUrl& url,
const KIO::MetaData& metaData,
QObject *parent = 0);
explicit AccessManagerReply(const QNetworkAccessManager::Operation op,
const QNetworkRequest &request,
QNetworkReply::NetworkError errorCode,
const QString& errorMessage,
QObject *parent = 0);
virtual ~AccessManagerReply();
virtual qint64 bytesAvailable() const;
virtual void abort();
void setIgnoreContentDisposition(bool on);
void setStatus(const QString& message, QNetworkReply::NetworkError);
void putOnHold();
static bool isLocalRequest(const KUrl& url);
protected:
virtual qint64 readData(char *data, qint64 maxSize);
bool ignoreContentDisposition(const KIO::MetaData&);
void setHeaderFromMetaData(const KIO::MetaData&);
void readHttpResponseHeaders(KIO::Job *);
int jobError(KJob *kJob);
bool ignoreContentDisposition(KIO::Job* job);
private Q_SLOTS:
void slotData(KIO::Job *kioJob, const QByteArray &data);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment