memcache/command.c
changeset 42 0e503189af2f
parent 41 540737bf6bac
child 43 e5b714190dee
--- 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;
 }