header_rewrite: allow for use of maxminddb as source of geo truth (#7695)

diff --git a/configure.ac b/configure.ac
index 2699734..514fc5a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1685,12 +1685,16 @@
 #
 AC_CHECK_HEADERS([GeoIP.h], [
   AC_CHECK_LIB([GeoIP], [GeoIP_new], [
-    AC_SUBST([GEO_LIBS], ["-lGeoIP"])
+    AC_SUBST([GEOIP_LIBS], ["-lGeoIP"])
+    AC_SUBST(has_geoip, 1)
   ], [
-    AC_SUBST([GEO_LIBS], [""])
+    AC_SUBST([GEOIP_LIBS], [""])
+    AC_SUBST(has_geoip, 0)
   ])
 ])
 
+AM_CONDITIONAL([HAS_GEOIP], [test "x${has_geoip}" = "x1" ])
+
 #
 # Check for libmaxmind.  This is the maxmind v2 API where GeoIP is the legacy
 # v1 dat file based API
@@ -1698,14 +1702,49 @@
 AC_CHECK_HEADERS([maxminddb.h], [
   AC_CHECK_LIB([maxminddb], [MMDB_open], [
     AC_SUBST([MAXMINDDB_LIBS], ["-lmaxminddb"])
-    AC_SUBST(has_maxmind, 1)
+    AC_SUBST(has_maxminddb, 1)
   ], [
     AC_SUBST([MAXMINDDB_LIBS], [""])
-    AC_SUBST(has_maxmind, 0)
+    AC_SUBST(has_maxminddb, 0)
   ])
 ])
 
-AM_CONDITIONAL([BUILD_MAXMIND_ACL_PLUGIN], [test "x${has_maxmind}" = "x1" ])
+AM_CONDITIONAL([HAS_MAXMINDDB], [test "x${has_maxminddb}" = "x1" ])
+
+AC_ARG_WITH([hrw-geo-provider],
+  [AS_HELP_STRING([--with-hrw-geo-provider=geoip|maxminddb],[geo provider to use with header_rewrite [default=auto] ])],
+  [geo_provider=$withval],
+  [geo_provider="auto"]
+)
+use_hrw_geoip=0
+use_hrw_maxminddb=0
+
+AS_IF([test "x$geo_provider" = "xauto"], [
+  if test "x$has_geoip" = "x1"; then
+    use_hrw_geoip=1
+    AC_MSG_NOTICE([Using GeoIP interface for header_rewrite])
+  elif test "x$has_maxminddb" = "x1"; then
+    use_hrw_maxminddb=1
+    AC_MSG_NOTICE([Using MaxMindDB interface for header_rewrite])
+  fi
+],[
+  case "x$geo_provider" in
+    xgeoip)
+      use_hrw_geoip=1
+      AC_MSG_RESULT([forced to GeoIP])
+      ;;
+    xmaxminddb)
+      use_hrw_maxminddb=1
+      AC_MSG_RESULT([forced to MaxMindDB])
+      ;;
+    *)
+      AC_MSG_RESULT([failed])
+      AC_MSG_FAILURE([unknown geo interface $geo_provider])
+  esac
+])
+
+AC_SUBST(use_hrw_geoip)
+AC_SUBST(use_hrw_maxminddb)
 
 # Right now, the healthcheck plugins requires inotify_init (and friends)
 AM_CONDITIONAL([BUILD_HEALTHCHECK_PLUGIN], [ test "$ac_cv_func_inotify_init" = "yes" ])
diff --git a/doc/admin-guide/plugins/header_rewrite.en.rst b/doc/admin-guide/plugins/header_rewrite.en.rst
index 40f86a3..5caa270 100644
--- a/doc/admin-guide/plugins/header_rewrite.en.rst
+++ b/doc/admin-guide/plugins/header_rewrite.en.rst
@@ -82,11 +82,16 @@
 rewriting rules are evaluated for every request made to your |TS| instance.
 This is done by adding the following line to your :file:`plugin.config`::
 
-  header_rewrite.so config_file_1.conf config_file_2.conf ...
+  header_rewrite.so [--geo-db-path=path/to/geoip.db] config_file_1.conf config_file_2.conf ...
 
 You may specify multiple configuration files. Their rules will be evaluated in
 the order the files are listed.
 
+The plugin takes an optional switch ``--geo-db-path``. If MaxMindDB support has
+been compiled in, use this switch to point at your .mmdb file. This also applies to
+the remap context.
+
+
 Enabling Per-Mapping
 --------------------
 
@@ -219,7 +224,7 @@
     cond %{GEO:<part>} <operand>
 
 Perform a GeoIP lookup of the client-IP, using a 3rd party library and
