mirror of
https://codeberg.org/redict/redict.git
synced 2025-01-22 16:18:28 -05:00
Initial command filter experiment.
This commit is contained in:
parent
8ea906a3e8
commit
c3e187190b
76
src/module.c
76
src/module.c
@ -270,6 +270,28 @@ typedef struct RedisModuleDictIter {
|
|||||||
raxIterator ri;
|
raxIterator ri;
|
||||||
} RedisModuleDictIter;
|
} RedisModuleDictIter;
|
||||||
|
|
||||||
|
/* Information about the command to be executed, as passed to and from a
|
||||||
|
* filter. */
|
||||||
|
typedef struct RedisModuleFilteredCommand {
|
||||||
|
RedisModuleString **argv;
|
||||||
|
int argc;
|
||||||
|
} RedisModuleFilteredCommand;
|
||||||
|
|
||||||
|
typedef void (*RedisModuleCommandFilterFunc) (RedisModuleCtx *ctx, RedisModuleFilteredCommand *cmd);
|
||||||
|
|
||||||
|
typedef struct RedisModuleCommandFilter {
|
||||||
|
/* The module that registered the filter */
|
||||||
|
RedisModule *module;
|
||||||
|
/* Filter callback function */
|
||||||
|
RedisModuleCommandFilterFunc callback;
|
||||||
|
/* Indicates a filter is active, avoid reentrancy */
|
||||||
|
int active;
|
||||||
|
} RedisModuleCommandFilter;
|
||||||
|
|
||||||
|
/* Registered filters */
|
||||||
|
static list *moduleCommandFilters;
|
||||||
|
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------
|
||||||
* Prototypes
|
* Prototypes
|
||||||
* -------------------------------------------------------------------------- */
|
* -------------------------------------------------------------------------- */
|
||||||
@ -4770,6 +4792,56 @@ int moduleUnregisterUsedAPI(RedisModule *module) {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------
|
||||||
|
* Module Command Filter API
|
||||||
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* Register a new command filter function. Filters get executed by Redis
|
||||||
|
* before processing an inbound command and can be used to manipulate the
|
||||||
|
* behavior of standard Redis commands. Filters must not attempt to
|
||||||
|
* perform Redis commands or operate on the dataset, and must restrict
|
||||||
|
* themselves to manipulation of the arguments.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int RM_RegisterCommandFilter(RedisModuleCtx *ctx, RedisModuleCommandFilterFunc callback) {
|
||||||
|
RedisModuleCommandFilter *filter = zmalloc(sizeof(*filter));
|
||||||
|
filter->module = ctx->module;
|
||||||
|
filter->callback = callback;
|
||||||
|
filter->active = 0;
|
||||||
|
|
||||||
|
listAddNodeTail(moduleCommandFilters, filter);
|
||||||
|
return REDISMODULE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void moduleCallCommandFilters(client *c) {
|
||||||
|
if (listLength(moduleCommandFilters) == 0) return;
|
||||||
|
|
||||||
|
listIter li;
|
||||||
|
listNode *ln;
|
||||||
|
listRewind(moduleCommandFilters,&li);
|
||||||
|
|
||||||
|
RedisModuleFilteredCommand cmd = {
|
||||||
|
.argv = c->argv,
|
||||||
|
.argc = c->argc
|
||||||
|
};
|
||||||
|
|
||||||
|
while((ln = listNext(&li))) {
|
||||||
|
RedisModuleCommandFilter *filter = ln->value;
|
||||||
|
if (filter->active) continue;
|
||||||
|
|
||||||
|
RedisModuleCtx ctx = REDISMODULE_CTX_INIT;
|
||||||
|
ctx.module = filter->module;
|
||||||
|
|
||||||
|
filter->active = 1;
|
||||||
|
filter->callback(&ctx, &cmd);
|
||||||
|
filter->active = 0;
|
||||||
|
moduleFreeContext(&ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
c->argv = cmd.argv;
|
||||||
|
c->argc = cmd.argc;
|
||||||
|
}
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------
|
||||||
* Modules API internals
|
* Modules API internals
|
||||||
* -------------------------------------------------------------------------- */
|
* -------------------------------------------------------------------------- */
|
||||||
@ -4816,6 +4888,9 @@ void moduleInitModulesSystem(void) {
|
|||||||
moduleFreeContextReusedClient->flags |= CLIENT_MODULE;
|
moduleFreeContextReusedClient->flags |= CLIENT_MODULE;
|
||||||
moduleFreeContextReusedClient->user = NULL; /* root user. */
|
moduleFreeContextReusedClient->user = NULL; /* root user. */
|
||||||
|
|
||||||
|
/* Set up filter list */
|
||||||
|
moduleCommandFilters = listCreate();
|
||||||
|
|
||||||
moduleRegisterCoreAPI();
|
moduleRegisterCoreAPI();
|
||||||
if (pipe(server.module_blocked_pipe) == -1) {
|
if (pipe(server.module_blocked_pipe) == -1) {
|
||||||
serverLog(LL_WARNING,
|
serverLog(LL_WARNING,
|
||||||
@ -5219,4 +5294,5 @@ void moduleRegisterCoreAPI(void) {
|
|||||||
REGISTER_API(DictCompare);
|
REGISTER_API(DictCompare);
|
||||||
REGISTER_API(ExportSharedAPI);
|
REGISTER_API(ExportSharedAPI);
|
||||||
REGISTER_API(GetSharedAPI);
|
REGISTER_API(GetSharedAPI);
|
||||||
|
REGISTER_API(RegisterCommandFilter);
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ endif
|
|||||||
|
|
||||||
.SUFFIXES: .c .so .xo .o
|
.SUFFIXES: .c .so .xo .o
|
||||||
|
|
||||||
all: helloworld.so hellotype.so helloblock.so testmodule.so hellocluster.so hellotimer.so hellodict.so
|
all: helloworld.so hellotype.so helloblock.so testmodule.so hellocluster.so hellotimer.so hellodict.so hellofilter.so
|
||||||
|
|
||||||
.c.xo:
|
.c.xo:
|
||||||
$(CC) -I. $(CFLAGS) $(SHOBJ_CFLAGS) -fPIC -c $< -o $@
|
$(CC) -I. $(CFLAGS) $(SHOBJ_CFLAGS) -fPIC -c $< -o $@
|
||||||
@ -46,6 +46,10 @@ hellotimer.so: hellotimer.xo
|
|||||||
hellodict.xo: ../redismodule.h
|
hellodict.xo: ../redismodule.h
|
||||||
|
|
||||||
hellodict.so: hellodict.xo
|
hellodict.so: hellodict.xo
|
||||||
|
|
||||||
|
hellofilter.xo: ../redismodule.h
|
||||||
|
|
||||||
|
hellofilter.so: hellofilter.xo
|
||||||
$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lc
|
$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lc
|
||||||
|
|
||||||
testmodule.xo: ../redismodule.h
|
testmodule.xo: ../redismodule.h
|
||||||
|
69
src/modules/hellofilter.c
Normal file
69
src/modules/hellofilter.c
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#define REDISMODULE_EXPERIMENTAL_API
|
||||||
|
#include "../redismodule.h"
|
||||||
|
|
||||||
|
static RedisModuleString *log_key_name;
|
||||||
|
|
||||||
|
static const char log_command_name[] = "hellofilter.log";
|
||||||
|
|
||||||
|
int HelloFilter_LogCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
||||||
|
{
|
||||||
|
RedisModuleString *s = RedisModule_CreateStringFromString(ctx, argv[0]);
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 1; i < argc; i++) {
|
||||||
|
size_t arglen;
|
||||||
|
const char *arg = RedisModule_StringPtrLen(argv[i], &arglen);
|
||||||
|
|
||||||
|
RedisModule_StringAppendBuffer(ctx, s, " ", 1);
|
||||||
|
RedisModule_StringAppendBuffer(ctx, s, arg, arglen);
|
||||||
|
}
|
||||||
|
|
||||||
|
RedisModuleKey *log = RedisModule_OpenKey(ctx, log_key_name, REDISMODULE_WRITE|REDISMODULE_READ);
|
||||||
|
RedisModule_ListPush(log, REDISMODULE_LIST_HEAD, s);
|
||||||
|
RedisModule_CloseKey(log);
|
||||||
|
RedisModule_FreeString(ctx, s);
|
||||||
|
|
||||||
|
size_t cmdlen;
|
||||||
|
const char *cmdname = RedisModule_StringPtrLen(argv[1], &cmdlen);
|
||||||
|
RedisModuleCallReply *reply = RedisModule_Call(ctx, cmdname, "v", &argv[2], argc - 2);
|
||||||
|
if (reply) {
|
||||||
|
RedisModule_ReplyWithCallReply(ctx, reply);
|
||||||
|
RedisModule_FreeCallReply(reply);
|
||||||
|
} else {
|
||||||
|
RedisModule_ReplyWithSimpleString(ctx, "Unknown command or invalid arguments");
|
||||||
|
}
|
||||||
|
return REDISMODULE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HelloFilter_CommandFilter(RedisModuleCtx *ctx, RedisModuleFilteredCommand *cmd)
|
||||||
|
{
|
||||||
|
cmd->argv = RedisModule_Realloc(cmd->argv, (cmd->argc+1)*sizeof(RedisModuleString *));
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = cmd->argc; i > 0; i--) {
|
||||||
|
cmd->argv[i] = cmd->argv[i-1];
|
||||||
|
}
|
||||||
|
cmd->argv[0] = RedisModule_CreateString(ctx, log_command_name, sizeof(log_command_name)-1);
|
||||||
|
cmd->argc++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
||||||
|
if (RedisModule_Init(ctx,"hellofilter",1,REDISMODULE_APIVER_1)
|
||||||
|
== REDISMODULE_ERR) return REDISMODULE_ERR;
|
||||||
|
|
||||||
|
if (argc != 1) {
|
||||||
|
RedisModule_Log(ctx, "warning", "Log key name not specified");
|
||||||
|
return REDISMODULE_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_key_name = RedisModule_CreateStringFromString(ctx, argv[0]);
|
||||||
|
|
||||||
|
if (RedisModule_CreateCommand(ctx,log_command_name,
|
||||||
|
HelloFilter_LogCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
|
||||||
|
return REDISMODULE_ERR;
|
||||||
|
|
||||||
|
if (RedisModule_RegisterCommandFilter(ctx, HelloFilter_CommandFilter)
|
||||||
|
== REDISMODULE_ERR) return REDISMODULE_ERR;
|
||||||
|
|
||||||
|
return REDISMODULE_OK;
|
||||||
|
}
|
@ -163,6 +163,12 @@ 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 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 {
|
||||||
uint64_t version;
|
uint64_t version;
|
||||||
@ -337,6 +343,7 @@ 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);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* This is included inline inside each Redis module. */
|
/* This is included inline inside each Redis module. */
|
||||||
@ -499,6 +506,7 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
|
|||||||
REDISMODULE_GET_API(SetClusterFlags);
|
REDISMODULE_GET_API(SetClusterFlags);
|
||||||
REDISMODULE_GET_API(ExportSharedAPI);
|
REDISMODULE_GET_API(ExportSharedAPI);
|
||||||
REDISMODULE_GET_API(GetSharedAPI);
|
REDISMODULE_GET_API(GetSharedAPI);
|
||||||
|
REDISMODULE_GET_API(RegisterCommandFilter);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (RedisModule_IsModuleNameBusy && RedisModule_IsModuleNameBusy(name)) return REDISMODULE_ERR;
|
if (RedisModule_IsModuleNameBusy && RedisModule_IsModuleNameBusy(name)) return REDISMODULE_ERR;
|
||||||
|
@ -3268,6 +3268,8 @@ void call(client *c, int flags) {
|
|||||||
* other operations can be performed by the caller. Otherwise
|
* other operations can be performed by the caller. Otherwise
|
||||||
* if C_ERR is returned the client was destroyed (i.e. after QUIT). */
|
* if C_ERR is returned the client was destroyed (i.e. after QUIT). */
|
||||||
int processCommand(client *c) {
|
int processCommand(client *c) {
|
||||||
|
moduleCallCommandFilters(c);
|
||||||
|
|
||||||
/* The QUIT command is handled separately. Normal command procs will
|
/* The QUIT command is handled separately. Normal command procs will
|
||||||
* go through checking for replication and QUIT will cause trouble
|
* go through checking for replication and QUIT will cause trouble
|
||||||
* when FORCE_REPLICATION is enabled and would be implemented in
|
* when FORCE_REPLICATION is enabled and would be implemented in
|
||||||
|
@ -1489,7 +1489,7 @@ size_t moduleCount(void);
|
|||||||
void moduleAcquireGIL(void);
|
void moduleAcquireGIL(void);
|
||||||
void moduleReleaseGIL(void);
|
void moduleReleaseGIL(void);
|
||||||
void moduleNotifyKeyspaceEvent(int type, const char *event, robj *key, int dbid);
|
void moduleNotifyKeyspaceEvent(int type, const char *event, robj *key, int dbid);
|
||||||
|
void moduleCallCommandFilters(client *c);
|
||||||
|
|
||||||
/* Utils */
|
/* Utils */
|
||||||
long long ustime(void);
|
long long ustime(void);
|
||||||
|
Loading…
Reference in New Issue
Block a user