reorganize directories
[m6w6/libmemcached] / src / libmemcachedprotocol / ascii_handler.c
1 /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
2 *
3 * Libmemcached library
4 *
5 * Copyright (C) 2011 Data Differential, http://datadifferential.com/
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
9 * met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * * Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following disclaimer
16 * in the documentation and/or other materials provided with the
17 * distribution.
18 *
19 * * The names of its contributors may not be used to endorse or
20 * promote products derived from this software without specific prior
21 * written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 *
35 */
36
37 #include <libmemcachedprotocol/common.h>
38
39 #include <ctype.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <errno.h>
44
45
46 static void print_ascii_command(memcached_protocol_client_st *client)
47 {
48 if (client->is_verbose)
49 {
50 switch (client->ascii_command)
51 {
52 case SET_CMD:
53 fprintf(stderr, "%s:%d SET_CMD\n", __FILE__, __LINE__);
54 break;
55
56 case ADD_CMD:
57 fprintf(stderr, "%s:%d ADD_CMD\n", __FILE__, __LINE__);
58 break;
59
60 case REPLACE_CMD:
61 fprintf(stderr, "%s:%d REPLACE_CMD\n", __FILE__, __LINE__);
62 break;
63
64 case CAS_CMD:
65 fprintf(stderr, "%s:%d CAS_CMD\n", __FILE__, __LINE__);
66 break;
67
68 case APPEND_CMD:
69 fprintf(stderr, "%s:%d APPEND_CMD\n", __FILE__, __LINE__);
70 break;
71
72 case PREPEND_CMD:
73 fprintf(stderr, "%s:%d PREPEND_CMD\n", __FILE__, __LINE__);
74 break;
75
76 case DELETE_CMD:
77 fprintf(stderr, "%s:%d DELETE_CMD\n", __FILE__, __LINE__);
78 break;
79
80 case INCR_CMD: /* FALLTHROUGH */
81 fprintf(stderr, "%s:%d INCR_CMD\n", __FILE__, __LINE__);
82 break;
83
84 case DECR_CMD:
85 fprintf(stderr, "%s:%d DECR_CMD\n", __FILE__, __LINE__);
86 break;
87
88 case STATS_CMD:
89 fprintf(stderr, "%s:%d STATS_CMD\n", __FILE__, __LINE__);
90 break;
91
92 case FLUSH_ALL_CMD:
93 fprintf(stderr, "%s:%d FLUSH_ALL_CMD\n", __FILE__, __LINE__);
94 break;
95
96 case VERSION_CMD:
97 fprintf(stderr, "%s:%d VERSION_CMD\n", __FILE__, __LINE__);
98 break;
99
100 case QUIT_CMD:
101 fprintf(stderr, "%s:%d QUIT_CMD\n", __FILE__, __LINE__);
102 break;
103
104 case VERBOSITY_CMD:
105 fprintf(stderr, "%s:%d VERBOSITY_CMD\n", __FILE__, __LINE__);
106 break;
107
108 case GET_CMD:
109 fprintf(stderr, "%s:%d GET_CMD\n", __FILE__, __LINE__);
110 break;
111
112 case GETS_CMD:
113 fprintf(stderr, "%s:%d GETS_CMD\n", __FILE__, __LINE__);
114 break;
115
116 default:
117 case UNKNOWN_CMD:
118 fprintf(stderr, "%s:%d UNKNOWN_CMD\n", __FILE__, __LINE__);
119 break;
120
121 }
122 }
123 }
124
125 /**
126 * Try to parse a key from the string.
127 * @pointer start pointer to a pointer to the string (IN and OUT)
128 * @return length of the string of -1 if this was an illegal key (invalid
129 * characters or invalid length)
130 * @todo add length!
131 */
132 static uint16_t parse_ascii_key(char **start)
133 {
134 uint16_t len= 0;
135 char *c= *start;
136 /* Strip leading whitespaces */
137 while (isspace(*c))
138 {
139 ++c;
140 }
141
142 *start= c;
143
144 while (*c != '\0' && !isspace(*c) && !iscntrl(*c))
145 {
146 ++c;
147 ++len;
148 }
149
150
151 if (len == 0 || len > 240 || (*c != '\0' && *c != '\r' && iscntrl(*c)))
152 {
153 return 0;
154 }
155
156 return len;
157 }
158
159 /**
160 * Spool a zero-terminated string
161 * @param client destination
162 * @param text the text to spool
163 * @return status of the spool operation
164 */
165 static protocol_binary_response_status raw_response_handler(memcached_protocol_client_st *client, const char *text)
166 {
167 if (client->is_verbose)
168 {
169 fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, text);
170 }
171
172 if (client->root->drain(client) == false)
173 {
174 return PROTOCOL_BINARY_RESPONSE_EINTERNAL;
175 }
176
177 assert(client->output != NULL);
178 #if 0
179 if (client->output == NULL)
180 {
181 /* I can write directly to the socket.... */
182 do
183 {
184 size_t num_bytes= len -offset;
185 ssize_t nw= client->root->send(client,
186 client->sock,
187 ptr + offset,
188 num_bytes);
189 if (nw == -1)
190 {
191 if (get_socket_errno() == EWOULDBLOCK)
192 {
193 break;
194 }
195 else if (get_socket_errno() != EINTR)
196 {
197 client->error= errno;
198 return PROTOCOL_BINARY_RESPONSE_EINTERNAL;
199 }
200 }
201 else
202 {
203 offset += (size_t)nw;
204 }
205 } while (offset < len);
206 }
207 #endif
208
209 return client->root->spool(client, text, strlen(text));
210 }
211
212 /**
213 * Send a "CLIENT_ERROR" message back to the client with the correct
214 * format of the command being sent
215 * @param client the client to send the message to
216 */
217 static void send_command_usage(memcached_protocol_client_st *client)
218 {
219 const char *errmsg[]= {
220 [GET_CMD]= "CLIENT_ERROR: Syntax error: get <key>*\r\n",
221 [GETS_CMD]= "CLIENT_ERROR: Syntax error: gets <key>*\r\n",
222 [SET_CMD]= "CLIENT_ERROR: Syntax error: set <key> <flags> <exptime> <bytes> [noreply]\r\n",
223 [ADD_CMD]= "CLIENT_ERROR: Syntax error: add <key> <flags> <exptime> <bytes> [noreply]\r\n",
224 [REPLACE_CMD]= "CLIENT_ERROR: Syntax error: replace <key> <flags> <exptime> <bytes> [noreply]\r\n",
225 [CAS_CMD]= "CLIENT_ERROR: Syntax error: cas <key> <flags> <exptime> <bytes> <casid> [noreply]\r\n",
226 [APPEND_CMD]= "CLIENT_ERROR: Syntax error: append <key> <flags> <exptime> <bytes> [noreply]\r\n",
227 [PREPEND_CMD]= "CLIENT_ERROR: Syntax error: prepend <key> <flags> <exptime> <bytes> [noreply]\r\n",
228 [DELETE_CMD]= "CLIENT_ERROR: Syntax error: delete_object <key> [noreply]\r\n",
229 [INCR_CMD]= "CLIENT_ERROR: Syntax error: incr <key> <value> [noreply]\r\n",
230 [DECR_CMD]= "CLIENT_ERROR: Syntax error: decr <key> <value> [noreply]\r\n",
231 [STATS_CMD]= "CLIENT_ERROR: Syntax error: stats [key]\r\n",
232 [FLUSH_ALL_CMD]= "CLIENT_ERROR: Syntax error: flush_all [timeout] [noreply]\r\n",
233 [VERSION_CMD]= "CLIENT_ERROR: Syntax error: version\r\n",
234 [QUIT_CMD]="CLIENT_ERROR: Syntax error: quit\r\n",
235
236 [VERBOSITY_CMD]= "CLIENT_ERROR: Syntax error: verbosity <num>\r\n",
237 [UNKNOWN_CMD]= "CLIENT_ERROR: Unknown command\r\n",
238 };
239
240 client->mute = false;
241 raw_response_handler(client, errmsg[client->ascii_command]);
242 }
243
244 /**
245 * Callback for the VERSION responses
246 * @param cookie client identifier
247 * @param text the length of the body
248 * @param textlen the length of the body
249 */
250 static protocol_binary_response_status ascii_version_response_handler(const void *cookie,
251 const void *text,
252 uint32_t textlen)
253 {
254 memcached_protocol_client_st *client= (memcached_protocol_client_st*)cookie;
255 raw_response_handler(client, "VERSION ");
256 client->root->spool(client, text, textlen);
257 raw_response_handler(client, "\r\n");
258 return PROTOCOL_BINARY_RESPONSE_SUCCESS;
259 }
260
261 /**
262 * Callback for the GET/GETQ/GETK and GETKQ responses
263 * @param cookie client identifier
264 * @param key the key for the item
265 * @param keylen the length of the key
266 * @param body the length of the body
267 * @param bodylen the length of the body
268 * @param flags the flags for the item
269 * @param cas the CAS id for the item
270 */
271 static protocol_binary_response_status
272 ascii_get_response_handler(const void *cookie,
273 const void *key,
274 uint16_t keylen,
275 const void *body,
276 uint32_t bodylen,
277 uint32_t flags,
278 uint64_t cas)
279 {
280 memcached_protocol_client_st *client= (void*)cookie;
281 char buffer[300];
282 strcpy(buffer, "VALUE ");
283 const char *source= key;
284 char *dest= buffer + 6;
285
286 for (int x= 0; x < keylen; ++x)
287 {
288 if (*source != '\0' && !isspace(*source) && !iscntrl(*source))
289 {
290 *dest= *source;
291 }
292 else
293 {
294 return PROTOCOL_BINARY_RESPONSE_EINVAL; /* key constraints in ascii */
295 }
296
297 ++dest;
298 ++source;
299 }
300
301 size_t used= (size_t)(dest - buffer);
302
303 if (client->ascii_command == GETS_CMD)
304 {
305 snprintf(dest, sizeof(buffer) - used, " %u %u %" PRIu64 "\r\n", flags,
306 bodylen, cas);
307 }
308 else
309 {
310 snprintf(dest, sizeof(buffer) - used, " %u %u\r\n", flags, bodylen);
311 }
312
313 client->root->spool(client, buffer, strlen(buffer));
314 client->root->spool(client, body, bodylen);
315 client->root->spool(client, "\r\n", 2);
316
317 return PROTOCOL_BINARY_RESPONSE_SUCCESS;
318 }
319
320 /**
321 * Callback for the STAT responses
322 * @param cookie client identifier
323 * @param key the key for the item
324 * @param keylen the length of the key
325 * @param body the length of the body
326 * @param bodylen the length of the body
327 */
328 static protocol_binary_response_status ascii_stat_response_handler(const void *cookie,
329 const void *key,
330 uint16_t keylen,
331 const void *body,
332 uint32_t bodylen)
333 {
334
335 memcached_protocol_client_st *client= (void*)cookie;
336
337 if (key != NULL)
338 {
339 raw_response_handler(client, "STAT ");
340 client->root->spool(client, key, keylen);
341 raw_response_handler(client, " ");
342 client->root->spool(client, body, bodylen);
343 raw_response_handler(client, "\r\n");
344 }
345 else
346 {
347 raw_response_handler(client, "END\r\n");
348 }
349
350 return PROTOCOL_BINARY_RESPONSE_SUCCESS;
351 }
352
353 /**
354 * Process a get or a gets request.
355 * @param client the client handle
356 * @param buffer the complete get(s) command
357 * @param end the last character in the command
358 */
359 static void ascii_process_gets(memcached_protocol_client_st *client,
360 char *buffer, char *end)
361 {
362 char *key= buffer;
363
364 /* Skip command */
365 key += (client->ascii_command == GETS_CMD) ? 5 : 4;
366
367 int num_keys= 0;
368 while (key < end)
369 {
370 uint16_t nkey= parse_ascii_key(&key);
371 if (nkey == 0) /* Invalid key... stop processing this line */
372 {
373 break;
374 }
375
376 (void)client->root->callback->interface.v1.get(client, key, nkey,
377 ascii_get_response_handler);
378 key += nkey;
379 ++num_keys;
380 }
381
382 if (num_keys == 0)
383 {
384 send_command_usage(client);
385 }
386 else
387 {
388 client->root->spool(client, "END\r\n", 5);
389 }
390 }
391
392 /**
393 * Try to split up the command line "asdf asdf asdf asdf\n" into an
394 * argument vector for easier parsing.
395 * @param start the first character in the command line
396 * @param end the last character in the command line ("\n")
397 * @param vec the vector to insert the pointers into
398 * @size the number of elements in the vector
399 * @return the number of tokens in the vector
400 */
401 static int ascii_tokenize_command(char *str, char *end, char **vec, int size)
402 {
403 int elem= 0;
404
405 while (str < end)
406 {
407 /* Skip leading blanks */
408 while (str < end && isspace(*str))
409 {
410 ++str;
411 }
412
413 if (str == end)
414 {
415 return elem;
416 }
417
418 vec[elem++]= str;
419 /* find the next non-blank field */
420 while (str < end && !isspace(*str))
421 {
422 ++str;
423 }
424
425 /* zero-terminate it for easier parsing later on */
426 *str= '\0';
427 ++str;
428
429 /* Is the vector full? */
430 if (elem == size)
431 {
432 break;
433 }
434 }
435
436 return elem;
437 }
438
439 /**
440 * If we for some reasons needs to push the line back to read more
441 * data we have to reverse the tokenization. Just do the brain-dead replace
442 * of all '\0' to ' ' and set the last character to '\n'. We could have used
443 * the vector we created, but then we would have to search for all of the
444 * spaces we ignored...
445 * @param start pointer to the first character in the buffer to recover
446 * @param end pointer to the last character in the buffer to recover
447 */
448 static void recover_tokenize_command(char *start, char *end)
449 {
450 while (start < end)
451 {
452 if (*start == '\0')
453 *start= ' ';
454 ++start;
455 }
456
457 *end= '\n';
458 }
459
460 /**
461 * Convert the textual command into a comcode
462 */
463 static enum ascii_cmd ascii_to_cmd(char *start, size_t length)
464 {
465 struct {
466 const char *cmd;
467 size_t len;
468 enum ascii_cmd cc;
469 } commands[]= {
470 { .cmd= "get", .len= 3, .cc= GET_CMD },
471 { .cmd= "gets", .len= 4, .cc= GETS_CMD },
472 { .cmd= "set", .len= 3, .cc= SET_CMD },
473 { .cmd= "add", .len= 3, .cc= ADD_CMD },
474 { .cmd= "replace", .len= 7, .cc= REPLACE_CMD },
475 { .cmd= "cas", .len= 3, .cc= CAS_CMD },
476 { .cmd= "append", .len= 6, .cc= APPEND_CMD },
477 { .cmd= "prepend", .len= 7, .cc= PREPEND_CMD },
478 { .cmd= "delete_object", .len= 6, .cc= DELETE_CMD },
479 { .cmd= "incr", .len= 4, .cc= INCR_CMD },
480 { .cmd= "decr", .len= 4, .cc= DECR_CMD },
481 { .cmd= "stats", .len= 5, .cc= STATS_CMD },
482 { .cmd= "flush_all", .len= 9, .cc= FLUSH_ALL_CMD },
483 { .cmd= "version", .len= 7, .cc= VERSION_CMD },
484 { .cmd= "quit", .len= 4, .cc= QUIT_CMD },
485 { .cmd= "verbosity", .len= 9, .cc= VERBOSITY_CMD },
486 { .cmd= NULL, .len= 0, .cc= UNKNOWN_CMD }};
487
488 int x= 0;
489 while (commands[x].len > 0) {
490 if (length >= commands[x].len)
491 {
492 if (strncmp(start, commands[x].cmd, commands[x].len) == 0)
493 {
494 /* Potential hit */
495 if (length == commands[x].len || isspace(*(start + commands[x].len)))
496 {
497 return commands[x].cc;
498 }
499 }
500 }
501 ++x;
502 }
503
504 return UNKNOWN_CMD;
505 }
506
507 /**
508 * Perform a delete_object operation.
509 *
510 * @param client client requesting the deletion
511 * @param tokens the command as a vector
512 * @param ntokens the number of items in the vector
513 */
514 static void process_delete(memcached_protocol_client_st *client,
515 char **tokens, int ntokens)
516 {
517 char *key= tokens[1];
518 uint16_t nkey;
519
520 if (ntokens != 2 || (nkey= parse_ascii_key(&key)) == 0)
521 {
522 send_command_usage(client);
523 return;
524 }
525
526 if (client->root->callback->interface.v1.delete_object == NULL)
527 {
528 raw_response_handler(client, "SERVER_ERROR: callback not implemented\r\n");
529 return;
530 }
531
532 protocol_binary_response_status rval= client->root->callback->interface.v1.delete_object(client, key, nkey, 0);
533
534 if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS)
535 {
536 raw_response_handler(client, "DELETED\r\n");
537 }
538 else if (rval == PROTOCOL_BINARY_RESPONSE_KEY_ENOENT)
539 {
540 raw_response_handler(client, "NOT_FOUND\r\n");
541 }
542 else
543 {
544 char msg[80];
545 snprintf(msg, sizeof(msg), "SERVER_ERROR: delete_object failed %u\r\n",(uint32_t)rval);
546 raw_response_handler(client, msg);
547 }
548 }
549
550 static void process_arithmetic(memcached_protocol_client_st *client,
551 char **tokens, int ntokens)
552 {
553 char *key= tokens[1];
554 uint16_t nkey;
555
556 if (ntokens != 3 || (nkey= parse_ascii_key(&key)) == 0)
557 {
558 send_command_usage(client);
559 return;
560 }
561
562 uint64_t cas;
563 uint64_t result;
564 errno= 0;
565 uint64_t delta= strtoull(tokens[2], NULL, 10);
566 if (errno != 0)
567 {
568 return; // Error
569 }
570
571 protocol_binary_response_status rval;
572 if (client->ascii_command == INCR_CMD)
573 {
574 if (client->root->callback->interface.v1.increment == NULL)
575 {
576 raw_response_handler(client, "SERVER_ERROR: callback not implemented\r\n");
577 return;
578 }
579 rval= client->root->callback->interface.v1.increment(client,
580 key, nkey,
581 delta, 0,
582 0,
583 &result,
584 &cas);
585 }
586 else
587 {
588 if (client->root->callback->interface.v1.decrement == NULL)
589 {
590 raw_response_handler(client, "SERVER_ERROR: callback not implemented\r\n");
591 return;
592 }
593 rval= client->root->callback->interface.v1.decrement(client,
594 key, nkey,
595 delta, 0,
596 0,
597 &result,
598 &cas);
599 }
600
601 if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS)
602 {
603 char buffer[80];
604 snprintf(buffer, sizeof(buffer), "%"PRIu64"\r\n", result);
605 raw_response_handler(client, buffer);
606 }
607 else
608 {
609 raw_response_handler(client, "NOT_FOUND\r\n");
610 }
611 }
612
613 /**
614 * Process the stats command (with or without a key specified)
615 * @param key pointer to the first character after "stats"
616 * @param end pointer to the "\n"
617 */
618 static void process_stats(memcached_protocol_client_st *client,
619 char *key, char *end)
620 {
621 if (client->root->callback->interface.v1.stat == NULL)
622 {
623 raw_response_handler(client, "SERVER_ERROR: callback not implemented\r\n");
624 return;
625 }
626
627 while (isspace(*key))
628 {
629 key++;
630 }
631
632 uint16_t nkey= (uint16_t)(end - key);
633 (void)client->root->callback->interface.v1.stat(client, key, nkey,
634 ascii_stat_response_handler);
635 }
636
637 static void process_version(memcached_protocol_client_st *client,
638 char **tokens, int ntokens)
639 {
640 (void)tokens;
641 if (ntokens != 1)
642 {
643 send_command_usage(client);
644 return;
645 }
646
647 if (client->root->callback->interface.v1.version == NULL)
648 {
649 raw_response_handler(client, "SERVER_ERROR: callback not implemented\r\n");
650 return;
651 }
652
653 client->root->callback->interface.v1.version(client,
654 ascii_version_response_handler);
655 }
656
657 static void process_flush(memcached_protocol_client_st *client,
658 char **tokens, int ntokens)
659 {
660 if (ntokens > 2)
661 {
662 send_command_usage(client);
663 return;
664 }
665
666 if (client->root->callback->interface.v1.flush_object == NULL)
667 {
668 raw_response_handler(client, "SERVER_ERROR: callback not implemented\r\n");
669 return;
670 }
671
672 uint32_t timeout= 0;
673 if (ntokens == 2)
674 {
675 errno= 0;
676 timeout= (uint32_t)strtoul(tokens[1], NULL, 10);
677 if (errno != 0)
678 {
679 return; // Error
680 }
681 }
682
683 protocol_binary_response_status rval;
684 rval= client->root->callback->interface.v1.flush_object(client, timeout);
685 if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS)
686 raw_response_handler(client, "OK\r\n");
687 else
688 raw_response_handler(client, "SERVER_ERROR: internal error\r\n");
689 }
690
691 /**
692 * Process one of the storage commands
693 * @param client the client performing the operation
694 * @param tokens the command tokens
695 * @param ntokens the number of tokens
696 * @param start pointer to the first character in the line
697 * @param end pointer to the pointer where the last character of this
698 * command is (IN and OUT)
699 * @param length the number of bytes available
700 * @return -1 if an error occurs (and we should just terminate the connection
701 * because we are out of sync)
702 * 0 storage command completed, continue processing
703 * 1 We need more data, so just go ahead and wait for more!
704 */
705 static inline int process_storage_command(memcached_protocol_client_st *client,
706 char **tokens, int ntokens, char *start,
707 char **end, ssize_t length)
708 {
709 (void)ntokens; /* already checked */
710 char *key= tokens[1];
711 uint16_t nkey= parse_ascii_key(&key);
712 if (nkey == 0)
713 {
714 /* return error */
715 raw_response_handler(client, "CLIENT_ERROR: bad key\r\n");
716 return -1;
717 }
718
719 errno= 0;
720 uint32_t flags= (uint32_t)strtoul(tokens[2], NULL, 10);
721 if (errno != 0)
722 {
723 /* return error */
724 raw_response_handler(client, "CLIENT_ERROR: bad key\r\n");
725 return -1;
726 }
727
728 uint32_t timeout= (uint32_t)strtoul(tokens[3], NULL, 10);
729 if (errno != 0)
730 {
731 /* return error */
732 raw_response_handler(client, "CLIENT_ERROR: bad key\r\n");
733 return -1;
734 }
735
736 unsigned long nbytes= strtoul(tokens[4], NULL, 10);
737 if (errno != 0)
738 {
739 /* return error */
740 raw_response_handler(client, "CLIENT_ERROR: bad key\r\n");
741 return -1;
742 }
743
744 /* Do we have all data? */
745 unsigned long need= nbytes + (unsigned long)((*end - start) + 1) + 2; /* \n\r\n */
746 if ((ssize_t)need > length)
747 {
748 /* Keep on reading */
749 recover_tokenize_command(start, *end);
750 return 1;
751 }
752
753 void *data= (*end) + 1;
754 uint64_t cas= 0;
755 uint64_t result_cas;
756 protocol_binary_response_status rval;
757 switch (client->ascii_command)
758 {
759 case SET_CMD:
760 rval= client->root->callback->interface.v1.set(client, key,
761 (uint16_t)nkey,
762 data,
763 (uint32_t)nbytes,
764 flags,
765 timeout, cas,
766 &result_cas);
767 break;
768 case ADD_CMD:
769 rval= client->root->callback->interface.v1.add(client, key,
770 (uint16_t)nkey,
771 data,
772 (uint32_t)nbytes,
773 flags,
774 timeout, &result_cas);
775 break;
776 case CAS_CMD:
777 errno= 0;
778 cas= strtoull(tokens[5], NULL, 10);
779 if (errno != 0)
780 {
781 /* return error */
782 raw_response_handler(client, "CLIENT_ERROR: bad key\r\n");
783 return -1;
784 }
785 /* FALLTHROUGH */
786 case REPLACE_CMD:
787 rval= client->root->callback->interface.v1.replace(client, key,
788 (uint16_t)nkey,
789 data,
790 (uint32_t)nbytes,
791 flags,
792 timeout, cas,
793 &result_cas);
794 break;
795 case APPEND_CMD:
796 rval= client->root->callback->interface.v1.append(client, key,
797 (uint16_t)nkey,
798 data,
799 (uint32_t)nbytes,
800 cas,
801 &result_cas);
802 break;
803 case PREPEND_CMD:
804 rval= client->root->callback->interface.v1.prepend(client, key,
805 (uint16_t)nkey,
806 data,
807 (uint32_t)nbytes,
808 cas,
809 &result_cas);
810 break;
811
812 /* gcc complains if I don't put all of the enums in here.. */
813 case GET_CMD:
814 case GETS_CMD:
815 case DELETE_CMD:
816 case DECR_CMD:
817 case INCR_CMD:
818 case STATS_CMD:
819 case FLUSH_ALL_CMD:
820 case VERSION_CMD:
821 case QUIT_CMD:
822 case VERBOSITY_CMD:
823 case UNKNOWN_CMD:
824 default:
825 abort(); /* impossible */
826 }
827
828 if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS)
829 {
830 raw_response_handler(client, "STORED\r\n");
831 }
832 else
833 {
834 if (client->ascii_command == CAS_CMD)
835 {
836 if (rval == PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS)
837 {
838 raw_response_handler(client, "EXISTS\r\n");
839 }
840 else if (rval == PROTOCOL_BINARY_RESPONSE_KEY_ENOENT)
841 {
842 raw_response_handler(client, "NOT_FOUND\r\n");
843 }
844 else
845 {
846 raw_response_handler(client, "NOT_STORED\r\n");
847 }
848 }
849 else
850 {
851 raw_response_handler(client, "NOT_STORED\r\n");
852 }
853 }
854
855 *end += nbytes + 2;
856
857 return 0;
858 }
859
860 static int process_cas_command(memcached_protocol_client_st *client,
861 char **tokens, int ntokens, char *start,
862 char **end, ssize_t length)
863 {
864 if (ntokens != 6)
865 {
866 send_command_usage(client);
867 return false;
868 }
869
870 if (client->root->callback->interface.v1.replace == NULL)
871 {
872 raw_response_handler(client, "SERVER_ERROR: callback not implemented\r\n");
873 return false;
874 }
875
876 return process_storage_command(client, tokens, ntokens, start, end, length);
877 }
878
879 static int process_set_command(memcached_protocol_client_st *client,
880 char **tokens, int ntokens, char *start,
881 char **end, ssize_t length)
882 {
883 if (ntokens != 5)
884 {
885 send_command_usage(client);
886 return false;
887 }
888
889 if (client->root->callback->interface.v1.set == NULL)
890 {
891 raw_response_handler(client, "SERVER_ERROR: callback not implemented\r\n");
892 return false;
893 }
894
895 return process_storage_command(client, tokens, ntokens, start, end, length);
896 }
897
898 static int process_add_command(memcached_protocol_client_st *client,
899 char **tokens, int ntokens, char *start,
900 char **end, ssize_t length)
901 {
902 if (ntokens != 5)
903 {
904 send_command_usage(client);
905 return false;
906 }
907
908 if (client->root->callback->interface.v1.add == NULL)
909 {
910 raw_response_handler(client, "SERVER_ERROR: callback not implemented\r\n");
911 return false;
912 }
913
914 return process_storage_command(client, tokens, ntokens, start, end, length);
915 }
916
917 static int process_replace_command(memcached_protocol_client_st *client,
918 char **tokens, int ntokens, char *start,
919 char **end, ssize_t length)
920 {
921 if (ntokens != 5)
922 {
923 send_command_usage(client);
924 return false;
925 }
926
927 if (client->root->callback->interface.v1.replace == NULL)
928 {
929 raw_response_handler(client, "SERVER_ERROR: callback not implemented\r\n");
930 return false;
931 }
932
933 return process_storage_command(client, tokens, ntokens, start, end, length);
934 }
935
936 static int process_append_command(memcached_protocol_client_st *client,
937 char **tokens, int ntokens, char *start,
938 char **end, ssize_t length)
939 {
940 if (ntokens != 5)
941 {
942 send_command_usage(client);
943 return false;
944 }
945
946 if (client->root->callback->interface.v1.append == NULL)
947 {
948 raw_response_handler(client, "SERVER_ERROR: callback not implemented\r\n");
949 return false;
950 }
951
952 return process_storage_command(client, tokens, ntokens, start, end, length);
953 }
954
955 static int process_prepend_command(memcached_protocol_client_st *client,
956 char **tokens, int ntokens, char *start,
957 char **end, ssize_t length)
958 {
959 if (ntokens != 5)
960 {
961 send_command_usage(client);
962 return false;
963 }
964
965 if (client->root->callback->interface.v1.prepend == NULL)
966 {
967 raw_response_handler(client, "SERVER_ERROR: callback not implemented\r\n");
968 return false;
969 }
970
971 return process_storage_command(client, tokens, ntokens, start, end, length);
972 }
973
974 /**
975 * The ASCII protocol support is just one giant big hack. Instead of adding
976 * a optimal ascii support, I just convert the ASCII commands to the binary
977 * protocol and calls back into the command handlers for the binary protocol ;)
978 */
979 memcached_protocol_event_t memcached_ascii_protocol_process_data(memcached_protocol_client_st *client, ssize_t *length, void **endptr)
980 {
981 char *ptr= (char*)client->root->input_buffer;
982 *endptr= ptr;
983
984 do {
985 /* Do we have \n (indicating the command preamble)*/
986 char *end= memchr(ptr, '\n', (size_t)*length);
987 if (end == NULL)
988 {
989 *endptr= ptr;
990 return MEMCACHED_PROTOCOL_READ_EVENT;
991 }
992
993 client->ascii_command= ascii_to_cmd(ptr, (size_t)(*length));
994
995 /* we got all data available, execute the callback! */
996 if (client->root->callback->pre_execute != NULL)
997 {
998 client->root->callback->pre_execute(client, NULL);
999 }
1000
1001
1002 /* A multiget lists all of the keys, and I don't want to have an
1003 * avector of let's say 512 pointers to tokenize all of them, so let's
1004 * just handle them immediately
1005 */
1006 if (client->ascii_command == GET_CMD ||
1007 client->ascii_command == GETS_CMD)
1008 {
1009 if (client->root->callback->interface.v1.get != NULL)
1010 {
1011 ascii_process_gets(client, ptr, end);
1012 }
1013 else
1014 {
1015 raw_response_handler(client, "SERVER_ERROR: Command not implemented\n");
1016 }
1017 }
1018 else
1019 {
1020 /* None of the defined commands takes 10 parameters, so lets just use
1021 * that as a maximum limit.
1022 */
1023 char *tokens[10];
1024 int ntokens= ascii_tokenize_command(ptr, end, tokens, 10);
1025
1026 if (ntokens < 10)
1027 {
1028 client->mute= strcmp(tokens[ntokens - 1], "noreply") == 0;
1029 if (client->mute)
1030 {
1031 --ntokens; /* processed noreply token*/
1032 }
1033 }
1034
1035 int error= 0;
1036
1037 print_ascii_command(client);
1038 switch (client->ascii_command)
1039 {
1040 case SET_CMD:
1041 error= process_set_command(client, tokens, ntokens, ptr, &end, *length);
1042 break;
1043
1044 case ADD_CMD:
1045 error= process_add_command(client, tokens, ntokens, ptr, &end, *length);
1046 break;
1047
1048 case REPLACE_CMD:
1049 error= process_replace_command(client, tokens, ntokens, ptr, &end, *length);
1050 break;
1051
1052 case CAS_CMD:
1053 error= process_cas_command(client, tokens, ntokens, ptr, &end, *length);
1054 break;
1055
1056 case APPEND_CMD:
1057 error= process_append_command(client, tokens, ntokens, ptr, &end, *length);
1058 break;
1059
1060 case PREPEND_CMD:
1061 error= process_prepend_command(client, tokens, ntokens, ptr, &end, *length);
1062 break;
1063
1064 case DELETE_CMD:
1065 process_delete(client, tokens, ntokens);
1066 break;
1067
1068 case INCR_CMD: /* FALLTHROUGH */
1069 case DECR_CMD:
1070 process_arithmetic(client, tokens, ntokens);
1071 break;
1072
1073 case STATS_CMD:
1074 if (client->mute)
1075 {
1076 send_command_usage(client);
1077 }
1078 else
1079 {
1080 recover_tokenize_command(ptr, end);
1081 process_stats(client, ptr + 6, end);
1082 }
1083 break;
1084
1085 case FLUSH_ALL_CMD:
1086 process_flush(client, tokens, ntokens);
1087 break;
1088
1089 case VERSION_CMD:
1090 if (client->mute)
1091 {
1092 send_command_usage(client);
1093 }
1094 else
1095 {
1096 process_version(client, tokens, ntokens);
1097 }
1098 break;
1099
1100 case QUIT_CMD:
1101 if (ntokens != 1 || client->mute)
1102 {
1103 send_command_usage(client);
1104 }
1105 else
1106 {
1107 if (client->root->callback->interface.v1.quit != NULL)
1108 {
1109 client->root->callback->interface.v1.quit(client);
1110 }
1111
1112 return MEMCACHED_PROTOCOL_ERROR_EVENT;
1113 }
1114 break;
1115
1116 case VERBOSITY_CMD:
1117 if (ntokens != 2)
1118 {
1119 send_command_usage(client);
1120 }
1121 else
1122 {
1123 raw_response_handler(client, "OK\r\n");
1124 }
1125 break;
1126
1127 case UNKNOWN_CMD:
1128 send_command_usage(client);
1129 break;
1130
1131 case GET_CMD:
1132 case GETS_CMD:
1133 default:
1134 /* Should already be handled */
1135 abort();
1136 }
1137
1138 if (error == -1)
1139 {
1140 return MEMCACHED_PROTOCOL_ERROR_EVENT;
1141 }
1142 else if (error == 1)
1143 {
1144 return MEMCACHED_PROTOCOL_READ_EVENT;
1145 }
1146 }
1147
1148 if (client->root->callback->post_execute != NULL)
1149 {
1150 client->root->callback->post_execute(client, NULL);
1151 }
1152
1153 /* Move past \n */
1154 ++end;
1155 *length -= end - ptr;
1156 ptr= end;
1157 } while (*length > 0);
1158
1159 *endptr= ptr;
1160 return MEMCACHED_PROTOCOL_READ_EVENT;
1161 }