-DB. Currently only the MaxMind GeoIP API is supported. The default is to
+DB. Currently the MaxMind GeoIP and MaxMindDB APIs are supported. The default is to
 do a Country lookup, but the following qualifiers are supported::
 
     %{GEO:COUNTRY}      The country code (e.g. "US")
@@ -495,7 +500,7 @@
 ~~~~~~~~
 ::
 
-	cond %{<name>}
+    cond %{<name>}
         add-header @PropertyName "%{TCP-INFO}"
 
 This operation records TCP Info struct field values as an Internal remap as well as global header at the event hook specified by the condition. Supported hook conditions include TXN_START_HOOK, SEND_RESPONSE_HEADER_HOOK and TXN_CLOSE_HOOK in the Global plugin and REMAP_PSEUDO_HOOK, SEND_RESPONSE_HEADER_HOOK and TXN_CLOSE_HOOK in the Remap plugin. Conditions supported as request headers include TXN_START_HOOK and REMAP_PSEUDO_HOOK. The other conditions are supported as response headers. TCP Info fields currently recorded include rtt, rto, snd_cwnd and all_retrans. This operation is not supported on transactions originated within Traffic Server (for e.g using the |TS| :c:func:`TSHttpTxnIsInternal`)
diff --git a/include/tscore/ink_config.h.in b/include/tscore/ink_config.h.in
index 8b80172..c23417f 100644
--- a/include/tscore/ink_config.h.in
+++ b/include/tscore/ink_config.h.in
@@ -81,6 +81,9 @@
 #define TS_HAS_TLS_EARLY_DATA @has_tls_early_data@
 #define TS_HAS_TLS_SESSION_TICKET @has_tls_session_ticket@
 
+#define TS_USE_HRW_GEOIP @use_hrw_geoip@
+#define TS_USE_HRW_MAXMINDDB @use_hrw_maxminddb@
+
 #define TS_HAS_SO_PEERCRED @has_so_peercred@
 
 /* OS API definitions */
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index b739a05..76addc5 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -39,7 +39,9 @@
 include generator/Makefile.inc
 include compress/Makefile.inc
 include header_rewrite/Makefile.inc
+if BUILD_HEALTHCHECK_PLUGIN
 include healthchecks/Makefile.inc
+endif
 include libloader/Makefile.inc
 if HAS_LUAJIT
 include lua/Makefile.inc
@@ -71,7 +73,7 @@
 include experimental/icap/Makefile.inc
 include experimental/inliner/Makefile.inc
 
-if BUILD_MAXMIND_ACL_PLUGIN
+if HAS_MAXMINDDB
 include experimental/maxmind_acl/Makefile.inc
 endif
 
diff --git a/plugins/experimental/geoip_acl/Makefile.inc b/plugins/experimental/geoip_acl/Makefile.inc
index 3ff84f6..150b3c3 100644
--- a/plugins/experimental/geoip_acl/Makefile.inc
+++ b/plugins/experimental/geoip_acl/Makefile.inc
@@ -20,4 +20,4 @@
   experimental/geoip_acl/acl.cc \
   experimental/geoip_acl/geoip_acl.cc
 
-experimental_geoip_acl_geoip_acl_la_LIBADD = $(GEO_LIBS)
+experimental_geoip_acl_geoip_acl_la_LIBADD = $(GEOIP_LIBS)
diff --git a/plugins/header_rewrite/Makefile.inc b/plugins/header_rewrite/Makefile.inc
index 0b5cfe8..a64fffa 100644
--- a/plugins/header_rewrite/Makefile.inc
+++ b/plugins/header_rewrite/Makefile.inc
@@ -44,16 +44,38 @@
 	header_rewrite/value.cc \
 	header_rewrite/value.h
 
+if HAS_MAXMINDDB
+header_rewrite_header_rewrite_la_SOURCES += header_rewrite/conditions_geo_maxmind.cc
+endif
+
+if HAS_GEOIP
+header_rewrite_header_rewrite_la_SOURCES += header_rewrite/conditions_geo_geoip.cc
+endif
+
 header_rewrite_parser_la_SOURCES = \
 	header_rewrite/parser.cc \
 	header_rewrite/parser.h
 
 header_rewrite_header_rewrite_la_LIBADD = \
-	header_rewrite/parser.la \
-	$(GEO_LIBS)
+	header_rewrite/parser.la
+
+if HAS_GEOIP
+header_rewrite_header_rewrite_la_LIBADD += $(GEOIP_LIBS)
+endif
+
+if HAS_MAXMINDDB
+header_rewrite_header_rewrite_la_LIBADD += $(MAXMINDDB_LIBS)
+endif
 
 check_PROGRAMS += header_rewrite/header_rewrite_test
 header_rewrite_header_rewrite_test_SOURCES = \
 	header_rewrite/header_rewrite_test.cc
 header_rewrite_header_rewrite_test_LDADD = \
 	header_rewrite/parser.la
