From f755b1f2773f515024816c0b1691f08744754caa Mon Sep 17 00:00:00 2001 From: Sean Geoghegan Date: Tue, 9 Oct 2012 14:56:33 +1100 Subject: [PATCH] Add blacklisting support to webkit server. Adds a SetBlacklist command that takes a list of URLs which will be black listed by the WebKit Server. When a request is made to one these URLs webkit_server will intercept the request and return a NoOpReply which contains no content. --- lib/capybara/webkit/browser.rb | 4 ++ spec/driver_spec.rb | 67 ++++++++++++++++++++++++++++++++++ src/CommandFactory.cpp | 1 + src/NetworkAccessManager.cpp | 57 +++++++++++++++++++++++------ src/NetworkAccessManager.h | 6 +++ src/NoOpReply.cpp | 29 +++++++++++++++ src/NoOpReply.h | 17 +++++++++ src/SetUrlBlacklist.cpp | 15 ++++++++ src/SetUrlBlacklist.h | 11 ++++++ src/find_command.h | 1 + src/webkit_server.pro | 4 ++ 11 files changed, 201 insertions(+), 11 deletions(-) create mode 100644 src/NoOpReply.cpp create mode 100644 src/NoOpReply.h create mode 100644 src/SetUrlBlacklist.cpp create mode 100644 src/SetUrlBlacklist.h diff --git a/lib/capybara/webkit/browser.rb b/lib/capybara/webkit/browser.rb index e9ab44f..3adbef2 100644 --- a/lib/capybara/webkit/browser.rb +++ b/lib/capybara/webkit/browser.rb @@ -141,6 +141,10 @@ module Capybara::Webkit command("ClearPromptText") end + def url_blacklist=(black_list) + command("SetUrlBlacklist", *Array(black_list)) + end + def command(name, *args) @connection.puts name @connection.puts args.size diff --git a/spec/driver_spec.rb b/spec/driver_spec.rb index 220c844..c6f2477 100644 --- a/spec/driver_spec.rb +++ b/spec/driver_spec.rb @@ -1833,6 +1833,73 @@ describe Capybara::Webkit::Driver do end end + describe "url blacklisting" do + let(:driver) do + driver_for_app do + get "/" do + <<-HTML + + + + + + + + + HTML + end + + get "/frame" do + <<-HTML + + +

Inner

