maxmind-geoip2 0.1.0 → 0.6.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/CHANGELOG.md +32 -0
- data/Gemfile +1 -5
- data/README.dev.md +1 -1
- data/README.md +82 -8
- data/lib/maxmind/geoip2.rb +1 -0
- data/lib/maxmind/geoip2/client.rb +333 -0
- data/lib/maxmind/geoip2/errors.rb +37 -3
- data/lib/maxmind/geoip2/model/abstract.rb +19 -15
- data/lib/maxmind/geoip2/model/anonymous_ip.rb +62 -50
- data/lib/maxmind/geoip2/model/asn.rb +33 -29
- data/lib/maxmind/geoip2/model/city.rb +59 -55
- data/lib/maxmind/geoip2/model/connection_type.rb +27 -23
- data/lib/maxmind/geoip2/model/country.rb +64 -53
- data/lib/maxmind/geoip2/model/domain.rb +27 -23
- data/lib/maxmind/geoip2/model/enterprise.rb +13 -9
- data/lib/maxmind/geoip2/model/insights.rb +18 -0
- data/lib/maxmind/geoip2/model/isp.rb +45 -41
- data/lib/maxmind/geoip2/reader.rb +260 -233
- data/lib/maxmind/geoip2/record/abstract.rb +17 -13
- data/lib/maxmind/geoip2/record/city.rb +33 -29
- data/lib/maxmind/geoip2/record/continent.rb +32 -28
- data/lib/maxmind/geoip2/record/country.rb +47 -43
- data/lib/maxmind/geoip2/record/location.rb +64 -60
- data/lib/maxmind/geoip2/record/maxmind.rb +21 -0
- data/lib/maxmind/geoip2/record/place.rb +22 -18
- data/lib/maxmind/geoip2/record/postal.rb +26 -22
- data/lib/maxmind/geoip2/record/represented_country.rb +20 -16
- data/lib/maxmind/geoip2/record/subdivision.rb +42 -38
- data/lib/maxmind/geoip2/record/traits.rb +204 -191
- data/maxmind-geoip2.gemspec +11 -3
- data/test/data/bad-data/maxminddb-python/bad-unicode-in-map-key.mmdb +0 -0
- data/test/data/source-data/GeoIP2-Anonymous-IP-Test.json +1 -0
- data/test/data/source-data/GeoIP2-ISP-Test.json +3 -1
- data/test/data/source-data/GeoIP2-Precision-Enterprise-Test.json +87 -0
- data/test/data/test-data/GeoIP2-Anonymous-IP-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-City-Test-Broken-Double-Format.mmdb +0 -0
- data/test/data/test-data/GeoIP2-City-Test-Invalid-Node-Count.mmdb +0 -0
- data/test/data/test-data/GeoIP2-City-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-Connection-Type-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-Country-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-DensityIncome-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-Domain-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-Enterprise-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-ISP-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-Precision-Enterprise-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-Static-IP-Score-Test.mmdb +0 -0
- data/test/data/test-data/GeoIP2-User-Count-Test.mmdb +0 -0
- data/test/data/test-data/GeoLite2-ASN-Test.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-no-ipv4-search-tree.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-string-value-entries.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-broken-pointers-24.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-broken-search-tree-24.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-decoder.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-ipv4-24.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-ipv4-28.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-ipv4-32.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-ipv6-24.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-ipv6-28.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-ipv6-32.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-metadata-pointers.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-mixed-24.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-mixed-28.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-mixed-32.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-nested.mmdb +0 -0
- data/test/data/test-data/MaxMind-DB-test-pointer-decoder.mmdb +0 -0
- data/test/data/test-data/write-test-data.pl +68 -18
- data/test/test_client.rb +426 -0
- data/test/test_model_country.rb +16 -0
- data/test/test_reader.rb +59 -0
- metadata +113 -10
- data/Gemfile.lock +0 -38
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2825710c836cdc4f085ac2e89f88753aa972124ac307af9669866bb355c61f10
|
4
|
+
data.tar.gz: c420943be58fd25b84afe0a51264333c4ef268259da7946b58cd7ef77dd8a46b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 30d7e6caeaeded96d60fb981bcc2c4ce518620e973f3dae47433cd1582265fa315706a826c1bc62d29dfce013d5cee78ab1fefff54f51ef858589a54075b24f0
|
7
|
+
data.tar.gz: ca89534bc5f0572ad5b69c7c7b92be70089d6e7e600f962d71a38c8c073299d01ed141c6b1b5089cbca9694c6f49b5b8bcb77a45fb1d2ab668cf3f698c40abdb
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,37 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 0.6.0 (2021-03-23)
|
4
|
+
|
5
|
+
* Updated the `MaxMind::GeoIP2::Reader` constructor to support being called
|
6
|
+
using keyword arguments. For example, you may now create a `Reader` using
|
7
|
+
`MaxMind::GeoIP2::Reader.new(database: 'GeoIP2-Country.mmdb')` instead of
|
8
|
+
using positional arguments. This is intended to make it easier to pass in
|
9
|
+
optional arguments. Positional argument calling is still supported.
|
10
|
+
* Proxy support was fixed. Pull request by Manoj Dayaram. GitHub #30.
|
11
|
+
|
12
|
+
## 0.5.0 (2020-09-25)
|
13
|
+
|
14
|
+
* Added the `residential_proxy?` method to
|
15
|
+
`MaxMind::GeoIP2::Model::AnonymousIP` and
|
16
|
+
`MaxMind::GeoIP2::Record::Traits` for use with the Anonymous IP database
|
17
|
+
and GeoIP2 Precision Insights.
|
18
|
+
|
19
|
+
## 0.4.0 (2020-03-06)
|
20
|
+
|
21
|
+
* HTTP connections are now persistent. There is a new parameter that
|
22
|
+
controls the maximum number of connections the client will use.
|
23
|
+
|
24
|
+
## 0.3.0 (2020-03-04)
|
25
|
+
|
26
|
+
* Modules are now always be defined. Previously we used a shorthand syntax
|
27
|
+
which meant including individual classes could leave module constants
|
28
|
+
undefined.
|
29
|
+
|
30
|
+
## 0.2.0 (2020-02-26)
|
31
|
+
|
32
|
+
* Added support for the GeoIP2 Precision web services: Country, City, and
|
33
|
+
Insights.
|
34
|
+
|
3
35
|
## 0.1.0 (2020-02-20)
|
4
36
|
|
5
37
|
* Added support for the Anonymous IP, ASN, Connection Type, Domain, and ISP
|
data/Gemfile
CHANGED
data/README.dev.md
CHANGED
data/README.md
CHANGED
@@ -47,7 +47,9 @@ for more details.
|
|
47
47
|
require 'maxmind/geoip2'
|
48
48
|
|
49
49
|
# This creates the Reader object which should be reused across lookups.
|
50
|
-
reader = MaxMind::GeoIP2::Reader.new(
|
50
|
+
reader = MaxMind::GeoIP2::Reader.new(
|
51
|
+
database: '/usr/share/GeoIP/GeoIP2-City.mmdb',
|
52
|
+
)
|
51
53
|
|
52
54
|
record = reader.city('128.101.101.101')
|
53
55
|
|
@@ -74,7 +76,9 @@ puts record.traits.network # 128.101.101.101/32
|
|
74
76
|
require 'maxmind/geoip2'
|
75
77
|
|
76
78
|
# This creates the Reader object which should be reused across lookups.
|
77
|
-
reader = MaxMind::GeoIP2::Reader.new(
|
79
|
+
reader = MaxMind::GeoIP2::Reader.new(
|
80
|
+
database: '/usr/share/GeoIP/GeoIP2-Country.mmdb',
|
81
|
+
)
|
78
82
|
|
79
83
|
record = reader.country('128.101.101.101')
|
80
84
|
|
@@ -89,7 +93,9 @@ puts record.country.names['zh-CN'] # '美国'
|
|
89
93
|
require 'maxmind/geoip2'
|
90
94
|
|
91
95
|
# This creates the Reader object which should be reused across lookups.
|
92
|
-
reader = MaxMind::GeoIP2::Reader.new(
|
96
|
+
reader = MaxMind::GeoIP2::Reader.new(
|
97
|
+
database: '/usr/share/GeoIP/GeoIP2-Enterprise.mmdb',
|
98
|
+
)
|
93
99
|
|
94
100
|
record = reader.enterprise('128.101.101.101')
|
95
101
|
|
@@ -120,7 +126,9 @@ puts record.traits.network # 128.101.101.101/32
|
|
120
126
|
require 'maxmind/geoip2'
|
121
127
|
|
122
128
|
# This creates the Reader object which should be reused across lookups.
|
123
|
-
reader = MaxMind::GeoIP2::Reader.new(
|
129
|
+
reader = MaxMind::GeoIP2::Reader.new(
|
130
|
+
database: '/usr/share/GeoIP/GeoIP2-Anonymous-IP.mmdb',
|
131
|
+
)
|
124
132
|
|
125
133
|
record = reader.anonymous_ip('128.101.101.101')
|
126
134
|
|
@@ -133,7 +141,9 @@ puts "Anonymous" if record.is_anonymous
|
|
133
141
|
require 'maxmind/geoip2'
|
134
142
|
|
135
143
|
# This creates the Reader object which should be reused across lookups.
|
136
|
-
reader = MaxMind::GeoIP2::Reader.new(
|
144
|
+
reader = MaxMind::GeoIP2::Reader.new(
|
145
|
+
database: '/usr/share/GeoIP/GeoLite2-ASN.mmdb',
|
146
|
+
)
|
137
147
|
|
138
148
|
record = reader.asn('128.101.101.101')
|
139
149
|
|
@@ -147,7 +157,9 @@ puts record.autonomous_system_organization # Example Ltd
|
|
147
157
|
require 'maxmind/geoip2'
|
148
158
|
|
149
159
|
# This creates the Reader object which should be reused across lookups.
|
150
|
-
reader = MaxMind::GeoIP2::Reader.new(
|
160
|
+
reader = MaxMind::GeoIP2::Reader.new(
|
161
|
+
database: '/usr/share/GeoIP/GeoIP2-Connection-Type.mmdb',
|
162
|
+
)
|
151
163
|
|
152
164
|
record = reader.connection_type('128.101.101.101')
|
153
165
|
|
@@ -160,7 +172,9 @@ puts record.connection_type # Cable/DSL
|
|
160
172
|
require 'maxmind/geoip2'
|
161
173
|
|
162
174
|
# This creates the Reader object which should be reused across lookups.
|
163
|
-
reader = MaxMind::GeoIP2::Reader.new(
|
175
|
+
reader = MaxMind::GeoIP2::Reader.new(
|
176
|
+
database: '/usr/share/GeoIP/GeoIP2-Domain.mmdb',
|
177
|
+
)
|
164
178
|
|
165
179
|
record = reader.domain('128.101.101.101')
|
166
180
|
|
@@ -173,7 +187,9 @@ puts record.domain # example.com
|
|
173
187
|
require 'maxmind/geoip2'
|
174
188
|
|
175
189
|
# This creates the Reader object which should be reused across lookups.
|
176
|
-
reader = MaxMind::GeoIP2::Reader.new(
|
190
|
+
reader = MaxMind::GeoIP2::Reader.new(
|
191
|
+
database: '/usr/share/GeoIP/GeoIP2-ISP.mmdb',
|
192
|
+
)
|
177
193
|
|
178
194
|
record = reader.isp('128.101.101.101')
|
179
195
|
|
@@ -183,6 +199,64 @@ puts record.isp # University of Minnesota
|
|
183
199
|
puts record.organization # University of Minnesota
|
184
200
|
```
|
185
201
|
|
202
|
+
## Web Service Client
|
203
|
+
|
204
|
+
### Usage
|
205
|
+
|
206
|
+
To use this API, you must create a new `MaxMind::GeoIP2::Client` object
|
207
|
+
with your account ID and license key. To use the GeoLite2 web service, you
|
208
|
+
may also set the `host` parameter to `geolite.info`. You may then you call
|
209
|
+
the method corresponding to a specific end point, passing it the IP address
|
210
|
+
you want to look up.
|
211
|
+
|
212
|
+
If the request succeeds, the method call will return a model class for the end
|
213
|
+
point you called. This model in turn contains multiple record classes, each of
|
214
|
+
which represents part of the data returned by the web service.
|
215
|
+
|
216
|
+
If there is an error, a structured exception is thrown.
|
217
|
+
|
218
|
+
See the [API documentation](https://www.rubydoc.info/gems/maxmind-geoip2)
|
219
|
+
for more details.
|
220
|
+
|
221
|
+
### Example
|
222
|
+
|
223
|
+
```ruby
|
224
|
+
require 'maxmind/geoip2'
|
225
|
+
|
226
|
+
# This creates a Client object that can be reused across requests.
|
227
|
+
# Replace "42" with your account ID and "license_key" with your license
|
228
|
+
# key.
|
229
|
+
client = MaxMind::GeoIP2::Client.new(
|
230
|
+
account_id: 42,
|
231
|
+
license_key: 'license_key',
|
232
|
+
|
233
|
+
# To use the GeoLite2 web service instead of GeoIP2 Precision, set
|
234
|
+
# the host parameter to "geolite.info":
|
235
|
+
# host: 'geolite.info',
|
236
|
+
)
|
237
|
+
|
238
|
+
# Replace "city" with the method corresponding to the web service that
|
239
|
+
# you are using, e.g., "country", "insights". Please note that Insights
|
240
|
+
# is only supported by GeoIP2 Precision and not the GeoLite2 web service.
|
241
|
+
record = client.city('128.101.101.101')
|
242
|
+
|
243
|
+
puts record.country.iso_code # US
|
244
|
+
puts record.country.name # United States
|
245
|
+
puts record.country.names['zh-CN'] # 美国
|
246
|
+
|
247
|
+
puts record.most_specific_subdivision.name # Minnesota
|
248
|
+
puts record.most_specific_subdivision.iso_code # MN
|
249
|
+
|
250
|
+
puts record.city.name # Minneapolis
|
251
|
+
|
252
|
+
puts record.postal.code # 55455
|
253
|
+
|
254
|
+
puts record.location.latitude # 44.9733
|
255
|
+
puts record.location.longitude # -93.2323
|
256
|
+
|
257
|
+
puts record.traits.network # 128.101.101.101/32
|
258
|
+
```
|
259
|
+
|
186
260
|
## Values to use for Database or Array Keys
|
187
261
|
|
188
262
|
**We strongly discourage you from using a value from any `names` property
|
data/lib/maxmind/geoip2.rb
CHANGED
@@ -0,0 +1,333 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'connection_pool'
|
4
|
+
require 'http'
|
5
|
+
require 'json'
|
6
|
+
require 'maxmind/geoip2/errors'
|
7
|
+
require 'maxmind/geoip2/model/city'
|
8
|
+
require 'maxmind/geoip2/model/country'
|
9
|
+
require 'maxmind/geoip2/model/insights'
|
10
|
+
|
11
|
+
module MaxMind
|
12
|
+
module GeoIP2
|
13
|
+
# This class provides a client API for all the
|
14
|
+
# {https://dev.maxmind.com/geoip/geoip2/web-services/ GeoIP2 Precision web
|
15
|
+
# services}. The services are Country, City, and Insights. Each service
|
16
|
+
# returns a different set of data about an IP address, with Country returning
|
17
|
+
# the least data and Insights the most.
|
18
|
+
#
|
19
|
+
# Each web service is represented by a different model class, and these model
|
20
|
+
# classes in turn contain multiple record classes. The record classes have
|
21
|
+
# attributes which contain data about the IP address.
|
22
|
+
#
|
23
|
+
# If the web service does not return a particular piece of data for an IP
|
24
|
+
# address, the associated attribute is not populated.
|
25
|
+
#
|
26
|
+
# The web service may not return any information for an entire record, in
|
27
|
+
# which case all of the attributes for that record class will be empty.
|
28
|
+
#
|
29
|
+
# == Usage
|
30
|
+
#
|
31
|
+
# The basic API for this class is the same for all of the web service end
|
32
|
+
# points. First you create a web service client object with your MaxMind
|
33
|
+
# account ID and license key, then you call the method corresponding to a
|
34
|
+
# specific end point, passing it the IP address you want to look up.
|
35
|
+
#
|
36
|
+
# If the request succeeds, the method call will return a model class for the
|
37
|
+
# service you called. This model in turn contains multiple record classes,
|
38
|
+
# each of which represents part of the data returned by the web service.
|
39
|
+
#
|
40
|
+
# If the request fails, the client class throws an exception.
|
41
|
+
#
|
42
|
+
# == Example
|
43
|
+
#
|
44
|
+
# require 'maxmind/geoip2'
|
45
|
+
#
|
46
|
+
# client = MaxMind::GeoIP2::Client.new(
|
47
|
+
# account_id: 42,
|
48
|
+
# license_key: 'abcdef123456',
|
49
|
+
# )
|
50
|
+
#
|
51
|
+
# # Replace 'city' with the method corresponding to the web service you
|
52
|
+
# # are using, e.g., 'country', 'insights'.
|
53
|
+
# record = client.city('128.101.101.101')
|
54
|
+
#
|
55
|
+
# puts record.country.iso_code
|
56
|
+
class Client
|
57
|
+
# rubocop:disable Metrics/ParameterLists
|
58
|
+
|
59
|
+
# Create a Client that may be used to query a GeoIP2 Precision web service.
|
60
|
+
#
|
61
|
+
# Once created, the Client is safe to use for lookups from multiple
|
62
|
+
# threads.
|
63
|
+
#
|
64
|
+
# @param account_id [Integer] your MaxMind account ID.
|
65
|
+
#
|
66
|
+
# @param license_key [String] your MaxMind license key.
|
67
|
+
#
|
68
|
+
# @param locales [Array<String>] a list of locale codes to use in the name
|
69
|
+
# property from most preferred to least preferred.
|
70
|
+
#
|
71
|
+
# @param host [String] the host to use when querying the web service. Set
|
72
|
+
# this to "geolite.info" to use the GeoLite2 web service instead of
|
73
|
+
# GeoIP2 Precision.
|
74
|
+
#
|
75
|
+
# @param timeout [Integer] the number of seconds to wait for a request
|
76
|
+
# before timing out. If 0, no timeout is set.
|
77
|
+
#
|
78
|
+
# @param proxy_address [String] proxy address to use, if any.
|
79
|
+
#
|
80
|
+
# @param proxy_port [Integer] proxy port to use, if any.
|
81
|
+
#
|
82
|
+
# @param proxy_username [String] proxy username to use, if any.
|
83
|
+
#
|
84
|
+
# @param proxy_password [String] proxy password to use, if any.
|
85
|
+
#
|
86
|
+
# @param pool_size [Integer] HTTP connection pool size
|
87
|
+
def initialize(
|
88
|
+
account_id:,
|
89
|
+
license_key:,
|
90
|
+
locales: ['en'],
|
91
|
+
host: 'geoip.maxmind.com',
|
92
|
+
timeout: 0,
|
93
|
+
proxy_address: '',
|
94
|
+
proxy_port: 0,
|
95
|
+
proxy_username: '',
|
96
|
+
proxy_password: '',
|
97
|
+
pool_size: 5
|
98
|
+
)
|
99
|
+
@account_id = account_id
|
100
|
+
@license_key = license_key
|
101
|
+
@locales = locales
|
102
|
+
@host = host
|
103
|
+
@timeout = timeout
|
104
|
+
@proxy_address = proxy_address
|
105
|
+
@proxy_port = proxy_port
|
106
|
+
@proxy_username = proxy_username
|
107
|
+
@proxy_password = proxy_password
|
108
|
+
@pool_size = pool_size
|
109
|
+
|
110
|
+
@connection_pool = ConnectionPool.new(size: @pool_size) do
|
111
|
+
make_http_client.persistent("https://#{@host}")
|
112
|
+
end
|
113
|
+
end
|
114
|
+
# rubocop:enable Metrics/ParameterLists
|
115
|
+
|
116
|
+
# This method calls the City web service.
|
117
|
+
#
|
118
|
+
# @param ip_address [String] IPv4 or IPv6 address as a string. If no
|
119
|
+
# address is provided, the address that the web service is called from is
|
120
|
+
# used.
|
121
|
+
#
|
122
|
+
# @raise [HTTP::Error] if there was an error performing the HTTP request,
|
123
|
+
# such as an error connecting.
|
124
|
+
#
|
125
|
+
# @raise [JSON::ParserError] if there was invalid JSON in the response.
|
126
|
+
#
|
127
|
+
# @raise [HTTPError] if there was a problem with the HTTP response, such as
|
128
|
+
# an unexpected HTTP status code.
|
129
|
+
#
|
130
|
+
# @raise [AddressInvalidError] if the web service believes the IP address
|
131
|
+
# to be invalid or missing.
|
132
|
+
#
|
133
|
+
# @raise [AddressNotFoundError] if the IP address was not found.
|
134
|
+
#
|
135
|
+
# @raise [AddressReservedError] if the IP address is reserved.
|
136
|
+
#
|
137
|
+
# @raise [AuthenticationError] if there was a problem authenticating to the
|
138
|
+
# web service, such as an invalid or missing license key.
|
139
|
+
#
|
140
|
+
# @raise [InsufficientFundsError] if your account is out of credit.
|
141
|
+
#
|
142
|
+
# @raise [PermissionRequiredError] if your account does not have permission
|
143
|
+
# to use the web service.
|
144
|
+
#
|
145
|
+
# @raise [InvalidRequestError] if the web service responded with an error
|
146
|
+
# and there is no more specific error to raise.
|
147
|
+
#
|
148
|
+
# @return [MaxMind::GeoIP2::Model::City]
|
149
|
+
def city(ip_address = 'me')
|
150
|
+
response_for('city', MaxMind::GeoIP2::Model::City, ip_address)
|
151
|
+
end
|
152
|
+
|
153
|
+
# This method calls the Country web service.
|
154
|
+
#
|
155
|
+
# @param ip_address [String] IPv4 or IPv6 address as a string. If no
|
156
|
+
# address is provided, the address that the web service is called from is
|
157
|
+
# used.
|
158
|
+
#
|
159
|
+
# @raise [HTTP::Error] if there was an error performing the HTTP request,
|
160
|
+
# such as an error connecting.
|
161
|
+
#
|
162
|
+
# @raise [JSON::ParserError] if there was invalid JSON in the response.
|
163
|
+
#
|
164
|
+
# @raise [HTTPError] if there was a problem with the HTTP response, such as
|
165
|
+
# an unexpected HTTP status code.
|
166
|
+
#
|
167
|
+
# @raise [AddressInvalidError] if the web service believes the IP address
|
168
|
+
# to be invalid or missing.
|
169
|
+
#
|
170
|
+
# @raise [AddressNotFoundError] if the IP address was not found.
|
171
|
+
#
|
172
|
+
# @raise [AddressReservedError] if the IP address is reserved.
|
173
|
+
#
|
174
|
+
# @raise [AuthenticationError] if there was a problem authenticating to the
|
175
|
+
# web service, such as an invalid or missing license key.
|
176
|
+
#
|
177
|
+
# @raise [InsufficientFundsError] if your account is out of credit.
|
178
|
+
#
|
179
|
+
# @raise [PermissionRequiredError] if your account does not have permission
|
180
|
+
# to use the web service.
|
181
|
+
#
|
182
|
+
# @raise [InvalidRequestError] if the web service responded with an error
|
183
|
+
# and there is no more specific error to raise.
|
184
|
+
#
|
185
|
+
# @return [MaxMind::GeoIP2::Model::Country]
|
186
|
+
def country(ip_address = 'me')
|
187
|
+
response_for('country', MaxMind::GeoIP2::Model::Country, ip_address)
|
188
|
+
end
|
189
|
+
|
190
|
+
# This method calls the Insights web service.
|
191
|
+
#
|
192
|
+
# Insights is only supported by the GeoIP2 Precision web service. The
|
193
|
+
# GeoLite2 web service does not support it.
|
194
|
+
#
|
195
|
+
# @param ip_address [String] IPv4 or IPv6 address as a string. If no
|
196
|
+
# address is provided, the address that the web service is called from is
|
197
|
+
# used.
|
198
|
+
#
|
199
|
+
# @raise [HTTP::Error] if there was an error performing the HTTP request,
|
200
|
+
# such as an error connecting.
|
201
|
+
#
|
202
|
+
# @raise [JSON::ParserError] if there was invalid JSON in the response.
|
203
|
+
#
|
204
|
+
# @raise [HTTPError] if there was a problem with the HTTP response, such as
|
205
|
+
# an unexpected HTTP status code.
|
206
|
+
#
|
207
|
+
# @raise [AddressInvalidError] if the web service believes the IP address
|
208
|
+
# to be invalid or missing.
|
209
|
+
#
|
210
|
+
# @raise [AddressNotFoundError] if the IP address was not found.
|
211
|
+
#
|
212
|
+
# @raise [AddressReservedError] if the IP address is reserved.
|
213
|
+
#
|
214
|
+
# @raise [AuthenticationError] if there was a problem authenticating to the
|
215
|
+
# web service, such as an invalid or missing license key.
|
216
|
+
#
|
217
|
+
# @raise [InsufficientFundsError] if your account is out of credit.
|
218
|
+
#
|
219
|
+
# @raise [PermissionRequiredError] if your account does not have permission
|
220
|
+
# to use the web service.
|
221
|
+
#
|
222
|
+
# @raise [InvalidRequestError] if the web service responded with an error
|
223
|
+
# and there is no more specific error to raise.
|
224
|
+
#
|
225
|
+
# @return [MaxMind::GeoIP2::Model::Insights]
|
226
|
+
def insights(ip_address = 'me')
|
227
|
+
response_for('insights', MaxMind::GeoIP2::Model::Insights, ip_address)
|
228
|
+
end
|
229
|
+
|
230
|
+
private
|
231
|
+
|
232
|
+
def response_for(endpoint, model_class, ip_address)
|
233
|
+
record = get(endpoint, ip_address)
|
234
|
+
|
235
|
+
model_class.new(record, @locales)
|
236
|
+
end
|
237
|
+
|
238
|
+
def make_http_client
|
239
|
+
headers = HTTP.basic_auth(user: @account_id, pass: @license_key)
|
240
|
+
.headers(
|
241
|
+
accept: 'application/json',
|
242
|
+
user_agent: 'MaxMind-GeoIP2-ruby',
|
243
|
+
)
|
244
|
+
timeout = @timeout > 0 ? headers.timeout(@timeout) : headers
|
245
|
+
|
246
|
+
proxy = timeout
|
247
|
+
if @proxy_address != ''
|
248
|
+
proxy_params = [@proxy_address]
|
249
|
+
proxy_params << (@proxy_port == 0 ? nil : @proxy_port)
|
250
|
+
proxy_params << (@proxy_username == '' ? nil : @proxy_username)
|
251
|
+
proxy_params << (@proxy_password == '' ? nil : @proxy_password)
|
252
|
+
proxy = timeout.via(*proxy_params)
|
253
|
+
end
|
254
|
+
|
255
|
+
proxy
|
256
|
+
end
|
257
|
+
|
258
|
+
def get(endpoint, ip_address)
|
259
|
+
url = "/geoip/v2.1/#{endpoint}/#{ip_address}"
|
260
|
+
|
261
|
+
response = nil
|
262
|
+
body = nil
|
263
|
+
@connection_pool.with do |client|
|
264
|
+
response = client.get(url)
|
265
|
+
body = response.to_s
|
266
|
+
end
|
267
|
+
|
268
|
+
is_json = response.headers[:content_type]&.include?('json')
|
269
|
+
|
270
|
+
if response.status.client_error?
|
271
|
+
return handle_client_error(endpoint, response.code, body, is_json)
|
272
|
+
end
|
273
|
+
|
274
|
+
if response.status.server_error?
|
275
|
+
raise HTTPError,
|
276
|
+
"Received server error response (#{response.code}) for #{endpoint} with body #{body}"
|
277
|
+
end
|
278
|
+
|
279
|
+
if response.code != 200
|
280
|
+
raise HTTPError,
|
281
|
+
"Received unexpected response (#{response.code}) for #{endpoint} with body #{body}"
|
282
|
+
end
|
283
|
+
|
284
|
+
handle_success(endpoint, body, is_json)
|
285
|
+
end
|
286
|
+
|
287
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
288
|
+
def handle_client_error(endpoint, status, body, is_json)
|
289
|
+
if !is_json
|
290
|
+
raise HTTPError,
|
291
|
+
"Received client error response (#{status}) for #{endpoint} but it is not JSON: #{body}"
|
292
|
+
end
|
293
|
+
|
294
|
+
error = JSON.parse(body)
|
295
|
+
|
296
|
+
if !error.key?('code') || !error.key?('error')
|
297
|
+
raise HTTPError,
|
298
|
+
"Received client error response (#{status}) that is JSON but does not specify code or error keys: #{body}"
|
299
|
+
end
|
300
|
+
|
301
|
+
case error['code']
|
302
|
+
when 'IP_ADDRESS_INVALID', 'IP_ADDRESS_REQUIRED'
|
303
|
+
raise AddressInvalidError, error['error']
|
304
|
+
when 'IP_ADDRESS_NOT_FOUND'
|
305
|
+
raise AddressNotFoundError, error['error']
|
306
|
+
when 'IP_ADDRESS_RESERVED'
|
307
|
+
raise AddressReservedError, error['error']
|
308
|
+
when 'ACCOUNT_ID_REQUIRED',
|
309
|
+
'ACCOUNT_ID_UNKNOWN',
|
310
|
+
'AUTHORIZATION_INVALID',
|
311
|
+
'LICENSE_KEY_REQUIRED'
|
312
|
+
raise AuthenticationError, error['error']
|
313
|
+
when 'INSUFFICIENT_FUNDS'
|
314
|
+
raise InsufficientFundsError, error['error']
|
315
|
+
when 'PERMISSION_REQUIRED'
|
316
|
+
raise PermissionRequiredError, error['error']
|
317
|
+
else
|
318
|
+
raise InvalidRequestError, error['error']
|
319
|
+
end
|
320
|
+
end
|
321
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
322
|
+
|
323
|
+
def handle_success(endpoint, body, is_json)
|
324
|
+
if !is_json
|
325
|
+
raise HTTPError,
|
326
|
+
"Received a success response for #{endpoint} but it is not JSON: #{body}"
|
327
|
+
end
|
328
|
+
|
329
|
+
JSON.parse(body)
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|