]> git.saurik.com Git - redis.git/blob - src/networking.c
Wrapper for adding unknown multi bulk length to reply list
[redis.git] / src / networking.c
1 #include "redis.h"
2 #include <sys/uio.h>
3
4 void *dupClientReplyValue(void *o) {
5 incrRefCount((robj*)o);
6 return o;
7 }
8
9 int listMatchObjects(void *a, void *b) {
10 return equalStringObjects(a,b);
11 }
12
13 redisClient *createClient(int fd) {
14 redisClient *c;
15
16 /* Make sure to allocate a multiple of the page size to prevent wasting
17 * memory. A page size of 4096 is assumed here. We need to compensate
18 * for the zmalloc overhead of sizeof(size_t) bytes. */
19 size_t size = 8192-sizeof(size_t);
20 redisAssert(size > sizeof(redisClient));
21 c = zmalloc(size);
22 c->buflen = size-sizeof(redisClient);
23 c->bufpos = 0;
24
25 anetNonBlock(NULL,fd);
26 anetTcpNoDelay(NULL,fd);
27 if (!c) return NULL;
28 selectDb(c,0);
29 c->fd = fd;
30 c->querybuf = sdsempty();
31 c->argc = 0;
32 c->argv = NULL;
33 c->bulklen = -1;
34 c->multibulk = 0;
35 c->mbargc = 0;
36 c->mbargv = NULL;
37 c->sentlen = 0;
38 c->flags = 0;
39 c->lastinteraction = time(NULL);
40 c->authenticated = 0;
41 c->replstate = REDIS_REPL_NONE;
42 c->reply = listCreate();
43 listSetFreeMethod(c->reply,decrRefCount);
44 listSetDupMethod(c->reply,dupClientReplyValue);
45 c->blocking_keys = NULL;
46 c->blocking_keys_num = 0;
47 c->io_keys = listCreate();
48 c->watched_keys = listCreate();
49 listSetFreeMethod(c->io_keys,decrRefCount);
50 c->pubsub_channels = dictCreate(&setDictType,NULL);
51 c->pubsub_patterns = listCreate();
52 listSetFreeMethod(c->pubsub_patterns,decrRefCount);
53 listSetMatchMethod(c->pubsub_patterns,listMatchObjects);
54 if (aeCreateFileEvent(server.el, c->fd, AE_READABLE,
55 readQueryFromClient, c) == AE_ERR) {
56 freeClient(c);
57 return NULL;
58 }
59 listAddNodeTail(server.clients,c);
60 initClientMultiState(c);
61 return c;
62 }
63
64 int _ensureFileEvent(redisClient *c) {
65 if (c->bufpos == 0 && listLength(c->reply) == 0 &&
66 (c->replstate == REDIS_REPL_NONE ||
67 c->replstate == REDIS_REPL_ONLINE) &&
68 aeCreateFileEvent(server.el, c->fd, AE_WRITABLE,
69 sendReplyToClient, c) == AE_ERR) return REDIS_ERR;
70 return REDIS_OK;
71 }
72
73 void _addReplyObjectToList(redisClient *c, robj *obj) {
74 redisAssert(obj->type == REDIS_STRING &&
75 obj->encoding == REDIS_ENCODING_RAW);
76 listAddNodeTail(c->reply,obj);
77 }
78
79 void _ensureBufferInReplyList(redisClient *c) {
80 sds buffer = sdsnewlen(NULL,REDIS_REPLY_CHUNK_SIZE);
81 sdsupdatelen(buffer); /* sdsnewlen expects non-empty string */
82 listAddNodeTail(c->reply,createObject(REDIS_REPLY_NODE,buffer));
83 }
84
85 void _addReplyStringToBuffer(redisClient *c, char *s, size_t len) {
86 size_t available = 0;
87 redisAssert(len < REDIS_REPLY_CHUNK_THRESHOLD);
88 if (listLength(c->reply) > 0) {
89 robj *o = listNodeValue(listLast(c->reply));
90
91 /* Make sure to append to a reply node with enough bytes available. */
92 if (o->type == REDIS_REPLY_NODE) available = sdsavail(o->ptr);
93 if (o->type != REDIS_REPLY_NODE || len > available) {
94 _ensureBufferInReplyList(c);
95 _addReplyStringToBuffer(c,s,len);
96 } else {
97 o->ptr = sdscatlen(o->ptr,s,len);
98 }
99 } else {
100 available = c->buflen-c->bufpos;
101 if (len > available) {
102 _ensureBufferInReplyList(c);
103 _addReplyStringToBuffer(c,s,len);
104 } else {
105 memcpy(c->buf+c->bufpos,s,len);
106 c->bufpos += len;
107 }
108 }
109 }
110
111 void addReply(redisClient *c, robj *obj) {
112 if (_ensureFileEvent(c) != REDIS_OK) return;
113 if (server.vm_enabled && obj->storage != REDIS_VM_MEMORY) {
114 /* Returns a new object with refcount 1 */
115 obj = dupStringObject(obj);
116 } else {
117 /* This increments the refcount. */
118 obj = getDecodedObject(obj);
119 }
120
121 if (sdslen(obj->ptr) < REDIS_REPLY_CHUNK_THRESHOLD) {
122 _addReplyStringToBuffer(c,obj->ptr,sdslen(obj->ptr));
123 decrRefCount(obj);
124 } else {
125 _addReplyObjectToList(c,obj);
126 }
127 }
128
129 void addReplySds(redisClient *c, sds s) {
130 if (_ensureFileEvent(c) != REDIS_OK) return;
131 if (sdslen(s) < REDIS_REPLY_CHUNK_THRESHOLD) {
132 _addReplyStringToBuffer(c,s,sdslen(s));
133 sdsfree(s);
134 } else {
135 _addReplyObjectToList(c,createObject(REDIS_STRING,s));
136 }
137 }
138
139 void addReplyString(redisClient *c, char *s, size_t len) {
140 if (_ensureFileEvent(c) != REDIS_OK) return;
141 if (len < REDIS_REPLY_CHUNK_THRESHOLD) {
142 _addReplyStringToBuffer(c,s,len);
143 } else {
144 _addReplyObjectToList(c,createStringObject(s,len));
145 }
146 }
147
148 /* Adds an empty object to the reply list that will contain the multi bulk
149 * length, which is not known when this function is called. */
150 void *addDeferredMultiBulkLength(redisClient *c) {
151 if (_ensureFileEvent(c) != REDIS_OK) return NULL;
152 _addReplyObjectToList(c,createObject(REDIS_STRING,NULL));
153 return listLast(c->reply);
154 }
155
156 /* Populate the length object and try glueing it to the next chunk. */
157 void setDeferredMultiBulkLength(redisClient *c, void *node, long length) {
158 listNode *ln = (listNode*)node;
159 robj *len, *next;
160
161 /* Abort when *node is NULL (see addDeferredMultiBulkLength). */
162 if (node == NULL) return;
163
164 len = listNodeValue(ln);
165 len->ptr = sdscatprintf(sdsempty(),"*%ld\r\n",length);
166 if (ln->next != NULL) {
167 next = listNodeValue(ln->next);
168 /* Only glue when the next node is a reply chunk. */
169 if (next->type == REDIS_REPLY_NODE) {
170 len->ptr = sdscatlen(len->ptr,next->ptr,sdslen(next->ptr));
171 listDelNode(c->reply,ln->next);
172 }
173 }
174 }
175
176 void addReplyDouble(redisClient *c, double d) {
177 char dbuf[128], sbuf[128];
178 int dlen, slen;
179 dlen = snprintf(dbuf,sizeof(dbuf),"%.17g",d);
180 slen = snprintf(sbuf,sizeof(sbuf),"$%d\r\n%s\r\n",dlen,dbuf);
181 addReplyString(c,sbuf,slen);
182 }
183
184 void _addReplyLongLong(redisClient *c, long long ll, char prefix) {
185 char buf[128];
186 int len;
187 buf[0] = prefix;
188 len = ll2string(buf+1,sizeof(buf)-1,ll);
189 buf[len+1] = '\r';
190 buf[len+2] = '\n';
191 addReplyString(c,buf,len+3);
192 }
193
194 void addReplyLongLong(redisClient *c, long long ll) {
195 _addReplyLongLong(c,ll,':');
196 }
197
198 void addReplyUlong(redisClient *c, unsigned long ul) {
199 _addReplyLongLong(c,(long long)ul,':');
200 }
201
202 void addReplyBulkLen(redisClient *c, robj *obj) {
203 size_t len;
204
205 if (obj->encoding == REDIS_ENCODING_RAW) {
206 len = sdslen(obj->ptr);
207 } else {
208 long n = (long)obj->ptr;
209
210 /* Compute how many bytes will take this integer as a radix 10 string */
211 len = 1;
212 if (n < 0) {
213 len++;
214 n = -n;
215 }
216 while((n = n/10) != 0) {
217 len++;
218 }
219 }
220 _addReplyLongLong(c,len,'$');
221 }
222
223 void addReplyBulk(redisClient *c, robj *obj) {
224 addReplyBulkLen(c,obj);
225 addReply(c,obj);
226 addReply(c,shared.crlf);
227 }
228
229 /* In the CONFIG command we need to add vanilla C string as bulk replies */
230 void addReplyBulkCString(redisClient *c, char *s) {
231 if (s == NULL) {
232 addReply(c,shared.nullbulk);
233 } else {
234 robj *o = createStringObject(s,strlen(s));
235 addReplyBulk(c,o);
236 decrRefCount(o);
237 }
238 }
239
240 void acceptHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
241 int cport, cfd;
242 char cip[128];
243 redisClient *c;
244 REDIS_NOTUSED(el);
245 REDIS_NOTUSED(mask);
246 REDIS_NOTUSED(privdata);
247
248 cfd = anetAccept(server.neterr, fd, cip, &cport);
249 if (cfd == AE_ERR) {
250 redisLog(REDIS_VERBOSE,"Accepting client connection: %s", server.neterr);
251 return;
252 }
253 redisLog(REDIS_VERBOSE,"Accepted %s:%d", cip, cport);
254 if ((c = createClient(cfd)) == NULL) {
255 redisLog(REDIS_WARNING,"Error allocating resoures for the client");
256 close(cfd); /* May be already closed, just ingore errors */
257 return;
258 }
259 /* If maxclient directive is set and this is one client more... close the
260 * connection. Note that we create the client instead to check before
261 * for this condition, since now the socket is already set in nonblocking
262 * mode and we can send an error for free using the Kernel I/O */
263 if (server.maxclients && listLength(server.clients) > server.maxclients) {
264 char *err = "-ERR max number of clients reached\r\n";
265
266 /* That's a best effort error message, don't check write errors */
267 if (write(c->fd,err,strlen(err)) == -1) {
268 /* Nothing to do, Just to avoid the warning... */
269 }
270 freeClient(c);
271 return;
272 }
273 server.stat_numconnections++;
274 }
275
276 static void freeClientArgv(redisClient *c) {
277 int j;
278
279 for (j = 0; j < c->argc; j++)
280 decrRefCount(c->argv[j]);
281 for (j = 0; j < c->mbargc; j++)
282 decrRefCount(c->mbargv[j]);
283 c->argc = 0;
284 c->mbargc = 0;
285 }
286
287 void freeClient(redisClient *c) {
288 listNode *ln;
289
290 /* Note that if the client we are freeing is blocked into a blocking
291 * call, we have to set querybuf to NULL *before* to call
292 * unblockClientWaitingData() to avoid processInputBuffer() will get
293 * called. Also it is important to remove the file events after
294 * this, because this call adds the READABLE event. */
295 sdsfree(c->querybuf);
296 c->querybuf = NULL;
297 if (c->flags & REDIS_BLOCKED)
298 unblockClientWaitingData(c);
299
300 /* UNWATCH all the keys */
301 unwatchAllKeys(c);
302 listRelease(c->watched_keys);
303 /* Unsubscribe from all the pubsub channels */
304 pubsubUnsubscribeAllChannels(c,0);
305 pubsubUnsubscribeAllPatterns(c,0);
306 dictRelease(c->pubsub_channels);
307 listRelease(c->pubsub_patterns);
308 /* Obvious cleanup */
309 aeDeleteFileEvent(server.el,c->fd,AE_READABLE);
310 aeDeleteFileEvent(server.el,c->fd,AE_WRITABLE);
311 listRelease(c->reply);
312 freeClientArgv(c);
313 close(c->fd);
314 /* Remove from the list of clients */
315 ln = listSearchKey(server.clients,c);
316 redisAssert(ln != NULL);
317 listDelNode(server.clients,ln);
318 /* Remove from the list of clients waiting for swapped keys, or ready
319 * to be restarted, but not yet woken up again. */
320 if (c->flags & REDIS_IO_WAIT) {
321 redisAssert(server.vm_enabled);
322 if (listLength(c->io_keys) == 0) {
323 ln = listSearchKey(server.io_ready_clients,c);
324
325 /* When this client is waiting to be woken up (REDIS_IO_WAIT),
326 * it should be present in the list io_ready_clients */
327 redisAssert(ln != NULL);
328 listDelNode(server.io_ready_clients,ln);
329 } else {
330 while (listLength(c->io_keys)) {
331 ln = listFirst(c->io_keys);
332 dontWaitForSwappedKey(c,ln->value);
333 }
334 }
335 server.vm_blocked_clients--;
336 }
337 listRelease(c->io_keys);
338 /* Master/slave cleanup.
339 * Case 1: we lost the connection with a slave. */
340 if (c->flags & REDIS_SLAVE) {
341 if (c->replstate == REDIS_REPL_SEND_BULK && c->repldbfd != -1)
342 close(c->repldbfd);
343 list *l = (c->flags & REDIS_MONITOR) ? server.monitors : server.slaves;
344 ln = listSearchKey(l,c);
345 redisAssert(ln != NULL);
346 listDelNode(l,ln);
347 }
348
349 /* Case 2: we lost the connection with the master. */
350 if (c->flags & REDIS_MASTER) {
351 server.master = NULL;
352 server.replstate = REDIS_REPL_CONNECT;
353 /* Since we lost the connection with the master, we should also
354 * close the connection with all our slaves if we have any, so
355 * when we'll resync with the master the other slaves will sync again
356 * with us as well. Note that also when the slave is not connected
357 * to the master it will keep refusing connections by other slaves. */
358 while (listLength(server.slaves)) {
359 ln = listFirst(server.slaves);
360 freeClient((redisClient*)ln->value);
361 }
362 }
363 /* Release memory */
364 zfree(c->argv);
365 zfree(c->mbargv);
366 freeClientMultiState(c);
367 zfree(c);
368 }
369
370 void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask) {
371 redisClient *c = privdata;
372 int nwritten = 0, totwritten = 0, objlen;
373 robj *o;
374 REDIS_NOTUSED(el);
375 REDIS_NOTUSED(mask);
376
377 /* Use writev() if we have enough buffers to send */
378 if (!server.glueoutputbuf &&
379 listLength(c->reply) > REDIS_WRITEV_THRESHOLD &&
380 !(c->flags & REDIS_MASTER))
381 {
382 sendReplyToClientWritev(el, fd, privdata, mask);
383 return;
384 }
385
386 while(c->bufpos > 0 || listLength(c->reply)) {
387 if (c->bufpos > 0) {
388 if (c->flags & REDIS_MASTER) {
389 /* Don't reply to a master */
390 nwritten = c->bufpos - c->sentlen;
391 } else {
392 nwritten = write(fd,c->buf+c->sentlen,c->bufpos-c->sentlen);
393 if (nwritten <= 0) break;
394 }
395 c->sentlen += nwritten;
396 totwritten += nwritten;
397
398 /* If the buffer was sent, set bufpos to zero to continue with
399 * the remainder of the reply. */
400 if (c->sentlen == c->bufpos) {
401 c->bufpos = 0;
402 c->sentlen = 0;
403 }
404 } else {
405 o = listNodeValue(listFirst(c->reply));
406 objlen = sdslen(o->ptr);
407
408 if (objlen == 0) {
409 listDelNode(c->reply,listFirst(c->reply));
410 continue;
411 }
412
413 if (c->flags & REDIS_MASTER) {
414 /* Don't reply to a master */
415 nwritten = objlen - c->sentlen;
416 } else {
417 nwritten = write(fd, ((char*)o->ptr)+c->sentlen,objlen-c->sentlen);
418 if (nwritten <= 0) break;
419 }
420 c->sentlen += nwritten;
421 totwritten += nwritten;
422
423 /* If we fully sent the object on head go to the next one */
424 if (c->sentlen == objlen) {
425 listDelNode(c->reply,listFirst(c->reply));
426 c->sentlen = 0;
427 }
428 }
429 /* Note that we avoid to send more thank REDIS_MAX_WRITE_PER_EVENT
430 * bytes, in a single threaded server it's a good idea to serve
431 * other clients as well, even if a very large request comes from
432 * super fast link that is always able to accept data (in real world
433 * scenario think about 'KEYS *' against the loopback interfae) */
434 if (totwritten > REDIS_MAX_WRITE_PER_EVENT) break;
435 }
436 if (nwritten == -1) {
437 if (errno == EAGAIN) {
438 nwritten = 0;
439 } else {
440 redisLog(REDIS_VERBOSE,
441 "Error writing to client: %s", strerror(errno));
442 freeClient(c);
443 return;
444 }
445 }
446 if (totwritten > 0) c->lastinteraction = time(NULL);
447 if (listLength(c->reply) == 0) {
448 c->sentlen = 0;
449 aeDeleteFileEvent(server.el,c->fd,AE_WRITABLE);
450 }
451 }
452
453 void sendReplyToClientWritev(aeEventLoop *el, int fd, void *privdata, int mask)
454 {
455 redisClient *c = privdata;
456 int nwritten = 0, totwritten = 0, objlen, willwrite;
457 robj *o;
458 struct iovec iov[REDIS_WRITEV_IOVEC_COUNT];
459 int offset, ion = 0;
460 REDIS_NOTUSED(el);
461 REDIS_NOTUSED(mask);
462
463 listNode *node;
464 while (listLength(c->reply)) {
465 offset = c->sentlen;
466 ion = 0;
467 willwrite = 0;
468
469 /* fill-in the iov[] array */
470 for(node = listFirst(c->reply); node; node = listNextNode(node)) {
471 o = listNodeValue(node);
472 objlen = sdslen(o->ptr);
473
474 if (totwritten + objlen - offset > REDIS_MAX_WRITE_PER_EVENT)
475 break;
476
477 if(ion == REDIS_WRITEV_IOVEC_COUNT)
478 break; /* no more iovecs */
479
480 iov[ion].iov_base = ((char*)o->ptr) + offset;
481 iov[ion].iov_len = objlen - offset;
482 willwrite += objlen - offset;
483 offset = 0; /* just for the first item */
484 ion++;
485 }
486
487 if(willwrite == 0)
488 break;
489
490 /* write all collected blocks at once */
491 if((nwritten = writev(fd, iov, ion)) < 0) {
492 if (errno != EAGAIN) {
493 redisLog(REDIS_VERBOSE,
494 "Error writing to client: %s", strerror(errno));
495 freeClient(c);
496 return;
497 }
498 break;
499 }
500
501 totwritten += nwritten;
502 offset = c->sentlen;
503
504 /* remove written robjs from c->reply */
505 while (nwritten && listLength(c->reply)) {
506 o = listNodeValue(listFirst(c->reply));
507 objlen = sdslen(o->ptr);
508
509 if(nwritten >= objlen - offset) {
510 listDelNode(c->reply, listFirst(c->reply));
511 nwritten -= objlen - offset;
512 c->sentlen = 0;
513 } else {
514 /* partial write */
515 c->sentlen += nwritten;
516 break;
517 }
518 offset = 0;
519 }
520 }
521
522 if (totwritten > 0)
523 c->lastinteraction = time(NULL);
524
525 if (listLength(c->reply) == 0) {
526 c->sentlen = 0;
527 aeDeleteFileEvent(server.el,c->fd,AE_WRITABLE);
528 }
529 }
530
531 /* resetClient prepare the client to process the next command */
532 void resetClient(redisClient *c) {
533 freeClientArgv(c);
534 c->bulklen = -1;
535 c->multibulk = 0;
536 }
537
538 void closeTimedoutClients(void) {
539 redisClient *c;
540 listNode *ln;
541 time_t now = time(NULL);
542 listIter li;
543
544 listRewind(server.clients,&li);
545 while ((ln = listNext(&li)) != NULL) {
546 c = listNodeValue(ln);
547 if (server.maxidletime &&
548 !(c->flags & REDIS_SLAVE) && /* no timeout for slaves */
549 !(c->flags & REDIS_MASTER) && /* no timeout for masters */
550 !(c->flags & REDIS_BLOCKED) && /* no timeout for BLPOP */
551 dictSize(c->pubsub_channels) == 0 && /* no timeout for pubsub */
552 listLength(c->pubsub_patterns) == 0 &&
553 (now - c->lastinteraction > server.maxidletime))
554 {
555 redisLog(REDIS_VERBOSE,"Closing idle client");
556 freeClient(c);
557 } else if (c->flags & REDIS_BLOCKED) {
558 if (c->blockingto != 0 && c->blockingto < now) {
559 addReply(c,shared.nullmultibulk);
560 unblockClientWaitingData(c);
561 }
562 }
563 }
564 }
565
566 void processInputBuffer(redisClient *c) {
567 again:
568 /* Before to process the input buffer, make sure the client is not
569 * waitig for a blocking operation such as BLPOP. Note that the first
570 * iteration the client is never blocked, otherwise the processInputBuffer
571 * would not be called at all, but after the execution of the first commands
572 * in the input buffer the client may be blocked, and the "goto again"
573 * will try to reiterate. The following line will make it return asap. */
574 if (c->flags & REDIS_BLOCKED || c->flags & REDIS_IO_WAIT) return;
575 if (c->bulklen == -1) {
576 /* Read the first line of the query */
577 char *p = strchr(c->querybuf,'\n');
578 size_t querylen;
579
580 if (p) {
581 sds query, *argv;
582 int argc, j;
583
584 query = c->querybuf;
585 c->querybuf = sdsempty();
586 querylen = 1+(p-(query));
587 if (sdslen(query) > querylen) {
588 /* leave data after the first line of the query in the buffer */
589 c->querybuf = sdscatlen(c->querybuf,query+querylen,sdslen(query)-querylen);
590 }
591 *p = '\0'; /* remove "\n" */
592 if (*(p-1) == '\r') *(p-1) = '\0'; /* and "\r" if any */
593 sdsupdatelen(query);
594
595 /* Now we can split the query in arguments */
596 argv = sdssplitlen(query,sdslen(query)," ",1,&argc);
597 sdsfree(query);
598
599 if (c->argv) zfree(c->argv);
600 c->argv = zmalloc(sizeof(robj*)*argc);
601
602 for (j = 0; j < argc; j++) {
603 if (sdslen(argv[j])) {
604 c->argv[c->argc] = createObject(REDIS_STRING,argv[j]);
605 c->argc++;
606 } else {
607 sdsfree(argv[j]);
608 }
609 }
610 zfree(argv);
611 if (c->argc) {
612 /* Execute the command. If the client is still valid
613 * after processCommand() return and there is something
614 * on the query buffer try to process the next command. */
615 if (processCommand(c) && sdslen(c->querybuf)) goto again;
616 } else {
617 /* Nothing to process, argc == 0. Just process the query
618 * buffer if it's not empty or return to the caller */
619 if (sdslen(c->querybuf)) goto again;
620 }
621 return;
622 } else if (sdslen(c->querybuf) >= REDIS_REQUEST_MAX_SIZE) {
623 redisLog(REDIS_VERBOSE, "Client protocol error");
624 freeClient(c);
625 return;
626 }
627 } else {
628 /* Bulk read handling. Note that if we are at this point
629 the client already sent a command terminated with a newline,
630 we are reading the bulk data that is actually the last
631 argument of the command. */
632 int qbl = sdslen(c->querybuf);
633
634 if (c->bulklen <= qbl) {
635 /* Copy everything but the final CRLF as final argument */
636 c->argv[c->argc] = createStringObject(c->querybuf,c->bulklen-2);
637 c->argc++;
638 c->querybuf = sdsrange(c->querybuf,c->bulklen,-1);
639 /* Process the command. If the client is still valid after
640 * the processing and there is more data in the buffer
641 * try to parse it. */
642 if (processCommand(c) && sdslen(c->querybuf)) goto again;
643 return;
644 }
645 }
646 }
647
648 void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
649 redisClient *c = (redisClient*) privdata;
650 char buf[REDIS_IOBUF_LEN];
651 int nread;
652 REDIS_NOTUSED(el);
653 REDIS_NOTUSED(mask);
654
655 nread = read(fd, buf, REDIS_IOBUF_LEN);
656 if (nread == -1) {
657 if (errno == EAGAIN) {
658 nread = 0;
659 } else {
660 redisLog(REDIS_VERBOSE, "Reading from client: %s",strerror(errno));
661 freeClient(c);
662 return;
663 }
664 } else if (nread == 0) {
665 redisLog(REDIS_VERBOSE, "Client closed connection");
666 freeClient(c);
667 return;
668 }
669 if (nread) {
670 c->querybuf = sdscatlen(c->querybuf, buf, nread);
671 c->lastinteraction = time(NULL);
672 } else {
673 return;
674 }
675 processInputBuffer(c);
676 }