2 +--------------------------------------------------------------------+
3 | libmemcached - 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 Michael Wallner <mike@php.net> |
13 +--------------------------------------------------------------------+
16 #include "libmemcachedprotocol/common.h"
24 static void print_ascii_command(memcached_protocol_client_st
*client
) {
25 if (client
->is_verbose
) {
26 switch (client
->ascii_command
) {
27 case SET_CMD
: fprintf(stderr
, "%s:%d SET_CMD\n", __FILE__
, __LINE__
); break;
29 case ADD_CMD
: fprintf(stderr
, "%s:%d ADD_CMD\n", __FILE__
, __LINE__
); break;
31 case REPLACE_CMD
: fprintf(stderr
, "%s:%d REPLACE_CMD\n", __FILE__
, __LINE__
); break;
33 case CAS_CMD
: fprintf(stderr
, "%s:%d CAS_CMD\n", __FILE__
, __LINE__
); break;
35 case APPEND_CMD
: fprintf(stderr
, "%s:%d APPEND_CMD\n", __FILE__
, __LINE__
); break;
37 case PREPEND_CMD
: fprintf(stderr
, "%s:%d PREPEND_CMD\n", __FILE__
, __LINE__
); break;
39 case DELETE_CMD
: fprintf(stderr
, "%s:%d DELETE_CMD\n", __FILE__
, __LINE__
); break;
41 case INCR_CMD
: /* FALLTHROUGH */ fprintf(stderr
, "%s:%d INCR_CMD\n", __FILE__
, __LINE__
); break;
43 case DECR_CMD
: fprintf(stderr
, "%s:%d DECR_CMD\n", __FILE__
, __LINE__
); break;
45 case STATS_CMD
: fprintf(stderr
, "%s:%d STATS_CMD\n", __FILE__
, __LINE__
); break;
47 case FLUSH_ALL_CMD
: fprintf(stderr
, "%s:%d FLUSH_ALL_CMD\n", __FILE__
, __LINE__
); break;
49 case VERSION_CMD
: fprintf(stderr
, "%s:%d VERSION_CMD\n", __FILE__
, __LINE__
); break;
51 case QUIT_CMD
: fprintf(stderr
, "%s:%d QUIT_CMD\n", __FILE__
, __LINE__
); break;
53 case VERBOSITY_CMD
: fprintf(stderr
, "%s:%d VERBOSITY_CMD\n", __FILE__
, __LINE__
); break;
55 case GET_CMD
: fprintf(stderr
, "%s:%d GET_CMD\n", __FILE__
, __LINE__
); break;
57 case GETS_CMD
: fprintf(stderr
, "%s:%d GETS_CMD\n", __FILE__
, __LINE__
); break;
60 case UNKNOWN_CMD
: fprintf(stderr
, "%s:%d UNKNOWN_CMD\n", __FILE__
, __LINE__
); break;
66 * Try to parse a key from the string.
67 * @pointer start pointer to a pointer to the string (IN and OUT)
68 * @return length of the string of -1 if this was an illegal key (invalid
69 * characters or invalid length)
72 static uint16_t parse_ascii_key(char **start
) {
75 /* Strip leading whitespaces */
82 while (*c
!= '\0' && !isspace(*c
) && !iscntrl(*c
)) {
87 if (len
== 0 || len
> 240 || (*c
!= '\0' && *c
!= '\r' && iscntrl(*c
))) {
95 * Spool a zero-terminated string
96 * @param client destination
97 * @param text the text to spool
98 * @return status of the spool operation
100 static protocol_binary_response_status
101 ascii_raw_response_handler(memcached_protocol_client_st
*client
, const char *text
) {
102 if (client
->is_verbose
) {
103 fprintf(stderr
, "%s:%d %s\n", __FILE__
, __LINE__
, text
);
106 if (client
->root
->drain(client
) == false) {
107 return PROTOCOL_BINARY_RESPONSE_EINTERNAL
;
110 assert(client
->output
!= NULL
);
112 if (client
->output
== NULL
)
114 /* I can write directly to the socket.... */
117 size_t num_bytes
= len
-offset
;
118 ssize_t nw
= client
->root
->send(client
,
124 if (get_socket_errno() == EWOULDBLOCK
)
128 else if (get_socket_errno() != EINTR
)
130 client
->error
= errno
;
131 return PROTOCOL_BINARY_RESPONSE_EINTERNAL
;
136 offset
+= (size_t)nw
;
138 } while (offset
< len
);
142 return client
->root
->spool(client
, text
, strlen(text
));
146 * Send a "CLIENT_ERROR" message back to the client with the correct
147 * format of the command being sent
148 * @param client the client to send the message to
150 static void send_command_usage(memcached_protocol_client_st
*client
) {
151 const char *errmsg
[] = {
152 [GET_CMD
] = "CLIENT_ERROR: Syntax error: get <key>*\r\n",
153 [GETS_CMD
] = "CLIENT_ERROR: Syntax error: gets <key>*\r\n",
154 [SET_CMD
] = "CLIENT_ERROR: Syntax error: set <key> <flags> <exptime> <bytes> [noreply]\r\n",
155 [ADD_CMD
] = "CLIENT_ERROR: Syntax error: add <key> <flags> <exptime> <bytes> [noreply]\r\n",
157 "CLIENT_ERROR: Syntax error: replace <key> <flags> <exptime> <bytes> [noreply]\r\n",
159 "CLIENT_ERROR: Syntax error: cas <key> <flags> <exptime> <bytes> <casid> [noreply]\r\n",
161 "CLIENT_ERROR: Syntax error: append <key> <flags> <exptime> <bytes> [noreply]\r\n",
163 "CLIENT_ERROR: Syntax error: prepend <key> <flags> <exptime> <bytes> [noreply]\r\n",
164 [DELETE_CMD
] = "CLIENT_ERROR: Syntax error: delete_object <key> [noreply]\r\n",
165 [INCR_CMD
] = "CLIENT_ERROR: Syntax error: incr <key> <value> [noreply]\r\n",
166 [DECR_CMD
] = "CLIENT_ERROR: Syntax error: decr <key> <value> [noreply]\r\n",
167 [STATS_CMD
] = "CLIENT_ERROR: Syntax error: stats [key]\r\n",
168 [FLUSH_ALL_CMD
] = "CLIENT_ERROR: Syntax error: flush_all [timeout] [noreply]\r\n",
169 [VERSION_CMD
] = "CLIENT_ERROR: Syntax error: version\r\n",
170 [QUIT_CMD
] = "CLIENT_ERROR: Syntax error: quit\r\n",
172 [VERBOSITY_CMD
] = "CLIENT_ERROR: Syntax error: verbosity <num>\r\n",
173 [UNKNOWN_CMD
] = "CLIENT_ERROR: Unknown command\r\n",
176 client
->mute
= false;
177 ascii_raw_response_handler(client
, errmsg
[client
->ascii_command
]);
181 * Callback for the VERSION responses
182 * @param cookie client identifier
183 * @param text the length of the body
184 * @param textlen the length of the body
186 static protocol_binary_response_status
187 ascii_version_response_handler(const void *cookie
, const void *text
, uint32_t textlen
) {
188 memcached_protocol_client_st
*client
= (memcached_protocol_client_st
*) cookie
;
189 ascii_raw_response_handler(client
, "VERSION ");
190 client
->root
->spool(client
, text
, textlen
);
191 ascii_raw_response_handler(client
, "\r\n");
192 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
196 * Callback for the GET/GETQ/GETK and GETKQ responses
197 * @param cookie client identifier
198 * @param key the key for the item
199 * @param keylen the length of the key
200 * @param body the length of the body
201 * @param bodylen the length of the body
202 * @param flags the flags for the item
203 * @param cas the CAS id for the item
205 static protocol_binary_response_status
206 ascii_get_response_handler(const void *cookie
, const void *key
, uint16_t keylen
, const void *body
,
207 uint32_t bodylen
, uint32_t flags
, uint64_t cas
) {
208 memcached_protocol_client_st
*client
= (void *) cookie
;
210 strcpy(buffer
, "VALUE ");
211 const char *source
= key
;
212 char *dest
= buffer
+ 6;
214 for (int x
= 0; x
< keylen
; ++x
) {
215 if (*source
!= '\0' && !isspace(*source
) && !iscntrl(*source
)) {
218 return PROTOCOL_BINARY_RESPONSE_EINVAL
; /* key constraints in ascii */
225 size_t used
= (size_t)(dest
- buffer
);
227 if (client
->ascii_command
== GETS_CMD
) {
228 snprintf(dest
, sizeof(buffer
) - used
, " %u %u %" PRIu64
"\r\n", flags
, bodylen
, cas
);
230 snprintf(dest
, sizeof(buffer
) - used
, " %u %u\r\n", flags
, bodylen
);
233 client
->root
->spool(client
, buffer
, strlen(buffer
));
234 client
->root
->spool(client
, body
, bodylen
);
235 client
->root
->spool(client
, "\r\n", 2);
237 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
241 * Callback for the STAT responses
242 * @param cookie client identifier
243 * @param key the key for the item
244 * @param keylen the length of the key
245 * @param body the length of the body
246 * @param bodylen the length of the body
248 static protocol_binary_response_status
ascii_stat_response_handler(const void *cookie
,
249 const void *key
, uint16_t keylen
,
252 memcached_protocol_client_st
*client
= (void *) cookie
;
255 ascii_raw_response_handler(client
, "STAT ");
256 client
->root
->spool(client
, key
, keylen
);
257 ascii_raw_response_handler(client
, " ");
258 client
->root
->spool(client
, body
, bodylen
);
259 ascii_raw_response_handler(client
, "\r\n");
261 ascii_raw_response_handler(client
, "END\r\n");
264 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
268 * Process a get or a gets request.
269 * @param client the client handle
270 * @param buffer the complete get(s) command
271 * @param end the last character in the command
273 static void ascii_process_gets(memcached_protocol_client_st
*client
, char *buffer
, char *end
) {
277 key
+= (client
->ascii_command
== GETS_CMD
) ? 5 : 4;
281 uint16_t nkey
= parse_ascii_key(&key
);
282 if (nkey
== 0) /* Invalid key... stop processing this line */ {
286 (void) client
->root
->callback
->interface
.v1
.get(client
, key
, nkey
, ascii_get_response_handler
);
292 send_command_usage(client
);
294 client
->root
->spool(client
, "END\r\n", 5);
299 * Try to split up the command line "asdf asdf asdf asdf\n" into an
300 * argument vector for easier parsing.
301 * @param start the first character in the command line
302 * @param end the last character in the command line ("\n")
303 * @param vec the vector to insert the pointers into
304 * @size the number of elements in the vector
305 * @return the number of tokens in the vector
307 static int ascii_tokenize_command(char *str
, char *end
, char **vec
, int size
) {
311 /* Skip leading blanks */
312 while (str
< end
&& isspace(*str
)) {
321 /* find the next non-blank field */
322 while (str
< end
&& !isspace(*str
)) {
326 /* zero-terminate it for easier parsing later on */
330 /* Is the vector full? */
340 * If we for some reasons needs to push the line back to read more
341 * data we have to reverse the tokenization. Just do the brain-dead replace
342 * of all '\0' to ' ' and set the last character to '\n'. We could have used
343 * the vector we created, but then we would have to search for all of the
344 * spaces we ignored...
345 * @param start pointer to the first character in the buffer to recover
346 * @param end pointer to the last character in the buffer to recover
348 static void recover_tokenize_command(char *start
, char *end
) {
349 while (start
< end
) {
359 * Convert the textual command into a comcode
361 static enum ascii_cmd
ascii_to_cmd(char *start
, size_t length
) {
366 } commands
[] = {{.cmd
= "get", .len
= 3, .cc
= GET_CMD
},
367 {.cmd
= "gets", .len
= 4, .cc
= GETS_CMD
},
368 {.cmd
= "set", .len
= 3, .cc
= SET_CMD
},
369 {.cmd
= "add", .len
= 3, .cc
= ADD_CMD
},
370 {.cmd
= "replace", .len
= 7, .cc
= REPLACE_CMD
},
371 {.cmd
= "cas", .len
= 3, .cc
= CAS_CMD
},
372 {.cmd
= "append", .len
= 6, .cc
= APPEND_CMD
},
373 {.cmd
= "prepend", .len
= 7, .cc
= PREPEND_CMD
},
374 {.cmd
= "delete_object", .len
= 6, .cc
= DELETE_CMD
},
375 {.cmd
= "incr", .len
= 4, .cc
= INCR_CMD
},
376 {.cmd
= "decr", .len
= 4, .cc
= DECR_CMD
},
377 {.cmd
= "stats", .len
= 5, .cc
= STATS_CMD
},
378 {.cmd
= "flush_all", .len
= 9, .cc
= FLUSH_ALL_CMD
},
379 {.cmd
= "version", .len
= 7, .cc
= VERSION_CMD
},
380 {.cmd
= "quit", .len
= 4, .cc
= QUIT_CMD
},
381 {.cmd
= "verbosity", .len
= 9, .cc
= VERBOSITY_CMD
},
382 {.cmd
= NULL
, .len
= 0, .cc
= UNKNOWN_CMD
}};
385 while (commands
[x
].len
> 0) {
386 if (length
>= commands
[x
].len
) {
387 if (strncmp(start
, commands
[x
].cmd
, commands
[x
].len
) == 0) {
389 if (length
== commands
[x
].len
|| isspace(*(start
+ commands
[x
].len
))) {
390 return commands
[x
].cc
;
401 * Perform a delete_object operation.
403 * @param client client requesting the deletion
404 * @param tokens the command as a vector
405 * @param ntokens the number of items in the vector
407 static void process_delete(memcached_protocol_client_st
*client
, char **tokens
, int ntokens
) {
408 char *key
= tokens
[1];
411 if (ntokens
!= 2 || (nkey
= parse_ascii_key(&key
)) == 0) {
412 send_command_usage(client
);
416 if (client
->root
->callback
->interface
.v1
.delete_object
== NULL
) {
417 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
421 protocol_binary_response_status rval
=
422 client
->root
->callback
->interface
.v1
.delete_object(client
, key
, nkey
, 0);
424 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
) {
425 ascii_raw_response_handler(client
, "DELETED\r\n");
426 } else if (rval
== PROTOCOL_BINARY_RESPONSE_KEY_ENOENT
) {
427 ascii_raw_response_handler(client
, "NOT_FOUND\r\n");
430 snprintf(msg
, sizeof(msg
), "SERVER_ERROR: delete_object failed %u\r\n", (uint32_t) rval
);
431 ascii_raw_response_handler(client
, msg
);
435 static void process_arithmetic(memcached_protocol_client_st
*client
, char **tokens
, int ntokens
) {
436 char *key
= tokens
[1];
439 if (ntokens
!= 3 || (nkey
= parse_ascii_key(&key
)) == 0) {
440 send_command_usage(client
);
447 uint64_t delta
= strtoull(tokens
[2], NULL
, 10);
452 protocol_binary_response_status rval
;
453 if (client
->ascii_command
== INCR_CMD
) {
454 if (client
->root
->callback
->interface
.v1
.increment
== NULL
) {
455 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
458 rval
= client
->root
->callback
->interface
.v1
.increment(client
, key
, nkey
, delta
, 0, 0, &result
,
461 if (client
->root
->callback
->interface
.v1
.decrement
== NULL
) {
462 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
465 rval
= client
->root
->callback
->interface
.v1
.decrement(client
, key
, nkey
, delta
, 0, 0, &result
,
469 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
) {
471 snprintf(buffer
, sizeof(buffer
), "%" PRIu64
"\r\n", result
);
472 ascii_raw_response_handler(client
, buffer
);
474 ascii_raw_response_handler(client
, "NOT_FOUND\r\n");
479 * Process the stats command (with or without a key specified)
480 * @param key pointer to the first character after "stats"
481 * @param end pointer to the "\n"
483 static void process_stats(memcached_protocol_client_st
*client
, char *key
, char *end
) {
484 if (client
->root
->callback
->interface
.v1
.stat
== NULL
) {
485 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
489 while (isspace(*key
)) {
493 uint16_t nkey
= (uint16_t)(end
- key
);
494 (void) client
->root
->callback
->interface
.v1
.stat(client
, key
, nkey
, ascii_stat_response_handler
);
497 static void process_version(memcached_protocol_client_st
*client
, char **tokens
, int ntokens
) {
500 send_command_usage(client
);
504 if (client
->root
->callback
->interface
.v1
.version
== NULL
) {
505 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
509 client
->root
->callback
->interface
.v1
.version(client
, ascii_version_response_handler
);
512 static void process_flush(memcached_protocol_client_st
*client
, char **tokens
, int ntokens
) {
514 send_command_usage(client
);
518 if (client
->root
->callback
->interface
.v1
.flush_object
== NULL
) {
519 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
523 uint32_t timeout
= 0;
526 timeout
= (uint32_t) strtoul(tokens
[1], NULL
, 10);
532 protocol_binary_response_status rval
;
533 rval
= client
->root
->callback
->interface
.v1
.flush_object(client
, timeout
);
534 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
)
535 ascii_raw_response_handler(client
, "OK\r\n");
537 ascii_raw_response_handler(client
, "SERVER_ERROR: internal error\r\n");
541 * Process one of the storage commands
542 * @param client the client performing the operation
543 * @param tokens the command tokens
544 * @param ntokens the number of tokens
545 * @param start pointer to the first character in the line
546 * @param end pointer to the pointer where the last character of this
547 * command is (IN and OUT)
548 * @param length the number of bytes available
549 * @return -1 if an error occurs (and we should just terminate the connection
550 * because we are out of sync)
551 * 0 storage command completed, continue processing
552 * 1 We need more data, so just go ahead and wait for more!
554 static inline int process_storage_command(memcached_protocol_client_st
*client
, char **tokens
,
555 int ntokens
, char *start
, char **end
, ssize_t length
) {
556 (void) ntokens
; /* already checked */
557 char *key
= tokens
[1];
558 uint16_t nkey
= parse_ascii_key(&key
);
561 ascii_raw_response_handler(client
, "CLIENT_ERROR: bad key\r\n");
566 uint32_t flags
= (uint32_t) strtoul(tokens
[2], NULL
, 10);
569 ascii_raw_response_handler(client
, "CLIENT_ERROR: bad key\r\n");
573 uint32_t timeout
= (uint32_t) strtoul(tokens
[3], NULL
, 10);
576 ascii_raw_response_handler(client
, "CLIENT_ERROR: bad key\r\n");
580 unsigned long nbytes
= strtoul(tokens
[4], NULL
, 10);
583 ascii_raw_response_handler(client
, "CLIENT_ERROR: bad key\r\n");
587 /* Do we have all data? */
588 unsigned long need
= nbytes
+ (unsigned long) ((*end
- start
) + 1) + 2; /* \n\r\n */
589 if ((ssize_t
) need
> length
) {
590 /* Keep on reading */
591 recover_tokenize_command(start
, *end
);
595 void *data
= (*end
) + 1;
598 protocol_binary_response_status rval
;
599 switch (client
->ascii_command
) {
601 rval
= client
->root
->callback
->interface
.v1
.set(
602 client
, key
, (uint16_t) nkey
, data
, (uint32_t) nbytes
, flags
, timeout
, cas
, &result_cas
);
605 rval
= client
->root
->callback
->interface
.v1
.add(client
, key
, (uint16_t) nkey
, data
,
606 (uint32_t) nbytes
, flags
, timeout
, &result_cas
);
610 cas
= strtoull(tokens
[5], NULL
, 10);
613 ascii_raw_response_handler(client
, "CLIENT_ERROR: bad key\r\n");
618 rval
= client
->root
->callback
->interface
.v1
.replace(
619 client
, key
, (uint16_t) nkey
, data
, (uint32_t) nbytes
, flags
, timeout
, cas
, &result_cas
);
622 rval
= client
->root
->callback
->interface
.v1
.append(client
, key
, (uint16_t) nkey
, data
,
623 (uint32_t) nbytes
, cas
, &result_cas
);
626 rval
= client
->root
->callback
->interface
.v1
.prepend(client
, key
, (uint16_t) nkey
, data
,
627 (uint32_t) nbytes
, cas
, &result_cas
);
630 /* gcc complains if I don't put all of the enums in here.. */
642 default: abort(); /* impossible */
645 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
) {
646 ascii_raw_response_handler(client
, "STORED\r\n");
648 if (client
->ascii_command
== CAS_CMD
) {
649 if (rval
== PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS
) {
650 ascii_raw_response_handler(client
, "EXISTS\r\n");
651 } else if (rval
== PROTOCOL_BINARY_RESPONSE_KEY_ENOENT
) {
652 ascii_raw_response_handler(client
, "NOT_FOUND\r\n");
654 ascii_raw_response_handler(client
, "NOT_STORED\r\n");
657 ascii_raw_response_handler(client
, "NOT_STORED\r\n");
666 static int process_cas_command(memcached_protocol_client_st
*client
, char **tokens
, int ntokens
,
667 char *start
, char **end
, ssize_t length
) {
669 send_command_usage(client
);
673 if (client
->root
->callback
->interface
.v1
.replace
== NULL
) {
674 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
678 return process_storage_command(client
, tokens
, ntokens
, start
, end
, length
);
681 static int process_set_command(memcached_protocol_client_st
*client
, char **tokens
, int ntokens
,
682 char *start
, char **end
, ssize_t length
) {
684 send_command_usage(client
);
688 if (client
->root
->callback
->interface
.v1
.set
== NULL
) {
689 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
693 return process_storage_command(client
, tokens
, ntokens
, start
, end
, length
);
696 static int process_add_command(memcached_protocol_client_st
*client
, char **tokens
, int ntokens
,
697 char *start
, char **end
, ssize_t length
) {
699 send_command_usage(client
);
703 if (client
->root
->callback
->interface
.v1
.add
== NULL
) {
704 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
708 return process_storage_command(client
, tokens
, ntokens
, start
, end
, length
);
711 static int process_replace_command(memcached_protocol_client_st
*client
, char **tokens
, int ntokens
,
712 char *start
, char **end
, ssize_t length
) {
714 send_command_usage(client
);
718 if (client
->root
->callback
->interface
.v1
.replace
== NULL
) {
719 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
723 return process_storage_command(client
, tokens
, ntokens
, start
, end
, length
);
726 static int process_append_command(memcached_protocol_client_st
*client
, char **tokens
, int ntokens
,
727 char *start
, char **end
, ssize_t length
) {
729 send_command_usage(client
);
733 if (client
->root
->callback
->interface
.v1
.append
== NULL
) {
734 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
738 return process_storage_command(client
, tokens
, ntokens
, start
, end
, length
);
741 static int process_prepend_command(memcached_protocol_client_st
*client
, char **tokens
, int ntokens
,
742 char *start
, char **end
, ssize_t length
) {
744 send_command_usage(client
);
748 if (client
->root
->callback
->interface
.v1
.prepend
== NULL
) {
749 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
753 return process_storage_command(client
, tokens
, ntokens
, start
, end
, length
);
757 * The ASCII protocol support is just one giant big hack. Instead of adding
758 * a optimal ascii support, I just convert the ASCII commands to the binary
759 * protocol and calls back into the command handlers for the binary protocol ;)
761 memcached_protocol_event_t
762 memcached_ascii_protocol_process_data(memcached_protocol_client_st
*client
, ssize_t
*length
,
764 char *ptr
= (char *) client
->root
->input_buffer
;
768 /* Do we have \n (indicating the command preamble)*/
769 char *end
= memchr(ptr
, '\n', (size_t) *length
);
772 return MEMCACHED_PROTOCOL_READ_EVENT
;
775 client
->ascii_command
= ascii_to_cmd(ptr
, (size_t)(*length
));
777 /* we got all data available, execute the callback! */
778 if (client
->root
->callback
->pre_execute
!= NULL
) {
779 client
->root
->callback
->pre_execute(client
, NULL
);
782 /* A multiget lists all of the keys, and I don't want to have an
783 * avector of let's say 512 pointers to tokenize all of them, so let's
784 * just handle them immediately
786 if (client
->ascii_command
== GET_CMD
|| client
->ascii_command
== GETS_CMD
) {
787 if (client
->root
->callback
->interface
.v1
.get
!= NULL
) {
788 ascii_process_gets(client
, ptr
, end
);
790 ascii_raw_response_handler(client
, "SERVER_ERROR: Command not implemented\n");
793 /* None of the defined commands takes 10 parameters, so lets just use
794 * that as a maximum limit.
797 int ntokens
= ascii_tokenize_command(ptr
, end
, tokens
, 10);
800 client
->mute
= strcmp(tokens
[ntokens
- 1], "noreply") == 0;
802 --ntokens
; /* processed noreply token*/
808 print_ascii_command(client
);
809 switch (client
->ascii_command
) {
810 case SET_CMD
: error
= process_set_command(client
, tokens
, ntokens
, ptr
, &end
, *length
); break;
812 case ADD_CMD
: error
= process_add_command(client
, tokens
, ntokens
, ptr
, &end
, *length
); break;
815 error
= process_replace_command(client
, tokens
, ntokens
, ptr
, &end
, *length
);
818 case CAS_CMD
: error
= process_cas_command(client
, tokens
, ntokens
, ptr
, &end
, *length
); break;
821 error
= process_append_command(client
, tokens
, ntokens
, ptr
, &end
, *length
);
825 error
= process_prepend_command(client
, tokens
, ntokens
, ptr
, &end
, *length
);
828 case DELETE_CMD
: process_delete(client
, tokens
, ntokens
); break;
830 case INCR_CMD
: /* FALLTHROUGH */
831 case DECR_CMD
: process_arithmetic(client
, tokens
, ntokens
); break;
835 send_command_usage(client
);
837 recover_tokenize_command(ptr
, end
);
838 process_stats(client
, ptr
+ 6, end
);
842 case FLUSH_ALL_CMD
: process_flush(client
, tokens
, ntokens
); break;
846 send_command_usage(client
);
848 process_version(client
, tokens
, ntokens
);
853 if (ntokens
!= 1 || client
->mute
) {
854 send_command_usage(client
);
856 if (client
->root
->callback
->interface
.v1
.quit
!= NULL
) {
857 client
->root
->callback
->interface
.v1
.quit(client
);
860 return MEMCACHED_PROTOCOL_ERROR_EVENT
;
866 send_command_usage(client
);
868 ascii_raw_response_handler(client
, "OK\r\n");
872 case UNKNOWN_CMD
: send_command_usage(client
); break;
877 /* Should already be handled */
882 return MEMCACHED_PROTOCOL_ERROR_EVENT
;
883 } else if (error
== 1) {
884 return MEMCACHED_PROTOCOL_READ_EVENT
;
888 if (client
->root
->callback
->post_execute
!= NULL
) {
889 client
->root
->callback
->post_execute(client
, NULL
);
894 *length
-= end
- ptr
;
896 } while (*length
> 0);
899 return MEMCACHED_PROTOCOL_READ_EVENT
;