|
25 | 25 |
|
26 | 26 | #include "../../dprint.h" |
27 | 27 | #include "cachedb_redis_dbase.h" |
| 28 | +#include "cachedb_redis_utils.h" |
28 | 29 | #include "../../mem/mem.h" |
29 | 30 | #include "../../ut.h" |
30 | 31 | #include "../../cachedb/cachedb.h" |
@@ -104,6 +105,31 @@ cluster_node *get_redis_connection(redis_con *con,str *key) |
104 | 105 | } |
105 | 106 | } |
106 | 107 |
|
| 108 | +cluster_node *get_redis_connection_by_endpoint(redis_con *con, redis_moved *redis_info) |
| 109 | +{ |
| 110 | + cluster_node *it; |
| 111 | + |
| 112 | + if (con->flags & REDIS_SINGLE_INSTANCE) { |
| 113 | + LM_DBG("Single redis connection, returning %p\n",con->nodes); |
| 114 | + return con->nodes; |
| 115 | + } else { |
| 116 | + for (it=con->nodes;it;it=it->next) { |
| 117 | + if (match_prefix(redis_info->endpoint.s, redis_info->endpoint.len, it->ip, strlen(it->ip))) { |
| 118 | + if (it->port == redis_info->port) { |
| 119 | + // Removed slot comparison as it may be a little too aggressive of a match |
| 120 | + // Code is still here in the event that it needs to be added back in |
| 121 | + //if (it->start_slot <= redis_info->slot && it->end_slot >= redis_info->slot) { |
| 122 | + LM_DBG("Redis cluster connection, matched con %p for endpoint: %.*s:%d slot: [%u] %u [%u] \n", it, redis_info->endpoint.len, redis_info->endpoint.s, redis_info->port, it->start_slot, redis_info->slot, it->end_slot); |
| 123 | + return it; |
| 124 | + //} |
| 125 | + } |
| 126 | + } |
| 127 | + } |
| 128 | + LM_ERR("Redis cluster connection, No match found for endpoint: %.*s:%d slot %u\n", redis_info->endpoint.len, redis_info->endpoint.s, redis_info->port, redis_info->slot); |
| 129 | + return NULL; |
| 130 | + } |
| 131 | +} |
| 132 | + |
107 | 133 | void destroy_cluster_nodes(redis_con *con) |
108 | 134 | { |
109 | 135 | cluster_node *new,*foo; |
@@ -319,3 +345,93 @@ int build_cluster_nodes(redis_con *con,char *info,int size) |
319 | 345 | destroy_cluster_nodes(con); |
320 | 346 | return -1; |
321 | 347 | } |
| 348 | + |
| 349 | +/* |
| 350 | + When Redis is operating as a cluster, it is possible (very likely) |
| 351 | + that a MOVED redirection will be returned by the Redis nodes that |
| 352 | + received the request. The general format of the reply from Redis is: |
| 353 | + MOVED slot [IP|FQDN]:port |
| 354 | +
|
| 355 | + This routine will parse the Redis MOVED reply into its components. |
| 356 | + Note that the redisReply struct MUST be released outside of this routine |
| 357 | + to avoid a memory leak. The out->endpoint pointer must not be used after |
| 358 | + the redisReply has been released. |
| 359 | +
|
| 360 | + The parsed data is stored into the following redis_moved struct: |
| 361 | + |
| 362 | + typedef struct { |
| 363 | + int slot; |
| 364 | + const_str endpoint; |
| 365 | + int port; |
| 366 | + } redis_moved; |
| 367 | +
|
| 368 | +*/ |
| 369 | +int parse_moved_reply(redisReply *reply, redis_moved *out) { |
| 370 | + if (!reply || !reply->str || reply->len < MOVED_PREFIX_LEN || !out) |
| 371 | + return ERR_INVALID_REPLY; |
| 372 | + |
| 373 | + const char *p = reply->str; |
| 374 | + const char *end = reply->str + reply->len; |
| 375 | + |
| 376 | + for (int i = 0; i < MOVED_PREFIX_LEN; ++i) { |
| 377 | + if (p[i] != MOVED_PREFIX[i]) { |
| 378 | + return ERR_INVALID_REPLY; |
| 379 | + } |
| 380 | + } |
| 381 | + p += MOVED_PREFIX_LEN; |
| 382 | + |
| 383 | + // Parse slot number |
| 384 | + int slot = 0; |
| 385 | + while (p < end && *p >= '0' && *p <= '9') { |
| 386 | + slot = slot * 10 + (*p - '0'); |
| 387 | + p++; |
| 388 | + } |
| 389 | + if (slot == 0 && (p == reply->str + MOVED_PREFIX_LEN || *(p - 1) < '0' || *(p - 1) > '9')) |
| 390 | + return ERR_INVALID_SLOT; |
| 391 | + |
| 392 | + // Skip spaces |
| 393 | + while (p < end && *p == ' ') p++; |
| 394 | + |
| 395 | + // Parse host and port |
| 396 | + const char *host_start = p; |
| 397 | + const char *colon = NULL; |
| 398 | + while (p < end) { |
| 399 | + if (*p == ':') { |
| 400 | + colon = p; |
| 401 | + break; |
| 402 | + } |
| 403 | + p++; |
| 404 | + } |
| 405 | + |
| 406 | + out->endpoint.s = NULL; |
| 407 | + out->endpoint.len = 0; |
| 408 | + |
| 409 | + int port = REDIS_DF_PORT; // Default to Redis standard port |
| 410 | + |
| 411 | + if (colon) { |
| 412 | + out->endpoint.s = host_start; |
| 413 | + out->endpoint.len = colon - host_start; |
| 414 | + |
| 415 | + // Parse port |
| 416 | + const char *port_start = colon + 1; |
| 417 | + p = port_start; |
| 418 | + if (p < end) { |
| 419 | + port = 0; |
| 420 | + while (p < end && *p >= '0' && *p <= '9') { |
| 421 | + port = port * 10 + (*p - '0'); |
| 422 | + p++; |
| 423 | + } |
| 424 | + if (port < 0 || port > 65535 || port_start == p) |
| 425 | + return ERR_INVALID_PORT; |
| 426 | + } |
| 427 | + } else if (out->endpoint.s < end) { |
| 428 | + out->endpoint.s = host_start; |
| 429 | + out->endpoint.len = end - host_start; |
| 430 | + } |
| 431 | + |
| 432 | + // Fill output |
| 433 | + out->slot = slot; |
| 434 | + out->port = port; |
| 435 | + |
| 436 | + return 0; |
| 437 | +} |
0 commit comments