Skip to content

Commit feb280b

Browse files
authored
Merge pull request #183 from kahlau-sirius/master
Query multiple IPs via POST JSON array
2 parents a1cf612 + 9c2c1b9 commit feb280b

File tree

4 files changed

+124
-0
lines changed

4 files changed

+124
-0
lines changed

README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,48 @@ X-Geoip-Continent: EU
142142
X-Geoip-Timezone: Europe/Berlin
143143
```
144144

145+
### Querying multiple IPs via POST
146+
147+
```bash
148+
curl --location 'http://localhost:8080/' \
149+
--header 'Content-Type: application/json' \
150+
--data '[
151+
"8.8.8.8",
152+
"8.8.4.4"
153+
]'
154+
155+
{
156+
"8.8.4.4": {
157+
"country":"US",
158+
"latitude":"37.751",
159+
"longitude":"-97.822",
160+
"continent":"NA",
161+
"timezone":"America/Chicago",
162+
"accuracyRadius":1000,
163+
"asn":15169,
164+
"asnOrganization":"GOOGLE",
165+
"asnNetwork":"8.8.4.0/24"
166+
},
167+
"8.8.8.8": {
168+
"country":"US",
169+
"latitude":"37.751",
170+
"longitude":"-97.822",
171+
"continent":"NA",
172+
"timezone":"America/Chicago",
173+
"accuracyRadius":1000,
174+
"asn":15169,
175+
"asnOrganization":"GOOGLE",
176+
"asnNetwork":"8.8.8.0/24"
177+
}
178+
}
179+
```
180+
181+
Takes up to 100 addresses at once.
182+
183+
Result will be a JSON object indexed by requested IP address.
184+
185+
If a requested address can not be resolved, the entry will be missing in the response.
186+
145187
## 📦 Building the project
146188

147189
The project is built through multi stage Docker builds. You need

src/main/java/org/observabilitystack/geoip/web/GeoIpRestController.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
11
package org.observabilitystack.geoip.web;
22

33
import java.net.InetAddress;
4+
import java.util.Map;
45
import java.util.Optional;
6+
import java.util.TreeMap;
57

68
import org.springframework.beans.factory.annotation.Autowired;
79
import org.springframework.http.HttpStatus;
10+
import org.springframework.http.MediaType;
811
import org.springframework.http.ResponseEntity;
912
import org.springframework.web.bind.WebDataBinder;
1013
import org.springframework.web.bind.annotation.CrossOrigin;
1114
import org.springframework.web.bind.annotation.ExceptionHandler;
1215
import org.springframework.web.bind.annotation.GetMapping;
1316
import org.springframework.web.bind.annotation.InitBinder;
1417
import org.springframework.web.bind.annotation.PathVariable;
18+
import org.springframework.web.bind.annotation.RequestBody;
1519
import org.springframework.web.bind.annotation.RequestHeader;
20+
import org.springframework.web.bind.annotation.RequestMapping;
1621
import org.springframework.web.bind.annotation.RequestMethod;
1722
import org.springframework.web.bind.annotation.ResponseBody;
1823
import org.springframework.web.bind.annotation.ResponseStatus;
@@ -89,6 +94,29 @@ public ResponseEntity<GeoIpEntry> lookup(@PathVariable("address") InetAddress ad
8994
}
9095
}
9196

97+
@RequestMapping(path = "/", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
98+
public ResponseEntity<Map<String, GeoIpEntry>> multiLookup(@RequestBody InetAddress[] ipAddresses) {
99+
if (ipAddresses.length > 100) {
100+
throw new TooManyAddressesException("Only 100 address requests allowed at once");
101+
}
102+
103+
Map<String, GeoIpEntry> entries = new TreeMap<>();
104+
for (InetAddress address : ipAddresses) {
105+
Optional<GeoIpEntry> entry = geolocations.lookup(address);
106+
if (entry.isPresent()) {
107+
entries.put(address.getHostAddress(), entry.get());
108+
}
109+
}
110+
return ResponseEntity.ok(entries);
111+
}
112+
113+
@ExceptionHandler(TooManyAddressesException.class)
114+
@ResponseBody
115+
@ResponseStatus(HttpStatus.BAD_REQUEST)
116+
public String handleTooManyAddressesException(TooManyAddressesException e) {
117+
return e.getMessage();
118+
}
119+
92120
@ExceptionHandler(InvalidIpAddressException.class)
93121
@ResponseBody
94122
@ResponseStatus(HttpStatus.BAD_REQUEST)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package org.observabilitystack.geoip.web;
2+
3+
public class TooManyAddressesException extends RuntimeException {
4+
private static final long serialVersionUID = 742704466635106825L;
5+
6+
public TooManyAddressesException(String message) {
7+
super(message);
8+
}
9+
}

src/test/java/org/observabilitystack/geoip/web/GeoIpRestControllerTest.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
import static org.mockito.Mockito.mock;
55
import static org.mockito.Mockito.when;
66
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
7+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
78
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
9+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
810
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
911

1012
import java.net.InetAddress;
@@ -23,6 +25,8 @@
2325
public class GeoIpRestControllerTest {
2426

2527
private static final InetAddress IPV4_ADDR = InetAddresses.forString("192.168.1.1");
28+
private static final InetAddress IPV4_ADDR2 = InetAddresses.forString("172.16.0.1");
29+
private static final InetAddress IPV4_ADDR3 = InetAddresses.forString("10.0.0.1");
2630
private static final InetAddress IPV6_ADDR = InetAddresses.forString("2001:db8:1::1");
2731

2832
private MockMvc mockMvc;
@@ -33,6 +37,8 @@ public class GeoIpRestControllerTest {
3337
public void setUp() {
3438
provider = mock(GeolocationProvider.class);
3539
when(provider.lookup(eq(IPV4_ADDR))).thenReturn(Optional.of(GeoIpEntry.builder().setCountry("ZZ").build()));
40+
when(provider.lookup(eq(IPV4_ADDR2))).thenReturn(Optional.of(GeoIpEntry.builder().setCountry("ZZ").build()));
41+
when(provider.lookup(eq(IPV4_ADDR3))).thenReturn(Optional.of(GeoIpEntry.builder().setCountry("ZZ").build()));
3642
when(provider.lookup(eq(IPV6_ADDR))).thenReturn(Optional.of(GeoIpEntry.builder().setCountry("ZZ").build()));
3743
restController = new GeoIpRestController(provider);
3844
mockMvc = MockMvcBuilders.standaloneSetup(restController).build();
@@ -58,6 +64,45 @@ public void testIpv4Address() throws Exception {
5864
.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
5965
.andExpect(content().json("{\"country\":\"ZZ\"}"));
6066
}
67+
68+
@Test
69+
public void testMultiIpv4Addresses() throws Exception {
70+
mockMvc.perform(post("/").contentType(MediaType.APPLICATION_JSON).content(
71+
"[\"" +
72+
IPV4_ADDR.getHostAddress() +
73+
"\", \"" +
74+
IPV4_ADDR2.getHostAddress() +
75+
"\", \"" +
76+
IPV4_ADDR3.getHostAddress() +
77+
"\"]"
78+
))
79+
.andExpect(status().isOk())
80+
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
81+
.andExpect(jsonPath("$[\"" + IPV4_ADDR.getHostAddress() + "\"].country").value("ZZ"))
82+
.andExpect(jsonPath("$[\"" + IPV4_ADDR2.getHostAddress() + "\"].country").value("ZZ"))
83+
.andExpect(jsonPath("$[\"" + IPV4_ADDR3.getHostAddress() + "\"].country").value("ZZ"));
84+
}
85+
86+
@Test
87+
public void testMultiIpv4AddressesExceedingLimit() throws Exception {
88+
InetAddress[] ipAddresses = new InetAddress[101];
89+
for (int i = 0; i < 101; i++) {
90+
ipAddresses[i] = InetAddress.getByName("192.168.1." + i);
91+
}
92+
93+
String jsonContent = "[";
94+
for (InetAddress address : ipAddresses) {
95+
jsonContent += "\"" + address.getHostAddress() + "\",";
96+
}
97+
jsonContent = jsonContent.substring(0, jsonContent.length() - 1) + "]";
98+
99+
mockMvc.perform(post("/")
100+
.contentType(MediaType.APPLICATION_JSON)
101+
.content(jsonContent))
102+
.andExpect(status().isBadRequest())
103+
.andExpect(content().contentType("text/plain;charset=ISO-8859-1"))
104+
.andExpect(content().string("Only 100 address requests allowed at once"));
105+
}
61106

62107
@Test
63108
public void testIpv6Address() throws Exception {

0 commit comments

Comments
 (0)