+ + + HTML + end + + get "/script" do + <<-JS + document.write('

Script Run

') + JS + end + end + end + + before do + driver.browser.url_blacklist = ["http://example.org/path/to/file", + "http://example.com", + "#{Capybara.app_host}/script"] + end + + it "should not fetch urls blocked by host" do + driver.visit("/") + driver.within_frame('frame1') do + driver.find("//body").first.text.should be_empty + end + end + + it "should not fetch urls blocked by path" do + driver.visit('/') + driver.within_frame('frame2') do + driver.find("//body").first.text.should be_empty + end + end + + it "should not fetch blocked scripts" do + driver.visit("/") + driver.body.should_not include("Script Run") + end + + it "should fetch unblocked urls" do + driver.visit('/') + driver.within_frame('frame3') do + driver.find("//p").first.text.should == "Inner" + end + end + end + describe "timeout for long requests" do let(:driver) do driver_for_app do diff --git a/src/CommandFactory.cpp b/src/CommandFactory.cpp index d4f58a9..6b5fd1d 100644 --- a/src/CommandFactory.cpp +++ b/src/CommandFactory.cpp @@ -40,6 +40,7 @@ #include "JavascriptAlertMessages.h" #include "JavascriptConfirmMessages.h" #include "JavascriptPromptMessages.h" +#include "SetUrlBlacklist.h" CommandFactory::CommandFactory(WebPageManager *manager, QObject *parent) : QObject(parent) { m_manager = manager; diff --git a/src/NetworkAccessManager.cpp b/src/NetworkAccessManager.cpp index 98da30e..d5a869f 100644 --- a/src/NetworkAccessManager.cpp +++ b/src/NetworkAccessManager.cpp @@ -2,6 +2,7 @@ #include "WebPage.h" #include #include +#include "NoOpReply.h" NetworkAccessManager::NetworkAccessManager(QObject *parent):QNetworkAccessManager(parent) { connect(this, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), SLOT(provideAuthentication(QNetworkReply*,QAuthenticator*))); @@ -11,18 +12,22 @@ NetworkAccessManager::NetworkAccessManager(QObject *parent):QNetworkAccessManage QNetworkReply* NetworkAccessManager::createRequest(QNetworkAccessManager::Operation operation, const QNetworkRequest &request, QIODevice * outgoingData = 0) { QNetworkRequest new_request(request); QByteArray url = new_request.url().toEncoded(); - if (operation != QNetworkAccessManager::PostOperation && operation != QNetworkAccessManager::PutOperation) { - new_request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant()); + if (this->isBlacklisted(new_request.url())) { + return this->noOpRequest(); + } else { + if (operation != QNetworkAccessManager::PostOperation && operation != QNetworkAccessManager::PutOperation) { + new_request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant()); + } + QHashIterator item(m_headers); + while (item.hasNext()) { + item.next(); + new_request.setRawHeader(item.key().toAscii(), item.value().toAscii()); + } + QNetworkReply *reply = QNetworkAccessManager::createRequest(operation, new_request, outgoingData); + emit requestCreated(url, reply); + return reply; } - QHashIterator item(m_headers); - while (item.hasNext()) { - item.next(); - new_request.setRawHeader(item.key().toAscii(), item.value().toAscii()); - } - QNetworkReply *reply = QNetworkAccessManager::createRequest(operation, new_request, outgoingData); - emit requestCreated(url, reply); - return reply; -} +}; void NetworkAccessManager::finished(QNetworkReply *reply) { NetworkResponse response; @@ -60,3 +65,33 @@ int NetworkAccessManager::statusFor(QUrl url) { const QList &NetworkAccessManager::headersFor(QUrl url) { return m_responses[url].headers; } + +void NetworkAccessManager::setUrlBlacklist(QStringList urlBlacklist) { + m_urlBlacklist.clear(); + + QStringListIterator iter(urlBlacklist); + while (iter.hasNext()) { + m_urlBlacklist << QUrl(iter.next()); + } +}; + +bool NetworkAccessManager::isBlacklisted(QUrl url) { + QListIterator iter(m_urlBlacklist); + + while (iter.hasNext()) { + QUrl blacklisted = iter.next(); + + if (blacklisted == url) { + return true; + } else if (blacklisted.path().isEmpty() && blacklisted.isParentOf(url)) { + return true; + } + } + + return false; +}; + +QNetworkReply* NetworkAccessManager::noOpRequest() { + return new NoOpReply(); +}; + diff --git a/src/NetworkAccessManager.h b/src/NetworkAccessManager.h index d6c53df..56c8463 100644 --- a/src/NetworkAccessManager.h +++ b/src/NetworkAccessManager.h @@ -1,6 +1,7 @@ #include #include #include +#include class NetworkAccessManager : public QNetworkAccessManager { @@ -20,15 +21,20 @@ class NetworkAccessManager : public QNetworkAccessManager { void setPassword(const QString &password); int statusFor(QUrl url); const QList &headersFor(QUrl url); + void setUrlBlacklist(QStringList urlBlacklist); protected: QNetworkReply* createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice * outgoingData); QString m_userName; QString m_password; + QList m_urlBlacklist; + private: QHash m_headers; QHash m_responses; + bool isBlacklisted(QUrl url); + QNetworkReply* noOpRequest(); private slots: void provideAuthentication(QNetworkReply *reply, QAuthenticator *authenticator); diff --git a/src/NoOpReply.cpp b/src/NoOpReply.cpp new file mode 100644 index 0000000..224f555 --- /dev/null +++ b/src/NoOpReply.cpp @@ -0,0 +1,29 @@ +#include +#include "NoOpReply.h" + +NoOpReply::NoOpReply(QObject *parent) : QNetworkReply(parent) { + open(ReadOnly | Unbuffered); + setAttribute(QNetworkRequest::HttpStatusCodeAttribute, 200); + setHeader(QNetworkRequest::ContentLengthHeader, QVariant(0)); + setHeader(QNetworkRequest::ContentTypeHeader, QVariant(QString("text/plain"))); + + QTimer::singleShot( 0, this, SIGNAL(readyRead()) ); + QTimer::singleShot( 0, this, SIGNAL(finished()) ); +} + +void NoOpReply::abort() { + // NO-OP +} + +qint64 NoOpReply::bytesAvailable() const { + return 0; +} + +bool NoOpReply::isSequential() const { + return true; +} + +qint64 NoOpReply::readData(char *data, qint64 maxSize) { + return 0; +} + diff --git a/src/NoOpReply.h b/src/NoOpReply.h new file mode 100644 index 0000000..3a074f6 --- /dev/null +++ b/src/NoOpReply.h @@ -0,0 +1,17 @@ +#include + +class NoOpReply : public QNetworkReply { + Q_OBJECT + + public: + NoOpReply( QObject *parent=0 ); + + void abort(); + qint64 bytesAvailable() const; + bool isSequential() const; + + protected: + qint64 readData(char *data, qint64 maxSize); + +}; + diff --git a/src/SetUrlBlacklist.cpp b/src/SetUrlBlacklist.cpp new file mode 100644 index 0000000..7a91762 --- /dev/null +++ b/src/SetUrlBlacklist.cpp @@ -0,0 +1,15 @@ + +#include "SetUrlBlacklist.h" +#include "WebPageManager.h" +#include "WebPage.h" +#include "NetworkAccessManager.h" + +SetUrlBlacklist::SetUrlBlacklist(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) { +} + +void SetUrlBlacklist::start() { + NetworkAccessManager* networkAccessManager = qobject_cast(page()->networkAccessManager()); + networkAccessManager->setUrlBlacklist(arguments()); + emit finished(new Response(true)); +} + diff --git a/src/SetUrlBlacklist.h b/src/SetUrlBlacklist.h new file mode 100644 index 0000000..4673876 --- /dev/null +++ b/src/SetUrlBlacklist.h @@ -0,0 +1,11 @@ + +#include "SocketCommand.h" + +class SetUrlBlacklist : public SocketCommand { + Q_OBJECT + + public: + SetUrlBlacklist(WebPageManager *manager, QStringList &arguments, QObject *parent = 0); + virtual void start(); +}; + diff --git a/src/find_command.h b/src/find_command.h index a31cf60..3d55467 100644 --- a/src/find_command.h +++ b/src/find_command.h @@ -41,4 +41,5 @@ CHECK_COMMAND(JavascriptConfirmMessages) CHECK_COMMAND(JavascriptPromptMessages) CHECK_COMMAND(GetTimeout) CHECK_COMMAND(SetTimeout) +CHECK_COMMAND(SetUrlBlacklist) diff --git a/src/webkit_server.pro b/src/webkit_server.pro index 404c0e8..c2e89d7 100644 --- a/src/webkit_server.pro +++ b/src/webkit_server.pro @@ -56,6 +56,8 @@ HEADERS = \ GetTimeout.h \ SetTimeout.h \ TimeoutCommand.h \ + SetUrlBlacklist.h \ + NoOpReply.h \ SOURCES = \ EnableLogging.cpp \ @@ -113,6 +115,8 @@ SOURCES = \ GetWindowHandles.cpp \ GetWindowHandle.cpp \ TimeoutCommand.cpp \ + SetUrlBlacklist.cpp \ + NoOpReply.cpp \ RESOURCES = webkit_server.qrc QT += network webkit