--- 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 <string.h>
+#include <stdlib.h>
#include <assert.h>
#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:
+ // <key> <flags> <bytes> [<cas unique>]
+
+ // 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;
}