mirror of
https://github.com/ariya/phantomjs.git
synced 2024-09-11 12:55:33 +03:00
added response body to response object in onResourceReceived event
https://github.com/ariya/phantomjs/issues/10158
This commit is contained in:
parent
6897baae33
commit
434d4e0101
@ -43,6 +43,7 @@
|
||||
#include "config.h"
|
||||
#include "cookiejar.h"
|
||||
#include "networkaccessmanager.h"
|
||||
#include "networkreplyproxy.h"
|
||||
|
||||
// 10 MB
|
||||
const qint64 MAX_REQUEST_POST_BODY_SIZE = 10 * 1000 * 1000;
|
||||
@ -160,6 +161,7 @@ NetworkAccessManager::NetworkAccessManager(QObject *parent, const Config *config
|
||||
, m_idCounter(0)
|
||||
, m_networkDiskCache(0)
|
||||
, m_sslConfiguration(QSslConfiguration::defaultConfiguration())
|
||||
, m_replyTracker(this)
|
||||
{
|
||||
if (config->diskCacheEnabled()) {
|
||||
m_networkDiskCache = new QNetworkDiskCache(this);
|
||||
@ -215,7 +217,6 @@ NetworkAccessManager::NetworkAccessManager(QObject *parent, const Config *config
|
||||
}
|
||||
|
||||
connect(this, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), SLOT(provideAuthentication(QNetworkReply*,QAuthenticator*)));
|
||||
connect(this, SIGNAL(finished(QNetworkReply*)), SLOT(handleFinished(QNetworkReply*)));
|
||||
}
|
||||
|
||||
void NetworkAccessManager::setUserName(const QString &userName)
|
||||
@ -315,24 +316,17 @@ QNetworkReply *NetworkAccessManager::createRequest(Operation op, const QNetworkR
|
||||
JsNetworkRequest jsNetworkRequest(&req, this);
|
||||
emit resourceRequested(data, &jsNetworkRequest);
|
||||
|
||||
// Pass duty to the superclass - special case: file:/// may be disabled.
|
||||
// This conditional must match QNetworkAccessManager's own idea of what a
|
||||
// local file URL is.
|
||||
QNetworkReply *reply;
|
||||
if (!m_localUrlAccessEnabled && (isLocalFile || scheme == QLatin1String("qrc"))) {
|
||||
reply = new NoFileAccessReply(this, req, op);
|
||||
} else {
|
||||
reply = QNetworkAccessManager::createRequest(op, req, outgoingData);
|
||||
}
|
||||
QNetworkReply *nested_reply = QNetworkAccessManager::createRequest(op, req, outgoingData);
|
||||
QNetworkReply *tracked_reply = m_replyTracker.trackReply(nested_reply, m_idCounter);
|
||||
|
||||
// reparent jsNetworkRequest to make sure that it will be destroyed with QNetworkReply
|
||||
jsNetworkRequest.setParent(reply);
|
||||
jsNetworkRequest.setParent(tracked_reply);
|
||||
|
||||
// If there is a timeout set, create a TimeoutTimer
|
||||
if(m_resourceTimeout > 0){
|
||||
|
||||
TimeoutTimer *nt = new TimeoutTimer(reply);
|
||||
nt->reply = reply; // We need the reply object in order to abort it later on.
|
||||
TimeoutTimer *nt = new TimeoutTimer(tracked_reply);
|
||||
nt->reply = tracked_reply; // We need the reply object in order to abort it later on.
|
||||
nt->data = data;
|
||||
nt->setInterval(m_resourceTimeout);
|
||||
nt->setSingleShot(true);
|
||||
@ -341,13 +335,13 @@ QNetworkReply *NetworkAccessManager::createRequest(Operation op, const QNetworkR
|
||||
connect(nt, SIGNAL(timeout()), this, SLOT(handleTimeout()));
|
||||
}
|
||||
|
||||
m_ids[reply] = m_idCounter;
|
||||
|
||||
connect(reply, SIGNAL(readyRead()), this, SLOT(handleStarted()));
|
||||
connect(reply, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(handleSslErrors(const QList<QSslError> &)));
|
||||
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(handleNetworkError()));
|
||||
connect(&m_replyTracker, SIGNAL(started(QNetworkReply*, int)), this, SLOT(handleStarted(QNetworkReply*, int)));
|
||||
connect(&m_replyTracker, SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError> &)), this, SLOT(handleSslErrors(QNetworkReply*, const QList<QSslError> &)));
|
||||
connect(&m_replyTracker, SIGNAL(error(QNetworkReply*, int, QNetworkReply::NetworkError)), this, SLOT(handleNetworkError(QNetworkReply*, int)));
|
||||
connect(&m_replyTracker, SIGNAL(finished(QNetworkReply *, int, int, const QString&, const QString&)), SLOT(handleFinished(QNetworkReply *, int, int, const QString&, const QString&)));
|
||||
|
||||
return reply;
|
||||
return tracked_reply;
|
||||
}
|
||||
|
||||
void NetworkAccessManager::handleTimeout()
|
||||
@ -366,16 +360,8 @@ void NetworkAccessManager::handleTimeout()
|
||||
nt->reply->abort();
|
||||
}
|
||||
|
||||
void NetworkAccessManager::handleStarted()
|
||||
void NetworkAccessManager::handleStarted(QNetworkReply* reply, int requestId)
|
||||
{
|
||||
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
|
||||
if (!reply)
|
||||
return;
|
||||
if (m_started.contains(reply))
|
||||
return;
|
||||
|
||||
m_started += reply;
|
||||
|
||||
QVariantList headers;
|
||||
foreach (QByteArray headerName, reply->rawHeaderList()) {
|
||||
QVariantMap header;
|
||||
@ -386,7 +372,7 @@ void NetworkAccessManager::handleStarted()
|
||||
|
||||
QVariantMap data;
|
||||
data["stage"] = "start";
|
||||
data["id"] = m_ids.value(reply);
|
||||
data["id"] = requestId;
|
||||
data["url"] = reply->url().toEncoded().data();
|
||||
data["status"] = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
||||
data["statusText"] = reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute);
|
||||
@ -395,21 +381,11 @@ void NetworkAccessManager::handleStarted()
|
||||
data["redirectURL"] = reply->header(QNetworkRequest::LocationHeader);
|
||||
data["headers"] = headers;
|
||||
data["time"] = QDateTime::currentDateTime();
|
||||
data["body"] = "";
|
||||
|
||||
emit resourceReceived(data);
|
||||
}
|
||||
|
||||
void NetworkAccessManager::handleFinished(QNetworkReply *reply)
|
||||
{
|
||||
if (!m_ids.contains(reply))
|
||||
return;
|
||||
|
||||
QVariant status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
||||
QVariant statusText = reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute);
|
||||
|
||||
this->handleFinished(reply, status, statusText);
|
||||
}
|
||||
|
||||
void NetworkAccessManager::provideAuthentication(QNetworkReply *reply, QAuthenticator *authenticator)
|
||||
{
|
||||
if (m_authAttempts++ < m_maxAuthAttempts)
|
||||
@ -420,12 +396,11 @@ void NetworkAccessManager::provideAuthentication(QNetworkReply *reply, QAuthenti
|
||||
else
|
||||
{
|
||||
m_authAttempts = 0;
|
||||
this->handleFinished(reply, 401, "Authorization Required");
|
||||
reply->close();
|
||||
m_replyTracker.abort(reply, 401, "Authorization Required");
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkAccessManager::handleFinished(QNetworkReply *reply, const QVariant &status, const QVariant &statusText)
|
||||
void NetworkAccessManager::handleFinished(QNetworkReply *reply, int requestId, int status, const QString &statusText, const QString& body)
|
||||
{
|
||||
QVariantList headers;
|
||||
foreach (QByteArray headerName, reply->rawHeaderList()) {
|
||||
@ -437,7 +412,7 @@ void NetworkAccessManager::handleFinished(QNetworkReply *reply, const QVariant &
|
||||
|
||||
QVariantMap data;
|
||||
data["stage"] = "end";
|
||||
data["id"] = m_ids.value(reply);
|
||||
data["id"] = requestId;
|
||||
data["url"] = reply->url().toEncoded().data();
|
||||
data["status"] = status;
|
||||
data["statusText"] = statusText;
|
||||
@ -445,16 +420,14 @@ void NetworkAccessManager::handleFinished(QNetworkReply *reply, const QVariant &
|
||||
data["redirectURL"] = reply->header(QNetworkRequest::LocationHeader);
|
||||
data["headers"] = headers;
|
||||
data["time"] = QDateTime::currentDateTime();
|
||||
data["body"] = body;
|
||||
|
||||
m_ids.remove(reply);
|
||||
m_started.remove(reply);
|
||||
|
||||
emit resourceReceived(data);
|
||||
}
|
||||
|
||||
void NetworkAccessManager::handleSslErrors(const QList<QSslError> &errors)
|
||||
void NetworkAccessManager::handleSslErrors(QNetworkReply* reply, const QList<QSslError> &errors)
|
||||
{
|
||||
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
|
||||
foreach (QSslError e, errors) {
|
||||
qDebug() << "Network - SSL Error:" << e;
|
||||
}
|
||||
@ -463,16 +436,15 @@ void NetworkAccessManager::handleSslErrors(const QList<QSslError> &errors)
|
||||
reply->ignoreSslErrors();
|
||||
}
|
||||
|
||||
void NetworkAccessManager::handleNetworkError()
|
||||
void NetworkAccessManager::handleNetworkError(QNetworkReply* reply, int requestId)
|
||||
{
|
||||
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
|
||||
qDebug() << "Network - Resource request error:"
|
||||
<< reply->error()
|
||||
<< "(" << reply->errorString() << ")"
|
||||
<< "URL:" << reply->url().toString();
|
||||
|
||||
QVariantMap data;
|
||||
data["id"] = m_ids.value(reply);
|
||||
data["id"] = requestId;
|
||||
data["url"] = reply->url().toString();
|
||||
data["errorCode"] = reply->error();
|
||||
data["errorString"] = reply->errorString();
|
||||
|
@ -32,13 +32,13 @@
|
||||
#define NETWORKACCESSMANAGER_H
|
||||
|
||||
#include <QAuthenticator>
|
||||
#include <QHash>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QSet>
|
||||
#include <QSslConfiguration>
|
||||
#include <QTimer>
|
||||
|
||||
#include "networkreplytracker.h"
|
||||
|
||||
class Config;
|
||||
class QNetworkDiskCache;
|
||||
class QSslConfiguration;
|
||||
@ -102,7 +102,6 @@ protected:
|
||||
QString m_userName;
|
||||
QString m_password;
|
||||
QNetworkReply *createRequest(Operation op, const QNetworkRequest & req, QIODevice * outgoingData = 0);
|
||||
void handleFinished(QNetworkReply *reply, const QVariant &status, const QVariant &statusText);
|
||||
|
||||
signals:
|
||||
void resourceRequested(const QVariant& data, QObject *);
|
||||
@ -111,20 +110,20 @@ signals:
|
||||
void resourceTimeout(const QVariant& data);
|
||||
|
||||
private slots:
|
||||
void handleStarted();
|
||||
void handleFinished(QNetworkReply *reply);
|
||||
void handleStarted(QNetworkReply* reply, int requestId);
|
||||
void handleFinished(QNetworkReply *reply, int requestId, int status, const QString& statusText, const QString& body);
|
||||
void provideAuthentication(QNetworkReply *reply, QAuthenticator *authenticator);
|
||||
void handleSslErrors(const QList<QSslError> &errors);
|
||||
void handleNetworkError();
|
||||
void handleSslErrors(QNetworkReply* reply, const QList<QSslError> &errors);
|
||||
void handleNetworkError(QNetworkReply* reply, int requestId);
|
||||
void handleTimeout();
|
||||
|
||||
private:
|
||||
QHash<QNetworkReply*, int> m_ids;
|
||||
QSet<QNetworkReply*> m_started;
|
||||
int m_idCounter;
|
||||
QNetworkDiskCache* m_networkDiskCache;
|
||||
QVariantMap m_customHeaders;
|
||||
QSslConfiguration m_sslConfiguration;
|
||||
|
||||
NetworkReplyTracker m_replyTracker;
|
||||
};
|
||||
|
||||
#endif // NETWORKACCESSMANAGER_H
|
||||
|
156
src/networkreplyproxy.cpp
Normal file
156
src/networkreplyproxy.cpp
Normal file
@ -0,0 +1,156 @@
|
||||
/*
|
||||
This file is part of the PhantomJS project from Ofi Labs.
|
||||
|
||||
Copyright (C) 2011 Ariya Hidayat <ariya.hidayat@gmail.com>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the <organization> nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
|
||||
#include "networkreplyproxy.h"
|
||||
|
||||
NetworkReplyProxy::NetworkReplyProxy(QObject* parent, QNetworkReply* reply)
|
||||
: QNetworkReply(parent)
|
||||
, m_reply(reply)
|
||||
{
|
||||
// apply attributes...
|
||||
setOperation(m_reply->operation());
|
||||
setRequest(m_reply->request());
|
||||
setUrl(m_reply->url());
|
||||
|
||||
// handle these to forward
|
||||
connect(m_reply, SIGNAL(metaDataChanged()), SLOT(applyMetaData()));
|
||||
connect(m_reply, SIGNAL(readyRead()), SLOT(readInternal()));
|
||||
connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(errorInternal(QNetworkReply::NetworkError)));
|
||||
|
||||
// forward signals
|
||||
connect(m_reply, SIGNAL(finished()), SIGNAL(finished()));
|
||||
connect(m_reply, SIGNAL(uploadProgress(qint64,qint64)), SIGNAL(uploadProgress(qint64,qint64)));
|
||||
connect(m_reply, SIGNAL(downloadProgress(qint64,qint64)), SIGNAL(downloadProgress(qint64,qint64)));
|
||||
|
||||
// for the data proxy...
|
||||
setOpenMode(ReadOnly);
|
||||
}
|
||||
|
||||
QString NetworkReplyProxy::body()
|
||||
{
|
||||
return m_data.toBase64().data();
|
||||
}
|
||||
|
||||
void NetworkReplyProxy::abort()
|
||||
{
|
||||
m_reply->abort();
|
||||
}
|
||||
|
||||
void NetworkReplyProxy::close()
|
||||
{
|
||||
m_reply->close();
|
||||
}
|
||||
|
||||
bool NetworkReplyProxy::isSequential() const
|
||||
{
|
||||
return m_reply->isSequential();
|
||||
}
|
||||
|
||||
void NetworkReplyProxy::setReadBufferSize(qint64 size)
|
||||
{
|
||||
QNetworkReply::setReadBufferSize(size); m_reply->setReadBufferSize(size);
|
||||
}
|
||||
|
||||
qint64 NetworkReplyProxy::bytesAvailable() const
|
||||
{
|
||||
return m_buffer.size() + QIODevice::bytesAvailable();
|
||||
}
|
||||
|
||||
qint64 NetworkReplyProxy::bytesToWrite() const
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
qint64 NetworkReplyProxy::readData(char* data, qint64 maxlen)
|
||||
{
|
||||
qint64 size = qMin(maxlen, qint64(m_buffer.size()));
|
||||
memcpy(data, m_buffer.constData(), size);
|
||||
m_buffer.remove(0, size);
|
||||
return size;
|
||||
}
|
||||
|
||||
void NetworkReplyProxy::ignoreSslErrors()
|
||||
{
|
||||
m_reply->ignoreSslErrors();
|
||||
}
|
||||
|
||||
void NetworkReplyProxy::applyMetaData()
|
||||
{
|
||||
/*
|
||||
We have to store headers and attributes, otherwise
|
||||
QNetworkReply non-virtual methods (attribute, header, etc.)
|
||||
would not have data to return.
|
||||
*/
|
||||
|
||||
QList<QByteArray> headers = m_reply->rawHeaderList();
|
||||
foreach(QByteArray header, headers)
|
||||
setRawHeader(header, m_reply->rawHeader(header));
|
||||
|
||||
setHeader(QNetworkRequest::ContentTypeHeader, m_reply->header(QNetworkRequest::ContentTypeHeader));
|
||||
setHeader(QNetworkRequest::ContentLengthHeader, m_reply->header(QNetworkRequest::ContentLengthHeader));
|
||||
setHeader(QNetworkRequest::LocationHeader, m_reply->header(QNetworkRequest::LocationHeader));
|
||||
setHeader(QNetworkRequest::LastModifiedHeader, m_reply->header(QNetworkRequest::LastModifiedHeader));
|
||||
setHeader(QNetworkRequest::SetCookieHeader, m_reply->header(QNetworkRequest::SetCookieHeader));
|
||||
|
||||
setAttribute(QNetworkRequest::HttpStatusCodeAttribute, m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute));
|
||||
setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, m_reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute));
|
||||
setAttribute(QNetworkRequest::RedirectionTargetAttribute, m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute));
|
||||
setAttribute(QNetworkRequest::ConnectionEncryptedAttribute, m_reply->attribute(QNetworkRequest::ConnectionEncryptedAttribute));
|
||||
setAttribute(QNetworkRequest::CacheLoadControlAttribute, m_reply->attribute(QNetworkRequest::CacheLoadControlAttribute));
|
||||
setAttribute(QNetworkRequest::CacheSaveControlAttribute, m_reply->attribute(QNetworkRequest::CacheSaveControlAttribute));
|
||||
setAttribute(QNetworkRequest::SourceIsFromCacheAttribute, m_reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute));
|
||||
setAttribute(QNetworkRequest::DoNotBufferUploadDataAttribute, m_reply->attribute(QNetworkRequest::DoNotBufferUploadDataAttribute));
|
||||
emit metaDataChanged();
|
||||
}
|
||||
|
||||
|
||||
void NetworkReplyProxy::errorInternal(QNetworkReply::NetworkError _error)
|
||||
{
|
||||
/*
|
||||
Saving data for non-virtual QIODevice::errorString method
|
||||
*/
|
||||
setError(_error, m_reply->errorString());
|
||||
emit error(_error);
|
||||
}
|
||||
|
||||
|
||||
void NetworkReplyProxy::readInternal()
|
||||
{
|
||||
QByteArray data = m_reply->readAll();
|
||||
|
||||
//this is a response buffer, whole response is stored here
|
||||
m_data += data;
|
||||
|
||||
//this is a temporary buffer, data is wiped after a call to 'readData'
|
||||
m_buffer += data;
|
||||
emit readyRead();
|
||||
}
|
93
src/networkreplyproxy.h
Normal file
93
src/networkreplyproxy.h
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
This file is part of the PhantomJS project from Ofi Labs.
|
||||
|
||||
Copyright (C) 2011 Ariya Hidayat <ariya.hidayat@gmail.com>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the <organization> nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef NETWORKREPLYPROXY_H
|
||||
#define NETWORKREPLYPROXY_H
|
||||
|
||||
#include <QNetworkReply>
|
||||
|
||||
|
||||
/*
|
||||
Proxy class for QNetworkReply. Collects response body, which can
|
||||
be retreived using 'body' method.
|
||||
*/
|
||||
class NetworkReplyProxy : public QNetworkReply {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
/*
|
||||
reply - reply to be proxied(nested reply)
|
||||
*/
|
||||
NetworkReplyProxy(QObject* parent, QNetworkReply* reply);
|
||||
|
||||
|
||||
/*
|
||||
Returns collected body
|
||||
*/
|
||||
QString body();
|
||||
|
||||
|
||||
/*
|
||||
Returns nested reply
|
||||
*/
|
||||
QNetworkReply* nestedReply() const
|
||||
{
|
||||
//TODO: in cpp
|
||||
return m_reply;
|
||||
};
|
||||
|
||||
|
||||
// QNetworkReply proxied methods
|
||||
void abort();
|
||||
void close();
|
||||
bool isSequential() const;
|
||||
|
||||
|
||||
// not possible...
|
||||
void setReadBufferSize(qint64 size);
|
||||
|
||||
// QIODevice proxy...
|
||||
virtual qint64 bytesAvailable() const;
|
||||
virtual qint64 bytesToWrite() const;
|
||||
virtual qint64 readData(char* data, qint64 maxlen);
|
||||
|
||||
public Q_SLOTS:
|
||||
void ignoreSslErrors();
|
||||
void applyMetaData();
|
||||
|
||||
void errorInternal(QNetworkReply::NetworkError _error);
|
||||
void readInternal();
|
||||
|
||||
private:
|
||||
QNetworkReply* m_reply;
|
||||
QByteArray m_data;
|
||||
QByteArray m_buffer;
|
||||
};
|
||||
|
||||
#endif // NETWORKREPLYPROXY_H
|
153
src/networkreplytracker.cpp
Normal file
153
src/networkreplytracker.cpp
Normal file
@ -0,0 +1,153 @@
|
||||
/*
|
||||
This file is part of the PhantomJS project from Ofi Labs.
|
||||
|
||||
Copyright (C) 2011 Ariya Hidayat <ariya.hidayat@gmail.com>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the <organization> nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "networkreplytracker.h"
|
||||
#include "networkreplyproxy.h"
|
||||
|
||||
|
||||
NetworkReplyTracker::NetworkReplyTracker(QObject* parent)
|
||||
:QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
QNetworkReply* NetworkReplyTracker::trackReply(QNetworkReply *reply, int requestId)
|
||||
{
|
||||
NetworkReplyProxy *proxy = new NetworkReplyProxy(this, reply);
|
||||
|
||||
/*
|
||||
Tracking link between proxy and proxied reply.
|
||||
Its possible to get nested reply as a slot argument instead of proxy.
|
||||
QNetworkAccessManager::createRequest stores original reply internally
|
||||
and passes it in certain conditions as a signal argument -
|
||||
authenticationRequire slot, for instance, will receive original reply not the proxy.
|
||||
*/
|
||||
m_replies[reply] = proxy;
|
||||
|
||||
m_ids[proxy] = requestId;
|
||||
|
||||
connect(proxy, SIGNAL(readyRead()), this, SLOT(handleIncomingData()));
|
||||
connect(proxy, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(handleNetworkError(QNetworkReply::NetworkError)));
|
||||
connect(proxy, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(handleSslErrors(const QList<QSslError> &)));
|
||||
connect(proxy, SIGNAL(finished()), SLOT(handleReplyFinished()));
|
||||
|
||||
return proxy;
|
||||
}
|
||||
|
||||
|
||||
void NetworkReplyTracker::abort(QNetworkReply *reply, int status, const QString& statusString)
|
||||
{
|
||||
finishReply(reply, status, statusString);
|
||||
|
||||
reply->close();
|
||||
}
|
||||
|
||||
|
||||
void NetworkReplyTracker::finishReply(QNetworkReply *reply, int status, const QString& statusText)
|
||||
{
|
||||
NetworkReplyProxy *proxy = getProxyFor(reply);
|
||||
|
||||
if (!m_ids.contains(proxy))
|
||||
return;
|
||||
|
||||
m_replies.remove(proxy->nestedReply());
|
||||
|
||||
|
||||
int requestId = m_ids.value(proxy);
|
||||
m_ids.remove(proxy);
|
||||
|
||||
m_started.remove(proxy);
|
||||
|
||||
|
||||
emit finished(proxy, requestId, status, statusText, proxy->body());
|
||||
}
|
||||
|
||||
void NetworkReplyTracker::handleIncomingData()
|
||||
{
|
||||
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
|
||||
|
||||
if (!reply)
|
||||
return;
|
||||
|
||||
NetworkReplyProxy *proxy = getProxyFor(reply);
|
||||
|
||||
if (m_started.contains(proxy))
|
||||
return;
|
||||
|
||||
m_started.insert(proxy);
|
||||
|
||||
|
||||
emit started(proxy, m_ids.value(proxy));
|
||||
}
|
||||
|
||||
|
||||
void NetworkReplyTracker::handleReplyFinished()
|
||||
{
|
||||
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
|
||||
|
||||
QVariant status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
||||
QVariant statusText = reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute);
|
||||
|
||||
finishReply(reply, status.toInt(), statusText.toString());
|
||||
}
|
||||
|
||||
|
||||
void NetworkReplyTracker::handleSslErrors(const QList<QSslError> &errors)
|
||||
{
|
||||
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
|
||||
|
||||
NetworkReplyProxy *proxy = getProxyFor(reply);
|
||||
|
||||
emit sslErrors(proxy, errors);
|
||||
}
|
||||
|
||||
|
||||
void NetworkReplyTracker::handleNetworkError(QNetworkReply::NetworkError err)
|
||||
{
|
||||
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
|
||||
|
||||
NetworkReplyProxy *proxy = getProxyFor(reply);
|
||||
|
||||
emit error(proxy, m_ids.value(proxy), err);
|
||||
}
|
||||
|
||||
|
||||
NetworkReplyProxy* NetworkReplyTracker::getProxyFor(QNetworkReply* reply)
|
||||
{
|
||||
NetworkReplyProxy *proxy = NULL;
|
||||
// first trying to find proxy assuming that we got nested reply
|
||||
if(m_replies.contains(reply)) {
|
||||
proxy = m_replies.value(reply);
|
||||
} else {
|
||||
//if not then it is proxy
|
||||
proxy = qobject_cast<NetworkReplyProxy*>(reply);
|
||||
}
|
||||
|
||||
return proxy;
|
||||
}
|
100
src/networkreplytracker.h
Normal file
100
src/networkreplytracker.h
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
This file is part of the PhantomJS project from Ofi Labs.
|
||||
|
||||
Copyright (C) 2011 Ariya Hidayat <ariya.hidayat@gmail.com>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the <organization> nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef NETWORKREPLYTRACKER_H
|
||||
#define NETWORKREPLYTRACKER_H
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
#include <QHash>
|
||||
#include <QSet>
|
||||
#include <QNetworkReply>
|
||||
|
||||
|
||||
class QSslError;
|
||||
class NetworkReplyProxy;
|
||||
|
||||
/*
|
||||
This class tracks replies and converts reply signals
|
||||
to phantomjs specific ones. It uses NetworkReplyProxy to collect
|
||||
body of the response and passes body as a signal argument.
|
||||
*/
|
||||
class NetworkReplyTracker: public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
NetworkReplyTracker(QObject* parent = 0);
|
||||
|
||||
|
||||
/*
|
||||
reply - reply to track
|
||||
requestId - unique request id, used to distinguis replies internally
|
||||
*/
|
||||
QNetworkReply* trackReply(QNetworkReply *reply, int requestId);
|
||||
|
||||
|
||||
/*
|
||||
Abort request
|
||||
status - set custom HTTP status code
|
||||
statusString - text description of status code
|
||||
*/
|
||||
void abort(QNetworkReply *reply, int status, const QString& statusString);
|
||||
|
||||
|
||||
signals:
|
||||
|
||||
void started(QNetworkReply* reply, int requestId);
|
||||
void finished(QNetworkReply* reply, int requestId, int status, const QString& statusText, const QString& body);
|
||||
void sslErrors(QNetworkReply*, const QList<QSslError>&);
|
||||
void error(QNetworkReply*, int requestId, QNetworkReply::NetworkError);
|
||||
|
||||
|
||||
private slots:
|
||||
|
||||
void handleIncomingData();
|
||||
void handleReplyFinished();
|
||||
void handleSslErrors(const QList<QSslError> &errors);
|
||||
void handleNetworkError(QNetworkReply::NetworkError);
|
||||
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(NetworkReplyTracker)
|
||||
|
||||
|
||||
void finishReply(QNetworkReply *reply, int status, const QString& statusText);
|
||||
|
||||
NetworkReplyProxy* getProxyFor(QNetworkReply* reply);
|
||||
|
||||
QSet<QNetworkReply*> m_started;
|
||||
|
||||
QHash<QNetworkReply*, int> m_ids;
|
||||
|
||||
QHash<QNetworkReply*, NetworkReplyProxy*> m_replies;
|
||||
};
|
||||
|
||||
#endif // NETWORKREPLYPROXY_H
|
@ -24,6 +24,8 @@ HEADERS += \
|
||||
webserver.h \
|
||||
consts.h \
|
||||
utils.h \
|
||||
networkreplyproxy.h \
|
||||
networkreplytracker.h \
|
||||
networkaccessmanager.h \
|
||||
cookiejar.h \
|
||||
filesystem.h \
|
||||
@ -42,6 +44,8 @@ SOURCES += phantom.cpp \
|
||||
webserver.cpp \
|
||||
main.cpp \
|
||||
utils.cpp \
|
||||
networkreplyproxy.cpp \
|
||||
networkreplytracker.cpp \
|
||||
networkaccessmanager.cpp \
|
||||
cookiejar.cpp \
|
||||
filesystem.cpp \
|
||||
|
@ -146,7 +146,8 @@ describe("WebPage object", function() {
|
||||
});
|
||||
});
|
||||
|
||||
xit("should return properly from a 401 status", function() {
|
||||
it("should return properly from a 401 status", function() {
|
||||
var page = require('webpage').create();
|
||||
var server = require('webserver').create();
|
||||
server.listen(12345, function(request, response) {
|
||||
response.statusCode = 401;
|
||||
@ -179,7 +180,36 @@ describe("WebPage object", function() {
|
||||
|
||||
});
|
||||
|
||||
xit("should set valid cookie properly, then remove it", function() {
|
||||
it("should contain resource body when last chunk of resource received", function() {
|
||||
var page = require('webpage').create();
|
||||
var server = require('webserver').create();
|
||||
server.listen(12345, function(request, response) {
|
||||
response.statusCode = 200;
|
||||
response.write('resource body');
|
||||
response.close();
|
||||
});
|
||||
|
||||
var url = "http://localhost:12345/foo";
|
||||
var lastChunk = "";
|
||||
runs(function() {
|
||||
page.onResourceReceived = function(resource) {
|
||||
lastChunk = resource.body;
|
||||
};
|
||||
page.open(url, function(status) {
|
||||
});
|
||||
});
|
||||
|
||||
waits(50);
|
||||
|
||||
runs(function() {
|
||||
expect(lastChunk).toEqual('resource body');
|
||||
page.onResourceReceived = null;
|
||||
server.close();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it("should set valid cookie properly, then remove it", function() {
|
||||
var server = require('webserver').create();
|
||||
server.listen(12345, function(request, response) {
|
||||
// echo received request headers in response body
|
||||
|
Loading…
Reference in New Issue
Block a user