John’s Tidbits

Moo - Development, Trouble-shooting and Random thoughts…

Mongrel, rails and the theory of relativity

Summary (E = mc²)

When using mongrel for rails and you want to deploy an app under /other_url then use

    ActionController::AbstractRequest.relative_url_root = "/other_url"

in config/environments/production.rb instead of

    ENV['RAILS_RELATIVE_URL_ROOT'] = "/other_url"

Proof (From first principals)

At Vquence we have a pretty standard rails setup

  • Apache with mod_proxy
  • pen
  • mongrel

Silvia recently wrote an application to allow us to edit the news articles posted to our corporate website. I wanted to do something I thought would be pretty simple, have the application appear at /news on our admin web server.

Step one was the obvious change to mod_proxy

    ProxyPass /news http://localhost:8000
    ProxyPassReverse /news http://localhost:8000

Of course the problem is that the rails app still thinks it is living on / so it returns URLs like /stylesheets/moo.css instead of /news/stylesheets/moo.css.

A bit of googling found a few email threads with a common solution. In your environment.rb set

    ENV['RAILS_RELATIVE_URL_ROOT'] = "/other_url"

This is where things fell apart fairly quickly. I could not get this to work no matter what I tried. After a few hours of following a HTTP request through the whole Mongrel and rails stack I discovered the following.

Setting RAILS_RELATIVE_ROOT will work fine if you are running rails using CGI. For the simple reason, which should have been more obvious to me sooner, that CGIs use environment variables to access their parameters. This can be seen in the
ruby CGI class

/usr/lib/ruby/1.8/cgi.rb:


class CGI

def env_table
    ENV
end

However mongrel overloads env_table and does the following instead

/usr/lib/ruby/1.8/mongrel/cgi.rb:


class CGIWrapper < ::CGI

    # Used to wrap the normal env_table variable used inside CGI.
    def env_table
        @request.params
    end

This makes sense since the rails code is now running inside the web server so environment variables aren’t necessary. Upon investigation I found that the URL morphing magic is performed with rails as follows.

/usr/share/rails/actionpack/lib/action_controller/request.rb:


  class AbstractRequest
    cattr_accessor :relative_url_root

    # Returns the path minus the web server relative installation directory.
    # This can be set with the environment variable RAILS_RELATIVE_URL_ROOT.
    # It can be automatically extracted for Apache setups. If the server is not
    # Apache, this method returns an empty string.
    def relative_url_root
      @@relative_url_root ||= case
        when @env["RAILS_RELATIVE_URL_ROOT"]
          @env["RAILS_RELATIVE_URL_ROOT"]
        when server_software == 'apache'
          @env["SCRIPT_NAME"].to_s.sub(/\/dispatch\.(fcgi|rb|cgi)$/, '')
        else
          ''
      end
    end

What this all means is that you can solve the whole problem by placing the following in your config/environments/production.rb

    ActionController::AbstractRequest.relative_url_root = "/other_url"

Now if only Einstein had put his theories to good use and invented a time machine then maybe I could get the last 4 hours of my life back :)

Update: Make sure /other_url isn’t the same name as one of your controllers or bad things happen.

linux.conf.au brings about another change

Being Technical Guru for linux.conf.au 2007 was one of the most amazing experiences I’ve had in recent years. It was a lot of hard work but it was totally worth it. Having a room burst into applause at the penguin dinner when you say your the network guy is pretty unbelievable.

I went up to the Hunter for a week to recover from the conference and as usual after linux.conf.au I did a lot of thinking as to whether it was time to try something new. This time change won out at the end of the day and after 6 years at Bulletproof I decided it was time to move on.

At the beginning of March I started as Director of Engineering at Vquence. Since we are a video company it was decided that we each needed to have our own video on the web.

The past three weeks have been so hectic that Bulletproof already seems a lifetime ago. I’ve been involved in everything from setting up the new office and the corporate infrastructure to product development.

Joining a startup right at the beginning is always an amazing experience. With just a few people on the ground you always get pulled in a few million directions and there is always a new challenge just another five minutes away. I definitely recommend anyone else to jump at the opportunity if it ever presents itself.

SLUG VoIP Slides

I’ve finally gotten around to putting the slides from my SLUG talk up. Funnily enough linux.conf.au has kept me pretty busy, as usual I’ll take this opportunity to just blame Pia.

You can find the slides on my presentations page, and here is a direct link to the PDF.

A couple of people have asked me which VoIP phones and ATAs I recommend. I don’t have a load of experience with different brands but have done a fair bit of research and really like the SNOM phones and the Linksys (Sipura) ATAs the best.