+if HAS_GEOIP
+header_rewrite_header_rewrite_test_LDADD += $(GEOIP_LIBS)
+endif
+
+if HAS_MAXMINDDB
+header_rewrite_header_rewrite_test_LDADD += $(MAXMINDDB_LIBS)
+endif
diff --git a/plugins/header_rewrite/conditions.cc b/plugins/header_rewrite/conditions.cc
index 66c3e5d..f88dadc 100644
--- a/plugins/header_rewrite/conditions.cc
+++ b/plugins/header_rewrite/conditions.cc
@@ -15,10 +15,12 @@
   See the License for the specific language governing permissions and
   limitations under the License.
 */
+
 //////////////////////////////////////////////////////////////////////////////////////////////
 // conditions.cc: Implementation of the condition classes
 //
 //
+
 #include <sys/time.h>
 #include <unistd.h>
 #include <arpa/inet.h>
@@ -740,155 +742,11 @@
   return static_cast<const MatcherType *>(_matcher)->test(now);
 }
 
-// ConditionGeo: Geo-based information (integer). See ConditionGeoCountry for the string version.
-#if HAVE_GEOIP_H
-const char *
-ConditionGeo::get_geo_string(const sockaddr *addr) const
-{
-  const char *ret = "(unknown)";
-  int v           = 4;
-
-  if (addr) {
-    switch (_geo_qual) {
-    // Country database
-    case GEO_QUAL_COUNTRY:
-      switch (addr->sa_family) {
-      case AF_INET:
-        if (gGeoIP[GEOIP_COUNTRY_EDITION]) {
-          uint32_t ip = ntohl(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_addr.s_addr);
-
-          ret = GeoIP_country_code_by_ipnum(gGeoIP[GEOIP_COUNTRY_EDITION], ip);
-        }
-        break;
-      case AF_INET6: {
-        if (gGeoIP[GEOIP_COUNTRY_EDITION_V6]) {
-          geoipv6_t ip = reinterpret_cast<const struct sockaddr_in6 *>(addr)->sin6_addr;
-
-          v   = 6;
-          ret = GeoIP_country_code_by_ipnum_v6(gGeoIP[GEOIP_COUNTRY_EDITION_V6], ip);
-        }
-      } break;
-      default:
-        break;
-      }
-      TSDebug(PLUGIN_NAME, "eval(): Client IPv%d seems to come from Country: %s", v, ret);
-      break;
-
-    // ASN database
-    case GEO_QUAL_ASN_NAME:
-      switch (addr->sa_family) {
-      case AF_INET:
-        if (gGeoIP[GEOIP_ASNUM_EDITION]) {
-          uint32_t ip = ntohl(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_addr.s_addr);
-
-          ret = GeoIP_name_by_ipnum(gGeoIP[GEOIP_ASNUM_EDITION], ip);
-        }
-        break;
-      case AF_INET6: {
-        if (gGeoIP[GEOIP_ASNUM_EDITION_V6]) {
-          geoipv6_t ip = reinterpret_cast<const struct sockaddr_in6 *>(addr)->sin6_addr;
-
-          v   = 6;
-          ret = GeoIP_name_by_ipnum_v6(gGeoIP[GEOIP_ASNUM_EDITION_V6], ip);
-        }
-      } break;
-      default:
-        break;
-      }
-      TSDebug(PLUGIN_NAME, "eval(): Client IPv%d seems to come from ASN Name: %s", v, ret);
-      break;
-
-    default:
-      break;
-    }
-  }
-
-  return ret ? ret : "(unknown)";
-}
-
-int64_t
-ConditionGeo::get_geo_int(const sockaddr *addr) const
-{
-  int64_t ret = -1;
-  int v       = 4;
-
-  if (!addr) {
-    return 0;
-  }
-
-  switch (_geo_qual) {
-  // Country Database
-  case GEO_QUAL_COUNTRY_ISO:
-    switch (addr->sa_family) {
-    case AF_INET:
-      if (gGeoIP[GEOIP_COUNTRY_EDITION]) {
-        uint32_t ip = ntohl(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_addr.s_addr);
-
-        ret = GeoIP_id_by_ipnum(gGeoIP[GEOIP_COUNTRY_EDITION], ip);
-      }
-      break;
-    case AF_INET6: {
-      if (gGeoIP[GEOIP_COUNTRY_EDITION_V6]) {
-        geoipv6_t ip = reinterpret_cast<const struct sockaddr_in6 *>(addr)->sin6_addr;
-
-        v   = 6;
-        ret = GeoIP_id_by_ipnum_v6(gGeoIP[GEOIP_COUNTRY_EDITION_V6], ip);
-      }
-    } break;
-    default:
-      break;
-    }
-    TSDebug(PLUGIN_NAME, "eval(): Client IPv%d seems to come from Country ISO: %" PRId64, v, ret);
-    break;
-
-  case GEO_QUAL_ASN: {
-    const char *asn_name = nullptr;
-
-    switch (addr->sa_family) {
-    case AF_INET:
-      if (gGeoIP[GEOIP_ASNUM_EDITION]) {
-        uint32_t ip = ntohl(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_addr.s_addr);
-
-        asn_name = GeoIP_name_by_ipnum(gGeoIP[GEOIP_ASNUM_EDITION], ip);
-      }
-      break;
-    case AF_INET6:
-      if (gGeoIP[GEOIP_ASNUM_EDITION_V6]) {
-        geoipv6_t ip = reinterpret_cast<const struct sockaddr_in6 *>(addr)->sin6_addr;
-
-        v        = 6;
-        asn_name = GeoIP_name_by_ipnum_v6(gGeoIP[GEOIP_ASNUM_EDITION_V6], ip);
-      }
-      break;
-    }
-    if (asn_name) {
-      // This is a little odd, but the strings returned are e.g. "AS1234 Acme Inc"
-      while (*asn_name && !(isdigit(*asn_name))) {
-        ++asn_name;
-      }
-      ret = strtol(asn_name, nullptr, 10);
-    }
-  }
-    TSDebug(PLUGIN_NAME, "eval(): Client IPv%d seems to come from ASN #: %" PRId64, v, ret);
-    break;
-
-  // Likely shouldn't trip, should we assert?
-  default:
-    break;
-  }
-
-  return ret;
-}
-
-#else
-
-// No Geo library available, these are just stubs.
-
-const char *
+std::string
 ConditionGeo::get_geo_string(const sockaddr *addr) const
 {
   TSError("[%s] No Geo library available!", PLUGIN_NAME);
-  return nullptr;
+  return "";
 }
 
 int64_t
