Skip to content

Commit

Permalink
adds cookie management functions to Webkit::Browser
Browse files Browse the repository at this point in the history
  • Loading branch information
niklasb authored and halogenandtoast committed Sep 30, 2011
1 parent dc5130c commit fb119f6
Show file tree
Hide file tree
Showing 15 changed files with 298 additions and 2 deletions.
14 changes: 14 additions & 0 deletions lib/capybara/driver/webkit/browser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,20 @@ def render(path, width, height)
command "Render", path, width, height
end

def set_cookie(cookie)
command "SetCookie", cookie
end

def clear_cookies
command "ClearCookies"
end

def get_cookies
command("GetCookies").lines
.map { |line| line.strip }
.select { |line| !line.empty? }
end

private

def start_server
Expand Down
55 changes: 55 additions & 0 deletions spec/driver_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -884,6 +884,61 @@ def make_the_server_go_away
end
end

context "cookie-based app" do
before(:all) do
@cookie = 'cookie=abc; domain=127.0.0.1; path=/'
@app = lambda do |env|
request = ::Rack::Request.new(env)

body = <<-HTML
<html><body>
<p id="cookie">#{request.cookies["cookie"] || ""}</p>
</body></html>
HTML
[200,
{ 'Content-Type' => 'text/html; charset=UTF-8',
'Content-Length' => body.length.to_s,
'Set-Cookie' => @cookie,
},
[body]]
end
end

def echoed_cookie
subject.find('id("cookie")').first.text
end

it "remembers the cookie on second visit" do
echoed_cookie.should == ""
subject.visit "/"
echoed_cookie.should == "abc"
end

it "uses a custom cookie" do
subject.browser.set_cookie @cookie
subject.visit "/"
echoed_cookie.should == "abc"
end

it "clears cookies" do
subject.browser.clear_cookies
subject.visit "/"
echoed_cookie.should == ""
end

it "allows enumeration of cookies" do
cookies = subject.browser.get_cookies

cookies.size.should == 1

cookie = Hash[cookies[0].split(/\s*;\s*/)
.map { |x| x.split("=", 2) }]
cookie["cookie"].should == "abc"
cookie["domain"].should include "127.0.0.1"
cookie["path"].should == "/"
end
end