The main advantages of these units is that they are of fairly high quality a very good price. They are very configurable and have the advantage of being mass deployed via DHCP, TFTP and CGI based config files.

linux.conf.au payment gateway

Some of you may have noticed that we have been having a few problems with the linux.conf.au payment gateway. These have ranged from timeouts due to email and DNS issues to 500 server errors due to one or two bugs.

For those of you worried about duplicate payments, don’t :) We were just sending duplicate receipts for a while. You see Commsecure as well as redirecting the user back to the payment_received page, also does a GET on the page themselves. Which means we effectively receive duplicate transactions for everything and this meant we were sending two receipts.

Other than that the Commsecure setup is actually quite nice and does its best not to let users pay twice. It also seems to be written in python.

I had always tried to avoid python, being a long time perl hacker. In the last few months I’ve been dragged into it kicking and screaming. Scarily I’ve actually come to like it. Its nice having real exceptions! Pylons, Myghty and SQLAlchemy are also pretty cool frameworks and have meant I’ve come up to speed on the website code pretty quickly.

Anyway back to LCA, we are a handful of rego’s away from having 500! Don’t forget you’ve got till the 8th December to pay if you registered early enough to get earlybird rates.

Lindsay made me do it!

While at the Waugh Partners launch party tonight, a bunch of people, mainly Lindsay asked for some details on what I’d be talking about at SLUG on Friday. I thought that was a very good question and that I should make something up :)

So for those that are wondering I will attempt to cover the following topics in no particular order or level of detail

  • VoIP :)
  • Codecs, which one should I use
  • VoIP Hardware (Phones, ATAs, ISDN and PSTN cards, Mobile Pods)
  • VoIP Providers and what they offer
  • Asterisk and what it can do
  • Beagle Internet IVR and distributed VoIP Call Centre as a case study
  • Asterisk@Home

If there is anything else that in particular you are interested in or would like me to talk about then let me know.

I’ll also be bringing along various bits of hardware and hope to have a full demonstration running.

At Jeff’s request I will be doing an in depth overview of the difference between FXO and FXS and why it is critically important to any VoIP implementation. This will most likely require at least 20 slides and about 50 minutes of explanation :P

iptables evilness

Matt came to me with an interesting problem at Bulletproof this week. We have a dedicated hosting customer who talks to an external application for e-commerce. The IP for this was going to change but they needed to do to some testing before the switch. As usual with most enterprise applications, the hostname was hard coded. :(

Matt suggested we do some DNS poisoning or do some transparent proxying using squid or similar. While these would have worked they required firewall changes through three levels of firewalls and extra infrastructure.

So I turned to an evil solution, iptables. :) Most people use DNAT on the inbound connection from the Internet to their internal private network to port forward to internal servers, or perform one-to-one NAT mappings. There is nothing stopping you using it the other way around.

Lets say that every time someone browses to http://bulletproof.net we want them to hit http://inodes.org instead. All you need to do is use DNAT to translate one IP address into the other.
[code]
animal:~ johnf$ host bulletproof.net
bulletproof.net has address 202.44.98.174
animal:~ johnf$ host inodes.org
inodes.org has address 202.125.41.97
animal:~ johnf$ sudo iptables -t nat -A PREROUTING -d 202.44.98.174 -j DNAT –to 202.125.41.97

[/code]

Now for some testing, a ping looks normal

[code]

animal:~johnf$ ping www.bulletproof.net
PING www.bulletproof.net.au (202.44.98.174) 56(84) bytes of data.
64 bytes from 202.44.98.174: icmp_seq=1 ttl=241 time=198 ms

[/code]

but a tcpdump looks like

[code]

animal:~johnf$ sudo tcpdump -ni eth0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes
16:35:25.099510 IP 211.30.227.143 > 202.125.41.97: icmp 64: echo request seq 1
16:35:25.301712 IP 202.125.41.97 > 211.30.227.143: icmp 64: echo reply seq 1

[/code]

Of course if anyone needs to try and debug this they are going to have a really fun time working out what is going on. :)

If you want to test this yourself you can do it on your own machine using the OUTPUT chain instead of PREROUTING.

250!

We’ve just hit 250 registrations for linux.conf.au, only 5 days to go before early bird registrations close.

So here are some interesting stats of the attendee breakdown so far:

By Country

Country Number
Brazil 1
Canada 1
France 1
Ireland 1
Liberia 1
Nigeria 1
China 1
Singapore 1
Spain 1
UK 1
Croatia 4
Germany 4
Japan 4
Romania 9
New Zealand 13
USA 18
Australia 188

Australia by state

State Number
NT 1
TAS 3
WA 19
QLD 20
SA 20
ACT 23
VIC 24
NSW 77