2 +--------------------------------------------------------------------+
3 | libmemcached-awesome - C/C++ Client Library for memcached |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted under the terms of the BSD license. |
7 | You should have received a copy of the license in a bundled file |
8 | named LICENSE; in case you did not receive a copy you can review |
9 | the terms online at: https://opensource.org/licenses/BSD-3-Clause |
10 +--------------------------------------------------------------------+
11 | Copyright (c) 2006-2014 Brian Aker https://datadifferential.com/ |
12 | Copyright (c) 2020-2021 Michael Wallner https://awesome.co/ |
13 +--------------------------------------------------------------------+
16 #include "libmemcachedprotocol/common.h"
25 static void print_ascii_command(memcached_protocol_client_st
*client
) {
26 if (client
->is_verbose
) {
27 switch (client
->ascii_command
) {
29 fprintf(stderr
, "%s:%d SET_CMD\n", __FILE__
, __LINE__
);
33 fprintf(stderr
, "%s:%d ADD_CMD\n", __FILE__
, __LINE__
);
37 fprintf(stderr
, "%s:%d REPLACE_CMD\n", __FILE__
, __LINE__
);
41 fprintf(stderr
, "%s:%d CAS_CMD\n", __FILE__
, __LINE__
);
45 fprintf(stderr
, "%s:%d APPEND_CMD\n", __FILE__
, __LINE__
);
49 fprintf(stderr
, "%s:%d PREPEND_CMD\n", __FILE__
, __LINE__
);
53 fprintf(stderr
, "%s:%d DELETE_CMD\n", __FILE__
, __LINE__
);
56 case INCR_CMD
: /* FALLTHROUGH */
57 fprintf(stderr
, "%s:%d INCR_CMD\n", __FILE__
, __LINE__
);
61 fprintf(stderr
, "%s:%d DECR_CMD\n", __FILE__
, __LINE__
);
65 fprintf(stderr
, "%s:%d STATS_CMD\n", __FILE__
, __LINE__
);
69 fprintf(stderr
, "%s:%d FLUSH_ALL_CMD\n", __FILE__
, __LINE__
);
73 fprintf(stderr
, "%s:%d VERSION_CMD\n", __FILE__
, __LINE__
);
77 fprintf(stderr
, "%s:%d QUIT_CMD\n", __FILE__
, __LINE__
);
81 fprintf(stderr
, "%s:%d VERBOSITY_CMD\n", __FILE__
, __LINE__
);
85 fprintf(stderr
, "%s:%d GET_CMD\n", __FILE__
, __LINE__
);
89 fprintf(stderr
, "%s:%d GETS_CMD\n", __FILE__
, __LINE__
);
94 fprintf(stderr
, "%s:%d UNKNOWN_CMD\n", __FILE__
, __LINE__
);
101 * Try to parse a key from the string.
102 * @pointer start pointer to a pointer to the string (IN and OUT)
103 * @return length of the string of -1 if this was an illegal key (invalid
104 * characters or invalid length)
107 static uint16_t parse_ascii_key(char **start
) {
110 /* Strip leading whitespaces */
111 while (isspace(*c
)) {
117 while (*c
!= '\0' && !isspace(*c
) && !iscntrl(*c
)) {
122 if (len
== 0 || len
> 240 || (*c
!= '\0' && *c
!= '\r' && iscntrl(*c
))) {
130 * Spool a zero-terminated string
131 * @param client destination
132 * @param text the text to spool
133 * @return status of the spool operation
135 static protocol_binary_response_status
136 ascii_raw_response_handler(memcached_protocol_client_st
*client
, const char *text
) {
137 if (client
->is_verbose
) {
138 fprintf(stderr
, "%s:%d %s\n", __FILE__
, __LINE__
, text
);
141 if (client
->root
->drain(client
) == false) {
142 return PROTOCOL_BINARY_RESPONSE_EINTERNAL
;
145 assert(client
->output
);
147 if (client
->output
== NULL
)
149 /* I can write directly to the socket.... */
152 size_t num_bytes
= len
-offset
;
153 ssize_t nw
= client
->root
->send(client
,
159 if (get_socket_errno() == EWOULDBLOCK
)
163 else if (get_socket_errno() != EINTR
)
165 client
->error
= errno
;
166 return PROTOCOL_BINARY_RESPONSE_EINTERNAL
;
171 offset
+= (size_t)nw
;
173 } while (offset
< len
);
177 return client
->root
->spool(client
, text
, strlen(text
));
181 * Send a "CLIENT_ERROR" message back to the client with the correct
182 * format of the command being sent
183 * @param client the client to send the message to
185 static void send_command_usage(memcached_protocol_client_st
*client
) {
186 const char *errmsg
[] = {
187 [GET_CMD
] = "CLIENT_ERROR: Syntax error: get <key>*\r\n",
188 [GETS_CMD
] = "CLIENT_ERROR: Syntax error: gets <key>*\r\n",
189 [SET_CMD
] = "CLIENT_ERROR: Syntax error: set <key> <flags> <exptime> <bytes> [noreply]\r\n",
190 [ADD_CMD
] = "CLIENT_ERROR: Syntax error: add <key> <flags> <exptime> <bytes> [noreply]\r\n",
192 "CLIENT_ERROR: Syntax error: replace <key> <flags> <exptime> <bytes> [noreply]\r\n",
194 "CLIENT_ERROR: Syntax error: cas <key> <flags> <exptime> <bytes> <casid> [noreply]\r\n",
196 "CLIENT_ERROR: Syntax error: append <key> <flags> <exptime> <bytes> [noreply]\r\n",
198 "CLIENT_ERROR: Syntax error: prepend <key> <flags> <exptime> <bytes> [noreply]\r\n",
199 [DELETE_CMD
] = "CLIENT_ERROR: Syntax error: delete_object <key> [noreply]\r\n",
200 [INCR_CMD
] = "CLIENT_ERROR: Syntax error: incr <key> <value> [noreply]\r\n",
201 [DECR_CMD
] = "CLIENT_ERROR: Syntax error: decr <key> <value> [noreply]\r\n",
202 [STATS_CMD
] = "CLIENT_ERROR: Syntax error: stats [key]\r\n",
203 [FLUSH_ALL_CMD
] = "CLIENT_ERROR: Syntax error: flush_all [timeout] [noreply]\r\n",
204 [VERSION_CMD
] = "CLIENT_ERROR: Syntax error: version\r\n",
205 [QUIT_CMD
] = "CLIENT_ERROR: Syntax error: quit\r\n",
207 [VERBOSITY_CMD
] = "CLIENT_ERROR: Syntax error: verbosity <num>\r\n",
208 [UNKNOWN_CMD
] = "CLIENT_ERROR: Unknown command\r\n",
211 client
->mute
= false;
212 ascii_raw_response_handler(client
, errmsg
[client
->ascii_command
]);
216 * Callback for the VERSION responses
217 * @param cookie client identifier
218 * @param text the length of the body
219 * @param textlen the length of the body
221 static protocol_binary_response_status
222 ascii_version_response_handler(const void *cookie
, const void *text
, uint32_t textlen
) {
223 memcached_protocol_client_st
*client
= (memcached_protocol_client_st
*) cookie
;
224 ascii_raw_response_handler(client
, "VERSION ");
225 client
->root
->spool(client
, text
, textlen
);
226 ascii_raw_response_handler(client
, "\r\n");
227 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
231 * Callback for the GET/GETQ/GETK and GETKQ responses
232 * @param cookie client identifier
233 * @param key the key for the item
234 * @param keylen the length of the key
235 * @param body the length of the body
236 * @param bodylen the length of the body
237 * @param flags the flags for the item
238 * @param cas the CAS id for the item
240 static protocol_binary_response_status
241 ascii_get_response_handler(const void *cookie
, const void *key
, uint16_t keylen
, const void *body
,
242 uint32_t bodylen
, uint32_t flags
, uint64_t cas
) {
243 memcached_protocol_client_st
*client
= (void *) cookie
;
245 strcpy(buffer
, "VALUE ");
246 const char *source
= key
;
247 char *dest
= buffer
+ 6;
249 for (int x
= 0; x
< keylen
; ++x
) {
250 if (*source
!= '\0' && !isspace(*source
) && !iscntrl(*source
)) {
253 return PROTOCOL_BINARY_RESPONSE_EINVAL
; /* key constraints in ascii */
260 size_t used
= (size_t)(dest
- buffer
);
262 if (client
->ascii_command
== GETS_CMD
) {
263 snprintf(dest
, sizeof(buffer
) - used
, " %u %u %" PRIu64
"\r\n", flags
, bodylen
, cas
);
265 snprintf(dest
, sizeof(buffer
) - used
, " %u %u\r\n", flags
, bodylen
);
268 client
->root
->spool(client
, buffer
, strlen(buffer
));
269 client
->root
->spool(client
, body
, bodylen
);
270 client
->root
->spool(client
, "\r\n", 2);
272 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
276 * Callback for the STAT responses
277 * @param cookie client identifier
278 * @param key the key for the item
279 * @param keylen the length of the key
280 * @param body the length of the body
281 * @param bodylen the length of the body
283 static protocol_binary_response_status
ascii_stat_response_handler(const void *cookie
,
284 const void *key
, uint16_t keylen
,
287 memcached_protocol_client_st
*client
= (void *) cookie
;
290 ascii_raw_response_handler(client
, "STAT ");
291 client
->root
->spool(client
, key
, keylen
);
292 ascii_raw_response_handler(client
, " ");
293 client
->root
->spool(client
, body
, bodylen
);
294 ascii_raw_response_handler(client
, "\r\n");
296 ascii_raw_response_handler(client
, "END\r\n");
299 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
303 * Process a get or a gets request.
304 * @param client the client handle
305 * @param buffer the complete get(s) command
306 * @param end the last character in the command
308 static void ascii_process_gets(memcached_protocol_client_st
*client
, char *buffer
, char *end
) {
312 key
+= (client
->ascii_command
== GETS_CMD
) ? 5 : 4;
316 uint16_t nkey
= parse_ascii_key(&key
);
317 if (nkey
== 0) /* Invalid key... stop processing this line */ {
321 (void) client
->root
->callback
->interface
.v1
.get(client
, key
, nkey
, ascii_get_response_handler
);
327 send_command_usage(client
);
329 client
->root
->spool(client
, "END\r\n", 5);
334 * Try to split up the command line "asdf asdf asdf asdf\n" into an
335 * argument vector for easier parsing.
336 * @param start the first character in the command line
337 * @param end the last character in the command line ("\n")
338 * @param vec the vector to insert the pointers into
339 * @size the number of elements in the vector
340 * @return the number of tokens in the vector
342 static int ascii_tokenize_command(char *str
, char *end
, char **vec
, int size
) {
346 /* Skip leading blanks */
347 while (str
< end
&& isspace(*str
)) {
356 /* find the next non-blank field */
357 while (str
< end
&& !isspace(*str
)) {
361 /* zero-terminate it for easier parsing later on */
365 /* Is the vector full? */
375 * If we for some reasons needs to push the line back to read more
376 * data we have to reverse the tokenization. Just do the brain-dead replace
377 * of all '\0' to ' ' and set the last character to '\n'. We could have used
378 * the vector we created, but then we would have to search for all of the
379 * spaces we ignored...
380 * @param start pointer to the first character in the buffer to recover
381 * @param end pointer to the last character in the buffer to recover
383 static void recover_tokenize_command(char *start
, char *end
) {
384 while (start
< end
) {
394 * Convert the textual command into a comcode
396 static enum ascii_cmd
ascii_to_cmd(char *start
, size_t length
) {
401 } commands
[] = {{.cmd
= "get", .len
= 3, .cc
= GET_CMD
},
402 {.cmd
= "gets", .len
= 4, .cc
= GETS_CMD
},
403 {.cmd
= "set", .len
= 3, .cc
= SET_CMD
},
404 {.cmd
= "add", .len
= 3, .cc
= ADD_CMD
},
405 {.cmd
= "replace", .len
= 7, .cc
= REPLACE_CMD
},
406 {.cmd
= "cas", .len
= 3, .cc
= CAS_CMD
},
407 {.cmd
= "append", .len
= 6, .cc
= APPEND_CMD
},
408 {.cmd
= "prepend", .len
= 7, .cc
= PREPEND_CMD
},
409 {.cmd
= "delete_object", .len
= 6, .cc
= DELETE_CMD
},
410 {.cmd
= "incr", .len
= 4, .cc
= INCR_CMD
},
411 {.cmd
= "decr", .len
= 4, .cc
= DECR_CMD
},
412 {.cmd
= "stats", .len
= 5, .cc
= STATS_CMD
},
413 {.cmd
= "flush_all", .len
= 9, .cc
= FLUSH_ALL_CMD
},
414 {.cmd
= "version", .len
= 7, .cc
= VERSION_CMD
},
415 {.cmd
= "quit", .len
= 4, .cc
= QUIT_CMD
},
416 {.cmd
= "verbosity", .len
= 9, .cc
= VERBOSITY_CMD
},
417 {.cmd
= NULL
, .len
= 0, .cc
= UNKNOWN_CMD
}};
420 while (commands
[x
].len
> 0) {
421 if (length
>= commands
[x
].len
) {
422 if (strncmp(start
, commands
[x
].cmd
, commands
[x
].len
) == 0) {
424 if (length
== commands
[x
].len
|| isspace(*(start
+ commands
[x
].len
))) {
425 return commands
[x
].cc
;
436 * Perform a delete_object operation.
438 * @param client client requesting the deletion
439 * @param tokens the command as a vector
440 * @param ntokens the number of items in the vector
442 static void process_delete(memcached_protocol_client_st
*client
, char **tokens
, int ntokens
) {
443 char *key
= tokens
[1];
446 if (ntokens
!= 2 || (nkey
= parse_ascii_key(&key
)) == 0) {
447 send_command_usage(client
);
451 if (client
->root
->callback
->interface
.v1
.delete_object
== NULL
) {
452 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
456 protocol_binary_response_status rval
=
457 client
->root
->callback
->interface
.v1
.delete_object(client
, key
, nkey
, 0);
459 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
) {
460 ascii_raw_response_handler(client
, "DELETED\r\n");
461 } else if (rval
== PROTOCOL_BINARY_RESPONSE_KEY_ENOENT
) {
462 ascii_raw_response_handler(client
, "NOT_FOUND\r\n");
465 snprintf(msg
, sizeof(msg
), "SERVER_ERROR: delete_object failed %u\r\n", (uint32_t) rval
);
466 ascii_raw_response_handler(client
, msg
);
470 static void process_arithmetic(memcached_protocol_client_st
*client
, char **tokens
, int ntokens
) {
471 char *key
= tokens
[1];
474 if (ntokens
!= 3 || (nkey
= parse_ascii_key(&key
)) == 0) {
475 send_command_usage(client
);
482 uint64_t delta
= strtoull(tokens
[2], NULL
, 10);
487 protocol_binary_response_status rval
;
488 if (client
->ascii_command
== INCR_CMD
) {
489 if (client
->root
->callback
->interface
.v1
.increment
== NULL
) {
490 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
493 rval
= client
->root
->callback
->interface
.v1
.increment(client
, key
, nkey
, delta
, 0, 0, &result
,
496 if (client
->root
->callback
->interface
.v1
.decrement
== NULL
) {
497 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
500 rval
= client
->root
->callback
->interface
.v1
.decrement(client
, key
, nkey
, delta
, 0, 0, &result
,
504 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
) {
506 snprintf(buffer
, sizeof(buffer
), "%" PRIu64
"\r\n", result
);
507 ascii_raw_response_handler(client
, buffer
);
509 ascii_raw_response_handler(client
, "NOT_FOUND\r\n");
514 * Process the stats command (with or without a key specified)
515 * @param key pointer to the first character after "stats"
516 * @param end pointer to the "\n"
518 static void process_stats(memcached_protocol_client_st
*client
, char *key
, char *end
) {
519 if (client
->root
->callback
->interface
.v1
.stat
== NULL
) {
520 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
524 while (isspace(*key
)) {
528 uint16_t nkey
= (uint16_t)(end
- key
);
529 (void) client
->root
->callback
->interface
.v1
.stat(client
, key
, nkey
, ascii_stat_response_handler
);
532 static void process_version(memcached_protocol_client_st
*client
, char **tokens
, int ntokens
) {
535 send_command_usage(client
);
539 if (client
->root
->callback
->interface
.v1
.version
== NULL
) {
540 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
544 client
->root
->callback
->interface
.v1
.version(client
, ascii_version_response_handler
);
547 static void process_flush(memcached_protocol_client_st
*client
, char **tokens
, int ntokens
) {
549 send_command_usage(client
);
553 if (client
->root
->callback
->interface
.v1
.flush_object
== NULL
) {
554 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
558 uint32_t timeout
= 0;
561 timeout
= (uint32_t) strtoul(tokens
[1], NULL
, 10);
567 protocol_binary_response_status rval
;
568 rval
= client
->root
->callback
->interface
.v1
.flush_object(client
, timeout
);
569 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
)
570 ascii_raw_response_handler(client
, "OK\r\n");
572 ascii_raw_response_handler(client
, "SERVER_ERROR: internal error\r\n");
576 * Process one of the storage commands
577 * @param client the client performing the operation
578 * @param tokens the command tokens
579 * @param ntokens the number of tokens
580 * @param start pointer to the first character in the line
581 * @param end pointer to the pointer where the last character of this
582 * command is (IN and OUT)
583 * @param length the number of bytes available
584 * @return -1 if an error occurs (and we should just terminate the connection
585 * because we are out of sync)
586 * 0 storage command completed, continue processing
587 * 1 We need more data, so just go ahead and wait for more!
589 static inline int process_storage_command(memcached_protocol_client_st
*client
, char **tokens
,
590 int ntokens
, char *start
, char **end
, ssize_t length
) {
591 (void) ntokens
; /* already checked */
592 char *key
= tokens
[1];
593 uint16_t nkey
= parse_ascii_key(&key
);
596 ascii_raw_response_handler(client
, "CLIENT_ERROR: bad key\r\n");
601 uint32_t flags
= (uint32_t) strtoul(tokens
[2], NULL
, 10);
604 ascii_raw_response_handler(client
, "CLIENT_ERROR: bad key\r\n");
608 uint32_t timeout
= (uint32_t) strtoul(tokens
[3], NULL
, 10);
611 ascii_raw_response_handler(client
, "CLIENT_ERROR: bad key\r\n");
615 unsigned long nbytes
= strtoul(tokens
[4], NULL
, 10);
618 ascii_raw_response_handler(client
, "CLIENT_ERROR: bad key\r\n");
622 /* Do we have all data? */
623 unsigned long need
= nbytes
+ (unsigned long) ((*end
- start
) + 1) + 2; /* \n\r\n */
624 if ((ssize_t
) need
> length
) {
625 /* Keep on reading */
626 recover_tokenize_command(start
, *end
);
630 void *data
= (*end
) + 1;
633 protocol_binary_response_status rval
;
634 switch (client
->ascii_command
) {
636 rval
= client
->root
->callback
->interface
.v1
.set(
637 client
, key
, (uint16_t) nkey
, data
, (uint32_t) nbytes
, flags
, timeout
, cas
, &result_cas
);
640 rval
= client
->root
->callback
->interface
.v1
.add(client
, key
, (uint16_t) nkey
, data
,
641 (uint32_t) nbytes
, flags
, timeout
, &result_cas
);
645 cas
= strtoull(tokens
[5], NULL
, 10);
648 ascii_raw_response_handler(client
, "CLIENT_ERROR: bad key\r\n");
653 rval
= client
->root
->callback
->interface
.v1
.replace(
654 client
, key
, (uint16_t) nkey
, data
, (uint32_t) nbytes
, flags
, timeout
, cas
, &result_cas
);
657 rval
= client
->root
->callback
->interface
.v1
.append(client
, key
, (uint16_t) nkey
, data
,
658 (uint32_t) nbytes
, cas
, &result_cas
);
661 rval
= client
->root
->callback
->interface
.v1
.prepend(client
, key
, (uint16_t) nkey
, data
,
662 (uint32_t) nbytes
, cas
, &result_cas
);
665 /* gcc complains if I don't put all of the enums in here.. */
678 abort(); /* impossible */
681 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
) {
682 ascii_raw_response_handler(client
, "STORED\r\n");
684 if (client
->ascii_command
== CAS_CMD
) {
685 if (rval
== PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS
) {
686 ascii_raw_response_handler(client
, "EXISTS\r\n");
687 } else if (rval
== PROTOCOL_BINARY_RESPONSE_KEY_ENOENT
) {
688 ascii_raw_response_handler(client
, "NOT_FOUND\r\n");
690 ascii_raw_response_handler(client
, "NOT_STORED\r\n");
693 ascii_raw_response_handler(client
, "NOT_STORED\r\n");
702 static int process_cas_command(memcached_protocol_client_st
*client
, char **tokens
, int ntokens
,
703 char *start
, char **end
, ssize_t length
) {
705 send_command_usage(client
);
709 if (client
->root
->callback
->interface
.v1
.replace
== NULL
) {
710 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
714 return process_storage_command(client
, tokens
, ntokens
, start
, end
, length
);
717 static int process_set_command(memcached_protocol_client_st
*client
, char **tokens
, int ntokens
,
718 char *start
, char **end
, ssize_t length
) {
720 send_command_usage(client
);
724 if (client
->root
->callback
->interface
.v1
.set
== NULL
) {
725 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
729 return process_storage_command(client
, tokens
, ntokens
, start
, end
, length
);
732 static int process_add_command(memcached_protocol_client_st
*client
, char **tokens
, int ntokens
,
733 char *start
, char **end
, ssize_t length
) {
735 send_command_usage(client
);
739 if (client
->root
->callback
->interface
.v1
.add
== NULL
) {
740 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
744 return process_storage_command(client
, tokens
, ntokens
, start
, end
, length
);
747 static int process_replace_command(memcached_protocol_client_st
*client
, char **tokens
, int ntokens
,
748 char *start
, char **end
, ssize_t length
) {
750 send_command_usage(client
);
754 if (client
->root
->callback
->interface
.v1
.replace
== NULL
) {
755 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
759 return process_storage_command(client
, tokens
, ntokens
, start
, end
, length
);
762 static int process_append_command(memcached_protocol_client_st
*client
, char **tokens
, int ntokens
,
763 char *start
, char **end
, ssize_t length
) {
765 send_command_usage(client
);
769 if (client
->root
->callback
->interface
.v1
.append
== NULL
) {
770 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
774 return process_storage_command(client
, tokens
, ntokens
, start
, end
, length
);
777 static int process_prepend_command(memcached_protocol_client_st
*client
, char **tokens
, int ntokens
,
778 char *start
, char **end
, ssize_t length
) {
780 send_command_usage(client
);
784 if (client
->root
->callback
->interface
.v1
.prepend
== NULL
) {
785 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
789 return process_storage_command(client
, tokens
, ntokens
, start
, end
, length
);
793 * The ASCII protocol support is just one giant big hack. Instead of adding
794 * a optimal ascii support, I just convert the ASCII commands to the binary
795 * protocol and calls back into the command handlers for the binary protocol ;)
797 memcached_protocol_event_t
798 memcached_ascii_protocol_process_data(memcached_protocol_client_st
*client
, ssize_t
*length
,
800 char *ptr
= (char *) client
->root
->input_buffer
;
804 /* Do we have \n (indicating the command preamble)*/
805 char *end
= memchr(ptr
, '\n', (size_t) *length
);
808 return MEMCACHED_PROTOCOL_READ_EVENT
;
811 client
->ascii_command
= ascii_to_cmd(ptr
, (size_t)(*length
));
813 /* we got all data available, execute the callback! */
814 if (client
->root
->callback
->pre_execute
) {
815 client
->root
->callback
->pre_execute(client
, NULL
);
818 /* A multiget lists all of the keys, and I don't want to have an
819 * avector of let's say 512 pointers to tokenize all of them, so let's
820 * just handle them immediately
822 if (client
->ascii_command
== GET_CMD
|| client
->ascii_command
== GETS_CMD
) {
823 if (client
->root
->callback
->interface
.v1
.get
) {
824 ascii_process_gets(client
, ptr
, end
);
826 ascii_raw_response_handler(client
, "SERVER_ERROR: Command not implemented\n");
829 /* None of the defined commands takes 10 parameters, so lets just use
830 * that as a maximum limit.
833 int ntokens
= ascii_tokenize_command(ptr
, end
, tokens
, 10);
836 client
->mute
= strcmp(tokens
[ntokens
- 1], "noreply") == 0;
838 --ntokens
; /* processed noreply token*/
844 print_ascii_command(client
);
845 switch (client
->ascii_command
) {
847 error
= process_set_command(client
, tokens
, ntokens
, ptr
, &end
, *length
);
851 error
= process_add_command(client
, tokens
, ntokens
, ptr
, &end
, *length
);
855 error
= process_replace_command(client
, tokens
, ntokens
, ptr
, &end
, *length
);
859 error
= process_cas_command(client
, tokens
, ntokens
, ptr
, &end
, *length
);
863 error
= process_append_command(client
, tokens
, ntokens
, ptr
, &end
, *length
);
867 error
= process_prepend_command(client
, tokens
, ntokens
, ptr
, &end
, *length
);
871 process_delete(client
, tokens
, ntokens
);
874 case INCR_CMD
: /* FALLTHROUGH */
876 process_arithmetic(client
, tokens
, ntokens
);
881 send_command_usage(client
);
883 recover_tokenize_command(ptr
, end
);
884 process_stats(client
, ptr
+ 6, end
);
889 process_flush(client
, tokens
, ntokens
);
894 send_command_usage(client
);
896 process_version(client
, tokens
, ntokens
);
901 if (ntokens
!= 1 || client
->mute
) {
902 send_command_usage(client
);
904 if (client
->root
->callback
->interface
.v1
.quit
) {
905 client
->root
->callback
->interface
.v1
.quit(client
);
908 return MEMCACHED_PROTOCOL_ERROR_EVENT
;
914 send_command_usage(client
);
916 ascii_raw_response_handler(client
, "OK\r\n");
921 send_command_usage(client
);
927 /* Should already be handled */
932 return MEMCACHED_PROTOCOL_ERROR_EVENT
;
933 } else if (error
== 1) {
934 return MEMCACHED_PROTOCOL_READ_EVENT
;
938 if (client
->root
->callback
->post_execute
) {
939 client
->root
->callback
->post_execute(client
, NULL
);
944 *length
-= end
- ptr
;
946 } while (*length
> 0);
949 return MEMCACHED_PROTOCOL_READ_EVENT
;