Redis cluster DEFER & test results corrected
[exim.git] / src / src / lookups / redis.c
index 5f677ff067a11829126e632fa3f359c4616e3aca..e9ddf884c55572de5aa4255fee55d9249f7c8619 100644 (file)
@@ -2,12 +2,12 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2009 */
+/* Copyright (c) University of Cambridge 1995 - 2018 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 #include "../exim.h"
 
-#ifdef EXPERIMENTAL_REDIS
+#ifdef LOOKUP_REDIS
 
 #include "lf_functions.h"
 
@@ -80,13 +80,10 @@ redisReply *redis_reply = NULL;
 redisReply *entry = NULL;
 redisReply *tentry = NULL;
 redis_connection *cn;
-int ssize = 0;
-int offset = 0;
 int yield = DEFER;
 int i, j;
-uschar *result = NULL;
+gstring * result = NULL;
 uschar *server_copy = NULL;
-uschar *tmp, *ttmp;
 uschar *sdata[3];
 
 /* Disaggregate the parameters from the server argument.
@@ -118,7 +115,7 @@ if (sdata[2][0] == 0) sdata[2] = NULL;
 
 /* See if we have a cached connection to the server */
 
-for (cn = redis_connections; cn != NULL; cn = cn->next)
+for (cn = redis_connections; cn; cn = cn->next)
   if (Ustrcmp(cn->server, server_copy) == 0)
     {
     redis_handle = cn->handle;
@@ -217,10 +214,13 @@ if(sdata[1])
 
   for (i = 0; *s && i < nele(argv); i++)
     {
-    for (argv[i] = NULL, siz = ptr = 0; (c = *s) && !isspace(c); s++)
+    gstring * g;
+
+    for (g = NULL; (c = *s) && !isspace(c); s++)
       if (c != '\\' || *++s)           /* backslash protects next char */
-       argv[i] = string_cat(argv[i], &siz, &ptr, s, 1);
-    *(argv[i]+ptr) = '\0';
+       g = string_catn(g, s, 1);
+    argv[i] = string_from_gstring(g);
+
     DEBUG(D_lookup) debug_printf("REDIS: argv[%d] '%s'\n", i, argv[i]);
     while (isspace(*s)) s++;
     }
@@ -241,7 +241,20 @@ switch (redis_reply->type)
   {
   case REDIS_REPLY_ERROR:
     *errmsg = string_sprintf("REDIS: lookup result failed: %s\n", redis_reply->str);
-    *defer_break = FALSE;
+
+    /* trap MOVED cluster responses and follow them */
+    if (Ustrncmp(redis_reply->str, "MOVED", 5) == 0)
+      {
+      DEBUG(D_lookup)
+        debug_printf("REDIS: cluster redirect %s\n", redis_reply->str);
+      /* follow redirect
+      This is cheating, we simply set defer_break = FALSE to move on to
+      the next server in the redis_servers list */
+      *defer_break = FALSE;
+      return DEFER;
+      } else {
+      *defer_break = TRUE;
+      }
     *do_cache = 0;
     goto REDIS_EXIT;
     /* NOTREACHED */
@@ -249,20 +262,18 @@ switch (redis_reply->type)
   case REDIS_REPLY_NIL:
     DEBUG(D_lookup)
       debug_printf("REDIS: query was not one that returned any data\n");
-    result = string_sprintf("");
+    result = string_catn(result, US"", 1);
     *do_cache = 0;
     goto REDIS_EXIT;
     /* NOTREACHED */
 
   case REDIS_REPLY_INTEGER:
-    ttmp = (redis_reply->integer != 0) ? US"true" : US"false";
-    result = string_cat(result, &ssize, &offset, US ttmp, Ustrlen(ttmp));
+    result = string_cat(result, redis_reply->integer != 0 ? US"true" : US"false");
     break;
 
   case REDIS_REPLY_STRING:
   case REDIS_REPLY_STATUS:
-    result = string_cat(result, &ssize, &offset,
-                       US redis_reply->str, redis_reply->len);
+    result = string_catn(result, US redis_reply->str, redis_reply->len);
     break;
 
   case REDIS_REPLY_ARRAY:
@@ -275,17 +286,15 @@ switch (redis_reply->type)
       entry = redis_reply->element[i];
 
       if (result)
-       result = string_cat(result, &ssize, &offset, US"\n", 1);
+       result = string_catn(result, US"\n", 1);
 
       switch (entry->type)
        {
        case REDIS_REPLY_INTEGER:
-         tmp = string_sprintf("%d", entry->integer);
-         result = string_cat(result, &ssize, &offset, US tmp, Ustrlen(tmp));
+         result = string_cat(result, string_sprintf("%d", entry->integer));
          break;
        case REDIS_REPLY_STRING:
-         result = string_cat(result, &ssize, &offset,
-                             US entry->str, entry->len);
+         result = string_catn(result, US entry->str, entry->len);
          break;
        case REDIS_REPLY_ARRAY:
          for (j = 0; j < entry->elements; j++)
@@ -293,18 +302,15 @@ switch (redis_reply->type)
            tentry = entry->element[j];
 
            if (result)
-             result = string_cat(result, &ssize, &offset, US"\n", 1);
+             result = string_catn(result, US"\n", 1);
 
            switch (tentry->type)
              {
              case REDIS_REPLY_INTEGER:
-               ttmp = string_sprintf("%d", tentry->integer);
-               result = string_cat(result, &ssize, &offset,
-                                   US ttmp, Ustrlen(ttmp));
+               result = string_cat(result, string_sprintf("%d", tentry->integer));
                break;
              case REDIS_REPLY_STRING:
-               result = string_cat(result, &ssize, &offset,
-                                   US tentry->str, tentry->len);
+               result = string_catn(result, US tentry->str, tentry->len);
                break;
              case REDIS_REPLY_ARRAY:
                DEBUG(D_lookup)
@@ -328,10 +334,7 @@ switch (redis_reply->type)
 
 
 if (result)
-  {
-  result[offset] = 0;
-  store_reset(result + offset + 1);
-  }
+  store_reset(result->s + result->ptr + 1);
 else
   {
   yield = FAIL;
@@ -345,11 +348,11 @@ as it is cached. */
 
 if (redis_reply) freeReplyObject(redis_reply);
 
-/* Non-NULL result indicates a sucessful result */
+/* Non-NULL result indicates a successful result */
 
 if (result)
   {
-  *resultptr = result;
+  *resultptr = string_from_gstring(result);
   return OK;
   }
 else
@@ -458,11 +461,11 @@ static lookup_info redis_lookup_info = {
 };
 
 #ifdef DYNLOOKUP
-#define redis_lookup_module_info _lookup_module_info
+# define redis_lookup_module_info _lookup_module_info
 #endif /* DYNLOOKUP */
 
 static lookup_info *_lookup_list[] = { &redis_lookup_info };
 lookup_module_info redis_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 1 };
 
-#endif /* EXPERIMENTAL_REDIS */
+#endif /* LOOKUP_REDIS */
 /* End of lookups/redis.c */