@@ -898,8 +756,6 @@
   return 0;
 }
 
-#endif
-
 void
 ConditionGeo::initialize(Parser &p)
 {
diff --git a/plugins/header_rewrite/conditions.h b/plugins/header_rewrite/conditions.h
index ed6c61f..64edb72 100644
--- a/plugins/header_rewrite/conditions.h
+++ b/plugins/header_rewrite/conditions.h
@@ -437,12 +437,12 @@
     _int_type = flag;
   }
 
+private:
+  virtual int64_t get_geo_int(const sockaddr *addr) const;
+  virtual std::string get_geo_string(const sockaddr *addr) const;
+
 protected:
   bool eval(const Resources &res) override;
-
-private:
-  int64_t get_geo_int(const sockaddr *addr) const;
-  const char *get_geo_string(const sockaddr *addr) const;
   GeoQualifiers _geo_qual = GEO_QUAL_COUNTRY;
   bool _int_type          = false;
 };
diff --git a/plugins/header_rewrite/conditions_geo.h b/plugins/header_rewrite/conditions_geo.h
new file mode 100644
index 0000000..2547716
--- /dev/null
+++ b/plugins/header_rewrite/conditions_geo.h
@@ -0,0 +1,46 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#include <arpa/inet.h>
+#include <string>
+
+#include "conditions.h"
+
+class MMConditionGeo : public ConditionGeo
+{
+public:
+  MMConditionGeo() {}
+  virtual ~MMConditionGeo() {}
+
+  static void initLibrary(const std::string &path);
+
+  virtual int64_t get_geo_int(const sockaddr *addr) const override;
+  virtual std::string get_geo_string(const sockaddr *addr) const override;
+};
+
+class GeoIPConditionGeo : public ConditionGeo
+{
+public:
+  GeoIPConditionGeo() {}
+  virtual ~GeoIPConditionGeo() {}
+
+  static void initLibrary(const std::string &path);
+
+  virtual int64_t get_geo_int(const sockaddr *addr) const override;
+  virtual std::string get_geo_string(const sockaddr *addr) const override;
+};
diff --git a/plugins/header_rewrite/conditions_geo_geoip.cc b/plugins/header_rewrite/conditions_geo_geoip.cc
new file mode 100644
index 0000000..f883023
--- /dev/null
+++ b/plugins/header_rewrite/conditions_geo_geoip.cc
@@ -0,0 +1,188 @@
+/*
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+*/
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+// conditions_geo_geoip.cc: Implementation of the ConditionGeo class based on the GeoIP library
+//
+//
+
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <cctype>
+
+#include "ts/ts.h"
+
+#include "conditions_geo.h"
+
+#include <GeoIP.h>
+
+GeoIP *gGeoIP[NUM_DB_TYPES];
+
+void
+GeoIPConditionGeo::initLibrary(const std::string &)
+{
+  GeoIPDBTypes dbs[] = {GEOIP_COUNTRY_EDITION, GEOIP_COUNTRY_EDITION_V6, GEOIP_ASNUM_EDITION, GEOIP_ASNUM_EDITION_V6};
+
+  for (auto &db : dbs) {
+    if (!gGeoIP[db] && GeoIP_db_avail(db)) {
+      // GEOIP_STANDARD seems to break threaded apps...
+      gGeoIP[db] = GeoIP_open_type(db, GEOIP_MMAP_CACHE);
+
+      char *db_info = GeoIP_database_info(gGeoIP[db]);
+      TSDebug(PLUGIN_NAME, "initialized GeoIP-DB[%d] %s", db, db_info);
+      free(db_info);
+    }
+  }
+}
+
+std::string
+GeoIPConditionGeo::get_geo_string(const sockaddr *addr) const
+{
+  std::string ret = "(unknown)";
+  int v           = 4;
+
+  if (addr) {
+    switch (_geo_qual) {
+    // Country database
+    case GEO_QUAL_COUNTRY:
+      switch (addr->sa_family) {
+      case AF_INET:
+        if (gGeoIP[GEOIP_COUNTRY_EDITION]) {
+          uint32_t ip = ntohl(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_addr.s_addr);
+
+          ret = GeoIP_country_code_by_ipnum(gGeoIP[GEOIP_COUNTRY_EDITION], ip);
+        }
+        break;
+      case AF_INET6: {
+        if (gGeoIP[GEOIP_COUNTRY_EDITION_V6]) {
+          geoipv6_t ip = reinterpret_cast<const struct sockaddr_in6 *>(addr)->sin6_addr;
+
+          v   = 6;
+          ret = GeoIP_country_code_by_ipnum_v6(gGeoIP[GEOIP_COUNTRY_EDITION_V6], ip);
+        }
+      } break;
+      default:
+        break;
+      }
+      TSDebug(PLUGIN_NAME, "eval(): Client IPv%d seems to come from Country: %s", v, ret.c_str());
+      break;
+
+    // ASN database
+    case GEO_QUAL_ASN_NAME:
+      switch (addr->sa_family) {
+      case AF_INET:
+        if (gGeoIP[GEOIP_ASNUM_EDITION]) {
+          uint32_t ip = ntohl(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_addr.s_addr);
+
+          ret = GeoIP_name_by_ipnum(gGeoIP[GEOIP_ASNUM_EDITION], ip);
+        }
+        break;
+      case AF_INET6: {
+        if (gGeoIP[GEOIP_ASNUM_EDITION_V6]) {
+          geoipv6_t ip = reinterpret_cast<const struct sockaddr_in6 *>(addr)->sin6_addr;
+
+          v   = 6;
+          ret = GeoIP_name_by_ipnum_v6(gGeoIP[GEOIP_ASNUM_EDITION_V6], ip);
+        }
+      } break;
+      default:
+        break;
+      }
+      TSDebug(PLUGIN_NAME, "eval(): Client IPv%d seems to come from ASN Name: %s", v, ret.c_str());
+      break;
+    default:
+      break;
+    }
+  }
+
+  return ret;
+}
+
+int64_t
+GeoIPConditionGeo::get_geo_int(const sockaddr *addr) const
+{
+  int64_t ret = -1;
+  int v       = 4;
+
+  if (!addr) {
+    return 0;
+  }
+
+  switch (_geo_qual) {
+  // Country Database
+  case GEO_QUAL_COUNTRY_ISO:
+    switch (addr->sa_family) {
+    case AF_INET:
+      if (gGeoIP[GEOIP_COUNTRY_EDITION]) {
+        uint32_t ip = ntohl(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_addr.s_addr);
+
+        ret = GeoIP_id_by_ipnum(gGeoIP[GEOIP_COUNTRY_EDITION], ip);
+      }
+      break;
+    case AF_INET6: {
+      if (gGeoIP[GEOIP_COUNTRY_EDITION_V6]) {
+        geoipv6_t ip = reinterpret_cast<const struct sockaddr_in6 *>(addr)->sin6_addr;
+
+        v   = 6;
+        ret = GeoIP_id_by_ipnum_v6(gGeoIP[GEOIP_COUNTRY_EDITION_V6], ip);
+      }
+    } break;
+    default:
+      break;
+    }
+    TSDebug(PLUGIN_NAME, "eval(): Client IPv%d seems to come from Country ISO: %" PRId64, v, ret);
+    break;
+
+  case GEO_QUAL_ASN: {
+    const char *asn_name = nullptr;
+
+    switch (addr->sa_family) {
+    case AF_INET:
+      if (gGeoIP[GEOIP_ASNUM_EDITION]) {
+        uint32_t ip = ntohl(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_addr.s_addr);
+
+        asn_name = GeoIP_name_by_ipnum(gGeoIP[GEOIP_ASNUM_EDITION], ip);
+      }
+      break;
+    case AF_INET6:
+      if (gGeoIP[GEOIP_ASNUM_EDITION_V6]) {
+        geoipv6_t ip = reinterpret_cast<const struct sockaddr_in6 *>(addr)->sin6_addr;
+
+        v        = 6;
+        asn_name = GeoIP_name_by_ipnum_v6(gGeoIP[GEOIP_ASNUM_EDITION_V6], ip);
+      }
+      break;
+    }
+    if (asn_name) {
+      // This is a little odd, but the strings returned are e.g. "AS1234 Acme Inc"
+      while (*asn_name && !(isdigit(*asn_name))) {
+        ++asn_name;
+      }
+      ret = strtol(asn_name, nullptr, 10);
+    }
+  }
+    TSDebug(PLUGIN_NAME, "eval(): Client IPv%d seems to come from ASN #: %" PRId64, v, ret);
+    break;
+
+  // Likely shouldn't trip, should we assert?
+  default:
+    break;
+  }
+
+  return ret;
+}
diff --git a/plugins/header_rewrite/conditions_geo_maxmind.cc b/plugins/header_rewrite/conditions_geo_maxmind.cc
new file mode 100644
index 0000000..8438e1c
--- /dev/null
+++ b/plugins/header_rewrite/conditions_geo_maxmind.cc
@@ -0,0 +1,175 @@
+/*
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+*/
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+// conditions_geo_maxmind.cc: Implementation of the ConditionGeo class based on MaxMindDB
+//
+//
+
+#include <unistd.h>
+#include <arpa/inet.h>
+
+#include "ts/ts.h"
+
+#include "conditions_geo.h"
+
+#include <maxminddb.h>
+
+MMDB_s *gMaxMindDB = nullptr;
+
+void
+MMConditionGeo::initLibrary(const std::string &path)
+{
+  if (path.empty()) {
+    TSError("[%s] Empty db path specified. Not initializing!", PLUGIN_NAME);
+    return;
+  }
+  gMaxMindDB = new MMDB_s;
+
+  int status = MMDB_open(path.c_str(), MMDB_MODE_MMAP, gMaxMindDB);
+  if (MMDB_SUCCESS != status) {
+    TSDebug(PLUGIN_NAME, "Cannot open %s - %s", path.c_str(), MMDB_strerror(status));
+    return;
+  }
+  TSDebug(PLUGIN_NAME, "Loaded %s", path.c_str());
+}
+
+std::string
+MMConditionGeo::get_geo_string(const sockaddr *addr) const
+{
+  std::string ret = "(unknown)";
+  int mmdb_error;
+
+  if (gMaxMindDB == nullptr) {
+    return ret;
+  }
+
+  MMDB_lookup_result_s result = MMDB_lookup_sockaddr(gMaxMindDB, addr, &mmdb_error);
+
+  if (MMDB_SUCCESS != mmdb_error) {
+    TSDebug(PLUGIN_NAME, "Error during sockaddr lookup: %s", MMDB_strerror(mmdb_error));
+    return ret;
+  }
+
+  MMDB_entry_data_list_s *entry_data_list = nullptr;
+  if (!result.found_entry) {
+    TSDebug(PLUGIN_NAME, "No entry for this IP was found");
+    return ret;
+  }
+
+  int status = MMDB_get_entry_data_list(&result.entry, &entry_data_list);
+  if (MMDB_SUCCESS != status) {
+    TSDebug(PLUGIN_NAME, "Error looking up entry data: %s", MMDB_strerror(status));
+    return ret;
+  }
+
+  if (entry_data_list == nullptr) {
+    TSDebug(PLUGIN_NAME, "No data found");
+    return ret;
+  }
+
+  const char *field_name;
+  switch (_geo_qual) {
+  case GEO_QUAL_COUNTRY:
+    field_name = "country_code";
+    break;
+  case GEO_QUAL_ASN_NAME:
+    field_name = "autonomous_system_organization";
+    break;
+  default:
+    TSDebug(PLUGIN_NAME, "Unsupported field %d", _geo_qual);
+    return ret;
+    break;
+  }
+
+  MMDB_entry_data_s entry_data;
+
+  status = MMDB_get_value(&result.entry, &entry_data, field_name, NULL);
+  if (MMDB_SUCCESS != status) {
+    TSDebug(PLUGIN_NAME, "ERROR on get value asn value: %s", MMDB_strerror(status));
+    return ret;
+  }
+  ret = std::string(entry_data.utf8_string, entry_data.data_size);
+
+  if (NULL != entry_data_list) {
+    MMDB_free_entry_data_list(entry_data_list);
+  }
+
+  return ret;
+}
+
+int64_t
+MMConditionGeo::get_geo_int(const sockaddr *addr) const
+{
+  int64_t ret = -1;
+  int mmdb_error;
+
+  if (gMaxMindDB == nullptr) {
+    return ret;
+  }
+
+  MMDB_lookup_result_s result = MMDB_lookup_sockaddr(gMaxMindDB, addr, &mmdb_error);
+
+  if (MMDB_SUCCESS != mmdb_error) {
+    TSDebug(PLUGIN_NAME, "Error during sockaddr lookup: %s", MMDB_strerror(mmdb_error));
+    return ret;
+  }
+
+  MMDB_entry_data_list_s *entry_data_list = nullptr;
+  if (!result.found_entry) {
+    TSDebug(PLUGIN_NAME, "No entry for this IP was found");
+    return ret;
+  }
+
+  int status = MMDB_get_entry_data_list(&result.entry, &entry_data_list);
+  if (MMDB_SUCCESS != status) {
+    TSDebug(PLUGIN_NAME, "Error looking up entry data: %s", MMDB_strerror(status));
+    return ret;
+  }
+
+  if (entry_data_list == nullptr) {
+    TSDebug(PLUGIN_NAME, "No data found");
+    return ret;
+  }
+
+  const char *field_name;
+  switch (_geo_qual) {
+  case GEO_QUAL_ASN:
+    field_name = "autonomous_system";
+    break;
+  default:
+    TSDebug(PLUGIN_NAME, "Unsupported field %d", _geo_qual);
+    return ret;
+    break;
+  }
+
+  MMDB_entry_data_s entry_data;
+
+  status = MMDB_get_value(&result.entry, &entry_data, field_name, NULL);
+  if (MMDB_SUCCESS != status) {
+    TSDebug(PLUGIN_NAME, "ERROR on get value asn value: %s", MMDB_strerror(status));
+    return ret;
+  }
+  ret = entry_data.uint32;
+
+  if (NULL != entry_data_list) {
+    MMDB_free_entry_data_list(entry_data_list);
+  }
+
+  return ret;
+}
diff --git a/plugins/header_rewrite/factory.cc b/plugins/header_rewrite/factory.cc
index 26f9a4f..961d17a 100644
--- a/plugins/header_rewrite/factory.cc
+++ b/plugins/header_rewrite/factory.cc
@@ -23,6 +23,7 @@
 
 #include "operators.h"
 #include "conditions.h"
