geocoder 1.2.7 → 1.2.8
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of geocoder might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -1
- data/README.md +31 -28
- data/examples/reverse_geocode_job.rb +40 -0
- data/lib/geocoder.rb +1 -0
- data/lib/geocoder/configuration.rb +5 -1
- data/lib/geocoder/exceptions.rb +3 -0
- data/lib/geocoder/ip_address.rb +2 -10
- data/lib/geocoder/logger.rb +55 -0
- data/lib/geocoder/lookup.rb +2 -1
- data/lib/geocoder/lookups/baidu.rb +5 -5
- data/lib/geocoder/lookups/baidu_ip.rb +5 -5
- data/lib/geocoder/lookups/base.rb +21 -16
- data/lib/geocoder/lookups/bing.rb +15 -2
- data/lib/geocoder/lookups/esri.rb +1 -1
- data/lib/geocoder/lookups/geocoder_ca.rb +1 -2
- data/lib/geocoder/lookups/geocodio.rb +3 -3
- data/lib/geocoder/lookups/google.rb +3 -3
- data/lib/geocoder/lookups/google_places_details.rb +3 -3
- data/lib/geocoder/lookups/mapquest.rb +3 -3
- data/lib/geocoder/lookups/maxmind.rb +1 -1
- data/lib/geocoder/lookups/maxmind_geoip2.rb +69 -0
- data/lib/geocoder/lookups/nominatim.rb +1 -1
- data/lib/geocoder/lookups/opencagedata.rb +4 -4
- data/lib/geocoder/lookups/pointpin.rb +4 -4
- data/lib/geocoder/lookups/postcode_anywhere_uk.rb +3 -3
- data/lib/geocoder/lookups/yahoo.rb +3 -3
- data/lib/geocoder/lookups/yandex.rb +3 -3
- data/lib/geocoder/query.rb +1 -1
- data/lib/geocoder/request.rb +70 -12
- data/lib/geocoder/results/geoip2.rb +8 -14
- data/lib/geocoder/results/maxmind_geoip2.rb +9 -0
- data/lib/geocoder/results/yandex.rb +5 -5
- data/lib/geocoder/version.rb +1 -1
- metadata +6 -147
- data/.gitignore +0 -6
- data/.travis.yml +0 -32
- data/Rakefile +0 -25
- data/examples/sidekiq_worker.rb +0 -16
- data/gemfiles/Gemfile.mongoid-2.4.x +0 -16
- data/test/fixtures/baidu_invalid_key +0 -1
- data/test/fixtures/baidu_ip_202_198_16_3 +0 -19
- data/test/fixtures/baidu_ip_invalid_key +0 -1
- data/test/fixtures/baidu_ip_no_results +0 -1
- data/test/fixtures/baidu_no_results +0 -1
- data/test/fixtures/baidu_reverse +0 -1
- data/test/fixtures/baidu_shanghai_pearl_tower +0 -12
- data/test/fixtures/bing_invalid_key +0 -1
- data/test/fixtures/bing_madison_square_garden +0 -40
- data/test/fixtures/bing_no_results +0 -16
- data/test/fixtures/bing_reverse +0 -42
- data/test/fixtures/cloudmade_invalid_key +0 -1
- data/test/fixtures/cloudmade_madison_square_garden +0 -1
- data/test/fixtures/cloudmade_no_results +0 -1
- data/test/fixtures/esri_madison_square_garden +0 -59
- data/test/fixtures/esri_no_results +0 -8
- data/test/fixtures/esri_reverse +0 -21
- data/test/fixtures/freegeoip_74_200_247_59 +0 -12
- data/test/fixtures/freegeoip_no_results +0 -1
- data/test/fixtures/geocoder_ca_madison_square_garden +0 -1
- data/test/fixtures/geocoder_ca_no_results +0 -1
- data/test/fixtures/geocoder_ca_reverse +0 -34
- data/test/fixtures/geocoder_us_madison_square_garden +0 -1
- data/test/fixtures/geocoder_us_no_results +0 -1
- data/test/fixtures/geocodio_1101_pennsylvania_ave +0 -1
- data/test/fixtures/geocodio_bad_api_key +0 -3
- data/test/fixtures/geocodio_invalid +0 -4
- data/test/fixtures/geocodio_no_results +0 -1
- data/test/fixtures/geocodio_over_query_limit +0 -4
- data/test/fixtures/google_garbage +0 -456
- data/test/fixtures/google_madison_square_garden +0 -57
- data/test/fixtures/google_no_city_data +0 -44
- data/test/fixtures/google_no_locality +0 -51
- data/test/fixtures/google_no_results +0 -4
- data/test/fixtures/google_over_limit +0 -4
- data/test/fixtures/google_places_details_invalid_request +0 -4
- data/test/fixtures/google_places_details_madison_square_garden +0 -120
- data/test/fixtures/google_places_details_no_results +0 -4
- data/test/fixtures/google_places_details_no_reviews +0 -60
- data/test/fixtures/google_places_details_no_types +0 -66
- data/test/fixtures/here_madison_square_garden +0 -72
- data/test/fixtures/here_no_results +0 -8
- data/test/fixtures/mapquest_error +0 -16
- data/test/fixtures/mapquest_invalid_api_key +0 -16
- data/test/fixtures/mapquest_invalid_request +0 -16
- data/test/fixtures/mapquest_madison_square_garden +0 -52
- data/test/fixtures/mapquest_no_results +0 -16
- data/test/fixtures/maxmind_24_24_24_21 +0 -1
- data/test/fixtures/maxmind_24_24_24_22 +0 -1
- data/test/fixtures/maxmind_24_24_24_23 +0 -1
- data/test/fixtures/maxmind_24_24_24_24 +0 -1
- data/test/fixtures/maxmind_74_200_247_59 +0 -1
- data/test/fixtures/maxmind_invalid_key +0 -1
- data/test/fixtures/maxmind_no_results +0 -1
- data/test/fixtures/nominatim_madison_square_garden +0 -150
- data/test/fixtures/nominatim_no_results +0 -1
- data/test/fixtures/nominatim_over_limit +0 -1
- data/test/fixtures/okf_kirstinmaki +0 -67
- data/test/fixtures/okf_no_results +0 -4
- data/test/fixtures/opencagedata_invalid_api_key +0 -25
- data/test/fixtures/opencagedata_invalid_request +0 -26
- data/test/fixtures/opencagedata_madison_square_garden +0 -73
- data/test/fixtures/opencagedata_no_results +0 -29
- data/test/fixtures/opencagedata_over_limit +0 -31
- data/test/fixtures/ovi_madison_square_garden +0 -72
- data/test/fixtures/ovi_no_results +0 -8
- data/test/fixtures/pointpin_10_10_10_10 +0 -1
- data/test/fixtures/pointpin_555_555_555_555 +0 -1
- data/test/fixtures/pointpin_80_111_555_555 +0 -1
- data/test/fixtures/pointpin_no_results +0 -1
- data/test/fixtures/postcode_anywhere_uk_geocode_v2_00_WR26NJ +0 -1
- data/test/fixtures/postcode_anywhere_uk_geocode_v2_00_generic_error +0 -1
- data/test/fixtures/postcode_anywhere_uk_geocode_v2_00_hampshire +0 -1
- data/test/fixtures/postcode_anywhere_uk_geocode_v2_00_key_limit_exceeded +0 -1
- data/test/fixtures/postcode_anywhere_uk_geocode_v2_00_no_results +0 -1
- data/test/fixtures/postcode_anywhere_uk_geocode_v2_00_romsey +0 -1
- data/test/fixtures/postcode_anywhere_uk_geocode_v2_00_unknown_key +0 -1
- data/test/fixtures/smarty_streets_11211 +0 -1
- data/test/fixtures/smarty_streets_madison_square_garden +0 -47
- data/test/fixtures/smarty_streets_no_results +0 -1
- data/test/fixtures/telize_10_10_10_10 +0 -1
- data/test/fixtures/telize_555_555_555_555 +0 -4
- data/test/fixtures/telize_74_200_247_59 +0 -1
- data/test/fixtures/telize_no_results +0 -1
- data/test/fixtures/yahoo_error +0 -1
- data/test/fixtures/yahoo_invalid_key +0 -2
- data/test/fixtures/yahoo_madison_square_garden +0 -52
- data/test/fixtures/yahoo_no_results +0 -10
- data/test/fixtures/yahoo_over_limit +0 -2
- data/test/fixtures/yandex_canada_rue_dupuis_14 +0 -446
- data/test/fixtures/yandex_invalid_key +0 -1
- data/test/fixtures/yandex_kremlin +0 -48
- data/test/fixtures/yandex_new_york +0 -1
- data/test/fixtures/yandex_no_administrative_area +0 -53
- data/test/fixtures/yandex_no_city_and_town +0 -112
- data/test/fixtures/yandex_no_results +0 -16
- data/test/integration/http_client_test.rb +0 -31
- data/test/mongoid_test_helper.rb +0 -43
- data/test/test_helper.rb +0 -423
- data/test/unit/active_record_test.rb +0 -16
- data/test/unit/cache_test.rb +0 -37
- data/test/unit/calculations_test.rb +0 -220
- data/test/unit/configuration_test.rb +0 -55
- data/test/unit/error_handling_test.rb +0 -79
- data/test/unit/geocoder_test.rb +0 -78
- data/test/unit/https_test.rb +0 -17
- data/test/unit/ip_address_test.rb +0 -27
- data/test/unit/lookup_test.rb +0 -153
- data/test/unit/lookups/bing_test.rb +0 -68
- data/test/unit/lookups/dstk_test.rb +0 -26
- data/test/unit/lookups/esri_test.rb +0 -48
- data/test/unit/lookups/freegeoip_test.rb +0 -27
- data/test/unit/lookups/geocoder_ca_test.rb +0 -17
- data/test/unit/lookups/geocodio_test.rb +0 -56
- data/test/unit/lookups/geoip2_test.rb +0 -27
- data/test/unit/lookups/google_places_details_test.rb +0 -122
- data/test/unit/lookups/google_premier_test.rb +0 -22
- data/test/unit/lookups/google_test.rb +0 -84
- data/test/unit/lookups/mapquest_test.rb +0 -60
- data/test/unit/lookups/maxmind_local_test.rb +0 -28
- data/test/unit/lookups/maxmind_test.rb +0 -63
- data/test/unit/lookups/nominatim_test.rb +0 -31
- data/test/unit/lookups/okf_test.rb +0 -38
- data/test/unit/lookups/opencagedata_test.rb +0 -64
- data/test/unit/lookups/pointpin_test.rb +0 -30
- data/test/unit/lookups/postcode_anywhere_uk_test.rb +0 -70
- data/test/unit/lookups/smarty_streets_test.rb +0 -71
- data/test/unit/lookups/telize_test.rb +0 -36
- data/test/unit/lookups/yahoo_test.rb +0 -35
- data/test/unit/method_aliases_test.rb +0 -26
- data/test/unit/model_test.rb +0 -38
- data/test/unit/mongoid_test.rb +0 -47
- data/test/unit/near_test.rb +0 -87
- data/test/unit/oauth_util_test.rb +0 -31
- data/test/unit/proxy_test.rb +0 -37
- data/test/unit/query_test.rb +0 -52
- data/test/unit/rake_task_test.rb +0 -21
- data/test/unit/request_test.rb +0 -35
- data/test/unit/result_test.rb +0 -81
- data/test/unit/test_mode_test.rb +0 -70
@@ -43,9 +43,9 @@ module Geocoder::Lookup
|
|
43
43
|
if doc['statusCode'] == 200
|
44
44
|
return doc['resourceSets'].first['estimatedTotal'] > 0 ? doc['resourceSets'].first['resources'] : []
|
45
45
|
elsif doc['statusCode'] == 401 and doc["authenticationResultCode"] == "InvalidCredentials"
|
46
|
-
raise_error(Geocoder::InvalidApiKey) || warn
|
46
|
+
raise_error(Geocoder::InvalidApiKey) || Geocoder.log(:warn, "Invalid Bing API key.")
|
47
47
|
else
|
48
|
-
warn "Bing Geocoding API error: #{doc['statusCode']} (#{doc['statusDescription']})."
|
48
|
+
Geocoder.log(:warn, "Bing Geocoding API error: #{doc['statusCode']} (#{doc['statusDescription']}).")
|
49
49
|
end
|
50
50
|
return []
|
51
51
|
end
|
@@ -55,5 +55,18 @@ module Geocoder::Lookup
|
|
55
55
|
key: configuration.api_key
|
56
56
|
}.merge(super)
|
57
57
|
end
|
58
|
+
|
59
|
+
def check_response_for_errors!(response)
|
60
|
+
super
|
61
|
+
if response['x-ms-bm-ws-info'].to_i == 1
|
62
|
+
# Occasionally, the servers processing service requests can be overloaded,
|
63
|
+
# and you may receive some responses that contain no results for queries that
|
64
|
+
# you would normally receive a result. To identify this situation,
|
65
|
+
# check the HTTP headers of the response. If the HTTP header X-MS-BM-WS-INFO is set to 1,
|
66
|
+
# it is best to wait a few seconds and try again.
|
67
|
+
raise_error(Geocoder::ServiceUnavailable) ||
|
68
|
+
Geocoder.log(:warn, "Bing Geocoding API error: Service Unavailable")
|
69
|
+
end
|
70
|
+
end
|
58
71
|
end
|
59
72
|
end
|
@@ -21,7 +21,7 @@ module Geocoder::Lookup
|
|
21
21
|
elsif doc['error']['code'] == "005"
|
22
22
|
# "Postal Code is not in the proper Format" => no results, just shut up
|
23
23
|
else
|
24
|
-
warn "Geocoder.ca service error: #{doc['error']['code']} (#{doc['error']['description']})."
|
24
|
+
Geocoder.log(:warn, "Geocoder.ca service error: #{doc['error']['code']} (#{doc['error']['description']}).")
|
25
25
|
end
|
26
26
|
return []
|
27
27
|
end
|
@@ -51,4 +51,3 @@ module Geocoder::Lookup
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
end
|
54
|
-
|
@@ -19,13 +19,13 @@ module Geocoder::Lookup
|
|
19
19
|
|
20
20
|
if doc['error'] == 'Invalid API key'
|
21
21
|
raise_error(Geocoder::InvalidApiKey) ||
|
22
|
-
warn
|
22
|
+
Geocoder.log(:warn, "Geocodio service error: invalid API key.")
|
23
23
|
elsif doc['error'].match(/You have reached your daily maximum/)
|
24
24
|
raise_error(Geocoder::OverQueryLimitError, doc['error']) ||
|
25
|
-
warn
|
25
|
+
Geocoder.log(:warn, "Geocodio service error: #{doc['error']}.")
|
26
26
|
else
|
27
27
|
raise_error(Geocoder::InvalidRequest, doc['error']) ||
|
28
|
-
warn
|
28
|
+
Geocoder.log(:warn, "Geocodio service error: #{doc['error']}.")
|
29
29
|
end
|
30
30
|
[]
|
31
31
|
end
|
@@ -30,13 +30,13 @@ module Geocoder::Lookup
|
|
30
30
|
return doc['results']
|
31
31
|
when "OVER_QUERY_LIMIT"
|
32
32
|
raise_error(Geocoder::OverQueryLimitError) ||
|
33
|
-
warn
|
33
|
+
Geocoder.log(:warn, "Google Geocoding API error: over query limit.")
|
34
34
|
when "REQUEST_DENIED"
|
35
35
|
raise_error(Geocoder::RequestDenied) ||
|
36
|
-
warn
|
36
|
+
Geocoder.log(:warn, "Google Geocoding API error: request denied.")
|
37
37
|
when "INVALID_REQUEST"
|
38
38
|
raise_error(Geocoder::InvalidRequest) ||
|
39
|
-
warn
|
39
|
+
Geocoder.log(:warn, "Google Geocoding API error: invalid request.")
|
40
40
|
end
|
41
41
|
return []
|
42
42
|
end
|
@@ -29,11 +29,11 @@ module Geocoder
|
|
29
29
|
when "OK"
|
30
30
|
return [doc["result"]]
|
31
31
|
when "OVER_QUERY_LIMIT"
|
32
|
-
raise_error(Geocoder::OverQueryLimitError) || warn
|
32
|
+
raise_error(Geocoder::OverQueryLimitError) || Geocoder.log(:warn, "Google Places Details API error: over query limit.")
|
33
33
|
when "REQUEST_DENIED"
|
34
|
-
raise_error(Geocoder::RequestDenied) || warn
|
34
|
+
raise_error(Geocoder::RequestDenied) || Geocoder.log(:warn, "Google Places Details API error: request denied.")
|
35
35
|
when "INVALID_REQUEST"
|
36
|
-
raise_error(Geocoder::InvalidRequest) || warn
|
36
|
+
raise_error(Geocoder::InvalidRequest) || Geocoder.log(:warn, "Google Places Details API error: invalid request.")
|
37
37
|
end
|
38
38
|
|
39
39
|
[]
|
@@ -45,13 +45,13 @@ module Geocoder::Lookup
|
|
45
45
|
case doc['info']['statuscode']
|
46
46
|
when 400 # Error with input
|
47
47
|
raise_error(Geocoder::InvalidRequest, messages) ||
|
48
|
-
warn
|
48
|
+
Geocoder.log(:warn, "Mapquest Geocoding API error: #{messages}")
|
49
49
|
when 403 # Key related error
|
50
50
|
raise_error(Geocoder::InvalidApiKey, messages) ||
|
51
|
-
warn
|
51
|
+
Geocoder.log(:warn, "Mapquest Geocoding API error: #{messages}")
|
52
52
|
when 500 # Unknown error
|
53
53
|
raise_error(Geocoder::Error, messages) ||
|
54
|
-
warn
|
54
|
+
Geocoder.log(:warn, "Mapquest Geocoding API error: #{messages}")
|
55
55
|
end
|
56
56
|
[]
|
57
57
|
end
|
@@ -64,7 +64,7 @@ module Geocoder::Lookup
|
|
64
64
|
if !data_contains_error?(doc)
|
65
65
|
return [doc]
|
66
66
|
elsif doc.last == "INVALID_LICENSE_KEY"
|
67
|
-
raise_error(Geocoder::InvalidApiKey) || warn
|
67
|
+
raise_error(Geocoder::InvalidApiKey) || Geocoder.log(:warn, "Invalid MaxMind API key.")
|
68
68
|
end
|
69
69
|
end
|
70
70
|
return []
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'geocoder/lookups/base'
|
2
|
+
require 'geocoder/results/maxmind_geoip2'
|
3
|
+
|
4
|
+
module Geocoder::Lookup
|
5
|
+
class MaxmindGeoip2 < Base
|
6
|
+
|
7
|
+
def name
|
8
|
+
"MaxMind GeoIP2"
|
9
|
+
end
|
10
|
+
|
11
|
+
def use_ssl?
|
12
|
+
# Maxmind's GeoIP2 Precision Services only supports HTTPS,
|
13
|
+
# otherwise a `404 Not Found` HTTP response will be returned
|
14
|
+
true
|
15
|
+
end
|
16
|
+
|
17
|
+
def query_url(query)
|
18
|
+
"#{protocol}://geoip.maxmind.com/geoip/v2.1/#{configured_service!}/#{URI.escape(query.sanitized_text.strip)}"
|
19
|
+
end
|
20
|
+
|
21
|
+
private # ---------------------------------------------------------------
|
22
|
+
|
23
|
+
##
|
24
|
+
# Return the name of the configured service, or raise an exception.
|
25
|
+
#
|
26
|
+
def configured_service!
|
27
|
+
if s = configuration[:service] and services.include?(s) and configuration[:basic_auth][:user] and configuration[:basic_auth][:password]
|
28
|
+
return s
|
29
|
+
else
|
30
|
+
raise(
|
31
|
+
Geocoder::ConfigurationError,
|
32
|
+
"When using MaxMind GeoIP2 you MUST specify a service name and basic_auth: " +
|
33
|
+
"Geocoder.configure(:maxmind_geoip2 => {:service => ...}, " +
|
34
|
+
":basic_auth => {:user ..., :password => ...}), " +
|
35
|
+
"where service is one of: #{services.inspect}"
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def data_contains_error?(doc)
|
41
|
+
(["code", "error"] - doc.keys).empty?
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# Service names used in URL.
|
46
|
+
#
|
47
|
+
def services
|
48
|
+
[
|
49
|
+
:country,
|
50
|
+
:city,
|
51
|
+
:insights,
|
52
|
+
]
|
53
|
+
end
|
54
|
+
|
55
|
+
def results(query)
|
56
|
+
# don't look up a loopback address
|
57
|
+
return [] if query.loopback_ip_address?
|
58
|
+
doc = fetch_data(query)
|
59
|
+
if doc
|
60
|
+
if !data_contains_error?(doc)
|
61
|
+
return [doc]
|
62
|
+
else
|
63
|
+
Geocoder.log(:warn, "MaxMind GeoIP2 Geocoding API error: #{doc['code']} (#{doc['error']}).")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
return []
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -27,7 +27,7 @@ module Geocoder::Lookup
|
|
27
27
|
|
28
28
|
def parse_raw_data(raw_data)
|
29
29
|
if raw_data.include?("Bandwidth limit exceeded")
|
30
|
-
raise_error(Geocoder::OverQueryLimitError) || warn
|
30
|
+
raise_error(Geocoder::OverQueryLimitError) || Geocoder.log(:warn, "Over API query limit.")
|
31
31
|
else
|
32
32
|
super(raw_data)
|
33
33
|
end
|
@@ -26,16 +26,16 @@ module Geocoder::Lookup
|
|
26
26
|
case doc['status']['code']
|
27
27
|
when 400 # Error with input
|
28
28
|
raise_error(Geocoder::InvalidRequest, messages) ||
|
29
|
-
warn
|
29
|
+
Geocoder.log(:warn, "Opencagedata Geocoding API error: #{messages}")
|
30
30
|
when 403 # Key related error
|
31
31
|
raise_error(Geocoder::InvalidApiKey, messages) ||
|
32
|
-
warn
|
32
|
+
Geocoder.log(:warn, "Opencagedata Geocoding API error: #{messages}")
|
33
33
|
when 402 # Quata Exceeded
|
34
34
|
raise_error(Geocoder::OverQueryLimitError, messages) ||
|
35
|
-
warn
|
35
|
+
Geocoder.log(:warn, "Opencagedata Geocoding API error: #{messages}")
|
36
36
|
when 500 # Unknown error
|
37
37
|
raise_error(Geocoder::Error, messages) ||
|
38
|
-
warn
|
38
|
+
Geocoder.log(:warn, "Opencagedata Geocoding API error: #{messages}")
|
39
39
|
end
|
40
40
|
|
41
41
|
return doc["results"]
|
@@ -28,14 +28,14 @@ module Geocoder::Lookup
|
|
28
28
|
elsif doc['error']
|
29
29
|
case doc['error']
|
30
30
|
when "Invalid IP address"
|
31
|
-
raise_error(Geocoder::InvalidRequest) || warn
|
31
|
+
raise_error(Geocoder::InvalidRequest) || Geocoder.log(:warn, "Invalid Pointpin request.")
|
32
32
|
when "Invalid API key"
|
33
|
-
raise_error(Geocoder::InvalidApiKey) || warn
|
33
|
+
raise_error(Geocoder::InvalidApiKey) || Geocoder.log(:warn, "Invalid Pointpin API key.")
|
34
34
|
when "Address not found"
|
35
|
-
warn
|
35
|
+
Geocoder.log(:warn, "Address not found.")
|
36
36
|
end
|
37
37
|
else
|
38
|
-
raise_error(Geocoder::Error) || warn
|
38
|
+
raise_error(Geocoder::Error) || Geocoder.log(:warn, "Pointpin server error")
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
@@ -33,11 +33,11 @@ module Geocoder::Lookup
|
|
33
33
|
def raise_exception_for_response(response)
|
34
34
|
case response['Error']
|
35
35
|
when *DAILY_LIMIT_EXEEDED_ERROR_CODES
|
36
|
-
raise_error(Geocoder::OverQueryLimitError, response['Cause']) || warn
|
36
|
+
raise_error(Geocoder::OverQueryLimitError, response['Cause']) || Geocoder.log(:warn, response['Cause'])
|
37
37
|
when INVALID_API_KEY_ERROR_CODE
|
38
|
-
raise_error(Geocoder::InvalidApiKey, response['Cause']) || warn
|
38
|
+
raise_error(Geocoder::InvalidApiKey, response['Cause']) || Geocoder.log(:warn, response['Cause'])
|
39
39
|
else # anything else just raise general error with the api cause
|
40
|
-
raise_error(Geocoder::Error, response['Cause']) || warn
|
40
|
+
raise_error(Geocoder::Error, response['Cause']) || Geocoder.log(:warn, response['Cause'])
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
@@ -37,7 +37,7 @@ module Geocoder::Lookup
|
|
37
37
|
return []
|
38
38
|
end
|
39
39
|
else
|
40
|
-
warn "Yahoo Geocoding API error: #{doc['responsecode']} (#{doc['reason']})."
|
40
|
+
Geocoder.log(:warn, "Yahoo Geocoding API error: #{doc['responsecode']} (#{doc['reason']}).")
|
41
41
|
return []
|
42
42
|
end
|
43
43
|
end
|
@@ -52,9 +52,9 @@ module Geocoder::Lookup
|
|
52
52
|
def parse_raw_data(raw_data)
|
53
53
|
if raw_data.match(/^<\?xml/)
|
54
54
|
if raw_data.include?("Rate Limit Exceeded")
|
55
|
-
raise_error(Geocoder::OverQueryLimitError) || warn
|
55
|
+
raise_error(Geocoder::OverQueryLimitError) || Geocoder.log(:warn, "Over API query limit.")
|
56
56
|
elsif raw_data =~ /<yahoo:description>(Please provide valid credentials.*)<\/yahoo:description>/i
|
57
|
-
raise_error(Geocoder::InvalidApiKey) || warn
|
57
|
+
raise_error(Geocoder::InvalidApiKey) || Geocoder.log(:warn, "Invalid API key. Error response: #{$1}")
|
58
58
|
end
|
59
59
|
else
|
60
60
|
super(raw_data)
|
@@ -22,9 +22,9 @@ module Geocoder::Lookup
|
|
22
22
|
return [] unless doc = fetch_data(query)
|
23
23
|
if err = doc['error']
|
24
24
|
if err["status"] == 401 and err["message"] == "invalid key"
|
25
|
-
raise_error(Geocoder::InvalidApiKey) || warn
|
25
|
+
raise_error(Geocoder::InvalidApiKey) || Geocoder.log(:warn, "Invalid API key.")
|
26
26
|
else
|
27
|
-
warn "Yandex Geocoding API error: #{err['status']} (#{err['message']})."
|
27
|
+
Geocoder.log(:warn, "Yandex Geocoding API error: #{err['status']} (#{err['message']}).")
|
28
28
|
end
|
29
29
|
return []
|
30
30
|
end
|
@@ -32,7 +32,7 @@ module Geocoder::Lookup
|
|
32
32
|
meta = doc['metaDataProperty']['GeocoderResponseMetaData']
|
33
33
|
return meta['found'].to_i > 0 ? doc['featureMember'] : []
|
34
34
|
else
|
35
|
-
warn "Yandex Geocoding API error: unexpected response format."
|
35
|
+
Geocoder.log(:warn, "Yandex Geocoding API error: unexpected response format.")
|
36
36
|
return []
|
37
37
|
end
|
38
38
|
end
|
data/lib/geocoder/query.rb
CHANGED
@@ -32,7 +32,7 @@ module Geocoder
|
|
32
32
|
# appropriate to the Query text.
|
33
33
|
#
|
34
34
|
def lookup
|
35
|
-
if ip_address?
|
35
|
+
if !options[:street_address] and (options[:ip_address] or ip_address?)
|
36
36
|
name = options[:ip_lookup] || Configuration.ip_lookup || Geocoder::Lookup.ip_services.first
|
37
37
|
else
|
38
38
|
name = options[:lookup] || Configuration.lookup || Geocoder::Lookup.street_services.first
|
data/lib/geocoder/request.rb
CHANGED
@@ -1,21 +1,79 @@
|
|
1
1
|
module Geocoder
|
2
2
|
module Request
|
3
3
|
|
4
|
+
# The location() method is vulnerable to trivial IP spoofing.
|
5
|
+
# Don't use it in authorization/authentication code, or any
|
6
|
+
# other security-sensitive application. Use safe_location
|
7
|
+
# instead.
|
4
8
|
def location
|
5
|
-
@location ||=
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
9
|
+
@location ||= Geocoder.search(geocoder_spoofable_ip, ip_address: true).first
|
10
|
+
end
|
11
|
+
|
12
|
+
# This safe_location() protects you from trivial IP spoofing.
|
13
|
+
# For requests that go through a proxy that you haven't
|
14
|
+
# whitelisted as trusted in your Rack config, you will get the
|
15
|
+
# location for the IP of the last untrusted proxy in the chain,
|
16
|
+
# not the original client IP. You WILL NOT get the location
|
17
|
+
# corresponding to the original client IP for any request sent
|
18
|
+
# through a non-whitelisted proxy.
|
19
|
+
def safe_location
|
20
|
+
@location ||= Geocoder.search(ip, ip_address: true).first
|
21
|
+
end
|
22
|
+
|
23
|
+
# There's a whole zoo of nonstandard headers added by various
|
24
|
+
# proxy softwares to indicate original client IP.
|
25
|
+
# ANY of these can be trivially spoofed!
|
26
|
+
# (except REMOTE_ADDR, which should by set by your server,
|
27
|
+
# and is included at the end as a fallback.
|
28
|
+
# Order does matter: we're following the convention established in
|
29
|
+
# ActionDispatch::RemoteIp::GetIp::calculate_ip()
|
30
|
+
# https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/remote_ip.rb
|
31
|
+
# where the forwarded_for headers, possibly containing lists,
|
32
|
+
# are arbitrarily preferred over headers expected to contain a
|
33
|
+
# single address.
|
34
|
+
GEOCODER_CANDIDATE_HEADERS = ['HTTP_X_FORWARDED_FOR',
|
35
|
+
'HTTP_X_FORWARDED',
|
36
|
+
'HTTP_FORWARDED_FOR',
|
37
|
+
'HTTP_FORWARDED',
|
38
|
+
'HTTP_X_CLIENT_IP',
|
39
|
+
'HTTP_CLIENT_IP',
|
40
|
+
'HTTP_X_REAL_IP',
|
41
|
+
'HTTP_X_CLUSTER_CLIENT_IP',
|
42
|
+
'REMOTE_ADDR']
|
43
|
+
|
44
|
+
def geocoder_spoofable_ip
|
45
|
+
|
46
|
+
# We could use a more sophisticated IP-guessing algorithm here,
|
47
|
+
# in which we'd try to resolve the use of different headers by
|
48
|
+
# different proxies. The idea is that by comparing IPs repeated
|
49
|
+
# in different headers, you can sometimes decide which header
|
50
|
+
# was used by a proxy further along in the chain, and thus
|
51
|
+
# prefer the headers used earlier. However, the gains might not
|
52
|
+
# be worth the performance tradeoff, since this method is likely
|
53
|
+
# to be called on every request in a lot of applications.
|
54
|
+
GEOCODER_CANDIDATE_HEADERS.each do |header|
|
55
|
+
if @env.has_key? header
|
56
|
+
addrs = geocoder_split_ip_addresses(@env[header])
|
57
|
+
addrs = geocoder_reject_trusted_ip_addresses(addrs)
|
58
|
+
return addrs.first if addrs.any?
|
15
59
|
end
|
16
|
-
Geocoder.search(real_ip).first
|
17
60
|
end
|
18
|
-
|
61
|
+
|
62
|
+
@env['REMOTE_ADDR']
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def geocoder_split_ip_addresses(ip_addresses)
|
68
|
+
ip_addresses ? ip_addresses.strip.split(/[,\s]+/) : []
|
69
|
+
end
|
70
|
+
|
71
|
+
# use Rack's trusted_proxy?() method to filter out IPs that have
|
72
|
+
# been configured as trusted; includes private ranges by
|
73
|
+
# default. (we don't want every lookup to return the location
|
74
|
+
# of our own proxy/load balancer)
|
75
|
+
def geocoder_reject_trusted_ip_addresses(ip_addresses)
|
76
|
+
ip_addresses.reject { |ip| trusted_proxy?(ip) }
|
19
77
|
end
|
20
78
|
end
|
21
79
|
end
|
@@ -13,41 +13,35 @@ module Geocoder
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def latitude
|
16
|
-
|
17
|
-
@data['location']['latitude'].to_f
|
16
|
+
@data.fetch('location',{}).fetch('latitude',0.0)
|
18
17
|
end
|
19
18
|
|
20
19
|
def longitude
|
21
|
-
|
22
|
-
@data['location']['longitude'].to_f
|
20
|
+
@data.fetch('location',{}).fetch('longitude',0.0)
|
23
21
|
end
|
24
22
|
|
25
23
|
def city
|
26
|
-
|
27
|
-
@data['city']['names']['en']
|
24
|
+
@data.fetch('city', {}).fetch('names', {}).fetch('en', '')
|
28
25
|
end
|
29
26
|
|
30
27
|
def state
|
31
|
-
|
32
|
-
@data['subdivisions'][0]['names']['en']
|
28
|
+
@data.fetch('subdivisions',[]).fetch(0,{}).fetch('names',{}).fetch('en','')
|
33
29
|
end
|
34
30
|
|
35
31
|
def state_code
|
36
|
-
|
37
|
-
@data['subdivisions'][0]['iso_code']
|
32
|
+
@data.fetch('subdivisions',[]).fetch(0,{}).fetch('iso_code','')
|
38
33
|
end
|
39
34
|
|
40
35
|
def country
|
41
|
-
@data
|
36
|
+
@data.fetch('country', {}).fetch('names',{}).fetch('en','')
|
42
37
|
end
|
43
38
|
|
44
39
|
def country_code
|
45
|
-
@data
|
40
|
+
@data.fetch('country',{}).fetch('iso_code','')
|
46
41
|
end
|
47
42
|
|
48
43
|
def postal_code
|
49
|
-
|
50
|
-
@data['postal']['code']
|
44
|
+
@data.fetch('postal',{}).fetch('code','')
|
51
45
|
end
|
52
46
|
|
53
47
|
def self.response_attributes
|