diff -r 540737bf6bac -r 0e503189af2f memcache/command.c --- a/memcache/command.c Wed Aug 27 21:30:32 2008 +0300 +++ b/memcache/command.c Wed Aug 27 22:42:27 2008 +0300 @@ -1,4 +1,5 @@ - +#include +#include #include #include "command.h" @@ -14,32 +15,28 @@ "prepend" // MEMCACHE_CMD_STORE_PREPEND }; -/* static struct memcache_reply_info { enum memcache_reply type; char *name; - int has_data; -} *memcache_cmd_replies[MEMCACHE_RPL_MAX] = { - MEMCACHE_RPL_INVALID, - - MEMCACHE_RPL_ERROR, - MEMCACHE_RPL_CLIENT_ERROR, - MEMCACHE_RPL_SERVER_ERROR, +} memcache_cmd_replies[MEMCACHE_RPL_MAX] = { + { MEMCACHE_RPL_INVALID, NULL }, + { MEMCACHE_RPL_ERROR, "ERROR" }, + { MEMCACHE_RPL_CLIENT_ERROR, "CLIENT_ERROR" }, + { MEMCACHE_RPL_SERVER_ERROR, "SERVER_ERROR" }, // MEMCACHE_CMD_FETCH_* - MEMCACHE_RPL_VALUE, - MEMCACHE_RPL_END, + { MEMCACHE_RPL_VALUE, "VALUE" }, + { MEMCACHE_RPL_END, "END" }, // MEMCACHE_CMD_STORE_* - MEMCACHE_RPL_STORED, - MEMCACHE_RPL_NOT_STORED, - MEMCACHE_RPL_EXISTS, - MEMCACHE_RPL_NOT_FOUND, - + { MEMCACHE_RPL_STORED, "STORED" }, + { MEMCACHE_RPL_NOT_STORED, "NOT_STORED" }, + { MEMCACHE_RPL_EXISTS, "EXISTS" }, + { MEMCACHE_RPL_NOT_FOUND, "NOT_FOUND" } }; -*/ + int memcache_cmd_init (struct memcache_cmd *cmd, enum memcache_command cmd_type, struct memcache_key *key, struct memcache_obj *obj) { // shouldn't already have a request header yet? @@ -117,7 +114,142 @@ } int memcache_cmd_parse_header (struct evbuffer *buf, char **header_data, enum memcache_reply *reply_type, struct memcache_key *key, struct memcache_obj *obj, int *has_data) { - // XXX: implement - assert(0); + size_t line_length, buf_size; + char *line = NULL, *token_cursor, *token, *invalid; + int i; + + // take note of how long the buffer is + buf_size = evbuffer_get_length(buf); + + // first, try and read a full line + if ((line = evbuffer_readln(buf, &line_length, EVBUFFER_EOL_CRLF_STRICT)) == NULL) { + // check if any data was consumed + if (evbuffer_get_length(buf) != buf_size) { + // faaaail! + return -1; + + } else { + // no complete line found + return 0; + } + } + + // just check to make sure that it really is null-delimited + assert(line[line_length - 1] == '\0'); + + // empty lines? + if (line_length == 0) { + PWARNING("empty reply line !?!"); + return 0; + } + + // use strsep + token_cursor = line; + + // the first token should be the reply name + if ((token = strsep(&token_cursor, " ")) == NULL) + ERROR("no reply name in response line: %s", line); + + // figure out the reply type + for (i = MEMCACHE_RPL_INVALID + 1; i < MEMCACHE_RPL_MAX; i++) { + if (strcmp(token, memcache_cmd_replies[i].name) == 0) + break; + } + + // did we figure out what this reply means? + if (i == MEMCACHE_RPL_MAX) + ERROR("unrecognized reply: %s", line); + + // we found the type + *reply_type = memcache_cmd_replies[i].type; + + // default to no data + *has_data = 0; + + switch (*reply_type) { + case MEMCACHE_RPL_ERROR: + // no additional data + + break; + + case MEMCACHE_RPL_CLIENT_ERROR: + case MEMCACHE_RPL_SERVER_ERROR: + // the rest of the line is a human-readable error message + WARNING("received a %s reply: %s", token, token_cursor); + + break; + + case MEMCACHE_RPL_VALUE: + // [] + + // the key field + if ((key->buf = strsep(&token_cursor, " ")) == NULL) + ERROR("missing key in VALUE reply"); + + if ((key->len = strlen(key->buf)) == 0) + ERROR("zero-length key in VALUE reply"); + + // the flags field + if ((token = strsep(&token_cursor, " ")) == NULL) + ERROR("missing flags in VALUE reply"); + + obj->flags = (u_int32_t) strtol(token, &invalid, 10); + + if (*invalid != '\0') + ERROR("invalid flags in VALUE reply: %s (%s)", token, invalid); + + // the bytes field + if ((token = strsep(&token_cursor, " ")) == NULL) + ERROR("missing bytes in VALUE reply"); + + obj->bytes = (u_int32_t) strtol(token, &invalid, 10); + + if (*invalid != '\0') + ERROR("invalid bytes in VALUE reply: %s (%s)", token, invalid); + + // the optional cas field + if ((token = strsep(&token_cursor, " ")) != NULL) { + // there is a cas value present + obj->cas = (u_int64_t) strtoll(token, &invalid, 10); + + if (*invalid != '\0') + ERROR("invalid cas value in VALUE reply: %s (%s)", token, invalid); + + } else { + obj->cas = 0; + + } + + // and we do have data following this + *has_data = 1; + + break; + + case MEMCACHE_RPL_END: + // no additional data + + break; + + case MEMCACHE_RPL_STORED: + case MEMCACHE_RPL_NOT_STORED: + case MEMCACHE_RPL_EXISTS: + case MEMCACHE_RPL_NOT_FOUND: + // no additional data + + break; + + default: + assert(0); + }; + + // success + *header_data = line; + + return 0; + +error: + free(line); + + return -1; }