RSpec error – Host header or origin header is specified and is not whitelisted or localhost.

I encountered the following head-scratcher when trying to run the following RSpec test on MacOs:

➜ bundle exec rspec ./spec/features/some_spec.rb:12
...

Capybara starting Puma…
Version 4.3.8 , codename: Mysterious Traveller
Min threads: 0, max threads: 4
Listening on tcp://127.0.0.1:63422
...

Selenium::WebDriver::Error::WebDriverError:
unexpected response, code=500, content-type="text/html"
Host header or origin header is specified and is not whitelisted or localhost.


After much fruitless poking around in ./spec/rails_helper.rb, I checked my /etc/hosts file and realised I had mapped 127.0.0.1 to a custom name. I updated /etc/hosts to include only the following lines:

127.0.0.1 localhost
0.0.0.0 my.localdev


And hey presto, Selenium would now run the rest of my (still failing) tests.

As an aside, I tried passing the options '--headless', '--disable-web-security', '-–allow-file-access-from-files', '--allowed-origins=*‘ to Capybara in ./spec/rails_helper.rb, and none seemed to have any effect.

Rails HTTPs error on localhost – getaddrinfo SocketError

I recently began developing a Rails Plugin gem, and running the dummy app locally using HTTP worked flawlessly out of the box. However, when it came time to test HTTPs, I encountered the following error:

➜ rails s -b "ssl://0.0.0.0:3000?key=config/cert.key&cert=config/cert.crt"
=> Booting WEBrick
=> Rails 5.2.6 application starting in development on http://ssl://0.0.0.0:3000?key=config/cert.key&cert=config/cert.crt:3000
=> Run rails server -h for more startup options
INFO WEBrick 1.4.4
`getaddrinfo': getaddrinfo: nodename nor servname provided, or not known (SocketError)

This was puzzling at first as I had other apps running fine using HTTPs. After closer inspection I discovered the other apps were running under the Puma webserver, while my gem had defaulted to using WEBrick.

Solution

The solution was to find the latest version of Puma and add it to my gem:

➜ gem search puma
*** REMOTE GEMS ***
...
puma (5.5.2 ruby java java, 5.4.0, 4.3.10)
...

edit ./Gemfile:
...
gem "puma", "5.5.2"
...

Install the gem:

➜ bundle install
...
Installing puma 5.5.2 with native extensions
...

The Rails app then booted into HTTPs successfully under Puma:

➜ rails s -b "ssl://0.0.0.0:3000?key=config/cert.key&cert=config/cert.crt"
=> Booting Puma
...
Listening on ssl://0.0.0.0:3000?key=config/cert.key&cert=config/cert.crt


Rails: Error installing gem mysql2 on MacOs

When installing gems on a new project under MacOs using bundler, I encountered the following error for MySql:

Installing mysql2 0.5.2 with native extensions
Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

linking shared-object mysql2/mysql2.bundle
ld: library not found for -lssl
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [mysql2.bundle] Error 1

It seems the clang compiler couldn’t find an SSL library on the system. The solution was:

> brew install openssl
> bundle config --global build.mysql2 --with-opt-dir="$(brew --prefix openssl)
> bundle

# This also works:
> export LIBRARY_PATH=$LIBRARY_PATH:/usr/local/opt/openssl/lib/

Rails 6: Beware the master.key

Checking encrypted credentials into git using Rails 6 feels weird but is pretty great.

What isn’t great is getting this error when deploying to Heroku:

ActiveSupport::MessageEncryptor::InvalidMessage: ActiveSupport::MessageEncryptor::InvalidMessage

After running EDITOR=vim bundle exec rails credentials:edit Rails will create a master.key file which works fine, but expects all your environments to be in a single credentials file and creates a master.key to decrypt the file.

Subsequently running EDITOR=vim bundle exec rails credentials:edit --environment staging appears to not use the master key and instead autocreates a ./config/credentials/staging.key.

In Heroku, the RAILS_MASTER_KEY environment variable needs to be set to the value of staging.key, not master.key, otherwise the MessageEncryptor error is thrown. This wasn’t clear to me from various tutorials and StackOverflow posts.

adding a blocklist break-glass header for Rack::Attack

When blocklisting broad IP ranges using the Rails Rack::Attack gem, it can be valuable to have a break-glass HTTP header so that legitimate users in a blocked range can still access the webapp.

The Rack::Attack docs provide most of the information on how to do this, however the syntax in the current example does not match the format of a header injected into the browser using an extension such as SimpleModifyHeaders.

As it turns out, any such headers are uppercased and prefixed with HTTP_. So if in your browser extension you set your header as SuperSecretKey, Rack::Attack would pass it through as HTTP_SUPERSECRETKEY.

The below code snippet illustrates, and also dumps out all headers to the console as a comma delimited list.

class Rack::Attack

  p "~~~~~~~~ RAAAACK ATTAAAAAAAACK ~~~~~~~~"

  # safelist by HTTP header
  Rack::Attack.whitelist("mark any authenticated user as safe") do |request|

    p "~~ HEADER CHECK ~~"

    p request.env.sort.compact.reject(&:empty?).join(',')

    puts request.env.key?("HTTP_SUPERSECRETKEY")
    puts request.env["HTTP_SUPERSECRETKEY"]
    request.env["HTTP_SUPERSECRETKEY"] == "Hunter2"
  end

end

Rails: fix Bundler 2 lockfile error

The fix

gem update --system # update Rubygems
gem install bundler # update bundler
bundler update --bundler # update Gemfile.lock in your project

The error message

>bundle exec guard
14:41:40 - INFO - Guard::RSpec is running
14:41:40 - INFO - Guard is now watching at '/Users/user/project'
14:41:52 - INFO - Running: spec/spec.rb
You must use Bundler 2 or greater with this lockfile.
14:41:52 - ERROR - Failed: "bundle exec bin/rspec -f progress -r /Users/user/project/vendor/bundle/ruby/2.6.0/gems/guard-rspec-4.7.3/lib/guard/rspec_formatter.rb -f Guard::RSpecFormatter --failure-exit-code 2 spec/spec.rb" (exit code: 20)

Fix slow rspec on macOS – 10x speedup

For some reason rspec was running horribly slow on my macOS laptop, with a simple test suite taking over 30 seconds to run. This made TDD almost unbearable:

>rspec spec/features
...
Finished in 27.29 seconds (files took 33.71 seconds to load)
8 examples, 2 failures

After some googling, the recommended approach is to compile spring binstubs for rspec. For good hygiene, we’ll use bundler to do this, and use guard so we can leave a terminal window open on a second monitor and see test results automatically after saving code changes.

# edit /Gemfile
group :development do
gem 'guard'
gem 'guard-rspec'
gem 'spring'
gem 'spring-commands-rspec'
gem 'spring-watcher-listen'
end

>bundle install --path vendor/bundle --without=production --binstubs # install gems into vendor/bundle
>echo 'vendor/bundle' >> .gitignore # don't track installed gemfiles

Update 16/08/2020: I no longer install gems into ./vendor/bundle and instead let rbenv handle the location for me. I don’t believe this change in approach affects the meat of this article.

Now replace the bundler rails binstubs with updated rails gem versions and insert spring:

>bundle exec rails app:update:bin # now regenerate standard rails binstubs
>bin/rails app:update:bin # norly plox use teh rail binstubs
>bundle exec spring binstub --all # use spring versions of binstubs
>bundle exec guard init # create guard config file /Guardfile

Note: bin/rails can get overwritten by bundler and stop accepting commands eg “bin/rails server” will show the default output of “rails” instead of launching the server. This can be fixed by running “bundle binstubs railties” then updating the binstubs again as per the instructions above.

Running “bundle config –delete bin” will stop bundler from blatting bin/rails but this also means you will need to manually generate binstubs using eg “bundle binstubs cucumber”.

Note also that bundler pulls in config from $APP/.bundle/config and ~/.bundle.config, so disabling binstubs in your app folder can be overridden by your home directory config.

# edit /Guardfile
guard :rspec, cmd: “bundle exec bin/rspec”, failed_mode: :keep do

>bundle exec guard


Finished in 1.72 seconds (files took 3.17 seconds to load)
5 examples, 2 failures

From 33 seconds to 3 seconds. Nice!

Tip: now instead of using “bundle exec” rails or rake, use bin/$GEMNAME eg “bin/rails console”. The spring server running in the background will respond much faster than booting rails each time.