1 | /* |
2 | * Copyright (c) 2009-2021, Redis Labs Ltd. |
3 | * All rights reserved. |
4 | * |
5 | * Redistribution and use in source and binary forms, with or without |
6 | * modification, are permitted provided that the following conditions are met: |
7 | * |
8 | * * Redistributions of source code must retain the above copyright notice, |
9 | * this list of conditions and the following disclaimer. |
10 | * * Redistributions in binary form must reproduce the above copyright |
11 | * notice, this list of conditions and the following disclaimer in the |
12 | * documentation and/or other materials provided with the distribution. |
13 | * * Neither the name of Redis nor the names of its contributors may be used |
14 | * to endorse or promote products derived from this software without |
15 | * specific prior written permission. |
16 | * |
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
27 | * POSSIBILITY OF SUCH DAMAGE. |
28 | */ |
29 | |
30 | #include "server.h" |
31 | #include "call_reply.h" |
32 | |
33 | #define REPLY_FLAG_ROOT (1<<0) |
34 | #define REPLY_FLAG_PARSED (1<<1) |
35 | #define REPLY_FLAG_RESP3 (1<<2) |
36 | |
37 | /* -------------------------------------------------------- |
38 | * An opaque struct used to parse a RESP protocol reply and |
39 | * represent it. Used when parsing replies such as in RM_Call |
40 | * or Lua scripts. |
41 | * -------------------------------------------------------- */ |
42 | struct CallReply { |
43 | void *private_data; |
44 | sds original_proto; /* Available only for root reply. */ |
45 | const char *proto; |
46 | size_t proto_len; |
47 | int type; /* REPLY_... */ |
48 | int flags; /* REPLY_FLAG... */ |
49 | size_t len; /* Length of a string, or the number elements in an array. */ |
50 | union { |
51 | const char *str; /* String pointer for string and error replies. This |
52 | * does not need to be freed, always points inside |
53 | * a reply->proto buffer of the reply object or, in |
54 | * case of array elements, of parent reply objects. */ |
55 | struct { |
56 | const char *str; |
57 | const char *format; |
58 | } verbatim_str; /* Reply value for verbatim string */ |
59 | long long ll; /* Reply value for integer reply. */ |
60 | double d; /* Reply value for double reply. */ |
61 | struct CallReply *array; /* Array of sub-reply elements. used for set, array, map, and attribute */ |
62 | } val; |
63 | list *deferred_error_list; /* list of errors in sds form or NULL */ |
64 | struct CallReply *attribute; /* attribute reply, NULL if not exists */ |
65 | }; |
66 | |
67 | static void callReplySetSharedData(CallReply *rep, int type, const char *proto, size_t proto_len, int ) { |
68 | rep->type = type; |
69 | rep->proto = proto; |
70 | rep->proto_len = proto_len; |
71 | rep->flags |= extra_flags; |
72 | } |
73 | |
74 | static void callReplyNull(void *ctx, const char *proto, size_t proto_len) { |
75 | CallReply *rep = ctx; |
76 | callReplySetSharedData(rep, REDISMODULE_REPLY_NULL, proto, proto_len, REPLY_FLAG_RESP3); |
77 | } |
78 | |
79 | static void callReplyNullBulkString(void *ctx, const char *proto, size_t proto_len) { |
80 | CallReply *rep = ctx; |
81 | callReplySetSharedData(rep, REDISMODULE_REPLY_NULL, proto, proto_len, 0); |
82 | } |
83 | |
84 | static void callReplyNullArray(void *ctx, const char *proto, size_t proto_len) { |
85 | CallReply *rep = ctx; |
86 | callReplySetSharedData(rep, REDISMODULE_REPLY_NULL, proto, proto_len, 0); |
87 | } |
88 | |
89 | static void callReplyBulkString(void *ctx, const char *str, size_t len, const char *proto, size_t proto_len) { |
90 | CallReply *rep = ctx; |
91 | callReplySetSharedData(rep, REDISMODULE_REPLY_STRING, proto, proto_len, 0); |
92 | rep->len = len; |
93 | rep->val.str = str; |
94 | } |
95 | |
96 | static void callReplyError(void *ctx, const char *str, size_t len, const char *proto, size_t proto_len) { |
97 | CallReply *rep = ctx; |
98 | callReplySetSharedData(rep, REDISMODULE_REPLY_ERROR, proto, proto_len, 0); |
99 | rep->len = len; |
100 | rep->val.str = str; |
101 | } |
102 | |
103 | static void callReplySimpleStr(void *ctx, const char *str, size_t len, const char *proto, size_t proto_len) { |
104 | CallReply *rep = ctx; |
105 | callReplySetSharedData(rep, REDISMODULE_REPLY_STRING, proto, proto_len, 0); |
106 | rep->len = len; |
107 | rep->val.str = str; |
108 | } |
109 | |
110 | static void callReplyLong(void *ctx, long long val, const char *proto, size_t proto_len) { |
111 | CallReply *rep = ctx; |
112 | callReplySetSharedData(rep, REDISMODULE_REPLY_INTEGER, proto, proto_len, 0); |
113 | rep->val.ll = val; |
114 | } |
115 | |
116 | static void callReplyDouble(void *ctx, double val, const char *proto, size_t proto_len) { |
117 | CallReply *rep = ctx; |
118 | callReplySetSharedData(rep, REDISMODULE_REPLY_DOUBLE, proto, proto_len, REPLY_FLAG_RESP3); |
119 | rep->val.d = val; |
120 | } |
121 | |
122 | static void callReplyVerbatimString(void *ctx, const char *format, const char *str, size_t len, const char *proto, size_t proto_len) { |
123 | CallReply *rep = ctx; |
124 | callReplySetSharedData(rep, REDISMODULE_REPLY_VERBATIM_STRING, proto, proto_len, REPLY_FLAG_RESP3); |
125 | rep->len = len; |
126 | rep->val.verbatim_str.str = str; |
127 | rep->val.verbatim_str.format = format; |
128 | } |
129 | |
130 | static void callReplyBigNumber(void *ctx, const char *str, size_t len, const char *proto, size_t proto_len) { |
131 | CallReply *rep = ctx; |
132 | callReplySetSharedData(rep, REDISMODULE_REPLY_BIG_NUMBER, proto, proto_len, REPLY_FLAG_RESP3); |
133 | rep->len = len; |
134 | rep->val.str = str; |
135 | } |
136 | |
137 | static void callReplyBool(void *ctx, int val, const char *proto, size_t proto_len) { |
138 | CallReply *rep = ctx; |
139 | callReplySetSharedData(rep, REDISMODULE_REPLY_BOOL, proto, proto_len, REPLY_FLAG_RESP3); |
140 | rep->val.ll = val; |
141 | } |
142 | |
143 | static void callReplyParseCollection(ReplyParser *parser, CallReply *rep, size_t len, const char *proto, size_t elements_per_entry) { |
144 | rep->len = len; |
145 | rep->val.array = zcalloc(elements_per_entry * len * sizeof(CallReply)); |
146 | for (size_t i = 0; i < len * elements_per_entry; i += elements_per_entry) { |
147 | for (size_t j = 0 ; j < elements_per_entry ; ++j) { |
148 | rep->val.array[i + j].private_data = rep->private_data; |
149 | parseReply(parser, rep->val.array + i + j); |
150 | rep->val.array[i + j].flags |= REPLY_FLAG_PARSED; |
151 | if (rep->val.array[i + j].flags & REPLY_FLAG_RESP3) { |
152 | /* If one of the sub-replies is RESP3, then the current reply is also RESP3. */ |
153 | rep->flags |= REPLY_FLAG_RESP3; |
154 | } |
155 | } |
156 | } |
157 | rep->proto = proto; |
158 | rep->proto_len = parser->curr_location - proto; |
159 | } |
160 | |
161 | static void callReplyAttribute(ReplyParser *parser, void *ctx, size_t len, const char *proto) { |
162 | CallReply *rep = ctx; |
163 | rep->attribute = zcalloc(sizeof(CallReply)); |
164 | |
165 | /* Continue parsing the attribute reply */ |
166 | rep->attribute->len = len; |
167 | rep->attribute->type = REDISMODULE_REPLY_ATTRIBUTE; |
168 | callReplyParseCollection(parser, rep->attribute, len, proto, 2); |
169 | rep->attribute->flags |= REPLY_FLAG_PARSED | REPLY_FLAG_RESP3; |
170 | rep->attribute->private_data = rep->private_data; |
171 | |
172 | /* Continue parsing the reply */ |
173 | parseReply(parser, rep); |
174 | |
175 | /* In this case we need to fix the proto address and len, it should start from the attribute */ |
176 | rep->proto = proto; |
177 | rep->proto_len = parser->curr_location - proto; |
178 | rep->flags |= REPLY_FLAG_RESP3; |
179 | } |
180 | |
181 | static void callReplyArray(ReplyParser *parser, void *ctx, size_t len, const char *proto) { |
182 | CallReply *rep = ctx; |
183 | rep->type = REDISMODULE_REPLY_ARRAY; |
184 | callReplyParseCollection(parser, rep, len, proto, 1); |
185 | } |
186 | |
187 | static void callReplySet(ReplyParser *parser, void *ctx, size_t len, const char *proto) { |
188 | CallReply *rep = ctx; |
189 | rep->type = REDISMODULE_REPLY_SET; |
190 | callReplyParseCollection(parser, rep, len, proto, 1); |
191 | rep->flags |= REPLY_FLAG_RESP3; |
192 | } |
193 | |
194 | static void callReplyMap(ReplyParser *parser, void *ctx, size_t len, const char *proto) { |
195 | CallReply *rep = ctx; |
196 | rep->type = REDISMODULE_REPLY_MAP; |
197 | callReplyParseCollection(parser, rep, len, proto, 2); |
198 | rep->flags |= REPLY_FLAG_RESP3; |
199 | } |
200 | |
201 | static void callReplyParseError(void *ctx) { |
202 | CallReply *rep = ctx; |
203 | rep->type = REDISMODULE_REPLY_UNKNOWN; |
204 | } |
205 | |
206 | /* Recursively free the current call reply and its sub-replies. */ |
207 | static void freeCallReplyInternal(CallReply *rep) { |
208 | if (rep->type == REDISMODULE_REPLY_ARRAY || rep->type == REDISMODULE_REPLY_SET) { |
209 | for (size_t i = 0 ; i < rep->len ; ++i) { |
210 | freeCallReplyInternal(rep->val.array + i); |
211 | } |
212 | zfree(rep->val.array); |
213 | } |
214 | |
215 | if (rep->type == REDISMODULE_REPLY_MAP || rep->type == REDISMODULE_REPLY_ATTRIBUTE) { |
216 | for (size_t i = 0 ; i < rep->len ; ++i) { |
217 | freeCallReplyInternal(rep->val.array + i * 2); |
218 | freeCallReplyInternal(rep->val.array + i * 2 + 1); |
219 | } |
220 | zfree(rep->val.array); |
221 | } |
222 | |
223 | if (rep->attribute) { |
224 | freeCallReplyInternal(rep->attribute); |
225 | zfree(rep->attribute); |
226 | } |
227 | } |
228 | |
229 | /* Free the given call reply and its children (in case of nested reply) recursively. |
230 | * If private data was set when the CallReply was created it will not be freed, as it's |
231 | * the caller's responsibility to free it before calling freeCallReply(). */ |
232 | void freeCallReply(CallReply *rep) { |
233 | if (!(rep->flags & REPLY_FLAG_ROOT)) { |
234 | return; |
235 | } |
236 | if (rep->flags & REPLY_FLAG_PARSED) { |
237 | freeCallReplyInternal(rep); |
238 | } |
239 | sdsfree(rep->original_proto); |
240 | if (rep->deferred_error_list) |
241 | listRelease(rep->deferred_error_list); |
242 | zfree(rep); |
243 | } |
244 | |
245 | static const ReplyParserCallbacks DefaultParserCallbacks = { |
246 | .null_callback = callReplyNull, |
247 | .bulk_string_callback = callReplyBulkString, |
248 | .null_bulk_string_callback = callReplyNullBulkString, |
249 | .null_array_callback = callReplyNullArray, |
250 | .error_callback = callReplyError, |
251 | .simple_str_callback = callReplySimpleStr, |
252 | .long_callback = callReplyLong, |
253 | .array_callback = callReplyArray, |
254 | .set_callback = callReplySet, |
255 | .map_callback = callReplyMap, |
256 | .double_callback = callReplyDouble, |
257 | .bool_callback = callReplyBool, |
258 | .big_number_callback = callReplyBigNumber, |
259 | .verbatim_string_callback = callReplyVerbatimString, |
260 | .attribute_callback = callReplyAttribute, |
261 | .error = callReplyParseError, |
262 | }; |
263 | |
264 | /* Parse the buffer located in rep->original_proto and update the CallReply |
265 | * structure to represent its contents. */ |
266 | static void callReplyParse(CallReply *rep) { |
267 | if (rep->flags & REPLY_FLAG_PARSED) { |
268 | return; |
269 | } |
270 | |
271 | ReplyParser parser = {.curr_location = rep->proto, .callbacks = DefaultParserCallbacks}; |
272 | |
273 | parseReply(&parser, rep); |
274 | rep->flags |= REPLY_FLAG_PARSED; |
275 | } |
276 | |
277 | /* Return the call reply type (REDISMODULE_REPLY_...). */ |
278 | int callReplyType(CallReply *rep) { |
279 | if (!rep) return REDISMODULE_REPLY_UNKNOWN; |
280 | callReplyParse(rep); |
281 | return rep->type; |
282 | } |
283 | |
284 | /* Return reply string as buffer and len. Applicable to: |
285 | * - REDISMODULE_REPLY_STRING |
286 | * - REDISMODULE_REPLY_ERROR |
287 | * |
288 | * The return value is borrowed from CallReply, so it must not be freed |
289 | * explicitly or used after CallReply itself is freed. |
290 | * |
291 | * The returned value is not NULL terminated and its length is returned by |
292 | * reference through len, which must not be NULL. |
293 | */ |
294 | const char *callReplyGetString(CallReply *rep, size_t *len) { |
295 | callReplyParse(rep); |
296 | if (rep->type != REDISMODULE_REPLY_STRING && |
297 | rep->type != REDISMODULE_REPLY_ERROR) return NULL; |
298 | if (len) *len = rep->len; |
299 | return rep->val.str; |
300 | } |
301 | |
302 | /* Return a long long reply value. Applicable to: |
303 | * - REDISMODULE_REPLY_INTEGER |
304 | */ |
305 | long long callReplyGetLongLong(CallReply *rep) { |
306 | callReplyParse(rep); |
307 | if (rep->type != REDISMODULE_REPLY_INTEGER) return LLONG_MIN; |
308 | return rep->val.ll; |
309 | } |
310 | |
311 | /* Return a double reply value. Applicable to: |
312 | * - REDISMODULE_REPLY_DOUBLE |
313 | */ |
314 | double callReplyGetDouble(CallReply *rep) { |
315 | callReplyParse(rep); |
316 | if (rep->type != REDISMODULE_REPLY_DOUBLE) return LLONG_MIN; |
317 | return rep->val.d; |
318 | } |
319 | |
320 | /* Return a reply Boolean value. Applicable to: |
321 | * - REDISMODULE_REPLY_BOOL |
322 | */ |
323 | int callReplyGetBool(CallReply *rep) { |
324 | callReplyParse(rep); |
325 | if (rep->type != REDISMODULE_REPLY_BOOL) return INT_MIN; |
326 | return rep->val.ll; |
327 | } |
328 | |
329 | /* Return reply length. Applicable to: |
330 | * - REDISMODULE_REPLY_STRING |
331 | * - REDISMODULE_REPLY_ERROR |
332 | * - REDISMODULE_REPLY_ARRAY |
333 | * - REDISMODULE_REPLY_SET |
334 | * - REDISMODULE_REPLY_MAP |
335 | * - REDISMODULE_REPLY_ATTRIBUTE |
336 | */ |
337 | size_t callReplyGetLen(CallReply *rep) { |
338 | callReplyParse(rep); |
339 | switch(rep->type) { |
340 | case REDISMODULE_REPLY_STRING: |
341 | case REDISMODULE_REPLY_ERROR: |
342 | case REDISMODULE_REPLY_ARRAY: |
343 | case REDISMODULE_REPLY_SET: |
344 | case REDISMODULE_REPLY_MAP: |
345 | case REDISMODULE_REPLY_ATTRIBUTE: |
346 | return rep->len; |
347 | default: |
348 | return 0; |
349 | } |
350 | } |
351 | |
352 | static CallReply *callReplyGetCollectionElement(CallReply *rep, size_t idx, int elements_per_entry) { |
353 | if (idx >= rep->len * elements_per_entry) return NULL; // real len is rep->len * elements_per_entry |
354 | return rep->val.array+idx; |
355 | } |
356 | |
357 | /* Return a reply array element at a given index. Applicable to: |
358 | * - REDISMODULE_REPLY_ARRAY |
359 | * |
360 | * The return value is borrowed from CallReply, so it must not be freed |
361 | * explicitly or used after CallReply itself is freed. |
362 | */ |
363 | CallReply *callReplyGetArrayElement(CallReply *rep, size_t idx) { |
364 | callReplyParse(rep); |
365 | if (rep->type != REDISMODULE_REPLY_ARRAY) return NULL; |
366 | return callReplyGetCollectionElement(rep, idx, 1); |
367 | } |
368 | |
369 | /* Return a reply set element at a given index. Applicable to: |
370 | * - REDISMODULE_REPLY_SET |
371 | * |
372 | * The return value is borrowed from CallReply, so it must not be freed |
373 | * explicitly or used after CallReply itself is freed. |
374 | */ |
375 | CallReply *callReplyGetSetElement(CallReply *rep, size_t idx) { |
376 | callReplyParse(rep); |
377 | if (rep->type != REDISMODULE_REPLY_SET) return NULL; |
378 | return callReplyGetCollectionElement(rep, idx, 1); |
379 | } |
380 | |
381 | static int callReplyGetMapElementInternal(CallReply *rep, size_t idx, CallReply **key, CallReply **val, int type) { |
382 | callReplyParse(rep); |
383 | if (rep->type != type) return C_ERR; |
384 | if (idx >= rep->len) return C_ERR; |
385 | if (key) *key = callReplyGetCollectionElement(rep, idx * 2, 2); |
386 | if (val) *val = callReplyGetCollectionElement(rep, idx * 2 + 1, 2); |
387 | return C_OK; |
388 | } |
389 | |
390 | /* Retrieve a map reply key and value at a given index. Applicable to: |
391 | * - REDISMODULE_REPLY_MAP |
392 | * |
393 | * The key and value are returned by reference through key and val, |
394 | * which may also be NULL if not needed. |
395 | * |
396 | * Returns C_OK on success or C_ERR if reply type mismatches, or if idx is out |
397 | * of range. |
398 | * |
399 | * The returned values are borrowed from CallReply, so they must not be freed |
400 | * explicitly or used after CallReply itself is freed. |
401 | */ |
402 | int callReplyGetMapElement(CallReply *rep, size_t idx, CallReply **key, CallReply **val) { |
403 | return callReplyGetMapElementInternal(rep, idx, key, val, REDISMODULE_REPLY_MAP); |
404 | } |
405 | |
406 | /* Return reply attribute, or NULL if it does not exist. Applicable to all replies. |
407 | * |
408 | * The returned values are borrowed from CallReply, so they must not be freed |
409 | * explicitly or used after CallReply itself is freed. |
410 | */ |
411 | CallReply *callReplyGetAttribute(CallReply *rep) { |
412 | return rep->attribute; |
413 | } |
414 | |
415 | /* Retrieve attribute reply key and value at a given index. Applicable to: |
416 | * - REDISMODULE_REPLY_ATTRIBUTE |
417 | * |
418 | * The key and value are returned by reference through key and val, |
419 | * which may also be NULL if not needed. |
420 | * |
421 | * Returns C_OK on success or C_ERR if reply type mismatches, or if idx is out |
422 | * of range. |
423 | * |
424 | * The returned values are borrowed from CallReply, so they must not be freed |
425 | * explicitly or used after CallReply itself is freed. |
426 | */ |
427 | int callReplyGetAttributeElement(CallReply *rep, size_t idx, CallReply **key, CallReply **val) { |
428 | return callReplyGetMapElementInternal(rep, idx, key, val, REDISMODULE_REPLY_MAP); |
429 | } |
430 | |
431 | /* Return a big number reply value. Applicable to: |
432 | * - REDISMODULE_REPLY_BIG_NUMBER |
433 | * |
434 | * The returned values are borrowed from CallReply, so they must not be freed |
435 | * explicitly or used after CallReply itself is freed. |
436 | * |
437 | * The return value is guaranteed to be a big number, as described in the RESP3 |
438 | * protocol specifications. |
439 | * |
440 | * The returned value is not NULL terminated and its length is returned by |
441 | * reference through len, which must not be NULL. |
442 | */ |
443 | const char *callReplyGetBigNumber(CallReply *rep, size_t *len) { |
444 | callReplyParse(rep); |
445 | if (rep->type != REDISMODULE_REPLY_BIG_NUMBER) return NULL; |
446 | *len = rep->len; |
447 | return rep->val.str; |
448 | } |
449 | |
450 | /* Return a verbatim string reply value. Applicable to: |
451 | * - REDISMODULE_REPLY_VERBATIM_STRING |
452 | * |
453 | * If format is non-NULL, the verbatim reply format is also returned by value. |
454 | * |
455 | * The optional output argument can be given to get a verbatim reply |
456 | * format, or can be set NULL if not needed. |
457 | * |
458 | * The return value is borrowed from CallReply, so it must not be freed |
459 | * explicitly or used after CallReply itself is freed. |
460 | * |
461 | * The returned value is not NULL terminated and its length is returned by |
462 | * reference through len, which must not be NULL. |
463 | */ |
464 | const char *callReplyGetVerbatim(CallReply *rep, size_t *len, const char **format){ |
465 | callReplyParse(rep); |
466 | if (rep->type != REDISMODULE_REPLY_VERBATIM_STRING) return NULL; |
467 | *len = rep->len; |
468 | if (format) *format = rep->val.verbatim_str.format; |
469 | return rep->val.verbatim_str.str; |
470 | } |
471 | |
472 | /* Return the current reply blob. |
473 | * |
474 | * The return value is borrowed from CallReply, so it must not be freed |
475 | * explicitly or used after CallReply itself is freed. |
476 | */ |
477 | const char *callReplyGetProto(CallReply *rep, size_t *proto_len) { |
478 | *proto_len = rep->proto_len; |
479 | return rep->proto; |
480 | } |
481 | |
482 | /* Return CallReply private data, as set by the caller on callReplyCreate(). |
483 | */ |
484 | void *callReplyGetPrivateData(CallReply *rep) { |
485 | return rep->private_data; |
486 | } |
487 | |
488 | /* Return true if the reply or one of it sub-replies is RESP3 formatted. */ |
489 | int callReplyIsResp3(CallReply *rep) { |
490 | return rep->flags & REPLY_FLAG_RESP3; |
491 | } |
492 | |
493 | /* Returns a list of errors in sds form, or NULL. */ |
494 | list *callReplyDeferredErrorList(CallReply *rep) { |
495 | return rep->deferred_error_list; |
496 | } |
497 | |
498 | /* Create a new CallReply struct from the reply blob. |
499 | * |
500 | * The function will own the reply blob, so it must not be used or freed by |
501 | * the caller after passing it to this function. |
502 | * |
503 | * The reply blob will be freed when the returned CallReply struct is later |
504 | * freed using freeCallReply(). |
505 | * |
506 | * The deferred_error_list is an optional list of errors that are present |
507 | * in the reply blob, if given, this function will take ownership on it. |
508 | * |
509 | * The private_data is optional and can later be accessed using |
510 | * callReplyGetPrivateData(). |
511 | * |
512 | * NOTE: The parser used for parsing the reply and producing CallReply is |
513 | * designed to handle valid replies created by Redis itself. IT IS NOT |
514 | * DESIGNED TO HANDLE USER INPUT and using it to parse invalid replies is |
515 | * unsafe. |
516 | */ |
517 | CallReply *callReplyCreate(sds reply, list *deferred_error_list, void *private_data) { |
518 | CallReply *res = zmalloc(sizeof(*res)); |
519 | res->flags = REPLY_FLAG_ROOT; |
520 | res->original_proto = reply; |
521 | res->proto = reply; |
522 | res->proto_len = sdslen(reply); |
523 | res->private_data = private_data; |
524 | res->attribute = NULL; |
525 | res->deferred_error_list = deferred_error_list; |
526 | return res; |
527 | } |
528 | |
529 | /* Create a new CallReply struct from the reply blob representing an error message. |
530 | * Automatically creating deferred_error_list and set a copy of the reply in it. |
531 | * Refer to callReplyCreate for detailed explanation. |
532 | * Reply string can come in one of two forms: |
533 | * 1. A protocol reply starting with "-CODE" and ending with "\r\n" |
534 | * 2. A plain string, in which case this function adds the protocol header and footer. */ |
535 | CallReply *callReplyCreateError(sds reply, void *private_data) { |
536 | sds err_buff = reply; |
537 | if (err_buff[0] != '-') { |
538 | err_buff = sdscatfmt(sdsempty(), "-ERR %S\r\n" , reply); |
539 | sdsfree(reply); |
540 | } |
541 | list *deferred_error_list = listCreate(); |
542 | listSetFreeMethod(deferred_error_list, (void (*)(void*))sdsfree); |
543 | listAddNodeTail(deferred_error_list, sdsnew(err_buff)); |
544 | return callReplyCreate(err_buff, deferred_error_list, private_data); |
545 | } |
546 | |