1 /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
5 * Copyright (C) 2011 Data Differential, http://datadifferential.com/
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
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
19 * * The names of its contributors may not be used to endorse or
20 * promote products derived from this software without specific prior
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.
37 #include "libmemcachedprotocol/common.h"
46 static void print_ascii_command(memcached_protocol_client_st
*client
)
48 if (client
->is_verbose
)
50 switch (client
->ascii_command
)
53 fprintf(stderr
, "%s:%d SET_CMD\n", __FILE__
, __LINE__
);
57 fprintf(stderr
, "%s:%d ADD_CMD\n", __FILE__
, __LINE__
);
61 fprintf(stderr
, "%s:%d REPLACE_CMD\n", __FILE__
, __LINE__
);
65 fprintf(stderr
, "%s:%d CAS_CMD\n", __FILE__
, __LINE__
);
69 fprintf(stderr
, "%s:%d APPEND_CMD\n", __FILE__
, __LINE__
);
73 fprintf(stderr
, "%s:%d PREPEND_CMD\n", __FILE__
, __LINE__
);
77 fprintf(stderr
, "%s:%d DELETE_CMD\n", __FILE__
, __LINE__
);
80 case INCR_CMD
: /* FALLTHROUGH */
81 fprintf(stderr
, "%s:%d INCR_CMD\n", __FILE__
, __LINE__
);
85 fprintf(stderr
, "%s:%d DECR_CMD\n", __FILE__
, __LINE__
);
89 fprintf(stderr
, "%s:%d STATS_CMD\n", __FILE__
, __LINE__
);
93 fprintf(stderr
, "%s:%d FLUSH_ALL_CMD\n", __FILE__
, __LINE__
);
97 fprintf(stderr
, "%s:%d VERSION_CMD\n", __FILE__
, __LINE__
);
101 fprintf(stderr
, "%s:%d QUIT_CMD\n", __FILE__
, __LINE__
);
105 fprintf(stderr
, "%s:%d VERBOSITY_CMD\n", __FILE__
, __LINE__
);
109 fprintf(stderr
, "%s:%d GET_CMD\n", __FILE__
, __LINE__
);
113 fprintf(stderr
, "%s:%d GETS_CMD\n", __FILE__
, __LINE__
);
118 fprintf(stderr
, "%s:%d UNKNOWN_CMD\n", __FILE__
, __LINE__
);
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)
132 static uint16_t parse_ascii_key(char **start
)
136 /* Strip leading whitespaces */
144 while (*c
!= '\0' && !isspace(*c
) && !iscntrl(*c
))
151 if (len
== 0 || len
> 240 || (*c
!= '\0' && *c
!= '\r' && iscntrl(*c
)))
160 * Spool a zero-terminated string
161 * @param client destination
162 * @param text the text to spool
163 * @return status of the spool operation
165 static protocol_binary_response_status
ascii_raw_response_handler(memcached_protocol_client_st
*client
, const char *text
)
167 if (client
->is_verbose
)
169 fprintf(stderr
, "%s:%d %s\n", __FILE__
, __LINE__
, text
);
172 if (client
->root
->drain(client
) == false)
174 return PROTOCOL_BINARY_RESPONSE_EINTERNAL
;
177 assert(client
->output
!= NULL
);
179 if (client
->output
== NULL
)
181 /* I can write directly to the socket.... */
184 size_t num_bytes
= len
-offset
;
185 ssize_t nw
= client
->root
->send(client
,
191 if (get_socket_errno() == EWOULDBLOCK
)
195 else if (get_socket_errno() != EINTR
)
197 client
->error
= errno
;
198 return PROTOCOL_BINARY_RESPONSE_EINTERNAL
;
203 offset
+= (size_t)nw
;
205 } while (offset
< len
);
209 return client
->root
->spool(client
, text
, strlen(text
));
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
217 static void send_command_usage(memcached_protocol_client_st
*client
)
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",
236 [VERBOSITY_CMD
]= "CLIENT_ERROR: Syntax error: verbosity <num>\r\n",
237 [UNKNOWN_CMD
]= "CLIENT_ERROR: Unknown command\r\n",
240 client
->mute
= false;
241 ascii_raw_response_handler(client
, errmsg
[client
->ascii_command
]);
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
250 static protocol_binary_response_status
ascii_version_response_handler(const void *cookie
,
254 memcached_protocol_client_st
*client
= (memcached_protocol_client_st
*)cookie
;
255 ascii_raw_response_handler(client
, "VERSION ");
256 client
->root
->spool(client
, text
, textlen
);
257 ascii_raw_response_handler(client
, "\r\n");
258 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
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
271 static protocol_binary_response_status
272 ascii_get_response_handler(const void *cookie
,
280 memcached_protocol_client_st
*client
= (void*)cookie
;
282 strcpy(buffer
, "VALUE ");
283 const char *source
= key
;
284 char *dest
= buffer
+ 6;
286 for (int x
= 0; x
< keylen
; ++x
)
288 if (*source
!= '\0' && !isspace(*source
) && !iscntrl(*source
))
294 return PROTOCOL_BINARY_RESPONSE_EINVAL
; /* key constraints in ascii */
301 size_t used
= (size_t)(dest
- buffer
);
303 if (client
->ascii_command
== GETS_CMD
)
305 snprintf(dest
, sizeof(buffer
) - used
, " %u %u %" PRIu64
"\r\n", flags
,
310 snprintf(dest
, sizeof(buffer
) - used
, " %u %u\r\n", flags
, bodylen
);
313 client
->root
->spool(client
, buffer
, strlen(buffer
));
314 client
->root
->spool(client
, body
, bodylen
);
315 client
->root
->spool(client
, "\r\n", 2);
317 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
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
328 static protocol_binary_response_status
ascii_stat_response_handler(const void *cookie
,
335 memcached_protocol_client_st
*client
= (void*)cookie
;
339 ascii_raw_response_handler(client
, "STAT ");
340 client
->root
->spool(client
, key
, keylen
);
341 ascii_raw_response_handler(client
, " ");
342 client
->root
->spool(client
, body
, bodylen
);
343 ascii_raw_response_handler(client
, "\r\n");
347 ascii_raw_response_handler(client
, "END\r\n");
350 return PROTOCOL_BINARY_RESPONSE_SUCCESS
;
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
359 static void ascii_process_gets(memcached_protocol_client_st
*client
,
360 char *buffer
, char *end
)
365 key
+= (client
->ascii_command
== GETS_CMD
) ? 5 : 4;
370 uint16_t nkey
= parse_ascii_key(&key
);
371 if (nkey
== 0) /* Invalid key... stop processing this line */
376 (void)client
->root
->callback
->interface
.v1
.get(client
, key
, nkey
,
377 ascii_get_response_handler
);
384 send_command_usage(client
);
388 client
->root
->spool(client
, "END\r\n", 5);
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
401 static int ascii_tokenize_command(char *str
, char *end
, char **vec
, int size
)
407 /* Skip leading blanks */
408 while (str
< end
&& isspace(*str
))
419 /* find the next non-blank field */
420 while (str
< end
&& !isspace(*str
))
425 /* zero-terminate it for easier parsing later on */
429 /* Is the vector full? */
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
448 static void recover_tokenize_command(char *start
, char *end
)
461 * Convert the textual command into a comcode
463 static enum ascii_cmd
ascii_to_cmd(char *start
, size_t length
)
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
}};
489 while (commands
[x
].len
> 0) {
490 if (length
>= commands
[x
].len
)
492 if (strncmp(start
, commands
[x
].cmd
, commands
[x
].len
) == 0)
495 if (length
== commands
[x
].len
|| isspace(*(start
+ commands
[x
].len
)))
497 return commands
[x
].cc
;
508 * Perform a delete_object operation.
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
514 static void process_delete(memcached_protocol_client_st
*client
,
515 char **tokens
, int ntokens
)
517 char *key
= tokens
[1];
520 if (ntokens
!= 2 || (nkey
= parse_ascii_key(&key
)) == 0)
522 send_command_usage(client
);
526 if (client
->root
->callback
->interface
.v1
.delete_object
== NULL
)
528 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
532 protocol_binary_response_status rval
= client
->root
->callback
->interface
.v1
.delete_object(client
, key
, nkey
, 0);
534 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
)
536 ascii_raw_response_handler(client
, "DELETED\r\n");
538 else if (rval
== PROTOCOL_BINARY_RESPONSE_KEY_ENOENT
)
540 ascii_raw_response_handler(client
, "NOT_FOUND\r\n");
545 snprintf(msg
, sizeof(msg
), "SERVER_ERROR: delete_object failed %u\r\n",(uint32_t)rval
);
546 ascii_raw_response_handler(client
, msg
);
550 static void process_arithmetic(memcached_protocol_client_st
*client
,
551 char **tokens
, int ntokens
)
553 char *key
= tokens
[1];
556 if (ntokens
!= 3 || (nkey
= parse_ascii_key(&key
)) == 0)
558 send_command_usage(client
);
565 uint64_t delta
= strtoull(tokens
[2], NULL
, 10);
571 protocol_binary_response_status rval
;
572 if (client
->ascii_command
== INCR_CMD
)
574 if (client
->root
->callback
->interface
.v1
.increment
== NULL
)
576 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
579 rval
= client
->root
->callback
->interface
.v1
.increment(client
,
588 if (client
->root
->callback
->interface
.v1
.decrement
== NULL
)
590 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
593 rval
= client
->root
->callback
->interface
.v1
.decrement(client
,
601 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
)
604 snprintf(buffer
, sizeof(buffer
), "%"PRIu64
"\r\n", result
);
605 ascii_raw_response_handler(client
, buffer
);
609 ascii_raw_response_handler(client
, "NOT_FOUND\r\n");
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"
618 static void process_stats(memcached_protocol_client_st
*client
,
619 char *key
, char *end
)
621 if (client
->root
->callback
->interface
.v1
.stat
== NULL
)
623 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
627 while (isspace(*key
))
632 uint16_t nkey
= (uint16_t)(end
- key
);
633 (void)client
->root
->callback
->interface
.v1
.stat(client
, key
, nkey
,
634 ascii_stat_response_handler
);
637 static void process_version(memcached_protocol_client_st
*client
,
638 char **tokens
, int ntokens
)
643 send_command_usage(client
);
647 if (client
->root
->callback
->interface
.v1
.version
== NULL
)
649 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
653 client
->root
->callback
->interface
.v1
.version(client
,
654 ascii_version_response_handler
);
657 static void process_flush(memcached_protocol_client_st
*client
,
658 char **tokens
, int ntokens
)
662 send_command_usage(client
);
666 if (client
->root
->callback
->interface
.v1
.flush_object
== NULL
)
668 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
676 timeout
= (uint32_t)strtoul(tokens
[1], NULL
, 10);
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 ascii_raw_response_handler(client
, "OK\r\n");
688 ascii_raw_response_handler(client
, "SERVER_ERROR: internal error\r\n");
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!
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
)
709 (void)ntokens
; /* already checked */
710 char *key
= tokens
[1];
711 uint16_t nkey
= parse_ascii_key(&key
);
715 ascii_raw_response_handler(client
, "CLIENT_ERROR: bad key\r\n");
720 uint32_t flags
= (uint32_t)strtoul(tokens
[2], NULL
, 10);
724 ascii_raw_response_handler(client
, "CLIENT_ERROR: bad key\r\n");
728 uint32_t timeout
= (uint32_t)strtoul(tokens
[3], NULL
, 10);
732 ascii_raw_response_handler(client
, "CLIENT_ERROR: bad key\r\n");
736 unsigned long nbytes
= strtoul(tokens
[4], NULL
, 10);
740 ascii_raw_response_handler(client
, "CLIENT_ERROR: bad key\r\n");
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
)
748 /* Keep on reading */
749 recover_tokenize_command(start
, *end
);
753 void *data
= (*end
) + 1;
756 protocol_binary_response_status rval
;
757 switch (client
->ascii_command
)
760 rval
= client
->root
->callback
->interface
.v1
.set(client
, key
,
769 rval
= client
->root
->callback
->interface
.v1
.add(client
, key
,
774 timeout
, &result_cas
);
778 cas
= strtoull(tokens
[5], NULL
, 10);
782 ascii_raw_response_handler(client
, "CLIENT_ERROR: bad key\r\n");
787 rval
= client
->root
->callback
->interface
.v1
.replace(client
, key
,
796 rval
= client
->root
->callback
->interface
.v1
.append(client
, key
,
804 rval
= client
->root
->callback
->interface
.v1
.prepend(client
, key
,
812 /* gcc complains if I don't put all of the enums in here.. */
825 abort(); /* impossible */
828 if (rval
== PROTOCOL_BINARY_RESPONSE_SUCCESS
)
830 ascii_raw_response_handler(client
, "STORED\r\n");
834 if (client
->ascii_command
== CAS_CMD
)
836 if (rval
== PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS
)
838 ascii_raw_response_handler(client
, "EXISTS\r\n");
840 else if (rval
== PROTOCOL_BINARY_RESPONSE_KEY_ENOENT
)
842 ascii_raw_response_handler(client
, "NOT_FOUND\r\n");
846 ascii_raw_response_handler(client
, "NOT_STORED\r\n");
851 ascii_raw_response_handler(client
, "NOT_STORED\r\n");
860 static int process_cas_command(memcached_protocol_client_st
*client
,
861 char **tokens
, int ntokens
, char *start
,
862 char **end
, ssize_t length
)
866 send_command_usage(client
);
870 if (client
->root
->callback
->interface
.v1
.replace
== NULL
)
872 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
876 return process_storage_command(client
, tokens
, ntokens
, start
, end
, length
);
879 static int process_set_command(memcached_protocol_client_st
*client
,
880 char **tokens
, int ntokens
, char *start
,
881 char **end
, ssize_t length
)
885 send_command_usage(client
);
889 if (client
->root
->callback
->interface
.v1
.set
== NULL
)
891 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
895 return process_storage_command(client
, tokens
, ntokens
, start
, end
, length
);
898 static int process_add_command(memcached_protocol_client_st
*client
,
899 char **tokens
, int ntokens
, char *start
,
900 char **end
, ssize_t length
)
904 send_command_usage(client
);
908 if (client
->root
->callback
->interface
.v1
.add
== NULL
)
910 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
914 return process_storage_command(client
, tokens
, ntokens
, start
, end
, length
);
917 static int process_replace_command(memcached_protocol_client_st
*client
,
918 char **tokens
, int ntokens
, char *start
,
919 char **end
, ssize_t length
)
923 send_command_usage(client
);
927 if (client
->root
->callback
->interface
.v1
.replace
== NULL
)
929 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
933 return process_storage_command(client
, tokens
, ntokens
, start
, end
, length
);
936 static int process_append_command(memcached_protocol_client_st
*client
,
937 char **tokens
, int ntokens
, char *start
,
938 char **end
, ssize_t length
)
942 send_command_usage(client
);
946 if (client
->root
->callback
->interface
.v1
.append
== NULL
)
948 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
952 return process_storage_command(client
, tokens
, ntokens
, start
, end
, length
);
955 static int process_prepend_command(memcached_protocol_client_st
*client
,
956 char **tokens
, int ntokens
, char *start
,
957 char **end
, ssize_t length
)
961 send_command_usage(client
);
965 if (client
->root
->callback
->interface
.v1
.prepend
== NULL
)
967 ascii_raw_response_handler(client
, "SERVER_ERROR: callback not implemented\r\n");
971 return process_storage_command(client
, tokens
, ntokens
, start
, end
, length
);
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 ;)
979 memcached_protocol_event_t
memcached_ascii_protocol_process_data(memcached_protocol_client_st
*client
, ssize_t
*length
, void **endptr
)
981 char *ptr
= (char*)client
->root
->input_buffer
;
985 /* Do we have \n (indicating the command preamble)*/
986 char *end
= memchr(ptr
, '\n', (size_t)*length
);
990 return MEMCACHED_PROTOCOL_READ_EVENT
;
993 client
->ascii_command
= ascii_to_cmd(ptr
, (size_t)(*length
));
995 /* we got all data available, execute the callback! */
996 if (client
->root
->callback
->pre_execute
!= NULL
)
998 client
->root
->callback
->pre_execute(client
, NULL
);
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
1006 if (client
->ascii_command
== GET_CMD
||
1007 client
->ascii_command
== GETS_CMD
)
1009 if (client
->root
->callback
->interface
.v1
.get
!= NULL
)
1011 ascii_process_gets(client
, ptr
, end
);
1015 ascii_raw_response_handler(client
, "SERVER_ERROR: Command not implemented\n");
1020 /* None of the defined commands takes 10 parameters, so lets just use
1021 * that as a maximum limit.
1024 int ntokens
= ascii_tokenize_command(ptr
, end
, tokens
, 10);
1028 client
->mute
= strcmp(tokens
[ntokens
- 1], "noreply") == 0;
1031 --ntokens
; /* processed noreply token*/
1037 print_ascii_command(client
);
1038 switch (client
->ascii_command
)
1041 error
= process_set_command(client
, tokens
, ntokens
, ptr
, &end
, *length
);
1045 error
= process_add_command(client
, tokens
, ntokens
, ptr
, &end
, *length
);
1049 error
= process_replace_command(client
, tokens
, ntokens
, ptr
, &end
, *length
);
1053 error
= process_cas_command(client
, tokens
, ntokens
, ptr
, &end
, *length
);
1057 error
= process_append_command(client
, tokens
, ntokens
, ptr
, &end
, *length
);
1061 error
= process_prepend_command(client
, tokens
, ntokens
, ptr
, &end
, *length
);
1065 process_delete(client
, tokens
, ntokens
);
1068 case INCR_CMD
: /* FALLTHROUGH */
1070 process_arithmetic(client
, tokens
, ntokens
);
1076 send_command_usage(client
);
1080 recover_tokenize_command(ptr
, end
);
1081 process_stats(client
, ptr
+ 6, end
);
1086 process_flush(client
, tokens
, ntokens
);
1092 send_command_usage(client
);
1096 process_version(client
, tokens
, ntokens
);
1101 if (ntokens
!= 1 || client
->mute
)
1103 send_command_usage(client
);
1107 if (client
->root
->callback
->interface
.v1
.quit
!= NULL
)
1109 client
->root
->callback
->interface
.v1
.quit(client
);
1112 return MEMCACHED_PROTOCOL_ERROR_EVENT
;
1119 send_command_usage(client
);
1123 ascii_raw_response_handler(client
, "OK\r\n");
1128 send_command_usage(client
);
1134 /* Should already be handled */
1140 return MEMCACHED_PROTOCOL_ERROR_EVENT
;
1142 else if (error
== 1)
1144 return MEMCACHED_PROTOCOL_READ_EVENT
;
1148 if (client
->root
->callback
->post_execute
!= NULL
)
1150 client
->root
->callback
->post_execute(client
, NULL
);
1155 *length
-= end
- ptr
;
1157 } while (*length
> 0);
1160 return MEMCACHED_PROTOCOL_READ_EVENT
;