IPinfo 1.0.1 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/cd_rubygems.yaml +37 -0
- data/.github/workflows/unittest.yaml +40 -0
- data/.rubocop.yml +6 -0
- data/.ruby-version +1 -1
- data/README.md +107 -20
- data/ipinfo.gemspec +10 -12
- data/lib/ipinfo/adapter.rb +131 -4
- data/lib/ipinfo/countriesData.rb +1045 -0
- data/lib/ipinfo/ipAddressMatcher.rb +92 -0
- data/lib/ipinfo/mod.rb +3 -0
- data/lib/ipinfo/version.rb +1 -1
- data/lib/ipinfo.rb +141 -32
- data/lib/ipinfo_core.rb +124 -0
- data/lib/ipinfo_lite.rb +116 -0
- data/lib/ipinfo_plus.rb +124 -0
- metadata +100 -13
- data/.travis.yml +0 -11
- data/lib/ipinfo/countries.json +0 -1
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'resolv'
|
|
4
|
+
require 'ipaddr'
|
|
5
|
+
|
|
6
|
+
class IPinfo::IpAddressMatcher
|
|
7
|
+
attr_accessor :required_address
|
|
8
|
+
|
|
9
|
+
@@bogon_list = [
|
|
10
|
+
"0.0.0.0/8",
|
|
11
|
+
"10.0.0.0/8",
|
|
12
|
+
"100.64.0.0/10",
|
|
13
|
+
"127.0.0.0/8",
|
|
14
|
+
"169.254.0.0/16",
|
|
15
|
+
"172.16.0.0/12",
|
|
16
|
+
"192.0.0.0/24",
|
|
17
|
+
"192.0.2.0/24",
|
|
18
|
+
"192.168.0.0/16",
|
|
19
|
+
"198.18.0.0/15",
|
|
20
|
+
"198.51.100.0/24",
|
|
21
|
+
"203.0.113.0/24",
|
|
22
|
+
"224.0.0.0/4",
|
|
23
|
+
"240.0.0.0/4",
|
|
24
|
+
"255.255.255.255/32",
|
|
25
|
+
"::/128",
|
|
26
|
+
"::1/128",
|
|
27
|
+
"::ffff:0:0/96",
|
|
28
|
+
"::/96",
|
|
29
|
+
"100::/64",
|
|
30
|
+
"2001:10::/28",
|
|
31
|
+
"2001:db8::/32",
|
|
32
|
+
"fc00::/7",
|
|
33
|
+
"fe80::/10",
|
|
34
|
+
"fec0::/10",
|
|
35
|
+
"ff00::/8",
|
|
36
|
+
"2002::/24",
|
|
37
|
+
"2002:a00::/24",
|
|
38
|
+
"2002:7f00::/24",
|
|
39
|
+
"2002:a9fe::/32",
|
|
40
|
+
"2002:ac10::/28",
|
|
41
|
+
"2002:c000::/40",
|
|
42
|
+
"2002:c000:200::/40",
|
|
43
|
+
"2002:c0a8::/32",
|
|
44
|
+
"2002:c612::/31",
|
|
45
|
+
"2002:c633:6400::/40",
|
|
46
|
+
"2002:cb00:7100::/40",
|
|
47
|
+
"2002:e000::/20",
|
|
48
|
+
"2002:f000::/20",
|
|
49
|
+
"2002:ffff:ffff::/48",
|
|
50
|
+
"2001::/40",
|
|
51
|
+
"2001:0:a00::/40",
|
|
52
|
+
"2001:0:7f00::/40",
|
|
53
|
+
"2001:0:a9fe::/48",
|
|
54
|
+
"2001:0:ac10::/44",
|
|
55
|
+
"2001:0:c000::/56",
|
|
56
|
+
"2001:0:c000:200::/56",
|
|
57
|
+
"2001:0:c0a8::/48",
|
|
58
|
+
"2001:0:c612::/47",
|
|
59
|
+
"2001:0:c633:6400::/56",
|
|
60
|
+
"2001:0:cb00:7100::/56",
|
|
61
|
+
"2001:0:e000::/36",
|
|
62
|
+
"2001:0:f000::/36",
|
|
63
|
+
"2001:0:ffff:ffff::/64"
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
def initialize(ip)
|
|
67
|
+
if !ip.index('/').nil? and ip.index('/') > 0
|
|
68
|
+
addr_mask = ip.split('/')
|
|
69
|
+
ip = addr_mask[0]
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
@required_address = parseAddress(ip)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def parseAddress(ip)
|
|
76
|
+
begin
|
|
77
|
+
Resolv.getaddress ip
|
|
78
|
+
rescue Resolv::ResolvError => e
|
|
79
|
+
pp e
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def matches
|
|
84
|
+
for bogon in @@bogon_list do
|
|
85
|
+
if bogon.include? required_address
|
|
86
|
+
return true
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
false
|
|
91
|
+
end
|
|
92
|
+
end
|
data/lib/ipinfo/mod.rb
CHANGED
data/lib/ipinfo/version.rb
CHANGED
data/lib/ipinfo.rb
CHANGED
|
@@ -8,14 +8,18 @@ require 'ipinfo/errors'
|
|
|
8
8
|
require 'ipinfo/response'
|
|
9
9
|
require 'ipinfo/version'
|
|
10
10
|
require 'json'
|
|
11
|
+
require_relative 'ipinfo/ipAddressMatcher'
|
|
12
|
+
require_relative 'ipinfo/countriesData'
|
|
11
13
|
|
|
12
14
|
module IPinfo
|
|
15
|
+
include CountriesData
|
|
13
16
|
DEFAULT_CACHE_MAXSIZE = 4096
|
|
14
17
|
DEFAULT_CACHE_TTL = 60 * 60 * 24
|
|
15
|
-
DEFAULT_COUNTRY_FILE = File.join(File.dirname(__FILE__),
|
|
16
|
-
'ipinfo/countries.json')
|
|
17
18
|
RATE_LIMIT_MESSAGE = 'To increase your limits, please review our ' \
|
|
18
19
|
'paid plans at https://ipinfo.io/pricing'
|
|
20
|
+
# Base URL to get country flag image link.
|
|
21
|
+
# "PK" -> "https://cdn.ipinfo.io/static/images/countries-flags/PK.svg"
|
|
22
|
+
COUNTRY_FLAGS_URL = "https://cdn.ipinfo.io/static/images/countries-flags/"
|
|
19
23
|
|
|
20
24
|
class << self
|
|
21
25
|
def create(access_token = nil, settings = {})
|
|
@@ -30,43 +34,120 @@ class IPinfo::IPinfo
|
|
|
30
34
|
|
|
31
35
|
def initialize(access_token = nil, settings = {})
|
|
32
36
|
@access_token = access_token
|
|
33
|
-
|
|
37
|
+
prepare_http_client(settings.fetch('http_client', nil))
|
|
34
38
|
|
|
35
39
|
maxsize = settings.fetch('maxsize', DEFAULT_CACHE_MAXSIZE)
|
|
36
40
|
ttl = settings.fetch('ttl', DEFAULT_CACHE_TTL)
|
|
37
41
|
@cache = settings.fetch('cache', DefaultCache.new(ttl, maxsize))
|
|
38
|
-
@countries =
|
|
39
|
-
|
|
42
|
+
@countries = settings.fetch('countries', DEFAULT_COUNTRY_LIST)
|
|
43
|
+
@eu_countries = settings.fetch('eu_countries', DEFAULT_EU_COUNTRIES_LIST)
|
|
44
|
+
@countries_flags = settings.fetch('countries_flags', DEFAULT_COUNTRIES_FLAG_LIST)
|
|
45
|
+
@countries_currencies = settings.fetch('countries_currencies', DEFAULT_COUNTRIES_CURRENCIES_LIST)
|
|
46
|
+
@continents = settings.fetch('continents', DEFAULT_CONTINENT_LIST)
|
|
40
47
|
end
|
|
41
48
|
|
|
42
49
|
def details(ip_address = nil)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
50
|
+
details_base(ip_address, :v4)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def details_v6(ip_address = nil)
|
|
54
|
+
details_base(ip_address, :v6)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def resproxy(ip_address)
|
|
58
|
+
cache_key_str = "resproxy:#{ip_address}"
|
|
59
|
+
res = @cache.get(cache_key(cache_key_str))
|
|
60
|
+
return Response.new(res) unless res.nil?
|
|
61
|
+
|
|
62
|
+
response = @httpc.get("/resproxy/#{CGI.escape(ip_address)}", :v4)
|
|
63
|
+
|
|
64
|
+
if response.status.eql?(429)
|
|
65
|
+
raise RateLimitError,
|
|
66
|
+
RATE_LIMIT_MESSAGE
|
|
47
67
|
end
|
|
48
68
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
69
|
+
details = JSON.parse(response.body, symbolize_names: true)
|
|
70
|
+
@cache.set(cache_key(cache_key_str), details)
|
|
71
|
+
Response.new(details)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def get_map_url(ips)
|
|
75
|
+
if !ips.kind_of?(Array)
|
|
76
|
+
return JSON.generate({:error => 'Invalid input. Array required!'})
|
|
77
|
+
end
|
|
78
|
+
if ips.length > 500000
|
|
79
|
+
return JSON.generate({:error => 'No more than 500,000 ips allowed!'})
|
|
52
80
|
end
|
|
53
81
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
82
|
+
json_ips = JSON.generate({:ips => ips})
|
|
83
|
+
res = @httpc.post('/tools/map', json_ips)
|
|
84
|
+
|
|
85
|
+
obj = JSON.parse(res.body)
|
|
86
|
+
obj['reportUrl']
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def batch_requests(url_array, api_token)
|
|
90
|
+
result = Hash.new
|
|
91
|
+
lookup_ips = []
|
|
92
|
+
|
|
93
|
+
url_array.each { |url|
|
|
94
|
+
ip = @cache.get(cache_key(url))
|
|
95
|
+
|
|
96
|
+
unless ip.nil?
|
|
97
|
+
result.store(url, ip)
|
|
98
|
+
else
|
|
99
|
+
lookup_ips << url
|
|
100
|
+
end
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if lookup_ips.empty?
|
|
104
|
+
return result
|
|
58
105
|
end
|
|
59
106
|
|
|
60
|
-
|
|
107
|
+
begin
|
|
108
|
+
lookup_ips.each_slice(1000){ |ips|
|
|
109
|
+
json_arr = JSON.generate(lookup_ips)
|
|
110
|
+
res = @httpc.post("/batch?token=#{api_token}", json_arr, 5)
|
|
111
|
+
|
|
112
|
+
raise StandardError, "Request Quota Exceeded" if res.status == 429
|
|
113
|
+
|
|
114
|
+
data = JSON.parse(res.body)
|
|
115
|
+
data.each { |key, val|
|
|
116
|
+
@cache.set(cache_key(key), val)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
result.merge!(data)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
rescue StandardError => e
|
|
123
|
+
return e.message
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
result
|
|
61
127
|
end
|
|
62
128
|
|
|
63
129
|
protected
|
|
64
130
|
|
|
65
|
-
def
|
|
66
|
-
|
|
131
|
+
def prepare_http_client(httpc = nil)
|
|
132
|
+
@httpc = Adapter.new(access_token, httpc || :net_http)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
private
|
|
136
|
+
|
|
137
|
+
def request_details(ip_address = nil, host_type)
|
|
138
|
+
if isBogon(ip_address)
|
|
139
|
+
details = {}
|
|
140
|
+
details[:ip] = ip_address
|
|
141
|
+
details[:bogon] = true
|
|
142
|
+
details[:ip_address] = IPAddr.new(ip_address)
|
|
143
|
+
|
|
144
|
+
return details
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
res = @cache.get(cache_key(ip_address))
|
|
67
148
|
return res unless res.nil?
|
|
68
149
|
|
|
69
|
-
response = @httpc.get(escape_path(ip_address))
|
|
150
|
+
response = @httpc.get(escape_path(ip_address), host_type)
|
|
70
151
|
|
|
71
152
|
if response.status.eql?(429)
|
|
72
153
|
raise RateLimitError,
|
|
@@ -74,26 +155,54 @@ class IPinfo::IPinfo
|
|
|
74
155
|
end
|
|
75
156
|
|
|
76
157
|
details = JSON.parse(response.body, symbolize_names: true)
|
|
77
|
-
@cache.set(ip_address, details)
|
|
158
|
+
@cache.set(cache_key(ip_address), details)
|
|
78
159
|
details
|
|
79
160
|
end
|
|
80
161
|
|
|
81
|
-
def
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
162
|
+
def details_base(ip_address, host_type)
|
|
163
|
+
details = request_details(ip_address, host_type)
|
|
164
|
+
if details.key? :country
|
|
165
|
+
details[:country_name] =
|
|
166
|
+
@countries.fetch(details.fetch(:country), nil)
|
|
167
|
+
details[:is_eu] =
|
|
168
|
+
@eu_countries.include?(details.fetch(:country))
|
|
169
|
+
details[:country_flag] =
|
|
170
|
+
@countries_flags.fetch(details.fetch(:country), nil)
|
|
171
|
+
details[:country_currency] =
|
|
172
|
+
@countries_currencies.fetch(details.fetch(:country), nil)
|
|
173
|
+
details[:continent] =
|
|
174
|
+
@continents.fetch(details.fetch(:country), nil)
|
|
175
|
+
details[:country_flag_url] = COUNTRY_FLAGS_URL + details.fetch(:country) + ".svg"
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
if details.key? :ip
|
|
179
|
+
details[:ip_address] =
|
|
180
|
+
IPAddr.new(details.fetch(:ip))
|
|
181
|
+
end
|
|
88
182
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
183
|
+
if details.key? :loc
|
|
184
|
+
loc = details.fetch(:loc).split(',')
|
|
185
|
+
details[:latitude] = loc[0]
|
|
186
|
+
details[:longitude] = loc[1]
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
Response.new(details)
|
|
92
190
|
end
|
|
93
191
|
|
|
94
|
-
|
|
192
|
+
def isBogon(ip)
|
|
193
|
+
if ip.nil?
|
|
194
|
+
return false
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
matcher_object = IpAddressMatcher.new(ip)
|
|
198
|
+
matcher_object.matches
|
|
199
|
+
end
|
|
95
200
|
|
|
96
201
|
def escape_path(ip)
|
|
97
202
|
ip ? "/#{CGI.escape(ip)}" : '/'
|
|
98
203
|
end
|
|
204
|
+
|
|
205
|
+
def cache_key(ip)
|
|
206
|
+
"1:#{ip}"
|
|
207
|
+
end
|
|
99
208
|
end
|
data/lib/ipinfo_core.rb
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'ipinfo/adapter'
|
|
4
|
+
require 'ipinfo/cache/default_cache'
|
|
5
|
+
require 'ipinfo/errors'
|
|
6
|
+
require 'ipinfo/response'
|
|
7
|
+
require_relative 'ipinfo/ipAddressMatcher'
|
|
8
|
+
require_relative 'ipinfo/countriesData'
|
|
9
|
+
require 'ipaddr'
|
|
10
|
+
require 'cgi'
|
|
11
|
+
|
|
12
|
+
module IPinfoCore
|
|
13
|
+
include CountriesData
|
|
14
|
+
DEFAULT_CACHE_MAXSIZE = 4096
|
|
15
|
+
DEFAULT_CACHE_TTL = 60 * 60 * 24
|
|
16
|
+
RATE_LIMIT_MESSAGE = 'To increase your limits, please review our ' \
|
|
17
|
+
'paid plans at https://ipinfo.io/pricing'
|
|
18
|
+
# Base URL to get country flag image link.
|
|
19
|
+
# "PK" -> "https://cdn.ipinfo.io/static/images/countries-flags/PK.svg"
|
|
20
|
+
COUNTRY_FLAGS_URL = 'https://cdn.ipinfo.io/static/images/countries-flags/'
|
|
21
|
+
|
|
22
|
+
class << self
|
|
23
|
+
def create(access_token = nil, settings = {})
|
|
24
|
+
IPinfo::IPinfoCore.new(access_token, settings)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class IPinfo::IPinfoCore
|
|
30
|
+
include IPinfoCore
|
|
31
|
+
attr_accessor :access_token, :countries, :httpc
|
|
32
|
+
|
|
33
|
+
def initialize(access_token = nil, settings = {})
|
|
34
|
+
@access_token = access_token
|
|
35
|
+
@httpc = IPinfo::AdapterCore.new(access_token, httpc || :net_http)
|
|
36
|
+
|
|
37
|
+
maxsize = settings.fetch('maxsize', DEFAULT_CACHE_MAXSIZE)
|
|
38
|
+
ttl = settings.fetch('ttl', DEFAULT_CACHE_TTL)
|
|
39
|
+
@cache = settings.fetch('cache', IPinfo::DefaultCache.new(ttl, maxsize))
|
|
40
|
+
@countries = settings.fetch('countries', DEFAULT_COUNTRY_LIST)
|
|
41
|
+
@eu_countries = settings.fetch('eu_countries', DEFAULT_EU_COUNTRIES_LIST)
|
|
42
|
+
@countries_flags = settings.fetch('countries_flags', DEFAULT_COUNTRIES_FLAG_LIST)
|
|
43
|
+
@countries_currencies = settings.fetch('countries_currencies', DEFAULT_COUNTRIES_CURRENCIES_LIST)
|
|
44
|
+
@continents = settings.fetch('continents', DEFAULT_CONTINENT_LIST)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def details(ip_address = nil)
|
|
48
|
+
details_base(ip_address)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def request_details(ip_address = nil)
|
|
52
|
+
if ip_address && isBogon(ip_address)
|
|
53
|
+
details = {}
|
|
54
|
+
details[:ip] = ip_address
|
|
55
|
+
details[:bogon] = true
|
|
56
|
+
details[:ip_address] = IPAddr.new(ip_address)
|
|
57
|
+
return details
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
res = @cache.get(cache_key(ip_address))
|
|
61
|
+
return res unless res.nil?
|
|
62
|
+
|
|
63
|
+
response = @httpc.get(escape_path(ip_address))
|
|
64
|
+
|
|
65
|
+
if response.status.eql?(429)
|
|
66
|
+
raise RateLimitError,
|
|
67
|
+
RATE_LIMIT_MESSAGE
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
details = JSON.parse(response.body, symbolize_names: true)
|
|
71
|
+
@cache.set(cache_key(ip_address), details)
|
|
72
|
+
details
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def details_base(ip_address)
|
|
76
|
+
details = request_details(ip_address)
|
|
77
|
+
|
|
78
|
+
# Core response has nested geo object
|
|
79
|
+
if details.key?(:geo) && details[:geo].is_a?(Hash) && details[:geo].key?(:country_code)
|
|
80
|
+
country_code = details[:geo][:country_code]
|
|
81
|
+
details[:geo][:country_name] = @countries.fetch(country_code, nil)
|
|
82
|
+
details[:geo][:is_eu] = @eu_countries.include?(country_code)
|
|
83
|
+
details[:geo][:country_flag] = @countries_flags.fetch(country_code, nil)
|
|
84
|
+
details[:geo][:country_currency] = @countries_currencies.fetch(country_code, nil)
|
|
85
|
+
details[:geo][:continent] = @continents.fetch(country_code, nil)
|
|
86
|
+
details[:geo][:country_flag_url] = "#{COUNTRY_FLAGS_URL}#{country_code}.svg"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Handle top-level country_code if present (for certain edge cases)
|
|
90
|
+
if details.key?(:country_code)
|
|
91
|
+
country_code = details[:country_code]
|
|
92
|
+
details[:country_name] = @countries.fetch(country_code, nil)
|
|
93
|
+
details[:is_eu] = @eu_countries.include?(country_code)
|
|
94
|
+
details[:country_flag] = @countries_flags.fetch(country_code, nil)
|
|
95
|
+
details[:country_currency] = @countries_currencies.fetch(country_code, nil)
|
|
96
|
+
details[:continent] = @continents.fetch(country_code, nil)
|
|
97
|
+
details[:country_flag_url] = "#{COUNTRY_FLAGS_URL}#{country_code}.svg"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
if details.key? :ip
|
|
101
|
+
details[:ip_address] =
|
|
102
|
+
IPAddr.new(details.fetch(:ip))
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
IPinfo::Response.new(details)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def isBogon(ip)
|
|
109
|
+
if ip.nil?
|
|
110
|
+
return false
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
matcher_object = IPinfo::IpAddressMatcher.new(ip)
|
|
114
|
+
matcher_object.matches
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def escape_path(ip)
|
|
118
|
+
ip ? "/#{CGI.escape(ip)}" : '/'
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def cache_key(ip)
|
|
122
|
+
"1:#{ip}"
|
|
123
|
+
end
|
|
124
|
+
end
|
data/lib/ipinfo_lite.rb
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'ipinfo/adapter'
|
|
4
|
+
require 'ipinfo/cache/default_cache'
|
|
5
|
+
require 'ipinfo/errors'
|
|
6
|
+
require 'ipinfo/response'
|
|
7
|
+
require_relative 'ipinfo/ipAddressMatcher'
|
|
8
|
+
require_relative 'ipinfo/countriesData'
|
|
9
|
+
require 'ipaddr'
|
|
10
|
+
require 'cgi'
|
|
11
|
+
|
|
12
|
+
module IPinfoLite
|
|
13
|
+
include CountriesData
|
|
14
|
+
DEFAULT_CACHE_MAXSIZE = 4096
|
|
15
|
+
DEFAULT_CACHE_TTL = 60 * 60 * 24
|
|
16
|
+
RATE_LIMIT_MESSAGE = 'To increase your limits, please review our ' \
|
|
17
|
+
'paid plans at https://ipinfo.io/pricing'
|
|
18
|
+
# Base URL to get country flag image link.
|
|
19
|
+
# "PK" -> "https://cdn.ipinfo.io/static/images/countries-flags/PK.svg"
|
|
20
|
+
COUNTRY_FLAGS_URL = 'https://cdn.ipinfo.io/static/images/countries-flags/'
|
|
21
|
+
|
|
22
|
+
class << self
|
|
23
|
+
def create(access_token = nil, settings = {})
|
|
24
|
+
IPinfo::IPinfoLite.new(access_token, settings)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class IPinfo::IPinfoLite
|
|
30
|
+
include IPinfoLite
|
|
31
|
+
attr_accessor :access_token, :countries, :httpc
|
|
32
|
+
|
|
33
|
+
def initialize(access_token = nil, settings = {})
|
|
34
|
+
@access_token = access_token
|
|
35
|
+
@httpc = IPinfo::AdapterLite.new(access_token, httpc || :net_http)
|
|
36
|
+
|
|
37
|
+
maxsize = settings.fetch('maxsize', DEFAULT_CACHE_MAXSIZE)
|
|
38
|
+
ttl = settings.fetch('ttl', DEFAULT_CACHE_TTL)
|
|
39
|
+
@cache = settings.fetch('cache', IPinfo::DefaultCache.new(ttl, maxsize))
|
|
40
|
+
@countries = settings.fetch('countries', DEFAULT_COUNTRY_LIST)
|
|
41
|
+
@eu_countries = settings.fetch('eu_countries', DEFAULT_EU_COUNTRIES_LIST)
|
|
42
|
+
@countries_flags = settings.fetch('countries_flags', DEFAULT_COUNTRIES_FLAG_LIST)
|
|
43
|
+
@countries_currencies = settings.fetch('countries_currencies', DEFAULT_COUNTRIES_CURRENCIES_LIST)
|
|
44
|
+
@continents = settings.fetch('continents', DEFAULT_CONTINENT_LIST)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def details(ip_address = nil)
|
|
48
|
+
details_base(ip_address)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def request_details(ip_address = nil)
|
|
52
|
+
if ip_address && ip_address != 'me' && isBogon(ip_address)
|
|
53
|
+
details = {}
|
|
54
|
+
details[:ip] = ip_address
|
|
55
|
+
details[:bogon] = true
|
|
56
|
+
details[:ip_address] = IPAddr.new(ip_address)
|
|
57
|
+
return details
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
res = @cache.get(cache_key(ip_address))
|
|
61
|
+
return res unless res.nil?
|
|
62
|
+
|
|
63
|
+
ip_address ||= 'me'
|
|
64
|
+
response = @httpc.get(escape_path(ip_address))
|
|
65
|
+
|
|
66
|
+
if response.status.eql?(429)
|
|
67
|
+
raise RateLimitError,
|
|
68
|
+
RATE_LIMIT_MESSAGE
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
details = JSON.parse(response.body, symbolize_names: true)
|
|
72
|
+
@cache.set(cache_key(ip_address), details)
|
|
73
|
+
details
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def details_base(ip_address)
|
|
77
|
+
details = request_details(ip_address)
|
|
78
|
+
if details.key? :country_code
|
|
79
|
+
details[:country_name] =
|
|
80
|
+
@countries.fetch(details.fetch(:country_code), nil)
|
|
81
|
+
details[:is_eu] =
|
|
82
|
+
@eu_countries.include?(details.fetch(:country_code))
|
|
83
|
+
details[:country_flag] =
|
|
84
|
+
@countries_flags.fetch(details.fetch(:country_code), nil)
|
|
85
|
+
details[:country_currency] =
|
|
86
|
+
@countries_currencies.fetch(details.fetch(:country_code), nil)
|
|
87
|
+
details[:continent] =
|
|
88
|
+
@continents.fetch(details.fetch(:country_code), nil)
|
|
89
|
+
details[:country_flag_url] = "#{COUNTRY_FLAGS_URL}#{details.fetch(:country_code)}.svg"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
if details.key? :ip
|
|
93
|
+
details[:ip_address] =
|
|
94
|
+
IPAddr.new(details.fetch(:ip))
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
IPinfo::Response.new(details)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def isBogon(ip)
|
|
101
|
+
if ip.nil?
|
|
102
|
+
return false
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
matcher_object = IPinfo::IpAddressMatcher.new(ip)
|
|
106
|
+
matcher_object.matches
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def escape_path(ip)
|
|
110
|
+
ip ? "/#{CGI.escape(ip)}" : '/'
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def cache_key(ip)
|
|
114
|
+
"1:#{ip}"
|
|
115
|
+
end
|
|
116
|
+
end
|