Skip to content

Commit b154db1

Browse files
committed
Adding support for CIDR grups and more.
1 parent 61e4a2b commit b154db1

9 files changed

Lines changed: 285 additions & 47 deletions

File tree

‎config/apptest/subscribers.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
{
44
"customer": "BarcodeAPI.org",
55
"subscribed": 1735693200000,
6-
"active": false,
6+
"active": true,
77
"enforce": true,
88
"limit": -1,
99
"batch": 500,

‎resources/ext/js/type.js‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ function loadType(type) {
5454
// Render each of the examples
5555
for (var x in type.examples) {
5656

57-
var apiURL = ("api/" + target + "/" + type.examples[x]);
57+
var apiURL = ("/api/" + target + "/" + type.examples[x]);
5858

5959
var table = document.createElement("table");
6060

@@ -113,7 +113,7 @@ function loadType(type) {
113113
function buildOptions(type) {
114114

115115
// Build the exmaple URL root
116-
var exampleURL = "api/" + type.targets[0] + "/" + type.examples[0] + "?";
116+
var exampleURL = "/api/" + type.targets[0] + "/" + type.examples[0] + "?";
117117

118118
// Loop each of the parameters
119119
for (var x in type.options) {
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package org.barcodeapi.server.admin;
2+
3+
import java.io.IOException;
4+
5+
import javax.servlet.http.HttpServletResponse;
6+
7+
import org.barcodeapi.server.core.RequestContext;
8+
import org.barcodeapi.server.core.RestHandler;
9+
import org.json.JSONException;
10+
import org.json.JSONObject;
11+
12+
/**
13+
* SubscriberReloadHandler.java
14+
*
15+
* @author Matthew R. Clark (BarcodeAPI.org, 2017-2026)
16+
*/
17+
public class SubscriberStatusHandler extends RestHandler {
18+
19+
public SubscriberStatusHandler() {
20+
super(
21+
// Authentication required
22+
true,
23+
// Do not use client rate limit
24+
false,
25+
// Do not create new session
26+
false);
27+
}
28+
29+
@Override
30+
protected void onRequest(RequestContext c, HttpServletResponse r) throws JSONException, IOException {
31+
32+
if (c.getSubscriber() == null) {
33+
r.setStatus(HttpServletResponse.SC_BAD_REQUEST);
34+
r.setContentType("application/json");
35+
r.getOutputStream().println((new JSONObject() //
36+
.put("code", 400)//
37+
.put("message", "not subscribed")//
38+
).toString());
39+
}
40+
41+
// Load subscriber details
42+
JSONObject subscriberDetails = //
43+
c.getSubscriber().asJSON();
44+
45+
// Attach list of associations
46+
subscriberDetails.put("associations", //
47+
c.getSubscriber().getAssociations());
48+
49+
// Print response to client
50+
r.setStatus(HttpServletResponse.SC_OK);
51+
r.setContentType("application/json");
52+
r.getOutputStream().println(//
53+
subscriberDetails.toString(4));
54+
}
55+
}

‎src/main/java/org/barcodeapi/server/api/SubscriberHandler.java‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,14 @@ protected void onRequest(RequestContext c, HttpServletResponse r) throws IOExcep
3737
).toString());
3838
}
3939

40+
// Load subscriber details
41+
JSONObject subscriberDetails = //
42+
c.getSubscriber().asJSON();
43+
4044
// Print response to client
4145
r.setStatus(HttpServletResponse.SC_OK);
4246
r.setContentType("application/json");
4347
r.getOutputStream().println(//
44-
c.getSubscriber().asJSON().toString(4));
48+
subscriberDetails.toString(4));
4549
}
4650
}

‎src/main/java/org/barcodeapi/server/cache/Subscriber.java‎

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,14 @@ public JSONArray getApps() {
7777
return this.apps;
7878
}
7979

