Enable SSL with Heroku for https access

Posted on

For rstat.us, we've been meaning to enable ssl/https for a long time. I'm pleased to announce that we've finally gotten around to it, and everything seems to be working now!

What was the hold up? On heroku (where we started hosting rstat.us, then we weren't, now we are again), it used to cost $100/mo for the ssl hostname addon. I love rstat.us, but not that much. Recently, however, heroku came out with the SSL Endpoint addon which is only $20/mo. This is much more within my budget :)

I also wanted to make sure that the Certificate Authority we went with was in line with rstat.us' values. This is a project that started on the values of openness and simplicity, so I agonized a bit over our choice of CA. We ended up going with a free certificate from StartSSL for the reasons I mentioned in that thread.

Once you have a certificate, the instructions for the SSL Endpoint are pretty good. There are a few places that I either had issues with or think could use some further clarification:

Intermediate certs

In the SSL endpoint docs, there's a part that says "If you have uploaded a certificate that was signed by a root authority but you get the message that it is not trusted, then something is wrong with the certificate. For example, it may be missing intermediary certificates." This doesn't say what you need to do to add the intermediary certificates that you may be missing.

Some certificate authorities will have intermediate certificates that have to be included in your .crt file. The "Purchasing an SSL Certificate" heroku docs explain how to cat your certificate with a root certificate. If you have an intermediate certificate file as well, first cat your certificate as they show in the instructions, then cat the intermediate certificate, then cat the root certificate. I figured this out by trying it and it worked, but I felt unsure since the docs from heroku and the CA didn't explicitly say what order they should go in.

Add vs Edit CNAME

This may be obvious to everyone but me, but I thought I'd mention it anyway in case someone else has the same brain fart I did. The SSL endpoint docs say "Next, add a CNAME record in the DNS configuration that points from the domain name that will host secure traffic e.g. www.mydomain.com to the SSL endpoint hostname, e.g. tokyo-2121.herokussl.com."

So I did exactly that-- added a CNAME record. But we already HAD a CNAME record for www.rstat.us since we had already followed the heroku custom domains instructions. And CNAMES aren't additive, hah. So yeah, if you already have a CNAME for the domain name you're trying to enable ssl with, just edit that one.

Naked domain

This is the part I struggled with for a week. I got SSL all working for www.rstat.us according to the SSL endpoint docs-- https://www.rstat.us worked great! I also had a 301 URL Redirect set up in our DNS records with NameCheap to redirect the naked domain rstat.us to www. This may be unpopular, but naked domains can't be CNAMES and using A records for the naked domain is highly discouraged for availability reasons (see the naked domain section).

However, 301 redirects don't care about the protocol-- if you go to http://rstat.us, it redirects you to http://www.rstat.us, and if you go to https://rstat.us it (supposedly) redirects you to https://www.rstat.us. What I was seeing, though, if you went to https://rstat.us, was that the SSL handshake would just hang. This was very visible when doing a curl -v https://rstat.us on the command line-- it would just get to the SSL handshake and wait forever.

The certificate was good for both rstat.us and www.rstat.us, so that wasn't the problem. It looks like an ordering issue to me (correct me if I'm wrong)-- request https://rstat.us, do the SSL handshake since it's SSL, and THEN do whatever the DNS says to do-- but the CNAME with www that makes SSL work is AFTER the 301 redirect specified in the DNS settings for the naked domain.

Heroku's documented solution to this is "only circulating and publicizing the subdomain format of your secure URL." It is left as an exercise for the reader to determine why this is an unacceptable solution.

Not mentioned in that page, but linked in its references, is a blog post by DNSimple introducing ALIAS records. This is something that, as far as I can tell, only DNSimple has implemented, and it basically turns a CNAME into an A record behind the scenes without any need for user intervention should the IP addresses that the CNAME resolves to change.

This StackOverflow question confirmed that the DNSimple ALIAS record would solve the problem I was having-- so I set up a DNSimple account ($3/mo for up to 10 domains at the moment, but use my referral link and we both get a month free!), set up the following records:

ALIAS   rstat.us    www.rstat.us
CNAME   www.rstat.us    toyama-8790.herokussl.com

and changed the settings with NameCheap, where we have rstat.us registered, to use DNSimple's DNS instead of NameCheap's. Indeed, the ALIAS record magically makes all variations of http/https and naked domain/www work as desired!

Can I have my devops merit badge now?

Interesting Times

Posted on

So today I put in my 2 weeks' notice at Careerimp-- for a variety of reasons that I'm not going to go into publicly, I no longer think the position is the right thing for me at this point in my career.

So what is the right thing? I don't know yet :D I feel very lucky to be in a position where I can afford to take a few months off, and I feel lucky to be fairly certain that I have several options open to me.