context "with socket debugger" do
let(:socket_debugger_class){ Capybara::Driver::Webkit::SocketDebugger }
let(:browser_with_debugger){
Expand Down
18 changes: 18 additions & 0 deletions src/ClearCookies.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#include "ClearCookies.h"
#include "WebPage.h"
#include "NetworkCookieJar.h"
#include <QNetworkCookie>

ClearCookies::ClearCookies(WebPage *page, QObject *parent)
: Command(page, parent)
{ }

void ClearCookies::start(QStringList &arguments)
{
Q_UNUSED(arguments);
NetworkCookieJar *jar = qobject_cast<NetworkCookieJar*>(page()
->networkAccessManager()
->cookieJar());
jar->clearCookies();
emit finished(new Response(true));
}
11 changes: 11 additions & 0 deletions src/ClearCookies.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include "Command.h"

class WebPage;

class ClearCookies : public Command {
Q_OBJECT;

public:
ClearCookies(WebPage *page, QObject *parent = 0);
virtual void start(QStringList &arguments);
};
3 changes: 3 additions & 0 deletions src/Connection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
#include "Body.h"
#include "Status.h"
#include "Headers.h"
#include "SetCookie.h"
#include "ClearCookies.h"
#include "GetCookies.h"

#include <QTcpSocket>
#include <iostream>
Expand Down
22 changes: 22 additions & 0 deletions src/GetCookies.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include "GetCookies.h"
#include "WebPage.h"
#include "NetworkCookieJar.h"

GetCookies::GetCookies(WebPage *page, QObject *parent)
: Command(page, parent)
{
m_buffer = "";
}

void GetCookies::start(QStringList &arguments)
{
Q_UNUSED(arguments);
NetworkCookieJar *jar = qobject_cast<NetworkCookieJar*>(page()
->networkAccessManager()
->cookieJar());
foreach (QNetworkCookie cookie, jar->getAllCookies()) {
m_buffer.append(cookie.toRawForm());
m_buffer.append("\n");
}
emit finished(new Response(true, m_buffer));
}
14 changes: 14 additions & 0 deletions src/GetCookies.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#include "Command.h"

class WebPage;

class GetCookies : public Command {
Q_OBJECT;

public:
GetCookies(WebPage *page, QObject *parent = 0);
virtual void start(QStringList &arguments);

private:
QString m_buffer;
};
101 changes: 101 additions & 0 deletions src/NetworkCookieJar.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#include "NetworkCookieJar.h"
#include "QtCore/qdatetime.h"

NetworkCookieJar::NetworkCookieJar(QObject *parent)
: QNetworkCookieJar(parent)
{ }

QList<QNetworkCookie> NetworkCookieJar::getAllCookies() const
{
return allCookies();
}

void NetworkCookieJar::clearCookies()
{
setAllCookies(QList<QNetworkCookie>());
}

static inline bool isParentDomain(QString domain, QString reference)
{
if (!reference.startsWith(QLatin1Char('.')))
return domain == reference;

return domain.endsWith(reference) || domain == reference.mid(1);
}

void NetworkCookieJar::overwriteCookies(const QList<QNetworkCookie>& cookieList)
{
/* this function is basically a copy-and-paste of the original
QNetworkCookieJar::setCookiesFromUrl with the domain and
path validations removed */

QString defaultPath(QLatin1Char('/'));
QDateTime now = QDateTime::currentDateTime();
QList<QNetworkCookie> newCookies = allCookies();

foreach (QNetworkCookie cookie, cookieList) {
bool isDeletion = (!cookie.isSessionCookie() &&
cookie.expirationDate() < now);

// validate the cookie & set the defaults if unset
if (cookie.path().isEmpty())
cookie.setPath(defaultPath);

// don't do path checking. See http://bugreports.qt.nokia.com/browse/QTBUG-5815
// else if (!isParentPath(pathAndFileName, cookie.path())) {
// continue; // not accepted
// }

if (cookie.domain().isEmpty()) {
continue;
} else {
// Ensure the domain starts with a dot if its field was not empty
// in the HTTP header. There are some servers that forget the
// leading dot and this is actually forbidden according to RFC 2109,
// but all browsers accept it anyway so we do that as well.
if (!cookie.domain().startsWith(QLatin1Char('.')))
cookie.setDomain(QLatin1Char('.') + cookie.domain());

QString domain = cookie.domain();

// the check for effective TLDs makes the "embedded dot" rule from RFC 2109 section 4.3.2
// redundant; the "leading dot" rule has been relaxed anyway, see above
// we remove the leading dot for this check
/*
if (QNetworkCookieJarPrivate::isEffectiveTLD(domain.remove(0, 1)))
continue; // not accepted
*/
}

for (int i = 0; i < newCookies.size(); ++i) {
// does this cookie already exist?
const QNetworkCookie &current = newCookies.at(i);
if (cookie.name() == current.name() &&
cookie.domain() == current.domain() &&
cookie.path() == current.path()) {
// found a match
newCookies.removeAt(i);
break;
}
}

// did not find a match
if (!isDeletion) {
int countForDomain = 0;
for (int i = newCookies.size() - 1; i >= 0; --i) {
// Start from the end and delete the oldest cookies to keep a maximum count of 50.
const QNetworkCookie &current = newCookies.at(i);
if (isParentDomain(cookie.domain(), current.domain())
|| isParentDomain(current.domain(), cookie.domain())) {
if (countForDomain >= 49)
newCookies.removeAt(i);
else
++countForDomain;
}
}

newCookies += cookie;
}
}
setAllCookies(newCookies);
}
15 changes: 15 additions & 0 deletions src/NetworkCookieJar.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include <QtNetwork/QNetworkCookieJar>
#include <QtNetwork/QNetworkCookie>

class NetworkCookieJar : public QNetworkCookieJar {

Q_OBJECT;

public:

NetworkCookieJar(QObject *parent = 0);

QList<QNetworkCookie> getAllCookies() const;
void clearCookies();
void overwriteCookies(const QList<QNetworkCookie>& cookieList);
};
3 changes: 2 additions & 1 deletion src/Reset.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "Reset.h"
#include "WebPage.h"
#include "NetworkAccessManager.h"
#include "NetworkCookieJar.h"

Reset::Reset(WebPage *page, QObject *parent) : Command(page, parent) {
}
Expand All @@ -10,7 +11,7 @@ void Reset::start(QStringList &arguments) {

page()->triggerAction(QWebPage::Stop);
page()->currentFrame()->setHtml("<html><body></body></html>");
page()->networkAccessManager()->setCookieJar(new QNetworkCookieJar());
page()->networkAccessManager()->setCookieJar(new NetworkCookieJar());
page()->setCustomNetworkAccessManager();
page()->setUserAgent(NULL);
page()->resetResponseHeaders();
Expand Down
18 changes: 18 additions & 0 deletions src/SetCookie.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#include "SetCookie.h"
#include "WebPage.h"
#include "NetworkCookieJar.h"
#include <QNetworkCookie>

SetCookie::SetCookie(WebPage *page, QObject *parent)
: Command(page, parent)
{ }

void SetCookie::start(QStringList &arguments)
{
QList<QNetworkCookie> cookies = QNetworkCookie::parseCookies(arguments[0].toAscii());
NetworkCookieJar *jar = qobject_cast<NetworkCookieJar*>(page()
->networkAccessManager()
->cookieJar());
jar->overwriteCookies(cookies);
emit finished(new Response(true));
}
11 changes: 11 additions & 0 deletions src/SetCookie.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include "Command.h"

class WebPage;

class SetCookie : public Command {
Q_OBJECT;

public:
SetCookie(WebPage *page, QObject *parent = 0);
virtual void start(QStringList &arguments);
};
2 changes: 2 additions & 0 deletions src/WebPage.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "WebPage.h"
#include "JavascriptInvocation.h"
#include "NetworkAccessManager.h"
#include "NetworkCookieJar.h"
#include "UnsupportedContentHandler.h"
#include <QResource>
#include <iostream>
Expand All @@ -23,6 +24,7 @@ WebPage::WebPage(QObject *parent) : QWebPage(parent) {

void WebPage::setCustomNetworkAccessManager() {
NetworkAccessManager *manager = new NetworkAccessManager();
manager->setCookieJar(new NetworkCookieJar());
this->setNetworkAccessManager(manager);
connect(manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(replyFinished(QNetworkReply *)));
}
Expand Down
5 changes: 4 additions & 1 deletion src/find_command.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,7 @@ CHECK_COMMAND(Header)
CHECK_COMMAND(Render)
CHECK_COMMAND(Body)
CHECK_COMMAND(Status)
CHECK_COMMAND(Headers)
CHECK_COMMAND(Headers)
CHECK_COMMAND(SetCookie)
CHECK_COMMAND(ClearCookies)
CHECK_COMMAND(GetCookies)
8 changes: 8 additions & 0 deletions src/webkit_server.pro
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,16 @@ HEADERS = \
FrameFocus.h \
Response.h \
NetworkAccessManager.h \
NetworkCookieJar.h \
Header.h \
Render.h \
body.h \
Status.h \
Headers.h \
UnsupportedContentHandler.h \
SetCookie.h \
ClearCookies.h \
GetCookies.h \

SOURCES = \
main.cpp \
Expand All @@ -43,12 +47,16 @@ SOURCES = \
FrameFocus.cpp \
Response.cpp \
NetworkAccessManager.cpp \
NetworkCookieJar.cpp \
Header.cpp \
Render.cpp \
body.cpp \
Status.cpp \
Headers.cpp \
UnsupportedContentHandler.cpp \
SetCookie.cpp \
ClearCookies.cpp \
GetCookies.cpp \

RESOURCES = webkit_server.qrc
QT += network webkit
Expand Down

0 comments on commit fb119f6

Please sign in to comment.