2011-04-09 21:34:04 +04:00
|
|
|
/*
|
|
|
|
This file is part of the PhantomJS project from Ofi Labs.
|
|
|
|
|
|
|
|
Copyright (C) 2011 Ariya Hidayat <ariya.hidayat@gmail.com>
|
2011-08-31 19:29:40 +04:00
|
|
|
Copyright (C) 2011 Ivan De Marino <ivan.de.marino@gmail.com>
|
2011-04-09 21:34:04 +04:00
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
2011-09-07 03:37:54 +04:00
|
|
|
#include <QAuthenticator>
|
2011-06-09 10:32:14 +04:00
|
|
|
#include <QDateTime>
|
2011-04-12 18:21:09 +04:00
|
|
|
#include <QDesktopServices>
|
|
|
|
#include <QNetworkDiskCache>
|
2011-09-07 03:37:54 +04:00
|
|
|
#include <QNetworkReply>
|
|
|
|
#include <QNetworkRequest>
|
2012-09-04 03:14:58 +04:00
|
|
|
#include <QSslSocket>
|
2011-04-08 20:55:10 +04:00
|
|
|
|
2012-09-06 10:33:18 +04:00
|
|
|
#include "phantom.h"
|
2011-09-15 08:41:58 +04:00
|
|
|
#include "config.h"
|
|
|
|
#include "cookiejar.h"
|
2011-04-08 20:55:10 +04:00
|
|
|
#include "networkaccessmanager.h"
|
|
|
|
|
2011-06-07 10:56:23 +04:00
|
|
|
static const char *toString(QNetworkAccessManager::Operation op)
|
|
|
|
{
|
|
|
|
const char *str = 0;
|
|
|
|
switch (op) {
|
|
|
|
case QNetworkAccessManager::HeadOperation:
|
|
|
|
str = "HEAD";
|
|
|
|
break;
|
|
|
|
case QNetworkAccessManager::GetOperation:
|
|
|
|
str = "GET";
|
|
|
|
break;
|
|
|
|
case QNetworkAccessManager::PutOperation:
|
|
|
|
str = "PUT";
|
|
|
|
break;
|
|
|
|
case QNetworkAccessManager::PostOperation:
|
|
|
|
str = "POST";
|
|
|
|
break;
|
|
|
|
case QNetworkAccessManager::DeleteOperation:
|
|
|
|
str = "DELETE";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
str = "?";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2011-04-08 20:55:10 +04:00
|
|
|
// public:
|
2011-09-15 08:41:58 +04:00
|
|
|
NetworkAccessManager::NetworkAccessManager(QObject *parent, const Config *config)
|
2011-06-08 18:52:19 +04:00
|
|
|
: QNetworkAccessManager(parent)
|
2011-09-15 08:41:58 +04:00
|
|
|
, m_ignoreSslErrors(config->ignoreSslErrors())
|
2012-10-17 05:20:21 +04:00
|
|
|
, m_authAttempts(0)
|
|
|
|
, m_maxAuthAttempts(3)
|
2011-06-08 18:52:19 +04:00
|
|
|
, m_idCounter(0)
|
2011-09-07 03:38:51 +04:00
|
|
|
, m_networkDiskCache(0)
|
2012-10-17 19:03:22 +04:00
|
|
|
, m_sslConfiguration(QSslConfiguration::defaultConfiguration())
|
2011-04-08 20:55:10 +04:00
|
|
|
{
|
2012-09-06 10:33:18 +04:00
|
|
|
setCookieJar(CookieJar::instance());
|
2011-07-19 21:32:42 +04:00
|
|
|
|
2011-09-15 08:41:58 +04:00
|
|
|
if (config->diskCacheEnabled()) {
|
2011-09-07 03:35:19 +04:00
|
|
|
m_networkDiskCache = new QNetworkDiskCache(this);
|
2011-04-12 18:21:09 +04:00
|
|
|
m_networkDiskCache->setCacheDirectory(QDesktopServices::storageLocation(QDesktopServices::CacheLocation));
|
2011-09-15 08:41:58 +04:00
|
|
|
if (config->maxDiskCacheSize() >= 0)
|
|
|
|
m_networkDiskCache->setMaximumCacheSize(config->maxDiskCacheSize() * 1024);
|
2011-04-12 18:21:09 +04:00
|
|
|
setCache(m_networkDiskCache);
|
|
|
|
}
|
2011-09-02 19:35:58 +04:00
|
|
|
|
2012-10-17 19:03:22 +04:00
|
|
|
if (QSslSocket::supportsSsl()) {
|
|
|
|
m_sslConfiguration = QSslConfiguration::defaultConfiguration();
|
2012-10-27 23:10:33 +04:00
|
|
|
|
|
|
|
// set the SSL protocol to SSLv3 by the default
|
|
|
|
m_sslConfiguration.setProtocol(QSsl::SslV3);
|
|
|
|
|
|
|
|
if (config->sslProtocol() == "sslv2") {
|
2012-10-17 19:03:22 +04:00
|
|
|
m_sslConfiguration.setProtocol(QSsl::SslV2);
|
|
|
|
} else if (config->sslProtocol() == "tlsv1") {
|
|
|
|
m_sslConfiguration.setProtocol(QSsl::TlsV1);
|
2012-10-27 23:10:33 +04:00
|
|
|
} else if (config->sslProtocol() == "any") {
|
|
|
|
m_sslConfiguration.setProtocol(QSsl::AnyProtocol);
|
2012-10-17 19:03:22 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-07 03:39:50 +04:00
|
|
|
connect(this, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), SLOT(provideAuthentication(QNetworkReply*,QAuthenticator*)));
|
2011-04-08 22:28:39 +04:00
|
|
|
connect(this, SIGNAL(finished(QNetworkReply*)), SLOT(handleFinished(QNetworkReply*)));
|
2011-04-08 20:55:10 +04:00
|
|
|
}
|
|
|
|
|
2011-09-17 04:34:02 +04:00
|
|
|
void NetworkAccessManager::setUserName(const QString &userName)
|
|
|
|
{
|
|
|
|
m_userName = userName;
|
|
|
|
}
|
|
|
|
|
|
|
|
void NetworkAccessManager::setPassword(const QString &password)
|
|
|
|
{
|
|
|
|
m_password = password;
|
|
|
|
}
|
|
|
|
|
2012-10-17 05:20:21 +04:00
|
|
|
void NetworkAccessManager::setMaxAuthAttempts(int maxAttempts)
|
|
|
|
{
|
|
|
|
m_maxAuthAttempts = maxAttempts;
|
|
|
|
}
|
|
|
|
|
2012-02-19 12:46:08 +04:00
|
|
|
void NetworkAccessManager::setCustomHeaders(const QVariantMap &headers)
|
|
|
|
{
|
|
|
|
m_customHeaders = headers;
|
|
|
|
}
|
|
|
|
|
|
|
|
QVariantMap NetworkAccessManager::customHeaders() const
|
|
|
|
{
|
|
|
|
return m_customHeaders;
|
|
|
|
}
|
|
|
|
|
2012-09-06 10:33:18 +04:00
|
|
|
void NetworkAccessManager::setCookieJar(QNetworkCookieJar *cookieJar)
|
2012-06-14 11:43:20 +04:00
|
|
|
{
|
2012-09-06 10:33:18 +04:00
|
|
|
QNetworkAccessManager::setCookieJar(cookieJar);
|
|
|
|
// Remove NetworkAccessManager's ownership of this CookieJar and
|
|
|
|
// pass it to the PhantomJS Singleton object.
|
|
|
|
// CookieJar is a SINGLETON, shouldn't be deleted when
|
|
|
|
// the NetworkAccessManager is deleted but only when we shutdown.
|
|
|
|
cookieJar->setParent(Phantom::instance());
|
2012-06-14 11:43:20 +04:00
|
|
|
}
|
|
|
|
|
2011-04-08 20:55:10 +04:00
|
|
|
// protected:
|
2012-01-08 11:50:22 +04:00
|
|
|
QNetworkReply *NetworkAccessManager::createRequest(Operation op, const QNetworkRequest & request, QIODevice * outgoingData)
|
2011-04-08 20:55:10 +04:00
|
|
|
{
|
2012-01-08 11:50:22 +04:00
|
|
|
QNetworkRequest req(request);
|
|
|
|
|
2012-09-04 03:14:58 +04:00
|
|
|
if (!QSslSocket::supportsSsl()) {
|
|
|
|
if (req.url().scheme().toLower() == QLatin1String("https"))
|
|
|
|
qWarning() << "Request using https scheme without SSL support";
|
2012-10-17 19:03:22 +04:00
|
|
|
} else {
|
|
|
|
req.setSslConfiguration(m_sslConfiguration);
|
2012-09-04 03:14:58 +04:00
|
|
|
}
|
|
|
|
|
2011-12-07 00:25:52 +04:00
|
|
|
// Get the URL string before calling the superclass. Seems to work around
|
|
|
|
// segfaults in Qt 4.8: https://gist.github.com/1430393
|
2011-12-12 02:31:22 +04:00
|
|
|
QByteArray url = req.url().toEncoded();
|
2011-12-07 00:25:52 +04:00
|
|
|
|
2012-01-08 11:50:22 +04:00
|
|
|
// http://code.google.com/p/phantomjs/issues/detail?id=337
|
|
|
|
if (op == QNetworkAccessManager::PostOperation) {
|
|
|
|
QString contentType = req.header(QNetworkRequest::ContentTypeHeader).toString();
|
|
|
|
if (contentType.isEmpty()) {
|
|
|
|
req.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-19 12:46:08 +04:00
|
|
|
// set custom HTTP headers
|
|
|
|
QVariantMap::const_iterator i = m_customHeaders.begin();
|
|
|
|
while (i != m_customHeaders.end()) {
|
|
|
|
req.setRawHeader(i.key().toAscii(), i.value().toByteArray());
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
|
2011-04-08 20:55:10 +04:00
|
|
|
// Pass duty to the superclass - Nothing special to do here (yet?)
|
2011-04-29 12:24:35 +04:00
|
|
|
QNetworkReply *reply = QNetworkAccessManager::createRequest(op, req, outgoingData);
|
2011-06-07 10:56:23 +04:00
|
|
|
|
2011-06-07 11:19:12 +04:00
|
|
|
QVariantList headers;
|
|
|
|
foreach (QByteArray headerName, req.rawHeaderList()) {
|
|
|
|
QVariantMap header;
|
|
|
|
header["name"] = QString::fromUtf8(headerName);
|
|
|
|
header["value"] = QString::fromUtf8(req.rawHeader(headerName));
|
|
|
|
headers += header;
|
|
|
|
}
|
|
|
|
|
2011-06-08 18:52:19 +04:00
|
|
|
m_idCounter++;
|
|
|
|
m_ids[reply] = m_idCounter;
|
|
|
|
|
2011-06-07 10:56:23 +04:00
|
|
|
QVariantMap data;
|
2011-06-08 18:52:19 +04:00
|
|
|
data["id"] = m_idCounter;
|
2011-12-13 12:39:09 +04:00
|
|
|
data["url"] = url.data();
|
2011-06-07 10:56:23 +04:00
|
|
|
data["method"] = toString(op);
|
2011-06-07 11:19:12 +04:00
|
|
|
data["headers"] = headers;
|
2011-06-09 10:32:14 +04:00
|
|
|
data["time"] = QDateTime::currentDateTime();
|
2011-06-07 10:56:23 +04:00
|
|
|
|
2011-06-08 12:01:30 +04:00
|
|
|
connect(reply, SIGNAL(readyRead()), this, SLOT(handleStarted()));
|
2012-10-17 19:03:22 +04:00
|
|
|
connect(reply, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(handleSslErrors(const QList<QSslError> &)));
|
2011-06-08 12:01:30 +04:00
|
|
|
|
2011-06-07 10:56:23 +04:00
|
|
|
emit resourceRequested(data);
|
2011-09-21 10:05:34 +04:00
|
|
|
return reply;
|
2011-04-08 20:55:10 +04:00
|
|
|
}
|
2011-04-08 22:28:39 +04:00
|
|
|
|
2011-06-08 12:01:30 +04:00
|
|
|
void NetworkAccessManager::handleStarted()
|
2011-04-08 22:28:39 +04:00
|
|
|
{
|
2011-06-08 12:01:30 +04:00
|
|
|
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
|
|
|
|
if (!reply)
|
|
|
|
return;
|
2011-06-08 18:57:51 +04:00
|
|
|
if (m_started.contains(reply))
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_started += reply;
|
2011-06-08 12:01:30 +04:00
|
|
|
|
|
|
|
QVariantList headers;
|
|
|
|
foreach (QByteArray headerName, reply->rawHeaderList()) {
|
|
|
|
QVariantMap header;
|
|
|
|
header["name"] = QString::fromUtf8(headerName);
|
|
|
|
header["value"] = QString::fromUtf8(reply->rawHeader(headerName));
|
|
|
|
headers += header;
|
2011-04-13 18:45:54 +04:00
|
|
|
}
|
2011-06-08 12:01:30 +04:00
|
|
|
|
|
|
|
QVariantMap data;
|
|
|
|
data["stage"] = "start";
|
2011-06-08 18:52:19 +04:00
|
|
|
data["id"] = m_ids.value(reply);
|
2011-12-12 02:31:22 +04:00
|
|
|
data["url"] = reply->url().toEncoded().data();
|
2011-06-08 12:01:30 +04:00
|
|
|
data["status"] = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
2011-06-09 10:28:54 +04:00
|
|
|
data["statusText"] = reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute);
|
|
|
|
data["contentType"] = reply->header(QNetworkRequest::ContentTypeHeader);
|
|
|
|
data["bodySize"] = reply->size();
|
|
|
|
data["redirectURL"] = reply->header(QNetworkRequest::LocationHeader);
|
2011-06-08 12:01:30 +04:00
|
|
|
data["headers"] = headers;
|
2011-06-09 10:32:14 +04:00
|
|
|
data["time"] = QDateTime::currentDateTime();
|
2011-06-08 12:01:30 +04:00
|
|
|
|
|
|
|
emit resourceReceived(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
void NetworkAccessManager::handleFinished(QNetworkReply *reply)
|
2012-10-17 05:20:21 +04:00
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
authenticator->setUser(m_userName);
|
|
|
|
authenticator->setPassword(m_password);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_authAttempts = 0;
|
|
|
|
this->handleFinished(reply, 401, "Authorization Required");
|
|
|
|
reply->close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void NetworkAccessManager::handleFinished(QNetworkReply *reply, const QVariant &status, const QVariant &statusText)
|
2011-06-08 12:01:30 +04:00
|
|
|
{
|
|
|
|
QVariantList headers;
|
|
|
|
foreach (QByteArray headerName, reply->rawHeaderList()) {
|
|
|
|
QVariantMap header;
|
|
|
|
header["name"] = QString::fromUtf8(headerName);
|
|
|
|
header["value"] = QString::fromUtf8(reply->rawHeader(headerName));
|
|
|
|
headers += header;
|
2011-04-08 22:28:39 +04:00
|
|
|
}
|
2011-06-08 12:01:30 +04:00
|
|
|
|
|
|
|
QVariantMap data;
|
|
|
|
data["stage"] = "end";
|
2011-06-08 18:52:19 +04:00
|
|
|
data["id"] = m_ids.value(reply);
|
2011-12-12 02:31:22 +04:00
|
|
|
data["url"] = reply->url().toEncoded().data();
|
2012-10-17 05:20:21 +04:00
|
|
|
data["status"] = status;
|
|
|
|
data["statusText"] = statusText;
|
2011-06-09 10:28:54 +04:00
|
|
|
data["contentType"] = reply->header(QNetworkRequest::ContentTypeHeader);
|
|
|
|
data["redirectURL"] = reply->header(QNetworkRequest::LocationHeader);
|
2011-06-08 12:01:30 +04:00
|
|
|
data["headers"] = headers;
|
2011-06-09 10:32:14 +04:00
|
|
|
data["time"] = QDateTime::currentDateTime();
|
2011-06-08 12:01:30 +04:00
|
|
|
|
2011-06-08 18:52:19 +04:00
|
|
|
m_ids.remove(reply);
|
2011-06-08 18:57:51 +04:00
|
|
|
m_started.remove(reply);
|
2011-06-08 18:52:19 +04:00
|
|
|
|
2011-06-08 12:01:30 +04:00
|
|
|
emit resourceReceived(data);
|
2011-04-08 22:28:39 +04:00
|
|
|
}
|
2012-10-17 19:03:22 +04:00
|
|
|
|
|
|
|
void NetworkAccessManager::handleSslErrors(const QList<QSslError> &errors)
|
|
|
|
{
|
|
|
|
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
|
|
|
|
foreach (QSslError e, errors) {
|
2012-10-27 23:10:33 +04:00
|
|
|
qDebug() << "Network - SSL Error:" << e;
|
2012-10-17 19:03:22 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (m_ignoreSslErrors)
|
|
|
|
reply->ignoreSslErrors();
|
|
|
|
}
|