+#include "conditions_geo.h"
 
 ///////////////////////////////////////////////////////////////////////////////
 // "Factory" functions, processing the parsed lines
@@ -130,7 +131,13 @@
   } else if (c_name == "NOW") {
     c = new ConditionNow();
   } else if (c_name == "GEO") {
+#if TS_USE_HRW_GEOIP
+    c = new GeoIPConditionGeo();
+#elif TS_USE_HRW_MAXMINDDB
+    c = new MMConditionGeo();
+#else
     c = new ConditionGeo();
+#endif
   } else if (c_name == "ID") {
     c = new ConditionId();
   } else if (c_name == "CIDR") {
diff --git a/plugins/header_rewrite/header_rewrite.cc b/plugins/header_rewrite/header_rewrite.cc
index 85e0fee..55d8f0f 100644
--- a/plugins/header_rewrite/header_rewrite.cc
+++ b/plugins/header_rewrite/header_rewrite.cc
@@ -15,9 +15,12 @@
   See the License for the specific language governing permissions and
   limitations under the License.
 */
+
 #include <fstream>
+#include <mutex>
 #include <string>
 #include <stdexcept>
+#include <getopt.h>
 
 #include "ts/ts.h"
 #include "ts/remap.h"
@@ -25,41 +28,24 @@
 #include "parser.h"
 #include "ruleset.h"
 #include "resources.h"
+#include "conditions.h"
+#include "conditions_geo.h"
 
 // Debugs
 const char PLUGIN_NAME[]     = "header_rewrite";
 const char PLUGIN_NAME_DBG[] = "dbg_header_rewrite";
 
-// Geo information, currently only Maxmind. These have to be initialized when the plugin loads.
-#if HAVE_GEOIP_H
-#include <GeoIP.h>
-
-GeoIP *gGeoIP[NUM_DB_TYPES];
+static std::once_flag initGeoLibs;
 
 static void
-initGeoIP()
+initGeoLib(const std::string &dbPath)
 {
-  GeoIPDBTypes dbs[] = {GEOIP_COUNTRY_EDITION, GEOIP_COUNTRY_EDITION_V6, GEOIP_ASNUM_EDITION, GEOIP_ASNUM_EDITION_V6};
-
-  for (auto &db : dbs) {
-    if (!gGeoIP[db] && GeoIP_db_avail(db)) {
-      // GEOIP_STANDARD seems to break threaded apps...
-      gGeoIP[db] = GeoIP_open_type(db, GEOIP_MMAP_CACHE);
-
-      char *db_info = GeoIP_database_info(gGeoIP[db]);
-      TSDebug(PLUGIN_NAME, "initialized GeoIP-DB[%d] %s", db, db_info);
-      free(db_info);
-    }
-  }
-}
-
-#else
-
-static void
-initGeoIP()
-{
-}
+#if TS_USE_HRW_GEOIP
+  GeoIPConditionGeo::initLibrary(dbPath);
+#elif TS_USE_HRW_MAXMINDDB
+  MMConditionGeo::initLibrary(dbPath);
 #endif
+}
 
 // Forward declaration for the main continuation.
 static int cont_rewrite_headers(TSCont, TSEvent, void *);
