You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by ne...@apache.org on 2020/07/15 20:21:01 UTC

[trafficcontrol] branch master updated: RGB - Lookup Zip Code based on Lat/Long if Zip Code is null (#4833)

This is an automated email from the ASF dual-hosted git repository.

neuman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git


The following commit(s) were added to refs/heads/master by this push:
     new b2f32b3  RGB - Lookup Zip Code based on Lat/Long if Zip Code is null (#4833)
b2f32b3 is described below

commit b2f32b3fd9d4800aece68e5f4b12911a5f14a4a5
Author: Srijeet Chatterjee <30...@users.noreply.github.com>
AuthorDate: Wed Jul 15 14:20:49 2020 -0600

    RGB - Lookup Zip Code based on Lat/Long if Zip Code is null (#4833)
    
    * initial commit
    
    * Adding tests and documentation
    
    * Fixing PMD issues
    
    * Removing unneeded change
    
    * Addressing code review
---
 CHANGELOG.md                                       |  1 +
 docs/source/admin/quick_howto/regionalgeo.rst      |  5 +-
 .../traffic_router/core/loc/RegionalGeo.java       | 88 ++++++++++++++++------
 .../core/loc/RegionalGeoCoordinateRange.java       | 53 +++++++++++++
 .../traffic_router/core/loc/RegionalGeoRule.java   | 27 ++++++-
 .../traffic_router/core/router/TrafficRouter.java  | 87 +++++++++++----------
 .../core/loc/RegionalGeoRuleTest.java              | 76 ++++++++++++++-----
 .../traffic_router/core/loc/RegionalGeoTest.java   | 79 +++++++++++--------
 .../core/src/test/resources/regional_geoblock.json |  3 +-
 9 files changed, 296 insertions(+), 123 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index bce39b4..dd3eda9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -41,6 +41,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
 - Fixed `POST /api/x/steering` and `PUT /api/x/steering` so that a steering target with an invalid `type` is no longer accepted. [Related github issue](https://github.com/apache/trafficcontrol/issues/3531)
 - Fixed `cachegroups` READ endpoint, so that if a request is made with the `type` specified as a non integer value, you get back a `400` with error details, instead of a `500`. [Related github issue](https://github.com/apache/trafficcontrol/issues/4703)
 - Added Delivery Service Raw Remap `__RANGE_DIRECTIVE__` directive to allow inserting the Range Directive after the Raw Remap text. This allows Raw Remaps which manipulate the Range.
+- Added an option for `coordinateRange` in the RGB configuration file, so that in case a client doesn't have a postal code, we can still determine if it should be allowed or not, based on whether or not the latitude/ longitude of the client falls within the supplied ranges. [Related github issue](https://github.com/apache/trafficcontrol/issues/4372)
 
 ### Changed
 - Changed some Traffic Ops Go Client methods to use `DeliveryServiceNullable` inputs and outputs.
diff --git a/docs/source/admin/quick_howto/regionalgeo.rst b/docs/source/admin/quick_howto/regionalgeo.rst
index 6d0ee8c..0dd02e3 100644
--- a/docs/source/admin/quick_howto/regionalgeo.rst
+++ b/docs/source/admin/quick_howto/regionalgeo.rst
@@ -31,7 +31,8 @@ Configure Regional Geo-blocking (RGB)
 				{
 					"deliveryServiceId": "hls-live",
 					"urlRegex": ".*live4\\.m3u8",
-					"geoLocation": {"includePostalCode":["N0H", "L9V", "L9W"]},
+					"geoLocation": {"includePostalCode":["N0H", "L9V", "L9W"],
+									"coordinateRange": [{"minLat" : -12, "maxLat": 13, "minLon" : 55, "maxLon": 56}, {"minLat" : -13, "maxLat": 14, "minLon" : 55, "maxLon": 56}]},
 					"redirectUrl": "http://third-party.com/blacked_out.html"
 				},
 				{
@@ -58,7 +59,7 @@ Configure Regional Geo-blocking (RGB)
 	``urlRegex``
 		A regular expression to be used to determine to what URLs the rule shall apply; a URL that matches it is subject to the rule
 	``geoLocation``
-		An object that currently supports only the keys ``includePostalCode`` and ``excludePostalCode`` (mutually exclusive). When the ``includePostalCode`` key is used, only the clients whose :abbr:`FSA (Forward Sortation Areas)`\ s - the first three postal characters of Canadian postal codes - are in the ``includePostalCode`` list are able to view the content at URLs matched by the ``urlRegex``. When ``excludePostalCode`` is used, any client whose :abbr:`FSA (Forward Sortation Areas)` is not [...]
+		An object that currently supports only the keys ``includePostalCode``, ``excludePostalCode`` (mutually exclusive) and ``coordinateRange``. When the ``includePostalCode`` key is used, only the clients whose :abbr:`FSA (Forward Sortation Areas)`\ s - the first three postal characters of Canadian postal codes - are in the ``includePostalCode`` list are able to view the content at URLs matched by the ``urlRegex``. When ``excludePostalCode`` is used, any client whose :abbr:`FSA (Forward Sor [...]
 	``redirectUrl``
 		The URL that will be returned to the blocked clients. Without a domain name in the URL, the URL will still be served in the same :term:`Delivery Service`. Thus Traffic Router will redirect the client to a chosen :term:`cache server` assigned to the :term:`Delivery Service`. If the URL includes a domain name, Traffic Router simply redirects the client to the defined URL. In the latter case, the redirect URL must not match the ``urlRegex`` value, or an infinite loop of  HTTP ``302 Found` [...]
 	``ipWhiteList``
diff --git a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/loc/RegionalGeo.java b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/loc/RegionalGeo.java
index d302322..131e3b3 100644
--- a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/loc/RegionalGeo.java
+++ b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/loc/RegionalGeo.java
@@ -15,30 +15,34 @@
 
 package com.comcast.cdn.traffic_control.traffic_router.core.loc;
 
-import java.io.File;
-import java.util.*;
-import java.util.regex.Pattern;
-import java.net.MalformedURLException;
-import java.net.URL;
-
-import com.comcast.cdn.traffic_control.traffic_router.core.util.JsonUtils;
-import com.comcast.cdn.traffic_control.traffic_router.core.util.JsonUtilsException;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.apache.log4j.Logger;
-
 import com.comcast.cdn.traffic_control.traffic_router.core.ds.DeliveryService;
 import com.comcast.cdn.traffic_control.traffic_router.core.edge.Cache;
 import com.comcast.cdn.traffic_control.traffic_router.core.loc.RegionalGeoResult.RegionalGeoResultType;
 import com.comcast.cdn.traffic_control.traffic_router.core.request.HTTPRequest;
 import com.comcast.cdn.traffic_control.traffic_router.core.request.Request;
+import com.comcast.cdn.traffic_control.traffic_router.core.router.HTTPRouteResult;
 import com.comcast.cdn.traffic_control.traffic_router.core.router.StatTracker.Track;
-import com.comcast.cdn.traffic_control.traffic_router.core.router.StatTracker.Track.ResultType;
 import com.comcast.cdn.traffic_control.traffic_router.core.router.StatTracker.Track.ResultDetails;
+import com.comcast.cdn.traffic_control.traffic_router.core.router.StatTracker.Track.ResultType;
 import com.comcast.cdn.traffic_control.traffic_router.core.router.TrafficRouter;
-import com.comcast.cdn.traffic_control.traffic_router.core.router.HTTPRouteResult;
+import com.comcast.cdn.traffic_control.traffic_router.core.util.JsonUtils;
+import com.comcast.cdn.traffic_control.traffic_router.core.util.JsonUtilsException;
 import com.comcast.cdn.traffic_control.traffic_router.geolocation.Geolocation;
 import com.comcast.cdn.traffic_control.traffic_router.geolocation.GeolocationException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.log4j.Logger;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
 
 import static com.comcast.cdn.traffic_control.traffic_router.core.loc.RegionalGeoResult.RegionalGeoResultType.ALLOWED;
 import static com.comcast.cdn.traffic_control.traffic_router.core.loc.RegionalGeoResult.RegionalGeoResultType.ALTERNATE_WITHOUT_CACHE;
@@ -52,7 +56,6 @@ public final class RegionalGeo {
     public static final String HTTPS_SCHEME = "https://";
     private boolean fallback = false;
     private final Map<String, RegionalGeoDsvc> regionalGeoDsvcs = new HashMap<String, RegionalGeoDsvc>();
-
     private static RegionalGeo currentConfig = new RegionalGeo();
 
     private RegionalGeo() {
@@ -68,7 +71,6 @@ public final class RegionalGeo {
     }
 
     private RegionalGeoRule matchRule(final String dsvcId, final String url) {
-
         final RegionalGeoDsvc regionalGeoDsvc = regionalGeoDsvcs.get(dsvcId);
         if (regionalGeoDsvc == null) {
             LOGGER.debug("RegionalGeo: dsvc not found: " + dsvcId);
@@ -86,8 +88,8 @@ public final class RegionalGeo {
     }
 
     private boolean addRule(final String dsvcId, final String urlRegex,
-            final RegionalGeoRule.PostalsType postalsType, final Set<String> postals,
-            final NetworkNode networkRoot, final String alternateUrl, final boolean isSteeringDS) {
+                            final RegionalGeoRule.PostalsType postalsType, final Set<String> postals,
+                            final NetworkNode networkRoot, final String alternateUrl, final boolean isSteeringDS, final List<RegionalGeoCoordinateRange> coordinateRanges) {
 
         // Loop check for alternateUrl with fqdn against the regex before adding
         Pattern urlRegexPattern;
@@ -122,7 +124,7 @@ public final class RegionalGeo {
         final RegionalGeoRule urlRule = new RegionalGeoRule(regionalGeoDsvc,
                 urlRegex, urlRegexPattern,
                 postalsType, postals,
-                networkRoot, alternateUrl);
+                networkRoot, alternateUrl, coordinateRanges);
 
         LOGGER.info("RegionalGeo: adding " + urlRule);
         regionalGeoDsvc.addRule(urlRule);
@@ -149,6 +151,34 @@ public final class RegionalGeo {
         return root;
     }
 
+    private static boolean checkCoordinateRangeValidity (final RegionalGeoCoordinateRange cr) {
+        if ((cr.getMinLat() < -90.0 || cr.getMinLat() > 90.0) ||
+                (cr.getMaxLat() < -90.0 || cr.getMaxLat() > 90.0) ||
+                (cr.getMinLon() < -180.0 || cr.getMinLon() > 180.0) ||
+                (cr.getMaxLon() < -180.0 || cr.getMaxLon() > 180.0)) {
+            LOGGER.error("The supplied coordinate range is invalid. Latitude must be between -90.0 and +90.0, Longitude must be between -180.0 and +180.0.");
+            return false;
+        }
+        return true;
+    }
+
+    private static List<RegionalGeoCoordinateRange> parseLocationJsonCoordinateRange(final JsonNode locationJson) {
+        final List<RegionalGeoCoordinateRange> coordinateRange =  new ArrayList<>();
+        final JsonNode coordinateRangeJson = locationJson.get("coordinateRange");
+        if (coordinateRangeJson == null) {
+            return null;
+        }
+        final ObjectMapper mapper = new ObjectMapper();
+        RegionalGeoCoordinateRange cr = new RegionalGeoCoordinateRange();
+        for (final JsonNode cRange : coordinateRangeJson) {
+            cr  = mapper.convertValue(cRange, RegionalGeoCoordinateRange.class);
+            if (checkCoordinateRangeValidity(cr)) {
+                coordinateRange.add(cr);
+            }
+        }
+        return coordinateRange;
+    }
+
     private static RegionalGeoRule.PostalsType parseLocationJson(final JsonNode locationJson,
         final Set<String> postals) {
 
@@ -219,6 +249,8 @@ public final class RegionalGeo {
                     LOGGER.error("RegionalGeo ERR: geoLocation empty");
                     return null;
                 }
+                // coordinate range
+                final List<RegionalGeoCoordinateRange> coordinateRanges = parseLocationJsonCoordinateRange(locationJson);
 
                 // white list
                 NetworkNode whiteListRoot = null;
@@ -228,9 +260,8 @@ public final class RegionalGeo {
                 }
 
 
-
                 // add the rule
-                if (!regionalGeo.addRule(dsvcId, urlRegex, postalsType, postals, whiteListRoot, redirectUrl, isSteeringDS)) {
+                if (!regionalGeo.addRule(dsvcId, urlRegex, postalsType, postals, whiteListRoot, redirectUrl, isSteeringDS, coordinateRanges)) {
                     LOGGER.error("RegionalGeo ERR: add rule failed on parsing json file");
                     return null;
                 }
@@ -257,7 +288,7 @@ public final class RegionalGeo {
         }
 
         final RegionalGeo regionalGeo = parseConfigJson(json);
-        if (regionalGeo== null) {
+        if (regionalGeo == null) {
             currentConfig.setFallback(true);
             return false;
         }
@@ -273,7 +304,7 @@ public final class RegionalGeo {
 
 
     public static RegionalGeoResult enforce(final String dsvcId, final String url,
-        final String ip, final String postalCode) {
+                                            final String ip, final String postalCode, final double lat, final double lon) {
 
         final RegionalGeoResult result = new RegionalGeoResult();
         boolean allowed = false;
@@ -300,7 +331,7 @@ public final class RegionalGeo {
         } else {
             if (postalCode == null || postalCode.isEmpty()) {
                 LOGGER.warn("RegionalGeo: alternate a request with null or empty postal");
-                allowed = false;
+                allowed = rule.isAllowedCoordinates(lat, lon);
             } else {
                 allowed = rule.isAllowedPostal(postalCode);
             }
@@ -360,18 +391,24 @@ public final class RegionalGeo {
         }
 
         String postalCode = null;
+        double lat = 0.0;
+        double lon = 0.0;
+
         if (clientGeolocation != null) {
             postalCode = clientGeolocation.getPostalCode();
 
             // Get the first 3 chars in the postal code. These 3 chars are called FSA in Canadian postal codes.
             if (postalCode != null && postalCode.length() > 3) {
                 postalCode = postalCode.substring(0, 3);
+            } else {
+                lat = clientGeolocation.getLatitude();
+                lon = clientGeolocation.getLongitude();
             }
         }
 
         final HTTPRequest httpRequest = HTTPRequest.class.cast(request);
         final RegionalGeoResult result = enforce(deliveryService.getId(), httpRequest.getRequestedUrl(), 
-                                                 httpRequest.getClientIP(), postalCode);
+                                                 httpRequest.getClientIP(), postalCode, lat, lon);
 
         if (cache == null && result.getType() == ALTERNATE_WITH_CACHE) {
             LOGGER.debug("RegionalGeo: denied for dsvc " + deliveryService.getId() + ", url " + httpRequest.getRequestedUrl() + ", postal " + postalCode + ". Relative re-direct URLs not allowed for Multi Route Delivery Services.");
@@ -444,5 +481,6 @@ public final class RegionalGeo {
 
         return "Denied"; // DENIED
     }
+
 }
 
diff --git a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/loc/RegionalGeoCoordinateRange.java b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/loc/RegionalGeoCoordinateRange.java
new file mode 100644
index 0000000..7b9211c
--- /dev/null
+++ b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/loc/RegionalGeoCoordinateRange.java
@@ -0,0 +1,53 @@
+package com.comcast.cdn.traffic_control.traffic_router.core.loc;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class RegionalGeoCoordinateRange {
+    @JsonProperty
+    private double minLat;
+    @JsonProperty
+    private double minLon;
+    @JsonProperty
+    private double maxLat;
+    @JsonProperty
+    private double maxLon;
+
+    public RegionalGeoCoordinateRange() {
+        minLat = 0.0;
+        minLon = 0.0;
+        maxLat = 0.0;
+        maxLon = 0.0;
+    }
+
+    public double getMinLat() {
+        return minLat;
+    }
+
+    public void setMinLat(final double minLat) {
+        this.minLat = minLat;
+    }
+
+    public double getMinLon() {
+        return minLon;
+    }
+
+    public void setMinLon(final double minLon) {
+        this.minLon = minLon;
+    }
+
+    public double getMaxLat() {
+        return maxLat;
+    }
+
+    public void setMaxLat(final double maxLat) {
+        this.maxLat = maxLat;
+    }
+
+    public double getMaxLon() {
+        return maxLon;
+    }
+
+    public void setMaxLon(final double maxLon) {
+        this.maxLon = maxLon;
+    }
+}
diff --git a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/loc/RegionalGeoRule.java b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/loc/RegionalGeoRule.java
index 0b5b749..f80b92e 100644
--- a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/loc/RegionalGeoRule.java
+++ b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/loc/RegionalGeoRule.java
@@ -15,9 +15,11 @@
 
 package com.comcast.cdn.traffic_control.traffic_router.core.loc;
 
+import org.apache.log4j.Logger;
+
+import java.util.List;
 import java.util.Set;
 import java.util.regex.Pattern;
-import org.apache.log4j.Logger;
 
 
 public class RegionalGeoRule {
@@ -40,10 +42,12 @@ public class RegionalGeoRule {
 
     private final String alternateUrl; // if disallowed, client will be redirected to this url
 
+    private final List<RegionalGeoCoordinateRange> coordinateRanges;
+
     public RegionalGeoRule(final RegionalGeoDsvc regionalGeoDsvc,
-            final String urlRegex, final Pattern urlRegexPattern, final PostalsType postalsType,
-            final Set<String> postals, final NetworkNode whiteListRoot,
-            final String alternateUrl) {
+                           final String urlRegex, final Pattern urlRegexPattern, final PostalsType postalsType,
+                           final Set<String> postals, final NetworkNode whiteListRoot,
+                           final String alternateUrl, final List<RegionalGeoCoordinateRange> coordinateRanges) {
         this.regionalGeoDsvc = regionalGeoDsvc;
         this.urlRegex = urlRegex;
         this.pattern = urlRegexPattern;
@@ -51,6 +55,7 @@ public class RegionalGeoRule {
         this.postals = postals;
         this.whiteListRoot = whiteListRoot;
         this.alternateUrl = alternateUrl;
+        this.coordinateRanges = coordinateRanges;
     }
 
     public boolean matchesUrl(final String url) {
@@ -72,6 +77,20 @@ public class RegionalGeoRule {
         return false;
     }
 
+    public boolean isAllowedCoordinates(final double lat, final double lon) {
+        if (coordinateRanges == null) {
+            return false;
+        }
+        for (int i=0; i < coordinateRanges.size(); i++) {
+            final RegionalGeoCoordinateRange coordinateRange = coordinateRanges.get(i);
+            if ((lat >= coordinateRange.getMinLat() && lon >= coordinateRange.getMinLon()) &&
+                    (lat <= coordinateRange.getMaxLat() && lon <= coordinateRange.getMaxLon())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     public boolean isIpInWhiteList(final String ip) {
         if (whiteListRoot == null) {
             return false;
diff --git a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/TrafficRouter.java b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/TrafficRouter.java
index df6d14e..77191ad 100644
--- a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/TrafficRouter.java
+++ b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/TrafficRouter.java
@@ -15,57 +15,28 @@
 
 package com.comcast.cdn.traffic_control.traffic_router.core.router;
 
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Random;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
-
 import com.comcast.cdn.traffic_control.traffic_router.configuration.ConfigurationListener;
-import com.comcast.cdn.traffic_control.traffic_router.core.ds.SteeringResult;
-import com.comcast.cdn.traffic_control.traffic_router.core.ds.SteeringTarget;
+import com.comcast.cdn.traffic_control.traffic_router.core.dns.DNSAccessRecord;
+import com.comcast.cdn.traffic_control.traffic_router.core.dns.ZoneManager;
+import com.comcast.cdn.traffic_control.traffic_router.core.ds.DeliveryService;
 import com.comcast.cdn.traffic_control.traffic_router.core.ds.Steering;
+import com.comcast.cdn.traffic_control.traffic_router.core.ds.SteeringGeolocationComparator;
 import com.comcast.cdn.traffic_control.traffic_router.core.ds.SteeringRegistry;
-import com.comcast.cdn.traffic_control.traffic_router.core.hash.ConsistentHasher;
-import com.comcast.cdn.traffic_control.traffic_router.core.loc.MaxmindGeolocationService;
-import com.comcast.cdn.traffic_control.traffic_router.core.util.JsonUtils;
-import com.fasterxml.jackson.databind.JsonNode;
-import org.apache.log4j.Logger;
-import org.springframework.beans.BeansException;
-import org.springframework.context.ApplicationContext;
-import org.xbill.DNS.Name;
-import org.xbill.DNS.Type;
-import org.xbill.DNS.Zone;
-
+import com.comcast.cdn.traffic_control.traffic_router.core.ds.SteeringResult;
+import com.comcast.cdn.traffic_control.traffic_router.core.ds.SteeringTarget;
 import com.comcast.cdn.traffic_control.traffic_router.core.edge.Cache;
 import com.comcast.cdn.traffic_control.traffic_router.core.edge.CacheLocation;
 import com.comcast.cdn.traffic_control.traffic_router.core.edge.CacheLocation.LocalizationMethod;
 import com.comcast.cdn.traffic_control.traffic_router.core.edge.CacheRegister;
-import com.comcast.cdn.traffic_control.traffic_router.core.edge.Node;
 import com.comcast.cdn.traffic_control.traffic_router.core.edge.InetRecord;
 import com.comcast.cdn.traffic_control.traffic_router.core.edge.Location;
+import com.comcast.cdn.traffic_control.traffic_router.core.edge.Node;
 import com.comcast.cdn.traffic_control.traffic_router.core.edge.TrafficRouterLocation;
-import com.comcast.cdn.traffic_control.traffic_router.core.dns.ZoneManager;
-import com.comcast.cdn.traffic_control.traffic_router.core.dns.DNSAccessRecord;
-import com.comcast.cdn.traffic_control.traffic_router.core.ds.DeliveryService;
-import com.comcast.cdn.traffic_control.traffic_router.core.ds.SteeringGeolocationComparator;
+import com.comcast.cdn.traffic_control.traffic_router.core.hash.ConsistentHasher;
+import com.comcast.cdn.traffic_control.traffic_router.core.loc.AnonymousIp;
+import com.comcast.cdn.traffic_control.traffic_router.core.loc.AnonymousIpDatabaseService;
 import com.comcast.cdn.traffic_control.traffic_router.core.loc.FederationRegistry;
-import com.comcast.cdn.traffic_control.traffic_router.geolocation.Geolocation;
-import com.comcast.cdn.traffic_control.traffic_router.geolocation.GeolocationException;
-import com.comcast.cdn.traffic_control.traffic_router.geolocation.GeolocationService;
+import com.comcast.cdn.traffic_control.traffic_router.core.loc.MaxmindGeolocationService;
 import com.comcast.cdn.traffic_control.traffic_router.core.loc.NetworkNode;
 import com.comcast.cdn.traffic_control.traffic_router.core.loc.NetworkNodeException;
 import com.comcast.cdn.traffic_control.traffic_router.core.loc.RegionalGeo;
@@ -73,13 +44,41 @@ import com.comcast.cdn.traffic_control.traffic_router.core.request.DNSRequest;
 import com.comcast.cdn.traffic_control.traffic_router.core.request.HTTPRequest;
 import com.comcast.cdn.traffic_control.traffic_router.core.request.Request;
 import com.comcast.cdn.traffic_control.traffic_router.core.router.StatTracker.Track;
+import com.comcast.cdn.traffic_control.traffic_router.core.router.StatTracker.Track.ResultDetails;
 import com.comcast.cdn.traffic_control.traffic_router.core.router.StatTracker.Track.ResultType;
 import com.comcast.cdn.traffic_control.traffic_router.core.router.StatTracker.Track.RouteType;
-import com.comcast.cdn.traffic_control.traffic_router.core.util.TrafficOpsUtils;
 import com.comcast.cdn.traffic_control.traffic_router.core.util.CidrAddress;
-import com.comcast.cdn.traffic_control.traffic_router.core.router.StatTracker.Track.ResultDetails;
-import com.comcast.cdn.traffic_control.traffic_router.core.loc.AnonymousIp;
-import com.comcast.cdn.traffic_control.traffic_router.core.loc.AnonymousIpDatabaseService;
+import com.comcast.cdn.traffic_control.traffic_router.core.util.JsonUtils;
+import com.comcast.cdn.traffic_control.traffic_router.core.util.TrafficOpsUtils;
+import com.comcast.cdn.traffic_control.traffic_router.geolocation.Geolocation;
+import com.comcast.cdn.traffic_control.traffic_router.geolocation.GeolocationException;
+import com.comcast.cdn.traffic_control.traffic_router.geolocation.GeolocationService;
+import com.fasterxml.jackson.databind.JsonNode;
+import org.apache.log4j.Logger;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.xbill.DNS.Name;
+import org.xbill.DNS.Type;
+import org.xbill.DNS.Zone;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
 @SuppressWarnings({"PMD.TooManyFields", "PMD.ExcessivePublicCount"})
 public class TrafficRouter {
diff --git a/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/loc/RegionalGeoRuleTest.java b/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/loc/RegionalGeoRuleTest.java
index 7c9a7fe..fa6391c 100644
--- a/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/loc/RegionalGeoRuleTest.java
+++ b/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/loc/RegionalGeoRuleTest.java
@@ -15,24 +15,66 @@
 
 package com.comcast.cdn.traffic_control.traffic_router.core.loc;
 
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.equalTo;
+import org.junit.Test;
 
-import java.io.File;
-import java.io.FileReader;
-import java.util.regex.Pattern;
-import java.util.Set;
-import java.util.HashSet;
-import java.util.List;
 import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Pattern;
 
-import org.apache.log4j.Logger;
-import org.junit.Before;
-import org.junit.Test;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
 
 public class RegionalGeoRuleTest {
 
     @Test
+    public void testIsAllowedCoordinateRanges() throws Exception {
+        final String urlRegex = ".*abc.m3u8";
+        final RegionalGeoRule.PostalsType ruleType = RegionalGeoRule.PostalsType.INCLUDE;
+        final Set<String> postals = new HashSet<String>();
+
+        final NetworkNode whiteList = new NetworkNode.SuperNode();
+        final String alternateUrl = "/alternate.m3u8";
+        final ArrayList<RegionalGeoCoordinateRange> coordinateRanges = new ArrayList<>();
+        RegionalGeoCoordinateRange coordinateRange = new RegionalGeoCoordinateRange();
+        RegionalGeoCoordinateRange coordinateRange2 = new RegionalGeoCoordinateRange();
+        coordinateRange.setMinLat(10.0);
+        coordinateRange.setMinLon(165.0);
+        coordinateRange.setMaxLat(22.0);
+        coordinateRange.setMaxLon(179.0);
+        coordinateRanges.add(coordinateRange);
+        coordinateRange2.setMinLat(17.0);
+        coordinateRange2.setMinLon(-20.0);
+        coordinateRange2.setMaxLat(25.0);
+        coordinateRange2.setMaxLon(19.0);
+        coordinateRanges.add(coordinateRange2);
+
+        Pattern urlRegexPattern = Pattern.compile(urlRegex, Pattern.CASE_INSENSITIVE);
+
+        final RegionalGeoRule urlRule = new RegionalGeoRule(null,
+                urlRegex, urlRegexPattern,
+                ruleType, postals,
+                whiteList, alternateUrl, coordinateRanges);
+
+        boolean allowed;
+
+        allowed = urlRule.isAllowedCoordinates(11.0, 170.0);
+        assertThat(allowed, equalTo(true));
+
+        allowed = urlRule.isAllowedCoordinates(13.0, 162.0);
+        assertThat(allowed, equalTo(false));
+
+        allowed = urlRule.isAllowedCoordinates(23.0, 22.0);
+        assertThat(allowed, equalTo(false));
+
+        allowed = urlRule.isAllowedCoordinates(23.0, -12.0);
+        assertThat(allowed, equalTo(true));
+
+        allowed = urlRule.isAllowedCoordinates(9.0, 21.0);
+        assertThat(allowed, equalTo(false));
+    }
+
+    @Test
     public void testMatchesUrl() throws Exception {
         final String urlRegex = ".*abc.m3u8";
         final RegionalGeoRule.PostalsType ruleType = RegionalGeoRule.PostalsType.INCLUDE;
@@ -45,7 +87,7 @@ public class RegionalGeoRuleTest {
         final RegionalGeoRule urlRule = new RegionalGeoRule(null,
                 urlRegex, urlRegexPattern,
                 ruleType, postals,
-                whiteList, alternateUrl);
+                whiteList, alternateUrl, null);
 
         boolean matches;
         String url = "http://example.com/abc.m3u8";
@@ -88,7 +130,7 @@ public class RegionalGeoRuleTest {
         final RegionalGeoRule urlRule = new RegionalGeoRule(null,
                 urlRegex, urlRegexPattern,
                 ruleType, postals,
-                whiteList, alternateUrl);
+                whiteList, alternateUrl, null);
 
         boolean allowed;
 
@@ -117,7 +159,7 @@ public class RegionalGeoRuleTest {
         final RegionalGeoRule urlRule = new RegionalGeoRule(null,
                 urlRegex, urlRegexPattern,
                 ruleType, postals,
-                whiteList, alternateUrl);
+                whiteList, alternateUrl, null);
 
         boolean allowed;
 
@@ -153,7 +195,7 @@ public class RegionalGeoRuleTest {
         final RegionalGeoRule urlRule = new RegionalGeoRule(null,
                 urlRegex, urlRegexPattern,
                 ruleType, postals,
-                whiteList, alternateUrl);
+                whiteList, alternateUrl, null);
 
         boolean in;
 
@@ -209,7 +251,7 @@ public class RegionalGeoRuleTest {
             final RegionalGeoRule urlRule = new RegionalGeoRule(null,
                     urlRegex, urlRegexPattern,
                     ruleType, postals,
-                    whiteList, alternateUrl);
+                    whiteList, alternateUrl, null);
 
             boolean in;
 
@@ -245,7 +287,7 @@ public class RegionalGeoRuleTest {
         final RegionalGeoRule urlRule = new RegionalGeoRule(null,
                 urlRegex, urlRegexPattern,
                 ruleType, postals,
-                whiteList, alternateUrl);
+                whiteList, alternateUrl, null);
 
         boolean in;
 
diff --git a/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/loc/RegionalGeoTest.java b/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/loc/RegionalGeoTest.java
index 40a8de5..cc77ce8 100644
--- a/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/loc/RegionalGeoTest.java
+++ b/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/loc/RegionalGeoTest.java
@@ -15,32 +15,26 @@
 
 package com.comcast.cdn.traffic_control.traffic_router.core.loc;
 
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.equalTo;
-import static org.powermock.api.mockito.PowerMockito.*;
-
-import java.io.File;
-import java.io.FileReader;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.regex.Pattern;
-import java.util.Set;
-import java.util.HashSet;
-
-import com.comcast.cdn.traffic_control.traffic_router.core.edge.Cache;
 import com.comcast.cdn.traffic_control.traffic_router.core.ds.DeliveryService;
+import com.comcast.cdn.traffic_control.traffic_router.core.edge.Cache;
+import com.comcast.cdn.traffic_control.traffic_router.core.loc.RegionalGeoResult.RegionalGeoResultType;
 import com.comcast.cdn.traffic_control.traffic_router.core.request.HTTPRequest;
-import com.comcast.cdn.traffic_control.traffic_router.core.request.Request;
 import com.comcast.cdn.traffic_control.traffic_router.core.router.HTTPRouteResult;
 import com.comcast.cdn.traffic_control.traffic_router.core.router.StatTracker;
 import com.comcast.cdn.traffic_control.traffic_router.core.router.TrafficRouter;
 import com.comcast.cdn.traffic_control.traffic_router.geolocation.Geolocation;
 import com.comcast.cdn.traffic_control.traffic_router.geolocation.GeolocationException;
-import org.apache.log4j.Logger;
 import org.junit.Before;
 import org.junit.Test;
 
-import com.comcast.cdn.traffic_control.traffic_router.core.loc.RegionalGeoResult.RegionalGeoResultType;
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.powermock.api.mockito.PowerMockito.mock;
+import static org.powermock.api.mockito.PowerMockito.when;
 
 public class RegionalGeoTest {
     @Before
@@ -50,13 +44,38 @@ public class RegionalGeoTest {
     }
 
     @Test
+    public void testEnforceAllowedCoordinateRange() {
+        final String dsvcId = "ds-geoblock-exclude";
+        final String url = "http://ds1.example.com/live1";
+        final String postal = null;
+        final String ip = "10.0.0.1";
+
+        RegionalGeoResult result = RegionalGeo.enforce(dsvcId, url, ip, postal, 12.0, 55.0);
+
+        assertThat(result.getType(), equalTo(RegionalGeoResultType.ALLOWED));
+        assertThat(result.getUrl(), equalTo(url));
+    }
+
+    @Test
+    public void testEnforceAlternateWithCacheNoCoordinateRangeNoPostalCode() {
+        final String dsvcId = "ds-geoblock-include";
+        final String url = "http://ds2.example.com/live2";
+        final String postal = null;
+        final String ip = "10.0.0.1";
+
+        RegionalGeoResult result = RegionalGeo.enforce(dsvcId, url, ip, postal, 12.0, 55.0);
+
+        assertThat(result.getType(), equalTo(RegionalGeoResultType.ALTERNATE_WITH_CACHE));
+    }
+
+    @Test
     public void testEnforceAllowed() {
         final String dsvcId = "ds-geoblock-exclude";
         final String url = "http://ds1.example.com/live1";
         final String postal = "N7G";
         final String ip = "10.0.0.1";
 
-        RegionalGeoResult result = RegionalGeo.enforce(dsvcId, url, ip, postal);
+        RegionalGeoResult result = RegionalGeo.enforce(dsvcId, url, ip, postal, 0.0, 0.0);
 
         assertThat(result.getType(), equalTo(RegionalGeoResultType.ALLOWED));
         assertThat(result.getUrl(), equalTo(url));
@@ -69,7 +88,7 @@ public class RegionalGeoTest {
         final String postal = "N7G";
         final String ip = "10.0.0.1";
 
-        RegionalGeoResult result = RegionalGeo.enforce(dsvcId, url, ip, postal);
+        RegionalGeoResult result = RegionalGeo.enforce(dsvcId, url, ip, postal, 0.0, 0.0);
 
         assertThat(result.getType(), equalTo(RegionalGeoResultType.ALTERNATE_WITH_CACHE));
         assertThat(result.getUrl(), equalTo("/path/redirect_T2"));
@@ -82,7 +101,7 @@ public class RegionalGeoTest {
         final String postal = "V5G";
         final String ip = "10.0.0.1";
 
-        RegionalGeoResult result = RegionalGeo.enforce(dsvcId, url, ip, postal);
+        RegionalGeoResult result = RegionalGeo.enforce(dsvcId, url, ip, postal, 0.0, 0.0);
 
         assertThat(result.getType(), equalTo(RegionalGeoResultType.ALTERNATE_WITHOUT_CACHE));
         assertThat(result.getUrl(), equalTo("http://example.com/redirect_T1"));
@@ -95,7 +114,7 @@ public class RegionalGeoTest {
         final String postal = "V5G";
         final String ip = "10.0.0.1";
 
-        RegionalGeoResult result = RegionalGeo.enforce(dsvcId, url, ip, postal);
+        RegionalGeoResult result = RegionalGeo.enforce(dsvcId, url, ip, postal, 0.0, 0.0);
 
         assertThat(result.getType(), equalTo(RegionalGeoResultType.DENIED));
     }
@@ -107,7 +126,7 @@ public class RegionalGeoTest {
         final String postal = "V5G";
         final String ip = "10.0.0.1";
 
-        RegionalGeoResult result = RegionalGeo.enforce(dsvcId, url, ip, postal);
+        RegionalGeoResult result = RegionalGeo.enforce(dsvcId, url, ip, postal, 0.0, 0.0);
 
         assertThat(result.getType(), equalTo(RegionalGeoResultType.DENIED));
     }
@@ -119,7 +138,7 @@ public class RegionalGeoTest {
         final String postal = "V5D";
         final String ip = "10.0.0.1";
 
-        RegionalGeoResult result = RegionalGeo.enforce(dsvcId, url, ip, postal);
+        RegionalGeoResult result = RegionalGeo.enforce(dsvcId, url, ip, postal, 0.0, 0.0);
 
         assertThat(result.getType(), equalTo(RegionalGeoResultType.ALTERNATE_WITH_CACHE));
         assertThat(result.getUrl(), equalTo("/redirect_T3"));
@@ -132,7 +151,7 @@ public class RegionalGeoTest {
         final String postal = null;
         final String ip = "10.0.0.1";
 
-        RegionalGeoResult result = RegionalGeo.enforce(dsvcId, url, ip, postal);
+        RegionalGeoResult result = RegionalGeo.enforce(dsvcId, url, ip, postal, 0.0, 0.0);
 
         assertThat(result.getType(), equalTo(RegionalGeoResultType.ALTERNATE_WITHOUT_CACHE));
         assertThat(result.getUrl(), equalTo("http://example.com/redirect_T1"));
@@ -145,7 +164,7 @@ public class RegionalGeoTest {
         final String postal = "";
         final String ip = "10.0.0.1";
 
-        RegionalGeoResult result = RegionalGeo.enforce(dsvcId, url, ip, postal);
+        RegionalGeoResult result = RegionalGeo.enforce(dsvcId, url, ip, postal, 0.0, 0.0);
 
         assertThat(result.getType(), equalTo(RegionalGeoResultType.ALTERNATE_WITH_CACHE));
         assertThat(result.getUrl(), equalTo("/path/redirect_T2"));
@@ -158,7 +177,7 @@ public class RegionalGeoTest {
         final String postal = "";
         final String ip = "10.0.0.1";
 
-        RegionalGeoResult result = RegionalGeo.enforce(dsvcId, url, ip, postal);
+        RegionalGeoResult result = RegionalGeo.enforce(dsvcId, url, ip, postal, 0.0, 0.0);
 
         assertThat(result.getType(), equalTo(RegionalGeoResultType.ALTERNATE_WITHOUT_CACHE));
         assertThat(result.getUrl(), equalTo("http://example.com/redirect_T1"));
@@ -171,7 +190,7 @@ public class RegionalGeoTest {
         final String postal = null;
         final String ip = "129.100.254.2";
 
-        RegionalGeoResult result = RegionalGeo.enforce(dsvcId, url, ip, postal);
+        RegionalGeoResult result = RegionalGeo.enforce(dsvcId, url, ip, postal, 0.0, 0.0);
 
         assertThat(result.getType(), equalTo(RegionalGeoResultType.ALLOWED));
         assertThat(result.getUrl(), equalTo(url));
@@ -184,7 +203,7 @@ public class RegionalGeoTest {
         final String postal = null;
         final String ip = "129.100.254.2";
 
-        RegionalGeoResult result = RegionalGeo.enforce(dsvcId, url, ip, postal);
+        RegionalGeoResult result = RegionalGeo.enforce(dsvcId, url, ip, postal, 0.0, 0.0);
 
         assertThat(result.getType(), equalTo(RegionalGeoResultType.ALTERNATE_WITHOUT_CACHE));
         assertThat(result.getUrl(), equalTo("https://example.com/redirect_https"));
@@ -197,7 +216,7 @@ public class RegionalGeoTest {
         final String postal = null;
         final String ip = "129.100.254.4";
 
-        RegionalGeoResult result = RegionalGeo.enforce(dsvcId, url, ip, postal);
+        RegionalGeoResult result = RegionalGeo.enforce(dsvcId, url, ip, postal, 0.0, 0.0);
 
         assertThat(result.getType(), equalTo(RegionalGeoResultType.ALTERNATE_WITHOUT_CACHE));
         assertThat(result.getUrl(), equalTo("https://example.com/steering-test"));
@@ -211,7 +230,7 @@ public class RegionalGeoTest {
         final String postal = "N7G";
         final String ip = "129.202.254.2";
 
-        RegionalGeoResult result = RegionalGeo.enforce(dsvcId, url, ip, postal);
+        RegionalGeoResult result = RegionalGeo.enforce(dsvcId, url, ip, postal, 0.0, 0.0);
 
         assertThat(result.getType(), equalTo(RegionalGeoResultType.ALTERNATE_WITH_CACHE));
         assertThat(result.getUrl(), equalTo("/redirect_T4"));
@@ -224,7 +243,7 @@ public class RegionalGeoTest {
         final String postal = "N6G";
         final String ip = "129.202.254.2";
 
-        RegionalGeoResult result = RegionalGeo.enforce(dsvcId, url, ip, postal);
+        RegionalGeoResult result = RegionalGeo.enforce(dsvcId, url, ip, postal, 0.0, 0.0);
 
         assertThat(result.getType(), equalTo(RegionalGeoResultType.ALLOWED));
         assertThat(result.getUrl(), equalTo(url));
diff --git a/traffic_router/core/src/test/resources/regional_geoblock.json b/traffic_router/core/src/test/resources/regional_geoblock.json
index f97fe34..520e5c2 100644
--- a/traffic_router/core/src/test/resources/regional_geoblock.json
+++ b/traffic_router/core/src/test/resources/regional_geoblock.json
@@ -7,7 +7,8 @@
                 "excludePostalCode": [
                     "V5G",
                     "M7A"
-                ]
+                ],
+                "coordinateRange": [{"minLat" : -12, "maxLat": 13, "minLon" : 55, "maxLon": 56}, {"minLat" : -13, "maxLat": 14, "minLon" : 55, "maxLon": 56}]
             },
             "urlRegex": ".*live1"
         },