465b73969766432963007dfabe7ea9e4e42b2c43
[awesomized/libmemcached] / libmemcached / protocol / ascii_handler.c
1 /* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */
2 #include "libmemcached/protocol/common.h"
3
4 #include <ctype.h>
5 #include <string.h>
6 #include <strings.h>
7 #include <stdio.h>
8
9 /**
10 * Try to parse a key from the string.
11 * @pointer start pointer to a pointer to the string (IN and OUT)
12 * @return length of the string of -1 if this was an illegal key (invalid
13 * characters or invalid length)
14 * @todo add length!
15 */
16 static uint16_t parse_ascii_key(char **start)
17 {
18 uint16_t len= 0;
19 char *c= *start;
20 /* Strip leading whitespaces */
21 while (isspace(*c))
22 {
23 ++c;
24 }
25
26 *start= c;
27
28 while (*c != '\0' && !isspace(*c) && !iscntrl(*c))
29 {
30 ++c;
31 ++len;
32 }
33
34
35 if (len == 0 || len > 240 || (*c != '\0' && *c != '\r' && iscntrl(*c)))
36 {
37 return 0;
38 }
39
40 return len;
41 }
42
43 /**
44 * Spool a zero-terminated string
45 * @param client destination
46 * @param text the text to spool
47 * @return status of the spool operation
48 */
49 static protocol_binary_response_status
50 spool_string(memcached_protocol_client_st *client, const char *text)
51 {
52 return client->root->spool(client, text, strlen(text));
53 }
54
55 /**
56 * Send a "CLIENT_ERROR" message back to the client with the correct
57 * format of the command being sent
58 * @param client the client to send the message to
59 */
60 static void send_command_usage(memcached_protocol_client_st *client)
61 {
62 const char *errmsg[]= {
63 [GET_CMD]= "CLIENT_ERROR: Syntax error: get <key>*\r\n",
64 [GETS_CMD]= "CLIENT_ERROR: Syntax error: gets <key>*\r\n",
65 [SET_CMD]= "CLIENT_ERROR: Syntax error: set <key> <flags> <exptime> <bytes> [noreply]\r\n",
66 [ADD_CMD]= "CLIENT_ERROR: Syntax error: add <key> <flags> <exptime> <bytes> [noreply]\r\n",
67 [REPLACE_CMD]= "CLIENT_ERROR: Syntax error: replace <key> <flags> <exptime> <bytes> [noreply]\r\n",
68 [CAS_CMD]= "CLIENT_ERROR: Syntax error: cas <key> <flags> <exptime> <bytes> <casid> [noreply]\r\n",
69 [APPEND_CMD]= "CLIENT_ERROR: Syntax error: append <key> <flags> <exptime> <bytes> [noreply]\r\n",
70 [PREPEND_CMD]= "CLIENT_ERROR: Syntax error: prepend <key> <flags> <exptime> <bytes> [noreply]\r\n",
71 [DELETE_CMD]= "CLIENT_ERROR: Syntax error: delete <key> [noreply]\r\n",
72 [INCR_CMD]= "CLIENT_ERROR: Syntax error: incr <key> <value> [noreply]\r\n",
73 [DECR_CMD]= "CLIENT_ERROR: Syntax error: decr <key> <value> [noreply]\r\n",
74 [STATS_CMD]= "CLIENT_ERROR: Syntax error: stats [key]\r\n",
75 [FLUSH_ALL_CMD]= "CLIENT_ERROR: Syntax error: flush_all [timeout] [noreply]\r\n",
76 [VERSION_CMD]= "CLIENT_ERROR: Syntax error: version\r\n",
77 [QUIT_CMD]="CLIENT_ERROR: Syntax error: quit\r\n",
78
79 [VERBOSITY_CMD]= "CLIENT_ERROR: Syntax error: verbosity <num>\r\n",
80 [UNKNOWN_CMD]= "CLIENT_ERROR: Unknown command\r\n",
81 };
82
83 client->mute = false;
84 spool_string(client, errmsg[client->ascii_command]);
85 }
86
87 /**
88 * Callback for the VERSION responses
89 * @param cookie client identifier
90 * @param text the length of the body
91 * @param textlen the length of the body
92 */
93 static protocol_binary_response_status
94 ascii_version_response_handler(const void *cookie,
95 const void *text,
96 uint32_t textlen)
97 {
98 memcached_protocol_client_st *client= (memcached_protocol_client_st*)cookie;
99 spool_string(client, "VERSION ");
100 client->root->spool(client, text, textlen);
101 spool_string(client, "\r\n");
102 return PROTOCOL_BINARY_RESPONSE_SUCCESS;
103 }
104
105 /**
106 * Callback for the GET/GETQ/GETK and GETKQ responses
107 * @param cookie client identifier
108 * @param key the key for the item
109 * @param keylen the length of the key
110 * @param body the length of the body
111 * @param bodylen the length of the body
112 * @param flags the flags for the item
113 * @param cas the CAS id for the item
114 */
115 static protocol_binary_response_status
116 ascii_get_response_handler(const void *cookie,
117 const void *key,
118 uint16_t keylen,
119 const void *body,
120 uint32_t bodylen,
121 uint32_t flags,
122 uint64_t cas)
123 {
124 memcached_protocol_client_st *client= (void*)cookie;
125 char buffer[300];
126 strcpy(buffer, "VALUE ");
127 const char *source= key;
128 char *dest= buffer + 6;
129
130 for (int x= 0; x < keylen; ++x)
131 {
132 if (*source != '\0' && !isspace(*source) && !iscntrl(*source))
133 {
134 *dest= *source;
135 }
136 else
137 {
138 return PROTOCOL_BINARY_RESPONSE_EINVAL; /* key constraints in ascii */
139 }
140
141 ++dest;
142 ++source;
143 }
144
145 size_t used= (size_t)(dest - buffer);
146
147 if (client->ascii_command == GETS_CMD)
148 {
149 snprintf(dest, sizeof(buffer) - used, " %u %u %" PRIu64 "\r\n", flags,
150 bodylen, cas);
151 }
152 else
153 {
154 snprintf(dest, sizeof(buffer) - used, " %u %u\r\n", flags, bodylen);
155 }
156
157 client->root->spool(client, buffer, strlen(buffer));
158 client->root->spool(client, body, bodylen);
159 client->root->spool(client, "\r\n", 2);
160
161 return PROTOCOL_BINARY_RESPONSE_SUCCESS;
162 }
163
164 /**
165 * Callback for the STAT responses
166 * @param cookie client identifier
167 * @param key the key for the item
168 * @param keylen the length of the key
169 * @param body the length of the body
170 * @param bodylen the length of the body
171 */
172 static protocol_binary_response_status
173 ascii_stat_response_handler(const void *cookie,
174 const void *key,
175 uint16_t keylen,
176 const void *body,
177 uint32_t bodylen)
178 {
179
180 memcached_protocol_client_st *client= (void*)cookie;
181
182 if (key != NULL)
183 {
184 spool_string(client, "STAT ");
185 client->root->spool(client, key, keylen);
186 spool_string(client, " ");
187 client->root->spool(client, body, bodylen);
188 spool_string(client, "\r\n");
189 }
190 else
191 {
192 spool_string(client, "END\r\n");
193 }
194
195 return PROTOCOL_BINARY_RESPONSE_SUCCESS;
196 }
197
198 /**
199 * Process a get or a gets request.
200 * @param client the client handle
201 * @param buffer the complete get(s) command
202 * @param end the last character in the command
203 */
204 static void ascii_process_gets(memcached_protocol_client_st *client,
205 char *buffer, char *end)
206 {
207 char *key= buffer;
208
209 /* Skip command */
210 key += (client->ascii_command == GETS_CMD) ? 5 : 4;
211
212 int num_keys= 0;
213 while (key < end)
214 {
215 uint16_t nkey= parse_ascii_key(&key);
216 if (nkey == 0) /* Invalid key... stop processing this line */
217 {
218 break;
219 }
220
221 (void)client->root->callback->interface.v1.get(client, key, nkey,
222 ascii_get_response_handler);
223 key += nkey;
224 ++num_keys;
225 }
226
227 if (num_keys == 0)
228 {
229 send_command_usage(client);
230 }
231 else
232 client->root->spool(client, "END\r\n", 5);
233 }
234
235 /**
236 * Try to split up the command line "asdf asdf asdf asdf\n" into an
237 * argument vector for easier parsing.
238 * @param start the first character in the command line
239 * @param end the last character in the command line ("\n")
240 * @param vec the vector to insert the pointers into
241 * @size the number of elements in the vector
242 * @return the number of tokens in the vector
243 */
244 static int ascii_tokenize_command(char *str, char *end, char **vec, int size)
245 {
246 int elem= 0;
247
248 while (str < end)
249 {
250 /* Skip leading blanks */
251 while (str < end && isspace(*str))
252 {
253 ++str;
254 }
255
256 if (str == end)
257 {
258 return elem;
259 }
260
261 vec[elem++]= str;
262 /* find the next non-blank field */
263 while (str < end && !isspace(*str))
264 {
265 ++str;
266 }
267
268 /* zero-terminate it for easier parsing later on */
269 *str= '\0';
270 ++str;
271
272 /* Is the vector full? */
273 if (elem == size)
274 {
275 break;
276 }
277 }
278
279 return elem;
280 }
281
282 /**
283 * If we for some reasons needs to push the line back to read more
284 * data we have to reverse the tokenization. Just do the brain-dead replace
285 * of all '\0' to ' ' and set the last character to '\n'. We could have used
286 * the vector we created, but then we would have to search for all of the
287 * spaces we ignored...
288 * @param start pointer to the first character in the buffer to recover
289 * @param end pointer to the last character in the buffer to recover
290 */
291 static void recover_tokenize_command(char *start, char *end)
292 {
293 while (start < end)
294 {
295 if (*start == '\0')
296 *start= ' ';
297 ++start;
298 }
299
300 *end= '\n';
301 }
302
303 /**
304 * Convert the textual command into a comcode
305 */
306 static enum ascii_cmd ascii_to_cmd(char *start, size_t length)
307 {
308 struct {
309 const char *cmd;
310 size_t len;
311 enum ascii_cmd cc;
312 } commands[]= {
313 { .cmd= "get", .len= 3, .cc= GET_CMD },
314 { .cmd= "gets", .len= 4, .cc= GETS_CMD },
315 { .cmd= "set", .len= 3, .cc= SET_CMD },
316 { .cmd= "add", .len= 3, .cc= ADD_CMD },
317 { .cmd= "replace", .len= 7, .cc= REPLACE_CMD },
318 { .cmd= "cas", .len= 3, .cc= CAS_CMD },
319 { .cmd= "append", .len= 6, .cc= APPEND_CMD },
320 { .cmd= "prepend", .len= 7, .cc= PREPEND_CMD },
321 { .cmd= "delete", .len= 6, .cc= DELETE_CMD },
322 { .cmd= "incr", .len= 4, .cc= INCR_CMD },
323 { .cmd= "decr", .len= 4, .cc= DECR_CMD },
324 { .cmd= "stats", .len= 5, .cc= STATS_CMD },
325 { .cmd= "flush_all", .len= 9, .cc= FLUSH_ALL_CMD },
326 { .cmd= "version", .len= 7, .cc= VERSION_CMD },
327 { .cmd= "quit", .len= 4, .cc= QUIT_CMD },
328 { .cmd= "verbosity", .len= 9, .cc= VERBOSITY_CMD },
329 { .cmd= NULL, .len= 0, .cc= UNKNOWN_CMD }};
330
331 int x= 0;
332 while (commands[x].len > 0) {
333 if (length >= commands[x].len)
334 {
335 if (strncmp(start, commands[x].cmd, commands[x].len) == 0)
336 {
337 /* Potential hit */
338 if (length == commands[x].len || isspace(*(start + commands[x].len)))
339 {
340 return commands[x].cc;
341 }
342 }
343 }
344 ++x;
345 }
346
347 return UNKNOWN_CMD;
348 }
349
350 /**
351 * Perform a delete operation.
352 *
353 * @param client client requesting the deletion
354 * @param tokens the command as a vector
355 * @param ntokens the number of items in the vector
356 */
357 static void process_delete(memcached_protocol_client_st *client,
358 char **tokens, int ntokens)
359 {
360 char *key= tokens[1];
361 uint16_t nkey;
362
363 if (ntokens != 2 || (nkey= parse_ascii_key(&key)) == 0)
364 {
365 send_command_usage(client);
366 return;
367 }
368
369 if (client->root->callback->interface.v1.delete == NULL)
370 {
371 spool_string(client, "SERVER_ERROR: callback not implemented\r\n");
372 return;
373 }
374
375 protocol_binary_response_status rval;
376 rval= client->root->callback->interface.v1.delete(client, key, nkey, 0);
377
378 if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS)
379 {
380 spool_string(client, "DELETED\r\n");
381 }
382 else if (rval == PROTOCOL_BINARY_RESPONSE_KEY_ENOENT)
383 {
384 spool_string(client, "NOT_FOUND\r\n");
385 }
386 else
387 {
388 char msg[80];
389 snprintf(msg, sizeof(msg), "SERVER_ERROR: delete failed %u\r\n",(uint32_t)rval);
390 spool_string(client, msg);
391 }
392 }
393
394 static void process_arithmetic(memcached_protocol_client_st *client,
395 char **tokens, int ntokens)
396 {
397 char *key= tokens[1];
398 uint16_t nkey;
399
400 if (ntokens != 3 || (nkey= parse_ascii_key(&key)) == 0)
401 {
402 send_command_usage(client);
403 return;
404 }
405
406 uint64_t cas;
407 uint64_t result;
408 uint64_t delta= strtoull(tokens[2], NULL, 10);
409
410 protocol_binary_response_status rval;
411 if (client->ascii_command == INCR_CMD)
412 {
413 if (client->root->callback->interface.v1.increment == NULL)
414 {
415 spool_string(client, "SERVER_ERROR: callback not implemented\r\n");
416 return;
417 }
418 rval= client->root->callback->interface.v1.increment(client,
419 key, nkey,
420 delta, 0,
421 0,
422 &result,
423 &cas);
424 }
425 else
426 {
427 if (client->root->callback->interface.v1.decrement == NULL)
428 {
429 spool_string(client, "SERVER_ERROR: callback not implemented\r\n");
430 return;
431 }
432 rval= client->root->callback->interface.v1.decrement(client,
433 key, nkey,
434 delta, 0,
435 0,
436 &result,
437 &cas);
438 }
439
440 if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS)
441 {
442 char buffer[80];
443 snprintf(buffer, sizeof(buffer), "%"PRIu64"\r\n", result);
444 spool_string(client, buffer);
445 }
446 else
447 {
448 spool_string(client, "NOT_FOUND\r\n");
449 }
450 }
451
452 /**
453 * Process the stats command (with or without a key specified)
454 * @param key pointer to the first character after "stats"
455 * @param end pointer to the "\n"
456 */
457 static void process_stats(memcached_protocol_client_st *client,
458 char *key, char *end)
459 {
460 if (client->root->callback->interface.v1.stat == NULL)
461 {
462 spool_string(client, "SERVER_ERROR: callback not implemented\r\n");
463 return;
464 }
465
466 while (isspace(*key))
467 key++;
468
469 uint16_t nkey= (uint16_t)(end - key);
470 (void)client->root->callback->interface.v1.stat(client, key, nkey,
471 ascii_stat_response_handler);
472 }
473
474 static void process_version(memcached_protocol_client_st *client,
475 char **tokens, int ntokens)
476 {
477 (void)tokens;
478 if (ntokens != 1)
479 {
480 send_command_usage(client);
481 return;
482 }
483
484 if (client->root->callback->interface.v1.version == NULL)
485 {
486 spool_string(client, "SERVER_ERROR: callback not implemented\r\n");
487 return;
488 }
489
490 client->root->callback->interface.v1.version(client,
491 ascii_version_response_handler);
492 }
493
494 static void process_flush(memcached_protocol_client_st *client,
495 char **tokens, int ntokens)
496 {
497 if (ntokens > 2)
498 {
499 send_command_usage(client);
500 return;
501 }
502
503 if (client->root->callback->interface.v1.flush == NULL)
504 {
505 spool_string(client, "SERVER_ERROR: callback not implemented\r\n");
506 return;
507 }
508
509 uint32_t timeout= 0;
510 if (ntokens == 2)
511 {
512 timeout= (uint32_t)strtoul(tokens[1], NULL, 10);
513 }
514
515 protocol_binary_response_status rval;
516 rval= client->root->callback->interface.v1.flush(client, timeout);
517 if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS)
518 spool_string(client, "OK\r\n");
519 else
520 spool_string(client, "SERVER_ERROR: internal error\r\n");
521 }
522
523 /**
524 * Process one of the storage commands
525 * @param client the client performing the operation
526 * @param tokens the command tokens
527 * @param ntokens the number of tokens
528 * @param start pointer to the first character in the line
529 * @param end pointer to the pointer where the last character of this
530 * command is (IN and OUT)
531 * @param length the number of bytes available
532 * @return -1 if an error occurs (and we should just terminate the connection
533 * because we are out of sync)
534 * 0 storage command completed, continue processing
535 * 1 We need more data, so just go ahead and wait for more!
536 */
537 static inline int process_storage_command(memcached_protocol_client_st *client,
538 char **tokens, int ntokens, char *start,
539 char **end, ssize_t length)
540 {
541 (void)ntokens; /* already checked */
542 char *key= tokens[1];
543 uint16_t nkey= parse_ascii_key(&key);
544 if (nkey == 0)
545 {
546 /* return error */
547 spool_string(client, "CLIENT_ERROR: bad key\r\n");
548 return -1;
549 }
550
551 uint32_t flags= (uint32_t)strtoul(tokens[2], NULL, 10);
552 uint32_t timeout= (uint32_t)strtoul(tokens[3], NULL, 10);
553 unsigned long nbytes= strtoul(tokens[4], NULL, 10);
554
555 /* Do we have all data? */
556 unsigned long need= nbytes + (unsigned long)((*end - start) + 1) + 2; /* \n\r\n */
557 if ((ssize_t)need > length)
558 {
559 /* Keep on reading */
560 recover_tokenize_command(start, *end);
561 return 1;
562 }
563
564 void *data= (*end) + 1;
565 uint64_t cas= 0;
566 uint64_t result_cas;
567 protocol_binary_response_status rval;
568 switch (client->ascii_command)
569 {
570 case SET_CMD:
571 rval= client->root->callback->interface.v1.set(client, key,
572 (uint16_t)nkey,
573 data,
574 (uint32_t)nbytes,
575 flags,
576 timeout, cas,
577 &result_cas);
578 break;
579 case ADD_CMD:
580 rval= client->root->callback->interface.v1.add(client, key,
581 (uint16_t)nkey,
582 data,
583 (uint32_t)nbytes,
584 flags,
585 timeout, &result_cas);
586 break;
587 case CAS_CMD:
588 cas= strtoull(tokens[5], NULL, 10);
589 /* FALLTHROUGH */
590 case REPLACE_CMD:
591 rval= client->root->callback->interface.v1.replace(client, key,
592 (uint16_t)nkey,
593 data,
594 (uint32_t)nbytes,
595 flags,
596 timeout, cas,
597 &result_cas);
598 break;
599 case APPEND_CMD:
600 rval= client->root->callback->interface.v1.append(client, key,
601 (uint16_t)nkey,
602 data,
603 (uint32_t)nbytes,
604 cas,
605 &result_cas);
606 break;
607 case PREPEND_CMD:
608 rval= client->root->callback->interface.v1.prepend(client, key,
609 (uint16_t)nkey,
610 data,
611 (uint32_t)nbytes,
612 cas,
613 &result_cas);
614 break;
615
616 /* gcc complains if I don't put all of the enums in here.. */
617 case GET_CMD:
618 case GETS_CMD:
619 case DELETE_CMD:
620 case DECR_CMD:
621 case INCR_CMD:
622 case STATS_CMD:
623 case FLUSH_ALL_CMD:
624 case VERSION_CMD:
625 case QUIT_CMD:
626 case VERBOSITY_CMD:
627 case UNKNOWN_CMD:
628 default:
629 abort(); /* impossible */
630 }
631
632 if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS)
633 {
634 spool_string(client, "STORED\r\n");
635 }
636 else
637 {
638 if (client->ascii_command == CAS_CMD)
639 {
640 if (rval == PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS)
641 {
642 spool_string(client, "EXISTS\r\n");
643 }
644 else if (rval == PROTOCOL_BINARY_RESPONSE_KEY_ENOENT)
645 {
646 spool_string(client, "NOT_FOUND\r\n");
647 }
648 else
649 {
650 spool_string(client, "NOT_STORED\r\n");
651 }
652 }
653 else
654 {
655 spool_string(client, "NOT_STORED\r\n");
656 }
657 }
658
659 *end += nbytes + 2;
660
661 return 0;
662 }
663
664 static int process_cas_command(memcached_protocol_client_st *client,
665 char **tokens, int ntokens, char *start,
666 char **end, ssize_t length)
667 {
668 if (ntokens != 6)
669 {
670 send_command_usage(client);
671 return false;
672 }
673
674 if (client->root->callback->interface.v1.replace == NULL)
675 {
676 spool_string(client, "SERVER_ERROR: callback not implemented\r\n");
677 return false;
678 }
679
680 return process_storage_command(client, tokens, ntokens, start, end, length);
681 }
682
683 static int process_set_command(memcached_protocol_client_st *client,
684 char **tokens, int ntokens, char *start,
685 char **end, ssize_t length)
686 {
687 if (ntokens != 5)
688 {
689 send_command_usage(client);
690 return false;
691 }
692
693 if (client->root->callback->interface.v1.set == NULL)
694 {
695 spool_string(client, "SERVER_ERROR: callback not implemented\r\n");
696 return false;
697 }
698
699 return process_storage_command(client, tokens, ntokens, start, end, length);
700 }
701
702 static int process_add_command(memcached_protocol_client_st *client,
703 char **tokens, int ntokens, char *start,
704 char **end, ssize_t length)
705 {
706 if (ntokens != 5)
707 {
708 send_command_usage(client);
709 return false;
710 }
711
712 if (client->root->callback->interface.v1.add == NULL)
713 {
714 spool_string(client, "SERVER_ERROR: callback not implemented\r\n");
715 return false;
716 }
717
718 return process_storage_command(client, tokens, ntokens, start, end, length);
719 }
720
721 static int process_replace_command(memcached_protocol_client_st *client,
722 char **tokens, int ntokens, char *start,
723 char **end, ssize_t length)
724 {
725 if (ntokens != 5)
726 {
727 send_command_usage(client);
728 return false;
729 }
730
731 if (client->root->callback->interface.v1.replace == NULL)
732 {
733 spool_string(client, "SERVER_ERROR: callback not implemented\r\n");
734 return false;
735 }
736
737 return process_storage_command(client, tokens, ntokens, start, end, length);
738 }
739
740 static int process_append_command(memcached_protocol_client_st *client,
741 char **tokens, int ntokens, char *start,
742 char **end, ssize_t length)
743 {
744 if (ntokens != 5)
745 {
746 send_command_usage(client);
747 return false;
748 }
749
750 if (client->root->callback->interface.v1.append == NULL)
751 {
752 spool_string(client, "SERVER_ERROR: callback not implemented\r\n");
753 return false;
754 }
755
756 return process_storage_command(client, tokens, ntokens, start, end, length);
757 }
758
759 static int process_prepend_command(memcached_protocol_client_st *client,
760 char **tokens, int ntokens, char *start,
761 char **end, ssize_t length)
762 {
763 if (ntokens != 5)
764 {
765 send_command_usage(client);
766 return false;
767 }
768
769 if (client->root->callback->interface.v1.prepend == NULL)
770 {
771 spool_string(client, "SERVER_ERROR: callback not implemented\r\n");
772 return false;
773 }
774
775 return process_storage_command(client, tokens, ntokens, start, end, length);
776 }
777
778 /**
779 * The ASCII protocol support is just one giant big hack. Instead of adding
780 * a optimal ascii support, I just convert the ASCII commands to the binary
781 * protocol and calls back into the command handlers for the binary protocol ;)
782 */
783 memcached_protocol_event_t memcached_ascii_protocol_process_data(memcached_protocol_client_st *client, ssize_t *length, void **endptr)
784 {
785 char *ptr= (char*)client->root->input_buffer;
786 *endptr= ptr;
787
788 do {
789 /* Do we have \n (indicating the command preamble)*/
790 char *end= memchr(ptr, '\n', (size_t)*length);
791 if (end == NULL)
792 {
793 *endptr= ptr;
794 return MEMCACHED_PROTOCOL_READ_EVENT;
795 }
796
797 client->ascii_command= ascii_to_cmd(ptr, (size_t)(*length));
798
799 /* A multiget lists all of the keys, and I don't want to have an
800 * avector of let's say 512 pointers to tokenize all of them, so let's
801 * just handle them immediately
802 */
803 if (client->ascii_command == GET_CMD ||
804 client->ascii_command == GETS_CMD) {
805 if (client->root->callback->interface.v1.get != NULL)
806 ascii_process_gets(client, ptr, end);
807 else
808 spool_string(client, "SERVER_ERROR: Command not implemented\n");
809 } else {
810 /* None of the defined commands takes 10 parameters, so lets just use
811 * that as a maximum limit.
812 */
813 char *tokens[10];
814 int ntokens= ascii_tokenize_command(ptr, end, tokens, 10);
815
816 if (ntokens < 10)
817 {
818 client->mute= strcmp(tokens[ntokens - 1], "noreply") == 0;
819 if (client->mute)
820 --ntokens; /* processed noreply token*/
821 }
822
823 int error= 0;
824
825 switch (client->ascii_command) {
826 case SET_CMD:
827 error= process_set_command(client, tokens, ntokens, ptr, &end, *length);
828 break;
829 case ADD_CMD:
830 error= process_add_command(client, tokens, ntokens, ptr, &end, *length);
831 break;
832 case REPLACE_CMD:
833 error= process_replace_command(client, tokens, ntokens,
834 ptr, &end, *length);
835 break;
836 case CAS_CMD:
837 error= process_cas_command(client, tokens, ntokens, ptr, &end, *length);
838 break;
839 case APPEND_CMD:
840 error= process_append_command(client, tokens, ntokens,
841 ptr, &end, *length);
842 break;
843 case PREPEND_CMD:
844 error= process_prepend_command(client, tokens, ntokens,
845 ptr, &end, *length);
846 break;
847 case DELETE_CMD:
848 process_delete(client, tokens, ntokens);
849 break;
850
851 case INCR_CMD: /* FALLTHROUGH */
852 case DECR_CMD:
853 process_arithmetic(client, tokens, ntokens);
854 break;
855 case STATS_CMD:
856 if (client->mute)
857 {
858 send_command_usage(client);
859 }
860 else
861 {
862 recover_tokenize_command(ptr, end);
863 process_stats(client, ptr + 6, end);
864 }
865 break;
866 case FLUSH_ALL_CMD:
867 process_flush(client, tokens, ntokens);
868 break;
869 case VERSION_CMD:
870 if (client->mute)
871 {
872 send_command_usage(client);
873 }
874 else
875 {
876 process_version(client, tokens, ntokens);
877 }
878 break;
879 case QUIT_CMD:
880 if (ntokens != 1 || client->mute)
881 {
882 send_command_usage(client);
883 }
884 else
885 {
886 if (client->root->callback->interface.v1.quit != NULL)
887 client->root->callback->interface.v1.quit(client);
888
889 return MEMCACHED_PROTOCOL_ERROR_EVENT;
890 }
891 break;
892
893 case VERBOSITY_CMD:
894 if (ntokens != 2)
895 send_command_usage(client);
896 else
897 spool_string(client, "OK\r\n");
898 break;
899
900 case UNKNOWN_CMD:
901 send_command_usage(client);
902 break;
903
904 case GET_CMD:
905 case GETS_CMD:
906 default:
907 /* Should already be handled */
908 abort();
909 }
910
911 if (error == -1)
912 return MEMCACHED_PROTOCOL_ERROR_EVENT;
913 else if (error == 1)
914 return MEMCACHED_PROTOCOL_READ_EVENT;
915 }
916
917 /* Move past \n */
918 ++end;
919 *length -= end - ptr;
920 ptr= end;
921 } while (*length > 0);
922
923 *endptr= ptr;
924 return MEMCACHED_PROTOCOL_READ_EVENT;
925 }