@@ -315,6 +301,8 @@
   return 0;
 }
 
+static const struct option longopt[] = {{"geo-db-path", required_argument, NULL, 'm'}, {NULL, no_argument, NULL, '\0'}};
+
 ///////////////////////////////////////////////////////////////////////////////
 // Initialize the InkAPI plugin for the global hooks we support.
 //
@@ -329,6 +317,21 @@
 
   if (TS_SUCCESS != TSPluginRegister(&info)) {
     TSError("[%s] plugin registration failed", PLUGIN_NAME);
+    return;
+  }
+
+  std::string geoDBpath;
+  while (true) {
+    int opt = getopt_long(argc, (char *const *)argv, "m:", longopt, NULL);
+
+    switch (opt) {
+    case 'm': {
+      geoDBpath = optarg;
+    } break;
+    }
+    if (opt == -1) {
+      break;
+    }
   }
 
   // Parse the global config file(s). All rules are just appended
@@ -336,9 +339,9 @@
   RulesConfig *conf = new RulesConfig;
   bool got_config   = false;
 
-  initGeoIP();
+  std::call_once(initGeoLibs, [&geoDBpath]() { initGeoLib(geoDBpath); });
 
-  for (int i = 1; i < argc; ++i) {
+  for (int i = optind; i < argc; ++i) {
     // Parse the config file(s). Note that multiple config files are
     // just appended to the configurations.
     TSDebug(PLUGIN_NAME, "Loading global configuration file %s", argv[i]);
@@ -389,7 +392,6 @@
     return TS_ERROR;
   }
 
-  initGeoIP();
   TSDebug(PLUGIN_NAME, "Remap plugin is successfully initialized");
 
   return TS_SUCCESS;
@@ -405,9 +407,30 @@
     return TS_ERROR;
   }
 
