diff --git a/lib/geocoder.rb b/lib/geocoder.rb index 53e9860b7c8ca3e0aea3e59bd7f12c625d95f94c..cc5dcffc8689f4a152938d9ed54f3d429c9ecac9 100644 --- a/lib/geocoder.rb +++ b/lib/geocoder.rb @@ -5,6 +5,7 @@ require "geocoder/exceptions" require "geocoder/cache" require "geocoder/request" require "geocoder/lookup" +require "geocoder/ip_address" require "geocoder/models/active_record" if defined?(::ActiveRecord) require "geocoder/models/mongoid" if defined?(::Mongoid) require "geocoder/models/mongo_mapper" if defined?(::MongoMapper) diff --git a/lib/geocoder/ip_address.rb b/lib/geocoder/ip_address.rb new file mode 100644 index 0000000000000000000000000000000000000000..aabdca92da0e7f9410bac65ea6b4ae2d695904bb --- /dev/null +++ b/lib/geocoder/ip_address.rb @@ -0,0 +1,11 @@ +module Geocoder + class IpAddress < String + def loopback? + (self == "0.0.0.0" or self.match(/\A127/)) + end + + def valid? + !!self.match(/\A(::ffff:)?(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\z/) + end + end +end diff --git a/lib/geocoder/query.rb b/lib/geocoder/query.rb index 3c2384e0d14c955fb2ad29c86fa64b8b15a11a7e..d909ffcaaa8fc1e5f39d5417b4507644886583a5 100644 --- a/lib/geocoder/query.rb +++ b/lib/geocoder/query.rb @@ -63,14 +63,14 @@ module Geocoder # dot-delimited numbers. # def ip_address? - !!text.to_s.match(/\A(::ffff:)?(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\z/) + IpAddress.new(text).valid? rescue false end ## # Is the Query text a loopback IP address? # def loopback_ip_address? - !!(self.ip_address? and (text == "0.0.0.0" or text.to_s.match(/\A127/))) + ip_address? && IpAddress.new(text).loopback? end ## diff --git a/lib/geocoder/request.rb b/lib/geocoder/request.rb index f02aab4770dea1c1a563fc653778b130aaaa0bb5..b325c84a0768c05905ee98a9d8022c550dec2709 100644 --- a/lib/geocoder/request.rb +++ b/lib/geocoder/request.rb @@ -4,16 +4,13 @@ module Geocoder module Request def location - unless defined?(@location) - if env.has_key?('HTTP_X_REAL_IP') - @location = Geocoder.search(env['HTTP_X_REAL_IP']).first - elsif env.has_key?('HTTP_X_FORWARDED_FOR') - @location = Geocoder.search(env['HTTP_X_FORWARDED_FOR'].split(/\s*,\s*/)[0]).first - else - @location = Geocoder.search(ip).first - end + @location ||= begin + detected_ip = env['HTTP_X_REAL_IP'] || + env['HTTP_X_FORWARDED_FOR'] && env['HTTP_X_FORWARDED_FOR'].split(",").first.strip + + real_ip = detected_ip && (detected_ip = IpAddress.new(detected_ip)) && detected_ip.valid? && !detected_ip.loopback? && detected_ip.to_s || self.ip + Geocoder.search(real_ip).first end - @location end end end diff --git a/test/request_test.rb b/test/request_test.rb index 3acbe2841a0d9bc307eeb0a0ec3e301cfe70e901..046bca2a571b61657135e3534c03aa88eff89edd 100644 --- a/test/request_test.rb +++ b/test/request_test.rb @@ -26,4 +26,9 @@ class RequestTest < Test::Unit::TestCase req = MockRequest.new({}, "74.200.247.59") assert req.location.is_a?(Geocoder::Result::Freegeoip) end -end \ No newline at end of file + + def test_with_loopback_x_forwarded_for + req = MockRequest.new({"HTTP_X_FORWARDED_FOR" => "127.0.0.1"}, "74.200.247.59") + assert_equal "US", req.location.country_code + end +end