Today, I have added a server switch to Floorplanner.com. Now, it will load the floorplanner elements from the server that is nearest to you, which can yield a significant improvement in the initial loading time of your floorplans.
To determine your location, your IP address is matched against a table of locations. This worked fine in our development version, but it didn’t work at all on the production server. After some searching, I found that our server configuration was causing this. We use Apache as our web server, which uses mod_proxy to send the request to our Mongrel cluster. This intermediary step caused the IP address that Rails would receive to always be the IP address of the Apache server: 127.0.0.1. Therefore, the location matching did not work.
However, I found that mod_proxy adds an additional header to the request with the original IP address: HTTP_X_FORWARDED_FOR. This header can be used for our purpose. Now, I use the following function to determine the correct IP address:
1 2 3 4 5 | def determine_ip(request) # use HTTP_X_FORWARDED_FOR if available # otherwise fall back to default header request.env["HTTP_X_FORWARDED_FOR"] || request.remote_addr end |
On a related note: to match an IP address against ranges of IP addresses in our location table, it must be converted from a string (”1.2.3.4″) to a number (16909060). I use the following oneliner, which uses some nice functional programming tricks and an application of bit-shifting:
1 2 3 4 | def numeric_ip(ip_str) ip_str.split('.').inject(0) { |ip_num, part| ( ip_num << 8 ) + part.to_i } end |
Yes, I am really proud if this function!
UPDATE: I just found out that request.remote_ip does the same as my determine_ip-function. Unfortunately, it only works in Rails 2.0.



April 11th, 2008 at 2:48 pm
Nice tip and pulling off a bit shift and functional one liners is like a programming soda pop. I don’t know though I am taking such more of a liking to Python, it sucks but the main reason is based on the ‘end’ statement. Why did they do that. Look at it hanging out there, just so out of place. When I try to code Ruby since I do Python mostly now I always forget it.
end