summaryrefslogtreecommitdiff
path: root/examples/redis-unstable/tests/modules/usercall.c
diff options
context:
space:
mode:
Diffstat (limited to 'examples/redis-unstable/tests/modules/usercall.c')
-rw-r--r--examples/redis-unstable/tests/modules/usercall.c230
1 files changed, 230 insertions, 0 deletions
diff --git a/examples/redis-unstable/tests/modules/usercall.c b/examples/redis-unstable/tests/modules/usercall.c
new file mode 100644
index 0000000..cc28817
--- /dev/null
+++ b/examples/redis-unstable/tests/modules/usercall.c
@@ -0,0 +1,230 @@
1#include "redismodule.h"
2#include <pthread.h>
3#include <assert.h>
4
5#define UNUSED(V) ((void) V)
6
7RedisModuleUser *user = NULL;
8
9int call_without_user(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
10 if (argc < 2) {
11 return RedisModule_WrongArity(ctx);
12 }
13
14 const char *cmd = RedisModule_StringPtrLen(argv[1], NULL);
15
16 RedisModuleCallReply *rep = RedisModule_Call(ctx, cmd, "Ev", argv + 2, (size_t)argc - 2);
17 if (!rep) {
18 RedisModule_ReplyWithError(ctx, "NULL reply returned");
19 } else {
20 RedisModule_ReplyWithCallReply(ctx, rep);
21 RedisModule_FreeCallReply(rep);
22 }
23 return REDISMODULE_OK;
24}
25
26int call_with_user_flag(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
27 if (argc < 3) {
28 return RedisModule_WrongArity(ctx);
29 }
30
31 RedisModule_SetContextUser(ctx, user);
32
33 /* Append Ev to the provided flags. */
34 RedisModuleString *flags = RedisModule_CreateStringFromString(ctx, argv[1]);
35 RedisModule_StringAppendBuffer(ctx, flags, "Ev", 2);
36
37 const char* flg = RedisModule_StringPtrLen(flags, NULL);
38 const char* cmd = RedisModule_StringPtrLen(argv[2], NULL);
39
40 RedisModuleCallReply* rep = RedisModule_Call(ctx, cmd, flg, argv + 3, (size_t)argc - 3);
41 if (!rep) {
42 RedisModule_ReplyWithError(ctx, "NULL reply returned");
43 } else {
44 RedisModule_ReplyWithCallReply(ctx, rep);
45 RedisModule_FreeCallReply(rep);
46 }
47 RedisModule_FreeString(ctx, flags);
48
49 return REDISMODULE_OK;
50}
51
52int add_to_acl(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
53 if (argc != 2) {
54 return RedisModule_WrongArity(ctx);
55 }
56
57 size_t acl_len;
58 const char *acl = RedisModule_StringPtrLen(argv[1], &acl_len);
59
60 RedisModuleString *error;
61 int ret = RedisModule_SetModuleUserACLString(ctx, user, acl, &error);
62 if (ret) {
63 size_t len;
64 const char * e = RedisModule_StringPtrLen(error, &len);
65 RedisModule_ReplyWithError(ctx, e);
66 return REDISMODULE_OK;
67 }
68
69 RedisModule_ReplyWithSimpleString(ctx, "OK");
70
71 return REDISMODULE_OK;
72}
73
74int get_acl(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
75 REDISMODULE_NOT_USED(argv);
76
77 if (argc != 1) {
78 return RedisModule_WrongArity(ctx);
79 }
80
81 RedisModule_Assert(user != NULL);
82
83 RedisModuleString *acl = RedisModule_GetModuleUserACLString(user);
84
85 RedisModule_ReplyWithString(ctx, acl);
86
87 RedisModule_FreeString(NULL, acl);
88
89 return REDISMODULE_OK;
90}
91
92int reset_user(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
93 REDISMODULE_NOT_USED(argv);
94
95 if (argc != 1) {
96 return RedisModule_WrongArity(ctx);
97 }
98
99 if (user != NULL) {
100 RedisModule_FreeModuleUser(user);
101 }
102
103 user = RedisModule_CreateModuleUser("module_user");
104
105 RedisModule_ReplyWithSimpleString(ctx, "OK");
106
107 return REDISMODULE_OK;
108}
109
110typedef struct {
111 RedisModuleString **argv;
112 int argc;
113 RedisModuleBlockedClient *bc;
114} bg_call_data;
115
116void *bg_call_worker(void *arg) {
117 bg_call_data *bg = arg;
118 RedisModuleBlockedClient *bc = bg->bc;
119
120 // Get Redis module context
121 RedisModuleCtx *ctx = RedisModule_GetThreadSafeContext(bg->bc);
122
123 // Acquire GIL
124 RedisModule_ThreadSafeContextLock(ctx);
125
126 // Set user
127 RedisModule_SetContextUser(ctx, user);
128
129 // Call the command
130 size_t format_len;
131 RedisModuleString *format_redis_str = RedisModule_CreateString(NULL, "v", 1);
132 const char *format = RedisModule_StringPtrLen(bg->argv[1], &format_len);
133 RedisModule_StringAppendBuffer(NULL, format_redis_str, format, format_len);
134 RedisModule_StringAppendBuffer(NULL, format_redis_str, "E", 1);
135 format = RedisModule_StringPtrLen(format_redis_str, NULL);
136 const char *cmd = RedisModule_StringPtrLen(bg->argv[2], NULL);
137 RedisModuleCallReply *rep = RedisModule_Call(ctx, cmd, format, bg->argv + 3, (size_t)bg->argc - 3);
138 RedisModule_FreeString(NULL, format_redis_str);
139
140 /* Free the arguments within GIL to prevent simultaneous freeing in main thread. */
141 for (int i=0; i<bg->argc; i++)
142 RedisModule_FreeString(ctx, bg->argv[i]);
143 RedisModule_Free(bg->argv);
144 RedisModule_Free(bg);
145
146 // Release GIL
147 RedisModule_ThreadSafeContextUnlock(ctx);
148
149 // Reply to client
150 if (!rep) {
151 RedisModule_ReplyWithError(ctx, "NULL reply returned");
152 } else {
153 RedisModule_ReplyWithCallReply(ctx, rep);
154 RedisModule_FreeCallReply(rep);
155 }
156
157 // Unblock client
158 RedisModule_UnblockClient(bc, NULL);
159
160 // Free the Redis module context
161 RedisModule_FreeThreadSafeContext(ctx);
162
163 return NULL;
164}
165
166int call_with_user_bg(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
167{
168 UNUSED(argv);
169 UNUSED(argc);
170
171 /* Make sure we're not trying to block a client when we shouldn't */
172 int flags = RedisModule_GetContextFlags(ctx);
173 int allFlags = RedisModule_GetContextFlagsAll();
174 if ((allFlags & REDISMODULE_CTX_FLAGS_MULTI) &&
175 (flags & REDISMODULE_CTX_FLAGS_MULTI)) {
176 RedisModule_ReplyWithSimpleString(ctx, "Blocked client is not supported inside multi");
177 return REDISMODULE_OK;
178 }
179 if ((allFlags & REDISMODULE_CTX_FLAGS_DENY_BLOCKING) &&
180 (flags & REDISMODULE_CTX_FLAGS_DENY_BLOCKING)) {
181 RedisModule_ReplyWithSimpleString(ctx, "Blocked client is not allowed");
182 return REDISMODULE_OK;
183 }
184
185 /* Make a copy of the arguments and pass them to the thread. */
186 bg_call_data *bg = RedisModule_Alloc(sizeof(bg_call_data));
187 bg->argv = RedisModule_Alloc(sizeof(RedisModuleString*)*argc);
188 bg->argc = argc;
189 for (int i=0; i<argc; i++)
190 bg->argv[i] = RedisModule_HoldString(ctx, argv[i]);
191
192 /* Block the client */
193 bg->bc = RedisModule_BlockClient(ctx, NULL, NULL, NULL, 0);
194
195 /* Start a thread to handle the request */
196 pthread_t tid;
197 int res = pthread_create(&tid, NULL, bg_call_worker, bg);
198 assert(res == 0);
199 pthread_detach(tid);
200
201 return REDISMODULE_OK;
202}
203
204int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
205 REDISMODULE_NOT_USED(argv);
206 REDISMODULE_NOT_USED(argc);
207
208 if (RedisModule_Init(ctx,"usercall",1,REDISMODULE_APIVER_1)== REDISMODULE_ERR)
209 return REDISMODULE_ERR;
210
211 if (RedisModule_CreateCommand(ctx,"usercall.call_without_user", call_without_user,"write",0,0,0) == REDISMODULE_ERR)
212 return REDISMODULE_ERR;
213
214 if (RedisModule_CreateCommand(ctx,"usercall.call_with_user_flag", call_with_user_flag,"write",0,0,0) == REDISMODULE_ERR)
215 return REDISMODULE_ERR;
216
217 if (RedisModule_CreateCommand(ctx, "usercall.call_with_user_bg", call_with_user_bg, "write", 0, 0, 0) == REDISMODULE_ERR)
218 return REDISMODULE_ERR;
219
220 if (RedisModule_CreateCommand(ctx, "usercall.add_to_acl", add_to_acl, "write",0,0,0) == REDISMODULE_ERR)
221 return REDISMODULE_ERR;
222
223 if (RedisModule_CreateCommand(ctx,"usercall.reset_user", reset_user,"write",0,0,0) == REDISMODULE_ERR)
224 return REDISMODULE_ERR;
225
226 if (RedisModule_CreateCommand(ctx,"usercall.get_acl", get_acl,"write",0,0,0) == REDISMODULE_ERR)
227 return REDISMODULE_ERR;
228
229 return REDISMODULE_OK;
230}