Skip to content


Subversion checkout URL

You can clone with
Download ZIP


Ignore SSL errors #176

wants to merge 6 commits into from

6 participants


Ignore SSL errors, e.g. when testing against a staging server with self-signed certs etc.


I'd very much like to see this as an optional feature, but as I understand it, you disable certificate validation permanently by default, which is to be considered very bad in regards of security (and actually makes the use of SSL completely useless). Have I missed something?


I agree with @niklasb, this should be optional. I would love this feature though, it's actually stopping me from using capybara-webkit with a current project. Based on another similar discussion (#152) this feature would need an accompanying test to be accepted.


since this is a testing framework... I wouldn't consider it "very bad" or even "making ssl useless." It still allows you to test your production or development environments for acceptance.


@tobowers That's true, for that (main) purpose it's not that much of a security problem. Capybara can however also be used for screen scraping, where I consider certificate validation to be important.
Don't get me wrong, I definitely love this feature, but IMHO ignoring SSL validation errors should neither be the only option, nor should it be the default. Rather, there should be some IgnoreSSLErrors command that sets an internal flag that is checked when an SSL error occurs.
@rectalogic If you want, I can write an integration test for this feature.

@rectalogic rectalogic Make ignoring SSL errors an option.
webkit_server takes an --ignore-ssl-errors argument.

OK, so I added a new command line option to webkit_server, --ignore-ssl-errors. So by default this won't be specified and we won't ignore errors.

What needs to be done now (and I don't have time to do this) is to expose this through the ruby API, e.g. add :ignore_ssl_errors to the options for Capybara::Driver::Webkit::Browser and it can popen webkit_server with --ignore-ssl-errors or not based on the options.


@rectalogic as soon as I find the time, I can add the option to Capybara::Driver::Webkit::Browser and write a test for your new functionality. I'll let you know when I got this finished so you can merge it.


@rectalogic I pushed some fixes, can you also include them?


I get two test failures after merging this:


  1) Capybara::Driver::Webkit::Browser handling of SSL validation errors doesn't accept a self-signed certificate by default
     Failure/Error: @server.shutdown
       Socket is not connected
     # ./spec/browser_spec.rb:71:in `shutdown'
     # ./spec/browser_spec.rb:71

  2) Capybara::Driver::Webkit::Browser handling of SSL validation errors accepts a self-signed certificate if configured to do so
     Failure/Error: @server.shutdown
       Socket is not connected
     # ./spec/browser_spec.rb:71:in `shutdown'
     # ./spec/browser_spec.rb:71

Finished in 169.71 seconds
614 examples, 2 failures

I used Ruby 1.8.7-p334.


I only tested on Ruby 1.9.1 but will fix it for Ruby 1.8.7


It passes for me on Ruby 1.8.7 if I make a little change in browser.rb (will push it to ignore-ssl-errors). I cannot reproduce the exception you describe on Ruby 1.8.7-p352 [x86_64]. Can you debug this on your machine? The actual specs seem to pass, only the destruction of the temporary SSL-enabled server fails.

@niklasb niklasb referenced this pull request from a commit in niklasb/capybara-webkit
@niklasb niklasb IO.popen doesn't take Array argument in Ruby 1.8.7
refs #176

@jferris: can you try whether the specs pass if you replace shutdown with close?

@niklasb niklasb referenced this pull request from a commit in niklasb/capybara-webkit
@niklasb niklasb fixes wrongly used call to `Socket#shutdown`
refs #176

I might be misinterpreting the difference between shutdown and close and I wanted to clarify. #shutdown sends the shutdown(2) signal which will cause the full-duplex connection to shut down on both ends. We're currently running on OSX so not sure why ENOTCONN is being raised. However I don't see why you'd have both a shutdown and a close. If shutdown isn't supported won't close go through each of the file descriptors for the duplex and close them?


@halogenandtoast I noticed that the listening port cannot be listened upon right after simply closing the socket. As I am however sure that the server and its connections will not be used any longer after the tests are run, I thought it might be better to shutdown the socket before closing, so that there are no dangling connections. Most of the information that I got about this is from . I am open to suggestions here.

Quote from the thread regarding shutdown:
"However, it does not deallocate the socket and you still need to call close afterward."
Robert S. Barnes


Since OSX doesn't seem to work with the shutdown, I just want to confirm that #shutdown followed by #close doesn't error out with something like IOError: closed stream

@niklasb niklasb referenced this pull request from a commit in niklasb/capybara-webkit
@niklasb niklasb remove call to `Socket#shutdown`
refs #176

I removed the questionable code. Even if connections are dangling, the specs should always run without problems. Current code lives in branch ignore-ssl-errors



Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Sep 27, 2011
  1. @rectalogic

    Ignore SSL errors.

    rectalogic authored
  2. @rectalogic

    Fix slot signature.

    rectalogic authored
Commits on Sep 28, 2011
  1. @rectalogic

    Make ignoring SSL errors an option.

    rectalogic authored
    webkit_server takes an --ignore-ssl-errors argument.
  2. @niklasb
Commits on Sep 29, 2011
  1. @niklasb
  2. @niklasb