+  // argv contains the "to" and "from" URLs. Skip the first so that the
+  // second one poses as the program name.
+  --argc;
+  ++argv;
+
+  std::string geoDBPath;
+  while (true) {
+    int opt = getopt_long(argc, (char *const *)argv, "m:", longopt, NULL);
+
+    switch (opt) {
+    case 'm': {
+      geoDBPath = optarg;
+    } break;
+    }
+    if (opt == -1) {
+      break;
+    }
+  }
+
+  std::call_once(initGeoLibs, [&geoDBPath]() { initGeoLib(geoDBPath); });
+
   RulesConfig *conf = new RulesConfig;
 
-  for (int i = 2; i < argc; ++i) {
+  for (int i = optind; i < argc; ++i) {
     TSDebug(PLUGIN_NAME, "Loading remap configuration file %s", argv[i]);
     if (!conf->parse_config(argv[i], TS_REMAP_PSEUDO_HOOK)) {
       TSError("[%s] Unable to create remap instance", PLUGIN_NAME);
diff --git a/plugins/header_rewrite/resources.h b/plugins/header_rewrite/resources.h
index ee5e0f0..c3ddfa0 100644
--- a/plugins/header_rewrite/resources.h
+++ b/plugins/header_rewrite/resources.h
@@ -28,11 +28,6 @@
 
 #include "lulu.h"
 
-#if HAVE_GEOIP_H
-#include <GeoIP.h>
-extern GeoIP *gGeoIP[NUM_DB_TYPES];
-#endif
-
 enum ResourceIDs {
   RSRC_NONE                    = 0,
   RSRC_SERVER_RESPONSE_HEADERS = 1,
diff --git a/plugins/healthchecks/Makefile.inc b/plugins/healthchecks/Makefile.inc
index 2121b72..f9c027c 100644
--- a/plugins/healthchecks/Makefile.inc
+++ b/plugins/healthchecks/Makefile.inc
@@ -14,9 +14,5 @@
 #  See the License for the specific language governing permissions and
 #  limitations under the License.
 
-if BUILD_HEALTHCHECK_PLUGIN
-
 pkglib_LTLIBRARIES += healthchecks/healthchecks.la
 healthchecks_healthchecks_la_SOURCES = healthchecks/healthchecks.c
-
-endif