mirror of
https://codeberg.org/redict/redict.git
synced 2025-01-23 00:28:26 -05:00
CommandFilter API: Add unregister option.
A filter handle is returned and can be used to unregister a filter. In the future it can also be used to further configure or manipulate the filter. Filters are now automatically unregistered when a module unloads.
This commit is contained in:
parent
dd8b4be46b
commit
e2626f69ec
94
src/module.c
94
src/module.c
@ -49,6 +49,7 @@ struct RedisModule {
|
|||||||
list *types; /* Module data types. */
|
list *types; /* Module data types. */
|
||||||
list *usedby; /* List of modules using APIs from this one. */
|
list *usedby; /* List of modules using APIs from this one. */
|
||||||
list *using; /* List of modules we use some APIs of. */
|
list *using; /* List of modules we use some APIs of. */
|
||||||
|
list *filters; /* List of filters the module has registered. */
|
||||||
};
|
};
|
||||||
typedef struct RedisModule RedisModule;
|
typedef struct RedisModule RedisModule;
|
||||||
|
|
||||||
@ -748,6 +749,7 @@ void RM_SetModuleAttribs(RedisModuleCtx *ctx, const char *name, int ver, int api
|
|||||||
module->types = listCreate();
|
module->types = listCreate();
|
||||||
module->usedby = listCreate();
|
module->usedby = listCreate();
|
||||||
module->using = listCreate();
|
module->using = listCreate();
|
||||||
|
module->filters = listCreate();
|
||||||
ctx->module = module;
|
ctx->module = module;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4793,6 +4795,28 @@ int moduleUnregisterUsedAPI(RedisModule *module) {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Unregister all filters registered by a module.
|
||||||
|
* This is called when a module is being unloaded.
|
||||||
|
*
|
||||||
|
* Returns the number of filters unregistered. */
|
||||||
|
int moduleUnregisterFilters(RedisModule *module) {
|
||||||
|
listIter li;
|
||||||
|
listNode *ln;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
listRewind(module->filters,&li);
|
||||||
|
while((ln = listNext(&li))) {
|
||||||
|
RedisModuleCommandFilter *filter = ln->value;
|
||||||
|
listNode *ln = listSearchKey(moduleCommandFilters,filter);
|
||||||
|
if (ln) {
|
||||||
|
listDelNode(moduleCommandFilters,ln);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
zfree(filter);
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------
|
||||||
* Module Command Filter API
|
* Module Command Filter API
|
||||||
* -------------------------------------------------------------------------- */
|
* -------------------------------------------------------------------------- */
|
||||||
@ -4840,12 +4864,33 @@ int moduleUnregisterUsedAPI(RedisModule *module) {
|
|||||||
* are executed in the order of registration.
|
* are executed in the order of registration.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int RM_RegisterCommandFilter(RedisModuleCtx *ctx, RedisModuleCommandFilterFunc callback) {
|
RedisModuleCommandFilter *RM_RegisterCommandFilter(RedisModuleCtx *ctx, RedisModuleCommandFilterFunc callback) {
|
||||||
RedisModuleCommandFilter *filter = zmalloc(sizeof(*filter));
|
RedisModuleCommandFilter *filter = zmalloc(sizeof(*filter));
|
||||||
filter->module = ctx->module;
|
filter->module = ctx->module;
|
||||||
filter->callback = callback;
|
filter->callback = callback;
|
||||||
|
|
||||||
listAddNodeTail(moduleCommandFilters, filter);
|
listAddNodeTail(moduleCommandFilters, filter);
|
||||||
|
listAddNodeTail(ctx->module->filters, filter);
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unregister a command filter.
|
||||||
|
*/
|
||||||
|
int RM_UnregisterCommandFilter(RedisModuleCtx *ctx, RedisModuleCommandFilter *filter) {
|
||||||
|
listNode *ln;
|
||||||
|
|
||||||
|
/* A module can only remove its own filters */
|
||||||
|
if (filter->module != ctx->module) return REDISMODULE_ERR;
|
||||||
|
|
||||||
|
ln = listSearchKey(moduleCommandFilters,filter);
|
||||||
|
if (!ln) return REDISMODULE_ERR;
|
||||||
|
listDelNode(moduleCommandFilters,ln);
|
||||||
|
|
||||||
|
ln = listSearchKey(ctx->module->filters,filter);
|
||||||
|
if (ln) {
|
||||||
|
listDelNode(moduleCommandFilters,ln);
|
||||||
|
}
|
||||||
|
|
||||||
return REDISMODULE_OK;
|
return REDISMODULE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4874,18 +4919,18 @@ void moduleCallCommandFilters(client *c) {
|
|||||||
/* Return the number of arguments a filtered command has. The number of
|
/* Return the number of arguments a filtered command has. The number of
|
||||||
* arguments include the command itself.
|
* arguments include the command itself.
|
||||||
*/
|
*/
|
||||||
int RM_CommandFilterArgsCount(RedisModuleCommandFilterCtx *filter)
|
int RM_CommandFilterArgsCount(RedisModuleCommandFilterCtx *fctx)
|
||||||
{
|
{
|
||||||
return filter->argc;
|
return fctx->argc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return the specified command argument. The first argument (position 0) is
|
/* Return the specified command argument. The first argument (position 0) is
|
||||||
* the command itself, and the rest are user-provided args.
|
* the command itself, and the rest are user-provided args.
|
||||||
*/
|
*/
|
||||||
const RedisModuleString *RM_CommandFilterArgGet(RedisModuleCommandFilterCtx *filter, int pos)
|
const RedisModuleString *RM_CommandFilterArgGet(RedisModuleCommandFilterCtx *fctx, int pos)
|
||||||
{
|
{
|
||||||
if (pos < 0 || pos >= filter->argc) return NULL;
|
if (pos < 0 || pos >= fctx->argc) return NULL;
|
||||||
return filter->argv[pos];
|
return fctx->argv[pos];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Modify the filtered command by inserting a new argument at the specified
|
/* Modify the filtered command by inserting a new argument at the specified
|
||||||
@ -4894,18 +4939,18 @@ const RedisModuleString *RM_CommandFilterArgGet(RedisModuleCommandFilterCtx *fil
|
|||||||
* allocated, freed or used elsewhere.
|
* allocated, freed or used elsewhere.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int RM_CommandFilterArgInsert(RedisModuleCommandFilterCtx *filter, int pos, RedisModuleString *arg)
|
int RM_CommandFilterArgInsert(RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (pos < 0 || pos > filter->argc) return REDISMODULE_ERR;
|
if (pos < 0 || pos > fctx->argc) return REDISMODULE_ERR;
|
||||||
|
|
||||||
filter->argv = zrealloc(filter->argv, (filter->argc+1)*sizeof(RedisModuleString *));
|
fctx->argv = zrealloc(fctx->argv, (fctx->argc+1)*sizeof(RedisModuleString *));
|
||||||
for (i = filter->argc; i > pos; i--) {
|
for (i = fctx->argc; i > pos; i--) {
|
||||||
filter->argv[i] = filter->argv[i-1];
|
fctx->argv[i] = fctx->argv[i-1];
|
||||||
}
|
}
|
||||||
filter->argv[pos] = arg;
|
fctx->argv[pos] = arg;
|
||||||
filter->argc++;
|
fctx->argc++;
|
||||||
|
|
||||||
return REDISMODULE_OK;
|
return REDISMODULE_OK;
|
||||||
}
|
}
|
||||||
@ -4916,12 +4961,12 @@ int RM_CommandFilterArgInsert(RedisModuleCommandFilterCtx *filter, int pos, Redi
|
|||||||
* or used elsewhere.
|
* or used elsewhere.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int RM_CommandFilterArgReplace(RedisModuleCommandFilterCtx *filter, int pos, RedisModuleString *arg)
|
int RM_CommandFilterArgReplace(RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg)
|
||||||
{
|
{
|
||||||
if (pos < 0 || pos >= filter->argc) return REDISMODULE_ERR;
|
if (pos < 0 || pos >= fctx->argc) return REDISMODULE_ERR;
|
||||||
|
|
||||||
decrRefCount(filter->argv[pos]);
|
decrRefCount(fctx->argv[pos]);
|
||||||
filter->argv[pos] = arg;
|
fctx->argv[pos] = arg;
|
||||||
|
|
||||||
return REDISMODULE_OK;
|
return REDISMODULE_OK;
|
||||||
}
|
}
|
||||||
@ -4929,16 +4974,16 @@ int RM_CommandFilterArgReplace(RedisModuleCommandFilterCtx *filter, int pos, Red
|
|||||||
/* Modify the filtered command by deleting an argument at the specified
|
/* Modify the filtered command by deleting an argument at the specified
|
||||||
* position.
|
* position.
|
||||||
*/
|
*/
|
||||||
int RM_CommandFilterArgDelete(RedisModuleCommandFilterCtx *filter, int pos)
|
int RM_CommandFilterArgDelete(RedisModuleCommandFilterCtx *fctx, int pos)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
if (pos < 0 || pos >= filter->argc) return REDISMODULE_ERR;
|
if (pos < 0 || pos >= fctx->argc) return REDISMODULE_ERR;
|
||||||
|
|
||||||
decrRefCount(filter->argv[pos]);
|
decrRefCount(fctx->argv[pos]);
|
||||||
for (i = pos; i < filter->argc-1; i++) {
|
for (i = pos; i < fctx->argc-1; i++) {
|
||||||
filter->argv[i] = filter->argv[i+1];
|
fctx->argv[i] = fctx->argv[i+1];
|
||||||
}
|
}
|
||||||
filter->argc--;
|
fctx->argc--;
|
||||||
|
|
||||||
return REDISMODULE_OK;
|
return REDISMODULE_OK;
|
||||||
}
|
}
|
||||||
@ -5041,6 +5086,7 @@ void moduleLoadFromQueue(void) {
|
|||||||
|
|
||||||
void moduleFreeModuleStructure(struct RedisModule *module) {
|
void moduleFreeModuleStructure(struct RedisModule *module) {
|
||||||
listRelease(module->types);
|
listRelease(module->types);
|
||||||
|
listRelease(module->filters);
|
||||||
sdsfree(module->name);
|
sdsfree(module->name);
|
||||||
zfree(module);
|
zfree(module);
|
||||||
}
|
}
|
||||||
@ -5132,6 +5178,7 @@ int moduleUnload(sds name) {
|
|||||||
moduleUnregisterCommands(module);
|
moduleUnregisterCommands(module);
|
||||||
moduleUnregisterSharedAPI(module);
|
moduleUnregisterSharedAPI(module);
|
||||||
moduleUnregisterUsedAPI(module);
|
moduleUnregisterUsedAPI(module);
|
||||||
|
moduleUnregisterFilters(module);
|
||||||
|
|
||||||
/* Remove any notification subscribers this module might have */
|
/* Remove any notification subscribers this module might have */
|
||||||
moduleUnsubscribeNotifications(module);
|
moduleUnsubscribeNotifications(module);
|
||||||
@ -5396,6 +5443,7 @@ void moduleRegisterCoreAPI(void) {
|
|||||||
REGISTER_API(ExportSharedAPI);
|
REGISTER_API(ExportSharedAPI);
|
||||||
REGISTER_API(GetSharedAPI);
|
REGISTER_API(GetSharedAPI);
|
||||||
REGISTER_API(RegisterCommandFilter);
|
REGISTER_API(RegisterCommandFilter);
|
||||||
|
REGISTER_API(UnregisterCommandFilter);
|
||||||
REGISTER_API(CommandFilterArgsCount);
|
REGISTER_API(CommandFilterArgsCount);
|
||||||
REGISTER_API(CommandFilterArgGet);
|
REGISTER_API(CommandFilterArgGet);
|
||||||
REGISTER_API(CommandFilterArgInsert);
|
REGISTER_API(CommandFilterArgInsert);
|
||||||
|
@ -7,10 +7,27 @@ static RedisModuleString *log_key_name;
|
|||||||
|
|
||||||
static const char log_command_name[] = "hellofilter.log";
|
static const char log_command_name[] = "hellofilter.log";
|
||||||
static const char ping_command_name[] = "hellofilter.ping";
|
static const char ping_command_name[] = "hellofilter.ping";
|
||||||
|
static const char unregister_command_name[] = "hellofilter.unregister";
|
||||||
static int in_module = 0;
|
static int in_module = 0;
|
||||||
|
|
||||||
|
static RedisModuleCommandFilter *filter = NULL;
|
||||||
|
|
||||||
|
int HelloFilter_UnregisterCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
||||||
|
{
|
||||||
|
(void) argc;
|
||||||
|
(void) argv;
|
||||||
|
|
||||||
|
RedisModule_ReplyWithLongLong(ctx,
|
||||||
|
RedisModule_UnregisterCommandFilter(ctx, filter));
|
||||||
|
|
||||||
|
return REDISMODULE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
int HelloFilter_PingCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
int HelloFilter_PingCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
||||||
{
|
{
|
||||||
|
(void) argc;
|
||||||
|
(void) argv;
|
||||||
|
|
||||||
RedisModuleCallReply *reply = RedisModule_Call(ctx, "ping", "c", "@log");
|
RedisModuleCallReply *reply = RedisModule_Call(ctx, "ping", "c", "@log");
|
||||||
if (reply) {
|
if (reply) {
|
||||||
RedisModule_ReplyWithCallReply(ctx, reply);
|
RedisModule_ReplyWithCallReply(ctx, reply);
|
||||||
@ -115,11 +132,15 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
|||||||
return REDISMODULE_ERR;
|
return REDISMODULE_ERR;
|
||||||
|
|
||||||
if (RedisModule_CreateCommand(ctx,ping_command_name,
|
if (RedisModule_CreateCommand(ctx,ping_command_name,
|
||||||
HelloFilter_PingCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
|
HelloFilter_PingCommand,"deny-oom",1,1,1) == REDISMODULE_ERR)
|
||||||
return REDISMODULE_ERR;
|
return REDISMODULE_ERR;
|
||||||
|
|
||||||
if (RedisModule_RegisterCommandFilter(ctx, HelloFilter_CommandFilter)
|
if (RedisModule_CreateCommand(ctx,unregister_command_name,
|
||||||
== REDISMODULE_ERR) return REDISMODULE_ERR;
|
HelloFilter_UnregisterCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
|
||||||
|
return REDISMODULE_ERR;
|
||||||
|
|
||||||
|
if ((filter = RedisModule_RegisterCommandFilter(ctx, HelloFilter_CommandFilter))
|
||||||
|
== NULL) return REDISMODULE_ERR;
|
||||||
|
|
||||||
return REDISMODULE_OK;
|
return REDISMODULE_OK;
|
||||||
}
|
}
|
||||||
|
@ -151,6 +151,7 @@ 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 struct RedisModuleCommandFilterCtx RedisModuleCommandFilterCtx;
|
||||||
|
typedef struct RedisModuleCommandFilter RedisModuleCommandFilter;
|
||||||
|
|
||||||
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);
|
||||||
@ -339,12 +340,13 @@ void REDISMODULE_API_FUNC(RedisModule_SetDisconnectCallback)(RedisModuleBlockedC
|
|||||||
void REDISMODULE_API_FUNC(RedisModule_SetClusterFlags)(RedisModuleCtx *ctx, uint64_t flags);
|
void REDISMODULE_API_FUNC(RedisModule_SetClusterFlags)(RedisModuleCtx *ctx, uint64_t flags);
|
||||||
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);
|
RedisModuleCommandFilter *REDISMODULE_API_FUNC(RedisModule_RegisterCommandFilter)(RedisModuleCtx *ctx, RedisModuleCommandFilterFunc cb);
|
||||||
int REDISMODULE_API_FUNC(RedisModule_CommandFilterArgsCount)(RedisModuleCommandFilterCtx *filter);
|
int REDISMODULE_API_FUNC(RedisModule_UnregisterCommandFilter)(RedisModuleCtx *ctx, RedisModuleCommandFilter *filter);
|
||||||
const RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CommandFilterArgGet)(RedisModuleCommandFilterCtx *filter, int pos);
|
int REDISMODULE_API_FUNC(RedisModule_CommandFilterArgsCount)(RedisModuleCommandFilterCtx *fctx);
|
||||||
int REDISMODULE_API_FUNC(RedisModule_CommandFilterArgInsert)(RedisModuleCommandFilterCtx *filter, int pos, RedisModuleString *arg);
|
const RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CommandFilterArgGet)(RedisModuleCommandFilterCtx *fctx, int pos);
|
||||||
int REDISMODULE_API_FUNC(RedisModule_CommandFilterArgReplace)(RedisModuleCommandFilterCtx *filter, int pos, RedisModuleString *arg);
|
int REDISMODULE_API_FUNC(RedisModule_CommandFilterArgInsert)(RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg);
|
||||||
int REDISMODULE_API_FUNC(RedisModule_CommandFilterArgDelete)(RedisModuleCommandFilterCtx *filter, int pos);
|
int REDISMODULE_API_FUNC(RedisModule_CommandFilterArgReplace)(RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg);
|
||||||
|
int REDISMODULE_API_FUNC(RedisModule_CommandFilterArgDelete)(RedisModuleCommandFilterCtx *fctx, int pos);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* This is included inline inside each Redis module. */
|
/* This is included inline inside each Redis module. */
|
||||||
@ -508,6 +510,7 @@ 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(UnregisterCommandFilter);
|
||||||
REDISMODULE_GET_API(CommandFilterArgsCount);
|
REDISMODULE_GET_API(CommandFilterArgsCount);
|
||||||
REDISMODULE_GET_API(CommandFilterArgGet);
|
REDISMODULE_GET_API(CommandFilterArgGet);
|
||||||
REDISMODULE_GET_API(CommandFilterArgInsert);
|
REDISMODULE_GET_API(CommandFilterArgInsert);
|
||||||
|
@ -42,4 +42,26 @@ start_server {tags {"modules"}} {
|
|||||||
r eval "redis.call('hellofilter.ping')" 0
|
r eval "redis.call('hellofilter.ping')" 0
|
||||||
r lrange log-key 0 -1
|
r lrange log-key 0 -1
|
||||||
} "{ping @log}"
|
} "{ping @log}"
|
||||||
|
|
||||||
|
test {Command Filter is unregistered implicitly on module unload} {
|
||||||
|
r del log-key
|
||||||
|
r module unload hellofilter
|
||||||
|
r set mykey @log
|
||||||
|
r lrange log-key 0 -1
|
||||||
|
} {}
|
||||||
|
|
||||||
|
r module load $testmodule log-key-2
|
||||||
|
|
||||||
|
test {Command Filter unregister works as expected} {
|
||||||
|
# Validate reloading succeeded
|
||||||
|
r set mykey @log
|
||||||
|
assert_equal "{set mykey @log}" [r lrange log-key-2 0 -1]
|
||||||
|
|
||||||
|
# Unregister
|
||||||
|
r hellofilter.unregister
|
||||||
|
r del log-key-2
|
||||||
|
|
||||||
|
r set mykey @log
|
||||||
|
r lrange log-key-2 0 -1
|
||||||
|
} {}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user