Add command filtering argument handling API.

This commit is contained in:
Yossi Gottlieb 2019-03-18 18:36:46 +02:00
parent c3e187190b
commit 6711132083
3 changed files with 132 additions and 13 deletions

View File

@ -291,6 +291,10 @@ typedef struct RedisModuleCommandFilter {
/* Registered filters */ /* Registered filters */
static list *moduleCommandFilters; static list *moduleCommandFilters;
typedef struct RedisModuleCommandFilterCtx {
RedisModuleString **argv;
int argc;
} RedisModuleCommandFilterCtx;
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
* Prototypes * Prototypes
@ -4842,6 +4846,78 @@ void moduleCallCommandFilters(client *c) {
c->argc = cmd.argc; c->argc = cmd.argc;
} }
/* Return the number of arguments a filtered command has. The number of
* arguments include the command itself.
*/
int RM_CommandFilterArgsCount(RedisModuleCommandFilterCtx *filter)
{
return filter->argc;
}
/* Return the specified command argument. The first argument (position 0) is
* the command itself, and the rest are user-provided args.
*/
const RedisModuleString *RM_CommandFilterArgGet(RedisModuleCommandFilterCtx *filter, int pos)
{
if (pos < 0 || pos >= filter->argc) return NULL;
return filter->argv[pos];
}
/* Modify the filtered command by inserting a new argument at the specified
* position. The specified RedisModuleString argument may be used by Redis
* after the filter context is destroyed, so it must not be auto-memory
* allocated, freed or used elsewhere.
*/
int RM_CommandFilterArgInsert(RedisModuleCommandFilterCtx *filter, int pos, RedisModuleString *arg)
{
int i;
if (pos < 0 || pos > filter->argc) return REDISMODULE_ERR;
filter->argv = zrealloc(filter->argv, (filter->argc+1)*sizeof(RedisModuleString *));
for (i = filter->argc; i > pos; i--) {
filter->argv[i] = filter->argv[i-1];
}
filter->argv[pos] = arg;
filter->argc++;
return REDISMODULE_OK;
}
/* Modify the filtered command by replacing an existing argument with a new one.
* The specified RedisModuleString argument may be used by Redis after the
* filter context is destroyed, so it must not be auto-memory allocated, freed
* or used elsewhere.
*/
int RM_CommandFilterArgReplace(RedisModuleCommandFilterCtx *filter, int pos, RedisModuleString *arg)
{
if (pos < 0 || pos >= filter->argc) return REDISMODULE_ERR;
decrRefCount(filter->argv[pos]);
filter->argv[pos] = arg;
return REDISMODULE_OK;
}
/* Modify the filtered command by deleting an argument at the specified
* position.
*/
int RM_CommandFilterArgDelete(RedisModuleCommandFilterCtx *filter, int pos)
{
int i;
if (pos < 0 || pos >= filter->argc) return REDISMODULE_ERR;
decrRefCount(filter->argv[pos]);
for (i = pos; i < filter->argc-1; i++) {
filter->argv[i] = filter->argv[i+1];
}
filter->argc--;
return REDISMODULE_OK;
}
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
* Modules API internals * Modules API internals
* -------------------------------------------------------------------------- */ * -------------------------------------------------------------------------- */
@ -5295,4 +5371,9 @@ void moduleRegisterCoreAPI(void) {
REGISTER_API(ExportSharedAPI); REGISTER_API(ExportSharedAPI);
REGISTER_API(GetSharedAPI); REGISTER_API(GetSharedAPI);
REGISTER_API(RegisterCommandFilter); REGISTER_API(RegisterCommandFilter);
REGISTER_API(CommandFilterArgsCount);
REGISTER_API(CommandFilterArgGet);
REGISTER_API(CommandFilterArgInsert);
REGISTER_API(CommandFilterArgReplace);
REGISTER_API(CommandFilterArgDelete);
} }

View File