80+
public JSONObject getAssociations() {
81+
82+
return new JSONObject()//
83+
.put("ips", getIPs())//
84+
.put("keys", getKeys())//
85+
.put("apps", getApps());
86+
}
87+
8088
/**
8189
* Returns the subscriber info as a JSON object.
8290
*
@@ -90,9 +98,6 @@ public JSONObject asJSON() {
9098
.put("active", isActive())//
9199
.put("enforce", isEnforced())//
92100
.put("limit", getLimit())//
93-
.put("batch", getMaxBatch())//
94-
.put("ips", getIPs())//
95-
.put("keys", getKeys())//
96-
.put("apps", getApps());
101+
.put("batch", getMaxBatch());
97102
}
98103
}

‎src/main/java/org/barcodeapi/server/cache/SubscriberCache.java‎

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import org.barcodeapi.core.Config;
77
import org.barcodeapi.core.Config.Cfg;
8+
import org.barcodeapi.server.core.CidrTree;
89
import org.json.JSONArray;
910
import org.json.JSONObject;
1011

@@ -17,16 +18,15 @@
1718
*/
1819
public class SubscriberCache {
1920

20-
private static final Map<String, Subscriber> subscribersByName;
21-
private static final Map<String, Subscriber> subscribersByIP;
2221
private static final Map<String, Subscriber> subscribersByKey;
2322
private static final Map<String, Subscriber> subscribersByApp;
23+
private static final CidrTree<Subscriber> subscribersByIP;
2424

2525
static {
26-
subscribersByName = new ConcurrentHashMap<>();
27-
subscribersByIP = new ConcurrentHashMap<>();
28-
subscribersByKey = new ConcurrentHashMap<>();
26+
27+
subscribersByIP = new CidrTree<>();
2928
subscribersByApp = new ConcurrentHashMap<>();
29+
subscribersByKey = new ConcurrentHashMap<>();
3030

3131
reload();
3232
}
@@ -35,12 +35,12 @@ public class SubscriberCache {
3535
* Reload subscriber info from configuration on disk.
3636
*/
3737
public static void reload() {
38+
long timeStart = System.currentTimeMillis();
3839

3940
// Flush existing subscribers
40-
subscribersByName.clear();
4141
subscribersByIP.clear();
42-
subscribersByKey.clear();
4342
subscribersByApp.clear();
43+
subscribersByKey.clear();
4444

4545
// Load subscribers, force reload
4646
JSONArray users = Config//
@@ -71,41 +71,30 @@ public static void reload() {
7171
continue;
7272
}
7373

74-
// Map of Customer Names
75-
subscribersByName.put(//
76-
subscriber.getCustomer(), subscriber);
77-
7874
// Map Customer IPs
7975
JSONArray subIps = subscriber.getIPs();
8076
for (int y = 0; y < subIps.length(); y++) {
8177
subscribersByIP.put(//
8278
subIps.getString(y), subscriber);
8379
}
8480

85-
// Map Customer Keys
86-
JSONArray subKeys = subscriber.getKeys();
87-
for (int y = 0; y < subKeys.length(); y++) {
88-
subscribersByKey.put(//
89-
subKeys.getString(y), subscriber);
90-
}
91-
9281
// Map Customer Applications
9382
JSONArray subApps = subscriber.getApps();
9483
for (int y = 0; y < subApps.length(); y++) {
9584
subscribersByApp.put(//
9685
subApps.getString(y), subscriber);
9786
}
98-
}
99-
}
10087

101-
/**
102-
* Lookup a subscriber by customer name.
103-
*
104-
* @param name name of the customer
105-
* @return the subscriber info or null
106-
*/
107-
public static Subscriber getByName(String name) {
108-
return subscribersByName.get(name);
88+
// Map Customer Keys
89+
JSONArray subKeys = subscriber.getKeys();
90+
for (int y = 0; y < subKeys.length(); y++) {
91+
subscribersByKey.put(//
92+
subKeys.getString(y), subscriber);
93+
}
94+
}
95+
96+
long timeDiff = (System.currentTimeMillis() - timeStart);
97+
LibLog._logF("Subscribers reloaded in %dms", timeDiff);
10998
}
11099

111100
/**
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package org.barcodeapi.server.core;
2+
3+
import java.net.InetAddress;
4+
import java.net.UnknownHostException;
5+
6+
public final class CidrTree<T> {
7+
8+
private static final class Node<T> {
9+
Node<T> bit0;
10+
Node<T> bit1;
11+
T value;
12+
}
13+
14+
private Node<T> root = new Node<>();
15+
16+
public void put(String entry, T value) {
17+
int slash = entry.indexOf('/');
18+
19+
if (slash < 0) {
20+
entry += "/32";
21+
slash = entry.length() - 3;
22+
}
23+
24+
try {
25+
26+
// Resolve IP to byte array
27+
byte[] network = InetAddress.getByName(//
28+
entry.substring(0, slash)).getAddress();
29+
30+
if (network.length != 4) {
31+
throw new IllegalArgumentException("IPv4 only: " + entry);
32+
}
33+
34+
// Validate group is between 0 and 32
35+
int group = Integer.parseInt(entry.substring(slash + 1));
36+
if (group < 0 || group > 32) {
37+
throw new IllegalArgumentException("Invalid prefix: " + entry);
38+
}
39+
40+
Node<T> node = root;
41+
42+
for (int bit = 0; bit < group; bit++) {
43+
44+
int valueBit = (network[bit >>> 3] >>> (7 - (bit & 7))) & 1;
45+
46+
if (valueBit == 0) {
47+
if (node.bit0 == null) {
48+
node.bit0 = new Node<>();
49+
}
50+
51+
node = node.bit0;
52+
} else {
53+
if (node.bit1 == null) {
54+
node.bit1 = new Node<>();
55+
}
56+
57+
node = node.bit1;
58+
}
59+
}
60+
61+
node.value = value;
62+
63+
} catch (UnknownHostException e) {
64+
throw new IllegalArgumentException("Invalid IPv4 CIDR: " + entry, e);
65+
}
66+
}
67+
68+
public T get(String ip) {
69+
try {
70+
71+
// Resolve IP to byte array
72+
byte[] address = InetAddress.getByName(ip).getAddress();
73+
74+
if (address.length != 4) {
75+
throw new IllegalArgumentException("IPv4 only: " + ip);
76+
}
77+
78+
Node<T> node = root;
79+
T match = node.value;
80+
81+
for (int bit = 0; bit < 32; bit++) {
82+
int valueBit = (address[bit >>> 3] >>> (7 - (bit & 7))) & 1;
83+
84+
node = valueBit == 0 ? node.bit0 : node.bit1;
85+
86+
if (node == null) {
87+
break;
88+
}
89+
90+
if (node.value != null) {
91+
match = node.value;
92+
}
93+
}
94+
95+
return match;
96+
97+
} catch (UnknownHostException e) {
98+
throw new IllegalArgumentException("Invalid IPv4 address: " + ip, e);
99+
}
100+
}
101+
102+
public void clear() {
103+
root = new Node<>();
104+
}
105+
}

0 commit comments

Comments
 (0)