In my first month, my main goals are:

  • Put some focused work into rstat.us
  • Work on my house (one of my longest-lasting contributions to this world may very well be making this house able to stand for another hundred years!)
  • Cook new things that use all the fresh vegetables we're getting from a farm share before they go bad
  • Figure out goals for the month after that :D

And of course there's Steel City Ruby! I'll now have the week before the conference to concentrate on making it super awesome, and that I'm definitely excited about :D

Thank you to those of you who have been advising me lately-- you know who you are. <3 You helped make this a lot less scary for me.

Updating gems in rvm's global and default gemsets

Posted on

This is mostly for my own reference; I know I've looked this up before and it's always hard to find, so at least now I'll have one place to look.

If you want to update gems that you have in your default or global gemsets because you see 2 versions of, say, bundler when you do gem list, you can install and uninstall from them by using a command like this (including the parentheses):

(rvm use @global; gem uninstall -x bundler)

Switching global and default as necessary, and uninstall and install, and bundler and rake (the 2 gems I get in this situation with).

UPDATE: Just talked with Michal Papis (@mpapis) in IRC and he recommends using:

rvm @global do gem uninstall -x bundler

which looks much nicer. As an aside, I was having an issue yesterday and Michal fixed it by the time I got into work today! Thank you so much for your help and work on rvm, Michal!!

Sinatra:Base rackup: rubyeventmachine.bundle: [BUG] Segmentation fault ruby 1.8.7

Posted on

I was just saying yesterday that I haven't run into any strange errors lately. I guess I forgot to knock on wood!

Today I started a brand new sinatra app. I haven't written my own sinatra app from scratch before, so I'm copying pieces from some other apps that I have cloned on my machine. Here's what my code looked like:

# Gemfile
source :rubygems

gem 'sinatra'
gem 'thin'
# salmon_test.rb
require 'sinatra'

class SalmonTest < Sinatra::Base
  get "/" do
    "hello"
  end
end
# config.ru
require './salmon_test'
run SalmonTest

Then I bundled and ended up with this Gemfile.lock:

GEM
  remote: http://rubygems.org/
  specs:
    daemons (1.1.8)
    eventmachine (0.12.10)
    rack (1.4.1)
    rack-protection (1.2.0)
      rack
    sinatra (1.3.2)
      rack (~> 1.3, >= 1.3.6)
      rack-protection (~> 1.2)
      tilt (~> 1.3, >= 1.3.3)
    thin (1.3.1)
      daemons (>= 1.0.9)
      eventmachine (>= 0.12.6)
      rack (>= 1.0.0)
    tilt (1.3.3)

PLATFORMS
  ruby

DEPENDENCIES
  sinatra
  thin

Also note that I'm using rvm with ruby 1.9.2-p290 and a brand new gemset for this project.

When I ran rackup to start the server, I got this error message:

$ rackup
~/.rvm/gems/ruby-1.9.2-p290@salmon_test/gems/eventmachine-0.12.10/lib/rubyeventmachine.bundle: [BUG] Segmentation fault
ruby 1.8.7 (2010-01-10 patchlevel 249) [universal-darwin10.0]

Abort trap

Why is it doing something with 1.8.7??? Who knows! Right after that I did an rvm list:

$ rvm list

rvm rubies

   ruby-1.8.7-p358 [ i686 ]
=* ruby-1.9.2-p290 [ x86_64 ]
   ruby-1.9.2-p318 [ x86_64 ]
   ruby-1.9.3-p125 [ x86_64 ]

Yep, using 1.9.2...

The one thing I changed in the code before running rackup again was in salmon_test.rb:

- require 'sinatra'
+ require 'sinatra/base'

Then the next time I ran rackup everything worked fine. shrug Hope this helps someone else.

Google Chrome AJAX POST request: net_error = -100 (CONNECTION_CLOSED)

Posted on

I haven't totally figured this out yet. I'm probably doing something dumb.

I have a rails application with an action that I'm hitting via an AJAX POST request. This action generates a PDF and saves it on the server, then sends an email using an ActionMailer, then responds with either a success message or an error message.

Everything was working just hunky-dory except in Google Chrome. In Chrome, at first, it looked like the AJAX request wasn't even being fired because I wasn't seeing it in the console like I'm used to in Firebug's console. Eventually I found where it was hiding-- in the Net tab :P

There it didn't look like there were any errors except that in the Status column it said "(canceled)". WTF?!? I wasn't canceling it, the rails server said it was handling it and returning the response I was sending just like normal, so what was going on???

By clicking on the request in the Net tab, or by opening chrome://net-internals/ in a new tab and going to the Events tab, I finally noticed this in the response headers:

    Content-Transfer-Encoding: binary
    Content-Type: text/html
    Content-Disposition: inline; filename="pdf_141.pdf"
    Content-Length: 5274

