Tuesday, February 28, 2006

Firefox + Ruby on Rails = White screen of death.

One of the most obnoxious bugs I've found yet using Ruby on Rails occurs when running multiple script/server's on different ports on the the same domain. In other words, if I start 2 servers: ./script/server --port=3010 ./script/server --port=3011 Then open Firefox and navigate to both servers: http://localhost:3010 http://localhost:3011 I will get my Rails app on the first hit, and a blank white page on the other. My logs won't recognize the hit--except when run under apache/dispatch.cgi in which case the error_log will show "Premature end of script header (500)." Frustratingly, following the above procedure in Internet Explorer works just fine. Both pages display the Rails app as expected. So what is going on? Somewhere in Rails internals, there is a dependence on the stored session cookie. By default this is called _session_id and can be found in Firefox's cookies. Proper handling of cookie security says that servers running on different ports should not share the same cookies; however, Firefox sends the same cookie to any server running at the same domain. This must somehow confuse Rails--possibly by having Firefox send cookie data for a session that the Rails server can't make heads or tails of. Regardless, this is a real problem--especially when running multiple apps or development copies of the same app--and it is not clear at all from the error presentation what the cause is. A Workaround 1) Go to Firefox preferences->Security->Cookies->View Cookies->Search for your domain (localhost in this case) and delete _session_id. You'll now be able to hit one site or the other and see things fully operational. When you need to switch back, delete the cookie. 2) Don't use Firefox. A Fix Realizing that this is going to be an ongoing issue for me--since I really, really like Firefox--I decided to make a fix for my projects. The solution I found was to have each of my projects and project copies use a unique session id name. I chose a brain-dead simple uniqueness property, namely the directory that my project resides, and then hashed it into a per-project, per-project copy unique session key. The code goes in your config/environment.rb and looks like this: # This avoids mozilla's white-screen of death. ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS[:session_key] = "_project_name_#{`pwd`.hash}" So far this has worked wonders for avoiding the infamous white screen. I hope this saves others from an unnecessary debug session ;-).

Saturday, February 11, 2006

Ruby Web Service Support

Based off of my experience with PayPal business integration, out-of-the-box Ruby 1.8.6 does not handle web services well. Why? The shipping version of soap4r does not handle many header-types found in complex web service datatypes. Attempting to use the ruby paypal web service gives this error before upgrading soap4r:

test_successfulauthorization(PaypalTest): NoMethodError: undefined method `add_document_operation' for # ./../lib/defaultDriver.rb:167:in `init_methods' ./../lib/defaultDriver.rb:164:in `each' ./../lib/defaultDriver.rb:164:in `init_methods' ./../lib/defaultDriver.rb:158:in `initialize' ./../lib/paypal.rb:58:in `new' ./../lib/paypal.rb:58:in `create_client' ./../lib/paypal.rb:181:in `directauth' paypal_test.rb:52:in `test_successfulauthorization'
A system install of the latest soap4r solves this, but the following error then appears:
test_successfulauthorization(PaypalTest): NotImplementedError: SSL not supported /usr/lib/ruby/site_ruby/1.8/soap/httpconfigloader.rb:64:in `set_ssl_config' /usr/lib/ruby/site_ruby/1.8/soap/httpconfigloader.rb:61:in `each' /usr/lib/ruby/1.8/soap/property.rb:139:in `each' /usr/lib/ruby/1.8/soap/property.rb:139:in `each' /usr/lib/ruby/site_ruby/1.8/soap/httpconfigloader.rb:61:in `set_ssl_config' /usr/lib/ruby/site_ruby/1.8/soap/httpconfigloader.rb:36:in `set_options' /usr/lib/ruby/site_ruby/1.8/soap/httpconfigloader.rb:35:in `call' /usr/lib/ruby/1.8/soap/property.rb:115:in `[]=' /usr/lib/ruby/1.8/soap/property.rb:114:in `each' /usr/lib/ruby/1.8/soap/property.rb:114:in `[]=' ./../lib/paypal.rb:58:in `create_client' ./../lib/paypal.rb:180:in `directauth' paypal_test.rb:52:in `test_successfulauthorization'
This appears to be due to PayPal's use of web services over SSL (a good thing). The right package to solve this is http-access2. Installing this system-wide resolves the error above and yeilds a new error:
test_successfulauthorization(PaypalTest): OpenSSL::X509::CertificateError: wrong tag /usr/lib/ruby/site_ruby/1.8/soap/httpconfigloader.rb:110:in `initialize' /usr/lib/ruby/site_ruby/1.8/soap/httpconfigloader.rb:110:in `new' /usr/lib/ruby/site_ruby/1.8/soap/httpconfigloader.rb:110:in `cert_from_file' /usr/lib/ruby/site_ruby/1.8/soap/httpconfigloader.rb:68:in `set_ssl_config' /usr/lib/ruby/site_ruby/1.8/soap/httpconfigloader.rb:61:in `each' /usr/lib/ruby/1.8/soap/property.rb:139:in `each' /usr/lib/ruby/1.8/soap/property.rb:139:in `each' /usr/lib/ruby/site_ruby/1.8/soap/httpconfigloader.rb:61:in `set_ssl_config' /usr/lib/ruby/site_ruby/1.8/soap/httpconfigloader.rb:36:in `set_options' /usr/lib/ruby/site_ruby/1.8/soap/httpconfigloader.rb:35:in `call' /usr/lib/ruby/1.8/soap/property.rb:115:in `[]=' /usr/lib/ruby/1.8/soap/property.rb:114:in `each' /usr/lib/ruby/1.8/soap/property.rb:114:in `[]=' ./test/../lib/paypal.rb:65:in `create_client' ./test/../lib/paypal.rb:180:in `directauth' test/paypal_test.rb:52:in `test_successfulauthorization'
And luckily this was my own error--having garbage in the live_api.crt/live_api.key files for PayPal. Now that all is up and running again... I am wondering what the best way to deploy this solution on a shared-host like TextDrive. Any suggestions?