hiredis 0.5.2 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -39,9 +39,13 @@
39
39
  #define AF_LOCAL AF_UNIX
40
40
  #endif
41
41
 
42
- int redisCheckSocketError(redisContext *c, int fd);
43
- int redisContextSetTimeout(redisContext *c, struct timeval tv);
44
- int redisContextConnectTcp(redisContext *c, const char *addr, int port, struct timeval *timeout);
45
- int redisContextConnectUnix(redisContext *c, const char *path, struct timeval *timeout);
42
+ int redisCheckSocketError(redisContext *c);
43
+ int redisContextSetTimeout(redisContext *c, const struct timeval tv);
44
+ int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout);
45
+ int redisContextConnectBindTcp(redisContext *c, const char *addr, int port,
46
+ const struct timeval *timeout,
47
+ const char *source_addr);
48
+ int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout);
49
+ int redisKeepAlive(redisContext *c, int interval);
46
50
 
47
51
  #endif
@@ -0,0 +1,523 @@
1
+ /*
2
+ * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
3
+ * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
4
+ *
5
+ * All rights reserved.
6
+ *
7
+ * Redistribution and use in source and binary forms, with or without
8
+ * modification, are permitted provided that the following conditions are met:
9
+ *
10
+ * * Redistributions of source code must retain the above copyright notice,
11
+ * this list of conditions and the following disclaimer.
12
+ * * Redistributions in binary form must reproduce the above copyright
13
+ * notice, this list of conditions and the following disclaimer in the
14
+ * documentation and/or other materials provided with the distribution.
15
+ * * Neither the name of Redis nor the names of its contributors may be used
16
+ * to endorse or promote products derived from this software without
17
+ * specific prior written permission.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
+ * POSSIBILITY OF SUCH DAMAGE.
30
+ */
31
+
32
+
33
+ #include "fmacros.h"
34
+ #include <string.h>
35
+ #include <stdlib.h>
36
+ #include <unistd.h>
37
+ #include <assert.h>
38
+ #include <errno.h>
39
+ #include <ctype.h>
40
+
41
+ #include "read.h"
42
+ #include "sds.h"
43
+
44
+ static void __redisReaderSetError(redisReader *r, int type, const char *str) {
45
+ size_t len;
46
+
47
+ if (r->reply != NULL && r->fn && r->fn->freeObject) {
48
+ r->fn->freeObject(r->reply);
49
+ r->reply = NULL;
50
+ }
51
+
52
+ /* Clear input buffer on errors. */
53
+ if (r->buf != NULL) {
54
+ sdsfree(r->buf);
55
+ r->buf = NULL;
56
+ r->pos = r->len = 0;
57
+ }
58
+
59
+ /* Reset task stack. */
60
+ r->ridx = -1;
61
+
62
+ /* Set error. */
63
+ r->err = type;
64
+ len = strlen(str);
65
+ len = len < (sizeof(r->errstr)-1) ? len : (sizeof(r->errstr)-1);
66
+ memcpy(r->errstr,str,len);
67
+ r->errstr[len] = '\0';
68
+ }
69
+
70
+ static size_t chrtos(char *buf, size_t size, char byte) {
71
+ size_t len = 0;
72
+
73
+ switch(byte) {
74
+ case '\\':
75
+ case '"':
76
+ len = snprintf(buf,size,"\"\\%c\"",byte);
77
+ break;
78
+ case '\n': len = snprintf(buf,size,"\"\\n\""); break;
79
+ case '\r': len = snprintf(buf,size,"\"\\r\""); break;
80
+ case '\t': len = snprintf(buf,size,"\"\\t\""); break;
81
+ case '\a': len = snprintf(buf,size,"\"\\a\""); break;
82
+ case '\b': len = snprintf(buf,size,"\"\\b\""); break;
83
+ default:
84
+ if (isprint(byte))
85
+ len = snprintf(buf,size,"\"%c\"",byte);
86
+ else
87
+ len = snprintf(buf,size,"\"\\x%02x\"",(unsigned char)byte);
88
+ break;
89
+ }
90
+
91
+ return len;
92
+ }
93
+
94
+ static void __redisReaderSetErrorProtocolByte(redisReader *r, char byte) {
95
+ char cbuf[8], sbuf[128];
96
+
97
+ chrtos(cbuf,sizeof(cbuf),byte);
98
+ snprintf(sbuf,sizeof(sbuf),
99
+ "Protocol error, got %s as reply type byte", cbuf);
100
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,sbuf);
101
+ }
102
+
103
+ static void __redisReaderSetErrorOOM(redisReader *r) {
104
+ __redisReaderSetError(r,REDIS_ERR_OOM,"Out of memory");
105
+ }
106
+
107
+ static char *readBytes(redisReader *r, unsigned int bytes) {
108
+ char *p;
109
+ if (r->len-r->pos >= bytes) {
110
+ p = r->buf+r->pos;
111
+ r->pos += bytes;
112
+ return p;
113
+ }
114
+ return NULL;
115
+ }
116
+
117
+ /* Find pointer to \r\n. */
118
+ static char *seekNewline(char *s, size_t len) {
119
+ int pos = 0;
120
+ int _len = len-1;
121
+
122
+ /* Position should be < len-1 because the character at "pos" should be
123
+ * followed by a \n. Note that strchr cannot be used because it doesn't
124
+ * allow to search a limited length and the buffer that is being searched
125
+ * might not have a trailing NULL character. */
126
+ while (pos < _len) {
127
+ while(pos < _len && s[pos] != '\r') pos++;
128
+ if (s[pos] != '\r') {
129
+ /* Not found. */
130
+ return NULL;
131
+ } else {
132
+ if (s[pos+1] == '\n') {
133
+ /* Found. */
134
+ return s+pos;
135
+ } else {
136
+ /* Continue searching. */
137
+ pos++;
138
+ }
139
+ }
140
+ }
141
+ return NULL;
142
+ }
143
+
144
+ /* Read a long long value starting at *s, under the assumption that it will be
145
+ * terminated by \r\n. Ambiguously returns -1 for unexpected input. */
146
+ static long long readLongLong(char *s) {
147
+ long long v = 0;
148
+ int dec, mult = 1;
149
+ char c;
150
+
151
+ if (*s == '-') {
152
+ mult = -1;
153
+ s++;
154
+ } else if (*s == '+') {
155
+ mult = 1;
156
+ s++;
157
+ }
158
+
159
+ while ((c = *(s++)) != '\r') {
160
+ dec = c - '0';
161
+ if (dec >= 0 && dec < 10) {
162
+ v *= 10;
163
+ v += dec;
164
+ } else {
165
+ /* Should not happen... */
166
+ return -1;
167
+ }
168
+ }
169
+
170
+ return mult*v;
171
+ }
172
+
173
+ static char *readLine(redisReader *r, int *_len) {
174
+ char *p, *s;
175
+ int len;
176
+
177
+ p = r->buf+r->pos;
178
+ s = seekNewline(p,(r->len-r->pos));
179
+ if (s != NULL) {
180
+ len = s-(r->buf+r->pos);
181
+ r->pos += len+2; /* skip \r\n */
182
+ if (_len) *_len = len;
183
+ return p;
184
+ }
185
+ return NULL;
186
+ }
187
+
188
+ static void moveToNextTask(redisReader *r) {
189
+ redisReadTask *cur, *prv;
190
+ while (r->ridx >= 0) {
191
+ /* Return a.s.a.p. when the stack is now empty. */
192
+ if (r->ridx == 0) {
193
+ r->ridx--;
194
+ return;
195
+ }
196
+
197
+ cur = &(r->rstack[r->ridx]);
198
+ prv = &(r->rstack[r->ridx-1]);
199
+ assert(prv->type == REDIS_REPLY_ARRAY);
200
+ if (cur->idx == prv->elements-1) {
201
+ r->ridx--;
202
+ } else {
203
+ /* Reset the type because the next item can be anything */
204
+ assert(cur->idx < prv->elements);
205
+ cur->type = -1;
206
+ cur->elements = -1;
207
+ cur->idx++;
208
+ return;
209
+ }
210
+ }
211
+ }
212
+
213
+ static int processLineItem(redisReader *r) {
214
+ redisReadTask *cur = &(r->rstack[r->ridx]);
215
+ void *obj;
216
+ char *p;
217
+ int len;
218
+
219
+ if ((p = readLine(r,&len)) != NULL) {
220
+ if (cur->type == REDIS_REPLY_INTEGER) {
221
+ if (r->fn && r->fn->createInteger)
222
+ obj = r->fn->createInteger(cur,readLongLong(p));
223
+ else
224
+ obj = (void*)REDIS_REPLY_INTEGER;
225
+ } else {
226
+ /* Type will be error or status. */
227
+ if (r->fn && r->fn->createString)
228
+ obj = r->fn->createString(cur,p,len);
229
+ else
230
+ obj = (void*)(size_t)(cur->type);
231
+ }
232
+
233
+ if (obj == NULL) {
234
+ __redisReaderSetErrorOOM(r);
235
+ return REDIS_ERR;
236
+ }
237
+
238
+ /* Set reply if this is the root object. */
239
+ if (r->ridx == 0) r->reply = obj;
240
+ moveToNextTask(r);
241
+ return REDIS_OK;
242
+ }
243
+
244
+ return REDIS_ERR;
245
+ }
246
+
247
+ static int processBulkItem(redisReader *r) {
248
+ redisReadTask *cur = &(r->rstack[r->ridx]);
249
+ void *obj = NULL;
250
+ char *p, *s;
251
+ long len;
252
+ unsigned long bytelen;
253
+ int success = 0;
254
+
255
+ p = r->buf+r->pos;
256
+ s = seekNewline(p,r->len-r->pos);
257
+ if (s != NULL) {
258
+ p = r->buf+r->pos;
259
+ bytelen = s-(r->buf+r->pos)+2; /* include \r\n */
260
+ len = readLongLong(p);
261
+
262
+ if (len < 0) {
263
+ /* The nil object can always be created. */
264
+ if (r->fn && r->fn->createNil)
265
+ obj = r->fn->createNil(cur);
266
+ else
267
+ obj = (void*)REDIS_REPLY_NIL;
268
+ success = 1;
269
+ } else {
270
+ /* Only continue when the buffer contains the entire bulk item. */
271
+ bytelen += len+2; /* include \r\n */
272
+ if (r->pos+bytelen <= r->len) {
273
+ if (r->fn && r->fn->createString)
274
+ obj = r->fn->createString(cur,s+2,len);
275
+ else
276
+ obj = (void*)REDIS_REPLY_STRING;
277
+ success = 1;
278
+ }
279
+ }
280
+
281
+ /* Proceed when obj was created. */
282
+ if (success) {
283
+ if (obj == NULL) {
284
+ __redisReaderSetErrorOOM(r);
285
+ return REDIS_ERR;
286
+ }
287
+
288
+ r->pos += bytelen;
289
+
290
+ /* Set reply if this is the root object. */
291
+ if (r->ridx == 0) r->reply = obj;
292
+ moveToNextTask(r);
293
+ return REDIS_OK;
294
+ }
295
+ }
296
+
297
+ return REDIS_ERR;
298
+ }
299
+
300
+ static int processMultiBulkItem(redisReader *r) {
301
+ redisReadTask *cur = &(r->rstack[r->ridx]);
302
+ void *obj;
303
+ char *p;
304
+ long elements;
305
+ int root = 0;
306
+
307
+ /* Set error for nested multi bulks with depth > 7 */
308
+ if (r->ridx == 8) {
309
+ __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
310
+ "No support for nested multi bulk replies with depth > 7");
311
+ return REDIS_ERR;
312
+ }
313
+
314
+ if ((p = readLine(r,NULL)) != NULL) {
315
+ elements = readLongLong(p);
316
+ root = (r->ridx == 0);
317
+
318
+ if (elements == -1) {
319
+ if (r->fn && r->fn->createNil)
320
+ obj = r->fn->createNil(cur);
321
+ else
322
+ obj = (void*)REDIS_REPLY_NIL;
323
+
324
+ if (obj == NULL) {
325
+ __redisReaderSetErrorOOM(r);
326
+ return REDIS_ERR;
327
+ }
328
+
329
+ moveToNextTask(r);
330
+ } else {
331
+ if (r->fn && r->fn->createArray)
332
+ obj = r->fn->createArray(cur,elements);
333
+ else
334
+ obj = (void*)REDIS_REPLY_ARRAY;
335
+
336
+ if (obj == NULL) {
337
+ __redisReaderSetErrorOOM(r);
338
+ return REDIS_ERR;
339
+ }
340
+
341
+ /* Modify task stack when there are more than 0 elements. */
342
+ if (elements > 0) {
343
+ cur->elements = elements;
344
+ cur->obj = obj;
345
+ r->ridx++;
346
+ r->rstack[r->ridx].type = -1;
347
+ r->rstack[r->ridx].elements = -1;
348
+ r->rstack[r->ridx].idx = 0;
349
+ r->rstack[r->ridx].obj = NULL;
350
+ r->rstack[r->ridx].parent = cur;
351
+ r->rstack[r->ridx].privdata = r->privdata;
352
+ } else {
353
+ moveToNextTask(r);
354
+ }
355
+ }
356
+
357
+ /* Set reply if this is the root object. */
358
+ if (root) r->reply = obj;
359
+ return REDIS_OK;
360
+ }
361
+
362
+ return REDIS_ERR;
363
+ }
364
+
365
+ static int processItem(redisReader *r) {
366
+ redisReadTask *cur = &(r->rstack[r->ridx]);
367
+ char *p;
368
+
369
+ /* check if we need to read type */
370
+ if (cur->type < 0) {
371
+ if ((p = readBytes(r,1)) != NULL) {
372
+ switch (p[0]) {
373
+ case '-':
374
+ cur->type = REDIS_REPLY_ERROR;
375
+ break;
376
+ case '+':
377
+ cur->type = REDIS_REPLY_STATUS;
378
+ break;
379
+ case ':':
380
+ cur->type = REDIS_REPLY_INTEGER;
381
+ break;
382
+ case '$':
383
+ cur->type = REDIS_REPLY_STRING;
384
+ break;
385
+ case '*':
386
+ cur->type = REDIS_REPLY_ARRAY;
387
+ break;
388
+ default:
389
+ __redisReaderSetErrorProtocolByte(r,*p);
390
+ return REDIS_ERR;
391
+ }
392
+ } else {
393
+ /* could not consume 1 byte */
394
+ return REDIS_ERR;
395
+ }
396
+ }
397
+
398
+ /* process typed item */
399
+ switch(cur->type) {
400
+ case REDIS_REPLY_ERROR:
401
+ case REDIS_REPLY_STATUS:
402
+ case REDIS_REPLY_INTEGER:
403
+ return processLineItem(r);
404
+ case REDIS_REPLY_STRING:
405
+ return processBulkItem(r);
406
+ case REDIS_REPLY_ARRAY:
407
+ return processMultiBulkItem(r);
408
+ default:
409
+ assert(NULL);
410
+ return REDIS_ERR; /* Avoid warning. */
411
+ }
412
+ }
413
+
414
+ redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn) {
415
+ redisReader *r;
416
+
417
+ r = calloc(sizeof(redisReader),1);
418
+ if (r == NULL)
419
+ return NULL;
420
+
421
+ r->err = 0;
422
+ r->errstr[0] = '\0';
423
+ r->fn = fn;
424
+ r->buf = sdsempty();
425
+ r->maxbuf = REDIS_READER_MAX_BUF;
426
+ if (r->buf == NULL) {
427
+ free(r);
428
+ return NULL;
429
+ }
430
+
431
+ r->ridx = -1;
432
+ return r;
433
+ }
434
+
435
+ void redisReaderFree(redisReader *r) {
436
+ if (r->reply != NULL && r->fn && r->fn->freeObject)
437
+ r->fn->freeObject(r->reply);
438
+ if (r->buf != NULL)
439
+ sdsfree(r->buf);
440
+ free(r);
441
+ }
442
+
443
+ int redisReaderFeed(redisReader *r, const char *buf, size_t len) {
444
+ sds newbuf;
445
+
446
+ /* Return early when this reader is in an erroneous state. */
447
+ if (r->err)
448
+ return REDIS_ERR;
449
+
450
+ /* Copy the provided buffer. */
451
+ if (buf != NULL && len >= 1) {
452
+ /* Destroy internal buffer when it is empty and is quite large. */
453
+ if (r->len == 0 && r->maxbuf != 0 && sdsavail(r->buf) > r->maxbuf) {
454
+ sdsfree(r->buf);
455
+ r->buf = sdsempty();
456
+ r->pos = 0;
457
+
458
+ /* r->buf should not be NULL since we just free'd a larger one. */
459
+ assert(r->buf != NULL);
460
+ }
461
+
462
+ newbuf = sdscatlen(r->buf,buf,len);
463
+ if (newbuf == NULL) {
464
+ __redisReaderSetErrorOOM(r);
465
+ return REDIS_ERR;
466
+ }
467
+
468
+ r->buf = newbuf;
469
+ r->len = sdslen(r->buf);
470
+ }
471
+
472
+ return REDIS_OK;
473
+ }
474
+
475
+ int redisReaderGetReply(redisReader *r, void **reply) {
476
+ /* Default target pointer to NULL. */
477
+ if (reply != NULL)
478
+ *reply = NULL;
479
+
480
+ /* Return early when this reader is in an erroneous state. */
481
+ if (r->err)
482
+ return REDIS_ERR;
483
+
484
+ /* When the buffer is empty, there will never be a reply. */
485
+ if (r->len == 0)
486
+ return REDIS_OK;
487
+
488
+ /* Set first item to process when the stack is empty. */
489
+ if (r->ridx == -1) {
490
+ r->rstack[0].type = -1;
491
+ r->rstack[0].elements = -1;
492
+ r->rstack[0].idx = -1;
493
+ r->rstack[0].obj = NULL;
494
+ r->rstack[0].parent = NULL;
495
+ r->rstack[0].privdata = r->privdata;
496
+ r->ridx = 0;
497
+ }
498
+
499
+ /* Process items in reply. */
500
+ while (r->ridx >= 0)
501
+ if (processItem(r) != REDIS_OK)
502
+ break;
503
+
504
+ /* Return ASAP when an error occurred. */
505
+ if (r->err)
506
+ return REDIS_ERR;
507
+
508
+ /* Discard part of the buffer when we've consumed at least 1k, to avoid
509
+ * doing unnecessary calls to memmove() in sds.c. */
510
+ if (r->pos >= 1024) {
511
+ sdsrange(r->buf,r->pos,-1);
512
+ r->pos = 0;
513
+ r->len = sdslen(r->buf);
514
+ }
515
+
516
+ /* Emit a reply when there is one. */
517
+ if (r->ridx == -1) {
518
+ if (reply != NULL)
519
+ *reply = r->reply;
520
+ r->reply = NULL;
521
+ }
522
+ return REDIS_OK;
523
+ }