The headers from the PDF generation are bleeding out into my AJAX response for some reason!! I'm still not sure why this is happening. Then I think Chrome sees that the actual data doesn't match the headers and just stops processing.

Once I changed my rails code from:

    render :text => "OK"

to:

    send_data "OK", :type => "text/plain"

aside: yes, i know i fail REST forever

Then the AJAX request happened as I expected it to, and the headers had Content-type: text/plain. sigh.

Selenium::WebDriver::Error::UnhandledError (NS_ERROR_ILLEGAL_VALUE)

Posted on

Somewhere around the time that I got a Mac, upgraded Firefox, upgraded selenium-webdriver, upgraded Capybara and started running lots of acceptance tests in selenium, I started getting errors like this:

Selenium::WebDriver::Error::UnhandledError: Component returned failure code: 0x80070057 (NS_ERROR_ILLEGAL_VALUE) [nsIDOMXPathEvaluator.createNSResolver]

They don't appear to correlate with anything particular that my tests are doing-- I can run the same tests again immediately and have different tests get this error, or have no tests get this error at all. Like all good Heisenbugs, it was especially hard to trigger when I sat down to dig into this!!!

I did the usual googling and the most helpful thing I found was this Capybara mailing list post explaining different ways of debugging Selenium UnhandledErrors.

I didn't see anything initially useful in the output when running with $DEBUG on.

Dumping the firefox console log to disk (this post was clearer to me than the capybara docs on how to do this) showed this error that looked like what I saw:

nsCommandProcessor.js:314 - Exception caught by driver: findElements([object Object])
[Exception... "Component returned failure code: 0x80070057 (NS_ERROR_ILLEGAL_VALUE) [nsIDOMXPathEvaluator.createNSResolver]"  nsresult: "0x80070057 (NS_ERROR_ILLEGAL_VALUE)"  location: "JS frame :: resource://fxdriver/modules/atoms.js :: <TOP_LEVEL> :: line 2354"  data: no]

So then I searched for "firefox nsCommandProcessor.js:314 - Exception caught by driver" and found this selenium issue. Which says it's a timing issue in Selenium between get and findElement. I don't know right now if there's a way to sleep in my tests to solve it.

And now i've hit a timing issue that I know sleep will cure :D Good night-- I might look into a workaround more tomorrow.

My system:

  • OSX 10.6.8 (no, I haven't upgraded to Lion yet :P)
  • Ruby 1.8.7-p302
  • Firefox 5.0.1
  • selenium-webdriver 2.0.1
  • capybara at 4fc07dbdc814

UPDATE Jul 26: There have been a few updates to the bug today, with some speculation about the cause of the error. One commenter says that they're seeing the problem with post-redirect-gets, but that's not what I'm experiencing-- I'm getting the problem most with regular old links. The same commenter is suggesting a race condition, so I'm trying adding a sleep 1 after every line in my script that triggers the error-- after the click_link, before the next statement (that capybara automatically is doing a wait/find on). This seemed to help at first, but just kept happening at different places instead. I also tried adding a sleep 1 in capybara's click_link and that did not fix the issue.

BUT! If I add a sleep 1 to be the first line in capybara's selenium find in lib/capybara/selenium/driver.rb, this seems to be enough to get around the race condition (knock on wood). I've run tests that have consistently hit the error a few times now without hitting it, so...

To be clear: The workaround I've had good results with is changing def find in lib/capybara/selenium/driver.rb to be:

   def find(selector)
    sleep 1
     browser.find_elements(:xpath, selector).map { |node| Capybara::Selenium::Node.new(self, node) }
   end

Let me know if that works for you. Obviously this is going to make your tests slower, but since you're running Selenium tests already I'm assuming you aren't Gary Bernhardt and can put up with slow but passing tests for a while.

And this is a workaround, the bug is with Selenium, NOT with capybara. The real solution will be a fix to this bug, so if you are hitting this, go star that issue.

In which I buy a Mac

Posted on

me and my MBA Complete with RMU sticker!

Yep, I did it. After much agonizing and wrestling with my identity, I bought a 13" Macbook Air. It was mostly because I have started a new job at Careerimp (doing Rails dev for our web app Resunate.com) at which deploying Rails on Windows is not a concern as it was at my last job.

I still have my Windows 7 netbook, and I'm still planning on doing Rails dev on it occasionally. I'm actually really excited to be able to quickly verify whether something is a Windows issue or a personal problem by being able to try something out on both.

Rest assured that while this blog has been quiet lately, it will not go dark! Why just a few minutes ago, I was able to get "[BUG] rbgcmark(): unknown data type 0x3a(0x108e5eb20) corrupted object" on OSX using ruby 1.8.7! And while I'm enjoying how much faster and smoother working with Ruby on OSX is, I'd still like to help make the Ruby experience on Windows just as nice so that I don't have to be a Windows person or a Mac person or a Linux person, just a Ruby person :D

The system cannot find the path specified

Posted on

So recently I started getting many incidents of the error "The system cannot find the path specified" when running just about anything from the windows command line and sometimes from the Git Bash bash. It was really weird because everything seemed to be working just fine: tests would pass, rails would function normally, etc, but I would get "The system cannot find the path specified" between various lines of output with seemingly no rhyme or reason.

I let this error go for a while since it wasn't really breaking anything, but eventually it started to piss me off because it wasn't even letting me know WHAT path it couldn't find!

And to make matters worse, around the same time that the error started happening I had:

  • Installed JRuby on this computer for the first time
  • Moved the physical location of the computer and was plugged into a different ethernet port
  • Mucked about with my PATH and some other environment variables
  • Tried to organize some files in a saner way

I really had no idea which one of these things was likely responsible. Searching for this problem was tough since the error message was so general and can be caused by any number of things, and I didn't find anything that corresponded to the things I had done above, exactly.

So eventually I asked Jake what to do about this, and he recommended using Process Monitor to try and see what path was not being found. I fired it up, set up a filter to only show events from cmd.exe, ran a fast-running command that I knew would have the error, and started reading the logs.

The key entry had a Result of "PATH NOT FOUND":

Process Name Operation Path Result Detail
cmd.exe CreateFile C:\Documents and Settings\nichols\Desktop\ansi132\x86\ PATH NOT FOUND Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, AllocationSize: n/a

And there we have the culprit: me, surprise surprise ;) When the standard way of getting terminal colors on windows switched from the windows32 gem to ANSICON (in RubyInstaller, Cucumber and RSpec), the first time I had installed ANSICON (by running it with -i) was from that location on my desktop. Then I moved it when doing the cleanup mentioned above, and I didn't run -u before I moved it, so the registry entry that -i adds didn't get removed.