@ -1,6 +1,8 @@
#define REDISMODULE_EXPERIMENTAL_API #define REDISMODULE_EXPERIMENTAL_API
#include "../redismodule.h" #include "../redismodule.h"
#include <string.h>
static RedisModuleString *log_key_name; static RedisModuleString *log_key_name;
static const char log_command_name[] = "hellofilter.log"; static const char log_command_name[] = "hellofilter.log";
@ -35,16 +37,46 @@ int HelloFilter_LogCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int ar
return REDISMODULE_OK; return REDISMODULE_OK;
} }
void HelloFilter_CommandFilter(RedisModuleCtx *ctx, RedisModuleFilteredCommand *cmd) void HelloFilter_CommandFilter(RedisModuleCtx *ctx, RedisModuleCommandFilterCtx *filter)
{ {
cmd->argv = RedisModule_Realloc(cmd->argv, (cmd->argc+1)*sizeof(RedisModuleString *)); (void) ctx;
int i;
for (i = cmd->argc; i > 0; i--) { /* Fun manipulations:
cmd->argv[i] = cmd->argv[i-1]; * - Remove @delme
* - Replace @replaceme
* - Append @insertbefore or @insertafter
* - Prefix with Log command if @log encounterd
*/
int log = 0;
int pos = 0;
while (pos < RedisModule_CommandFilterArgsCount(filter)) {
const RedisModuleString *arg = RedisModule_CommandFilterArgGet(filter, pos);
size_t arg_len;
const char *arg_str = RedisModule_StringPtrLen(arg, &arg_len);
if (arg_len == 6 && !memcmp(arg_str, "@delme", 6)) {
RedisModule_CommandFilterArgDelete(filter, pos);
continue;
} }
cmd->argv[0] = RedisModule_CreateString(ctx, log_command_name, sizeof(log_command_name)-1); if (arg_len == 10 && !memcmp(arg_str, "@replaceme", 10)) {
cmd->argc++; RedisModule_CommandFilterArgReplace(filter, pos,
RedisModule_CreateString(NULL, "--replaced--", 12));
} else if (arg_len == 13 && !memcmp(arg_str, "@insertbefore", 13)) {
RedisModule_CommandFilterArgInsert(filter, pos,
RedisModule_CreateString(NULL, "--inserted-before--", 19));
pos++;
} else if (arg_len == 12 && !memcmp(arg_str, "@insertafter", 12)) {
RedisModule_CommandFilterArgInsert(filter, pos + 1,
RedisModule_CreateString(NULL, "--inserted-after--", 18));
pos++;
} else if (arg_len == 4 && !memcmp(arg_str, "@log", 4)) {
log = 1;
}
pos++;
}
if (log) RedisModule_CommandFilterArgInsert(filter, 0,
RedisModule_CreateString(NULL, log_command_name, sizeof(log_command_name)-1));
} }
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {

View File

@ -150,6 +150,7 @@ typedef struct RedisModuleBlockedClient RedisModuleBlockedClient;
typedef struct RedisModuleClusterInfo RedisModuleClusterInfo; typedef struct RedisModuleClusterInfo RedisModuleClusterInfo;
typedef struct RedisModuleDict RedisModuleDict; typedef struct RedisModuleDict RedisModuleDict;
typedef struct RedisModuleDictIter RedisModuleDictIter; typedef struct RedisModuleDictIter RedisModuleDictIter;
typedef struct RedisModuleCommandFilterCtx RedisModuleCommandFilterCtx;
typedef int (*RedisModuleCmdFunc)(RedisModuleCtx *ctx, RedisModuleString **argv, int argc); typedef int (*RedisModuleCmdFunc)(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
typedef void (*RedisModuleDisconnectFunc)(RedisModuleCtx *ctx, RedisModuleBlockedClient *bc); typedef void (*RedisModuleDisconnectFunc)(RedisModuleCtx *ctx, RedisModuleBlockedClient *bc);
@ -162,12 +163,7 @@ typedef void (*RedisModuleTypeDigestFunc)(RedisModuleDigest *digest, void *value
typedef void (*RedisModuleTypeFreeFunc)(void *value); typedef void (*RedisModuleTypeFreeFunc)(void *value);
typedef void (*RedisModuleClusterMessageReceiver)(RedisModuleCtx *ctx, const char *sender_id, uint8_t type, const unsigned char *payload, uint32_t len); typedef void (*RedisModuleClusterMessageReceiver)(RedisModuleCtx *ctx, const char *sender_id, uint8_t type, const unsigned char *payload, uint32_t len);
typedef void (*RedisModuleTimerProc)(RedisModuleCtx *ctx, void *data); typedef void (*RedisModuleTimerProc)(RedisModuleCtx *ctx, void *data);
typedef void (*RedisModuleCommandFilterFunc) (RedisModuleCtx *ctx, RedisModuleCommandFilterCtx *filter);
typedef struct RedisModuleFilteredCommand {
RedisModuleString **argv;
int argc;
} RedisModuleFilteredCommand;
typedef void (*RedisModuleCommandFilterFunc) (RedisModuleCtx *ctx, RedisModuleFilteredCommand *cmd);
#define REDISMODULE_TYPE_METHOD_VERSION 1 #define REDISMODULE_TYPE_METHOD_VERSION 1
typedef struct RedisModuleTypeMethods { typedef struct RedisModuleTypeMethods {
@ -344,6 +340,11 @@ void REDISMODULE_API_FUNC(RedisModule_SetClusterFlags)(RedisModuleCtx *ctx, uint
int REDISMODULE_API_FUNC(RedisModule_ExportSharedAPI)(RedisModuleCtx *ctx, const char *apiname, void *func); int REDISMODULE_API_FUNC(RedisModule_ExportSharedAPI)(RedisModuleCtx *ctx, const char *apiname, void *func);
void *REDISMODULE_API_FUNC(RedisModule_GetSharedAPI)(RedisModuleCtx *ctx, const char *apiname); void *REDISMODULE_API_FUNC(RedisModule_GetSharedAPI)(RedisModuleCtx *ctx, const char *apiname);
int REDISMODULE_API_FUNC(RedisModule_RegisterCommandFilter)(RedisModuleCtx *ctx, RedisModuleCommandFilterFunc cb); int REDISMODULE_API_FUNC(RedisModule_RegisterCommandFilter)(RedisModuleCtx *ctx, RedisModuleCommandFilterFunc cb);
int REDISMODULE_API_FUNC(RedisModule_CommandFilterArgsCount)(RedisModuleCommandFilterCtx *filter);
const RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CommandFilterArgGet)(RedisModuleCommandFilterCtx *filter, int pos);
int REDISMODULE_API_FUNC(RedisModule_CommandFilterArgInsert)(RedisModuleCommandFilterCtx *filter, int pos, RedisModuleString *arg);
int REDISMODULE_API_FUNC(RedisModule_CommandFilterArgReplace)(RedisModuleCommandFilterCtx *filter, int pos, RedisModuleString *arg);
int REDISMODULE_API_FUNC(RedisModule_CommandFilterArgDelete)(RedisModuleCommandFilterCtx *filter, int pos);
#endif #endif
/* This is included inline inside each Redis module. */ /* This is included inline inside each Redis module. */
@ -507,6 +508,11 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API(ExportSharedAPI); REDISMODULE_GET_API(ExportSharedAPI);
REDISMODULE_GET_API(GetSharedAPI); REDISMODULE_GET_API(GetSharedAPI);
REDISMODULE_GET_API(RegisterCommandFilter); REDISMODULE_GET_API(RegisterCommandFilter);
REDISMODULE_GET_API(CommandFilterArgsCount);
REDISMODULE_GET_API(CommandFilterArgGet);
REDISMODULE_GET_API(CommandFilterArgInsert);
REDISMODULE_GET_API(CommandFilterArgReplace);
REDISMODULE_GET_API(CommandFilterArgDelete);
#endif #endif
if (RedisModule_IsModuleNameBusy && RedisModule_IsModuleNameBusy(name)) return REDISMODULE_ERR; if (RedisModule_IsModuleNameBusy && RedisModule_IsModuleNameBusy(name)) return REDISMODULE_ERR;