This page is out of date. Refresh to see the latest.
3  lib/capybara/driver/webkit.rb
@@ -17,7 +17,8 @@ def initialize(app, options={})
@options = options
@rack_server =
@rack_server.boot if Capybara.run_server
- @browser = options[:browser] ||
+ @browser = options[:browser] ||
+ :ignore_ssl_errors => options[:ignore_ssl_errors])
def current_url
5 lib/capybara/driver/webkit/browser.rb
@@ -12,6 +12,7 @@ def initialize(options = {})
@stdout = options.has_key?(:stdout) ?
options[:stdout] :
+ @ignore_ssl_errors = options[:ignore_ssl_errors]
@@ -108,7 +109,9 @@ def fork_server
def server_pipe_and_pid(server_path)
- pipe = IO.popen(server_path)
+ cmdline = [server_path]
+ cmdline << "--ignore-ssl-errors" if @ignore_ssl_errors
+ pipe = IO.popen(cmdline)
47 spec/browser_spec.rb
@@ -1,10 +1,14 @@
require 'spec_helper'
+require 'self_signed_ssl_cert'
require 'stringio'
require 'capybara/driver/webkit/browser'
describe Capybara::Driver::Webkit::Browser do
let(:browser) { }
+ let(:browser_ignore_ssl_err) {
+ => true)
+ }
describe '#server_port' do
subject { browser.server_port }
@@ -32,4 +36,47 @@
io.string.should == "hello world\n"
+ context 'handling of SSL validation errors' do
+ before do
+ # set up minimal HTTPS server
+ @host = ""
+ @server =, 0)
+ @port = @server.addr[1]
+ # set up SSL layer
+ ssl_serv =, $openssl_self_signed_ctx)
+ @server_thread = do |serv|
+ while conn = serv.accept do
+ # read request
+ request = []
+ until (line = conn.readline.strip).empty?
+ request << line
+ end
+ # write response
+ html = "<html><body>D'oh!</body></html>"
+ conn.write "HTTP/1.1 200 OK\r\n"
+ conn.write "Content-Type:text/html\r\n"
+ conn.write "Content-Length: %i\r\n" % html.size
+ conn.write "\r\n"
+ conn.write html
+ conn.close
+ end
+ end
+ end
+ after do
+ @server_thread.kill
+ @server.shutdown
+ end
+ it "doesn't accept a self-signed certificate by default" do
+ lambda { browser.visit "https://#{@host}:#{@port}/" }.should raise_error
+ end
+ it 'accepts a self-signed certificate if configured to do so' do
+ browser_ignore_ssl_err.visit "https://#{@host}:#{@port}/"
+ end
+ end
42 spec/self_signed_ssl_cert.rb
@@ -0,0 +1,42 @@
+require 'openssl'
+pem = <<-PEM_ENCODED
+-----END PRIVATE KEY-----
+$openssl_self_signed_key = key =
+$openssl_self_signed_cert = cert =
+$openssl_self_signed_ctx = ssl_ctx =
+ssl_ctx.key = key
+ssl_ctx.cert = cert
3  src/Server.cpp
@@ -4,9 +4,10 @@
#include <QTcpServer>
-Server::Server(QObject *parent) : QObject(parent) {
+Server::Server(QObject *parent, bool ignoreSslErrors) : QObject(parent) {
m_tcp_server = new QTcpServer(this);
m_page = new WebPage(this);
+ m_page->setIgnoreSslErrors(ignoreSslErrors);
bool Server::start() {
2  src/Server.h
@@ -7,7 +7,7 @@ class Server : public QObject {
- Server(QObject *parent = 0);
+ Server(QObject *parent, bool ignoreSslErrors);
bool start();
quint16 server_port() const;
15 src/WebPage.cpp
@@ -25,6 +25,7 @@ void WebPage::setCustomNetworkAccessManager() {
NetworkAccessManager *manager = new NetworkAccessManager();
connect(manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(replyFinished(QNetworkReply *)));
+ connect(manager, SIGNAL(sslErrors(QNetworkReply *, QList<QSslError>)), this, SLOT(ignoreSslErrors(QNetworkReply *, QList<QSslError>)));
void WebPage::loadJavascript() {
@@ -190,6 +191,20 @@ void WebPage::replyFinished(QNetworkReply *reply) {
+void WebPage::ignoreSslErrors(QNetworkReply *reply, const QList<QSslError> &errors) {
+ if (m_ignoreSslErrors)
+ reply->ignoreSslErrors(errors);
+void WebPage::setIgnoreSslErrors(bool ignore) {
+ m_ignoreSslErrors = ignore;
+bool WebPage::ignoreSslErrors() {
+ return m_ignoreSslErrors;
int WebPage::getLastStatus() {
return m_lastStatus;
4 src/WebPage.h
@@ -15,6 +15,8 @@ class WebPage : public QWebPage {
void setCustomNetworkAccessManager();
bool render(const QString &fileName);
virtual bool extension (Extension extension, const ExtensionOption *option=0, ExtensionReturn *output=0);
+ void setIgnoreSslErrors(bool ignore);
+ bool ignoreSslErrors();
public slots:
bool shouldInterruptJavaScript();
@@ -25,6 +27,7 @@ class WebPage : public QWebPage {
QString pageHeaders();
void frameCreated(QWebFrame *);
void replyFinished(QNetworkReply *reply);
+ void ignoreSslErrors(QNetworkReply *reply, const QList<QSslError> &);
void handleUnsupportedContent(QNetworkReply *reply);
@@ -46,5 +49,6 @@ class WebPage : public QWebPage {
void setUserStylesheet();
int m_lastStatus;
QString m_pageHeaders;
+ bool m_ignoreSslErrors;
5 src/main.cpp
@@ -18,7 +18,10 @@ int main(int argc, char **argv) {
app.setOrganizationName("thoughtbot, inc");
- Server server;
+ QStringList args = app.arguments();
+ bool ignoreSslErrors = args.contains("--ignore-ssl-errors");
+ Server server(0, ignoreSslErrors);
if (server.start()) {
std::cout << "Capybara-webkit server started, listening on port: " << server.server_port() << std::endl;
Something went wrong with that request. Please try again.