The particular registry entry causing the trouble was a few lines up in the Process Monitor logs; it was "HKCU\Software\Microsoft\Command Processor\AutoRun". It actually had both the old location and the new location of ANSICON at this point, so I backed up my registry to be safe, removed the path to my Desktop from that value, and the errors were no more! Whew.

The json gem strikes again: heroku on Windows

Posted on

This json gem without windows binaries for Ruby 1.9.2 is the bane of my existence.

I just started using heroku today with the heroku gem, and on every command I got the dreaded "msvcrt-ruby18.dll was not found" popup.

As usual, the fix is to reinstall the json gem (after getting DevKit):

Edit: As pointed out by Chad Tipton in the comments, you'll need to add --version=1.4.6 because 1.5.1 is out but 1.4.6 is the version of the json gem that the heroku gem depends on.

> gem uninstall json
> gem install json --platform=ruby --version=1.4.6

I also learned that there's a batch script to set your PATH for using DevKit in your DevKit install dir called devkitvars.bat that will add install-dir\bin to your PATH... that's what I get for messing around with my PATH for no good reason.

I really should just start running "gem install json --platform=ruby" after every command.

attach_file with cucumber+capybara+selenium on Windows

Posted on

Yesterday I was working with some scenarios having to do with file upload, and I thought they were failing because of something I was doing in the code, but they were not failing for my colleague on a mac (you don't have to tell me what the obvious solution to this is, I know).

When I used the attach_file method in capybara with the selenium driver, the file input field would get filled in, but nothing would get submitted-- there just wouldn't be anything for the file parameter in the logs. What's weird is that file upload worked just fine if I did it manually.

I checked out the capybara code and ran the gem's specs on my machine, and sure enough, the specs testing attach_file with selenium failed for me as well.

The issue seems to be with the direction of the slashes in the paths. When I used attach_file, the paths would look like:

C:/Ruby/capybara/lib/capybara/spec/fixtures/test_file.txt

While doing it manually through the browser gave me paths that looked like:

C:\Ruby\capybara\lib\capybara\spec\fixtures\test_file.txt

So if I changed my step definition in features/stepdefinitions/websteps.rb for attaching a file to be:

When /^(?:|I )attach the file "([^\"]*)" to "([^\"]*)"(?: within "([^\"]*)")?$/ do |filename, field, selector|
  with_scope(selector) do
    filepath = File.join(RAILS_ROOT, 'features', 'upload_files', filename)
    if filepath.match(/^C:\//)
      filepath.gsub!(/\//, "\\")
    end
    attach_file(field, filepath)
  end
end

then my cucumber scenarios worked as expected.

I've posted to the capybara mailing list to see if anyone knows where this problem is originating (and where a patch should be made) and if this is a good solution or if it's just getting around a more fundamental problem. Anyone reading this have any ideas?

Page 2 of 3