Work inspired by matt knox
[m6w6/libmemcached] / libmemcached / memcached_response.c
1 /*
2 Memcached library
3
4 memcached_response() is used to determine the return result
5 from an issued command.
6 */
7
8 #include "common.h"
9 #include "memcached_io.h"
10
11 static memcached_return textual_read_one_response(memcached_server_st *ptr,
12 char *buffer, size_t buffer_length,
13 memcached_result_st *result);
14 static memcached_return binary_read_one_response(memcached_server_st *ptr,
15 char *buffer, size_t buffer_length,
16 memcached_result_st *result);
17
18 memcached_return memcached_read_one_response(memcached_server_st *ptr,
19 char *buffer, size_t buffer_length,
20 memcached_result_st *result)
21 {
22 memcached_server_response_decrement(ptr);
23
24 if (result == NULL)
25 result = &ptr->root->result;
26
27 memcached_return rc;
28 if (ptr->root->flags & MEM_BINARY_PROTOCOL)
29 rc= binary_read_one_response(ptr, buffer, buffer_length, result);
30 else
31 rc= textual_read_one_response(ptr, buffer, buffer_length, result);
32
33 unlikely(rc == MEMCACHED_UNKNOWN_READ_FAILURE ||
34 rc == MEMCACHED_PROTOCOL_ERROR ||
35 rc == MEMCACHED_CLIENT_ERROR ||
36 rc == MEMCACHED_MEMORY_ALLOCATION_FAILURE)
37 memcached_io_reset(ptr);
38
39 return rc;
40 }
41
42 memcached_return memcached_response(memcached_server_st *ptr,
43 char *buffer, size_t buffer_length,
44 memcached_result_st *result)
45 {
46 /* We may have old commands in the buffer not set, first purge */
47 if (ptr->root->flags & MEM_NO_BLOCK)
48 (void)memcached_io_write(ptr, NULL, 0, 1);
49
50 /*
51 * The previous implementation purged all pending requests and just
52 * returned the last one. Purge all pending messages to ensure backwards
53 * compatibility.
54 */
55 if ((ptr->root->flags & MEM_BINARY_PROTOCOL) == 0)
56 while (memcached_server_response_count(ptr) > 1)
57 {
58 memcached_return rc= memcached_read_one_response(ptr, buffer, buffer_length, result);
59
60 unlikely (rc != MEMCACHED_END &&
61 rc != MEMCACHED_STORED &&
62 rc != MEMCACHED_SUCCESS &&
63 rc != MEMCACHED_STAT &&
64 rc != MEMCACHED_DELETED &&
65 rc != MEMCACHED_NOTFOUND &&
66 rc != MEMCACHED_NOTSTORED &&
67 rc != MEMCACHED_DATA_EXISTS)
68 return rc;
69 }
70
71 return memcached_read_one_response(ptr, buffer, buffer_length, result);
72 }
73
74 static memcached_return textual_value_fetch(memcached_server_st *ptr,
75 char *buffer,
76 memcached_result_st *result)
77 {
78 memcached_return rc= MEMCACHED_SUCCESS;
79 char *string_ptr;
80 char *end_ptr;
81 char *next_ptr;
82 size_t value_length;
83 size_t to_read;
84 char *value_ptr;
85
86 if (ptr->root->flags & MEM_USE_UDP)
87 return MEMCACHED_NOT_SUPPORTED;
88
89 WATCHPOINT_ASSERT(ptr->root);
90 end_ptr= buffer + MEMCACHED_DEFAULT_COMMAND_SIZE;
91
92 memcached_result_reset(result);
93
94 string_ptr= buffer;
95 string_ptr+= 6; /* "VALUE " */
96
97
98 /* We load the key */
99 {
100 char *key;
101 size_t prefix_length;
102
103 key= result->key;
104 result->key_length= 0;
105
106 for (prefix_length= ptr->root->prefix_key_length; !(iscntrl(*string_ptr) || isspace(*string_ptr)) ; string_ptr++)
107 {
108 if (prefix_length == 0)
109 {
110 *key= *string_ptr;
111 key++;
112 result->key_length++;
113 }
114 else
115 prefix_length--;
116 }
117 result->key[result->key_length]= 0;
118 }
119
120 if (end_ptr == string_ptr)
121 goto read_error;
122
123 /* Flags fetch move past space */
124 string_ptr++;
125 if (end_ptr == string_ptr)
126 goto read_error;
127 for (next_ptr= string_ptr; isdigit(*string_ptr); string_ptr++);
128 result->flags= strtoul(next_ptr, &string_ptr, 10);
129
130 if (end_ptr == string_ptr)
131 goto read_error;
132
133 /* Length fetch move past space*/
134 string_ptr++;
135 if (end_ptr == string_ptr)
136 goto read_error;
137
138 for (next_ptr= string_ptr; isdigit(*string_ptr); string_ptr++);
139 value_length= (size_t)strtoull(next_ptr, &string_ptr, 10);
140
141 if (end_ptr == string_ptr)
142 goto read_error;
143
144 /* Skip spaces */
145 if (*string_ptr == '\r')
146 {
147 /* Skip past the \r\n */
148 string_ptr+= 2;
149 }
150 else
151 {
152 string_ptr++;
153 for (next_ptr= string_ptr; isdigit(*string_ptr); string_ptr++);
154 result->cas= strtoull(next_ptr, &string_ptr, 10);
155 }
156
157 if (end_ptr < string_ptr)
158 goto read_error;
159
160 /* We add two bytes so that we can walk the \r\n */
161 rc= memcached_string_check(&result->value, value_length+2);
162 if (rc != MEMCACHED_SUCCESS)
163 {
164 value_length= 0;
165 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
166 }
167
168 value_ptr= memcached_string_value(&result->value);
169 /*
170 We read the \r\n into the string since not doing so is more
171 cycles then the waster of memory to do so.
172
173 We are null terminating through, which will most likely make
174 some people lazy about using the return length.
175 */
176 to_read= (value_length) + 2;
177 ssize_t read_length= 0;
178 memcached_return rrc= memcached_io_read(ptr, value_ptr, to_read, &read_length);
179 if (rrc != MEMCACHED_SUCCESS)
180 return rrc;
181
182 if (read_length != (ssize_t)(value_length + 2))
183 {
184 goto read_error;
185 }
186
187 /* This next bit blows the API, but this is internal....*/
188 {
189 char *char_ptr;
190 char_ptr= memcached_string_value(&result->value);;
191 char_ptr[value_length]= 0;
192 char_ptr[value_length + 1]= 0;
193 memcached_string_set_length(&result->value, value_length);
194 }
195
196 return MEMCACHED_SUCCESS;
197
198 read_error:
199 memcached_io_reset(ptr);
200
201 return MEMCACHED_PARTIAL_READ;
202 }
203
204 static memcached_return textual_read_one_response(memcached_server_st *ptr,
205 char *buffer, size_t buffer_length,
206 memcached_result_st *result)
207 {
208 memcached_return rc= memcached_io_readline(ptr, buffer, buffer_length);
209 if (rc != MEMCACHED_SUCCESS)
210 return rc;
211
212 switch(buffer[0])
213 {
214 case 'V': /* VALUE || VERSION */
215 if (buffer[1] == 'A') /* VALUE */
216 {
217 /* We add back in one because we will need to search for END */
218 memcached_server_response_increment(ptr);
219 return textual_value_fetch(ptr, buffer, result);
220 }
221 else if (buffer[1] == 'E') /* VERSION */
222 {
223 return MEMCACHED_SUCCESS;
224 }
225 else
226 {
227 WATCHPOINT_STRING(buffer);
228 WATCHPOINT_ASSERT(0);
229 return MEMCACHED_UNKNOWN_READ_FAILURE;
230 }
231 case 'O': /* OK */
232 return MEMCACHED_SUCCESS;
233 case 'S': /* STORED STATS SERVER_ERROR */
234 {
235 if (buffer[2] == 'A') /* STORED STATS */
236 {
237 memcached_server_response_increment(ptr);
238 return MEMCACHED_STAT;
239 }
240 else if (buffer[1] == 'E')
241 return MEMCACHED_SERVER_ERROR;
242 else if (buffer[1] == 'T')
243 return MEMCACHED_STORED;
244 else
245 {
246 WATCHPOINT_STRING(buffer);
247 WATCHPOINT_ASSERT(0);
248 return MEMCACHED_UNKNOWN_READ_FAILURE;
249 }
250 }
251 case 'D': /* DELETED */
252 return MEMCACHED_DELETED;
253 case 'N': /* NOT_FOUND */
254 {
255 if (buffer[4] == 'F')
256 return MEMCACHED_NOTFOUND;
257 else if (buffer[4] == 'S')
258 return MEMCACHED_NOTSTORED;
259 else
260 return MEMCACHED_UNKNOWN_READ_FAILURE;
261 }
262 case 'E': /* PROTOCOL ERROR or END */
263 {
264 if (buffer[1] == 'N')
265 return MEMCACHED_END;
266 else if (buffer[1] == 'R')
267 return MEMCACHED_PROTOCOL_ERROR;
268 else if (buffer[1] == 'X')
269 return MEMCACHED_DATA_EXISTS;
270 else
271 return MEMCACHED_UNKNOWN_READ_FAILURE;
272 }
273 case 'I': /* CLIENT ERROR */
274 /* We add back in one because we will need to search for END */
275 memcached_server_response_increment(ptr);
276 return MEMCACHED_ITEM;
277 case 'C': /* CLIENT ERROR */
278 return MEMCACHED_CLIENT_ERROR;
279 default:
280 {
281 unsigned long long auto_return_value;
282
283 if (sscanf(buffer, "%llu", &auto_return_value) == 1)
284 return MEMCACHED_SUCCESS;
285
286 return MEMCACHED_UNKNOWN_READ_FAILURE;
287 }
288 }
289
290 /* NOTREACHED */
291 }
292
293 char *memcached_result_value(memcached_result_st *ptr)
294 {
295 memcached_string_st *sptr= &ptr->value;
296 return memcached_string_value(sptr);
297 }
298
299 size_t memcached_result_length(memcached_result_st *ptr)
300 {
301 memcached_string_st *sptr= &ptr->value;
302 return memcached_string_length(sptr);
303 }
304
305 static memcached_return binary_read_one_response(memcached_server_st *ptr,
306 char *buffer, size_t buffer_length,
307 memcached_result_st *result)
308 {
309 protocol_binary_response_header header;
310
311 unlikely (memcached_safe_read(ptr, &header.bytes,
312 sizeof(header.bytes)) != MEMCACHED_SUCCESS)
313 return MEMCACHED_UNKNOWN_READ_FAILURE;
314
315 unlikely (header.response.magic != PROTOCOL_BINARY_RES)
316 return MEMCACHED_PROTOCOL_ERROR;
317
318 /*
319 ** Convert the header to host local endian!
320 */
321 header.response.keylen= ntohs(header.response.keylen);
322 header.response.status= ntohs(header.response.status);
323 header.response.bodylen= ntohl(header.response.bodylen);
324 header.response.cas= ntohll(header.response.cas);
325 uint32_t bodylen= header.response.bodylen;
326
327 if (header.response.status == 0)
328 {
329 switch (header.response.opcode)
330 {
331 case PROTOCOL_BINARY_CMD_GETK:
332 case PROTOCOL_BINARY_CMD_GETKQ:
333 {
334 uint16_t keylen= header.response.keylen;
335 memcached_result_reset(result);
336 result->cas= header.response.cas;
337
338 if (memcached_safe_read(ptr, &result->flags,
339 sizeof (result->flags)) != MEMCACHED_SUCCESS)
340 return MEMCACHED_UNKNOWN_READ_FAILURE;
341
342 result->flags= ntohl(result->flags);
343 bodylen -= header.response.extlen;
344
345 result->key_length= keylen;
346 if (memcached_safe_read(ptr, result->key, keylen) != MEMCACHED_SUCCESS)
347 return MEMCACHED_UNKNOWN_READ_FAILURE;
348
349 bodylen -= keylen;
350 if (memcached_string_check(&result->value,
351 bodylen) != MEMCACHED_SUCCESS)
352 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
353
354 char *vptr= memcached_string_value(&result->value);
355 if (memcached_safe_read(ptr, vptr, bodylen) != MEMCACHED_SUCCESS)
356 return MEMCACHED_UNKNOWN_READ_FAILURE;
357
358 memcached_string_set_length(&result->value, bodylen);
359 }
360 break;
361 case PROTOCOL_BINARY_CMD_INCREMENT:
362 case PROTOCOL_BINARY_CMD_DECREMENT:
363 {
364 if (bodylen != sizeof(uint64_t) || buffer_length != sizeof(uint64_t))
365 return MEMCACHED_PROTOCOL_ERROR;
366
367 WATCHPOINT_ASSERT(bodylen == buffer_length);
368 uint64_t val;
369 if (memcached_safe_read(ptr, &val, sizeof(val)) != MEMCACHED_SUCCESS)
370 return MEMCACHED_UNKNOWN_READ_FAILURE;
371
372 val= ntohll(val);
373 memcpy(buffer, &val, sizeof(val));
374 }
375 break;
376 case PROTOCOL_BINARY_CMD_VERSION:
377 {
378 memset(buffer, 0, buffer_length);
379 if (bodylen >= buffer_length)
380 /* not enough space in buffer.. should not happen... */
381 return MEMCACHED_UNKNOWN_READ_FAILURE;
382 else if (memcached_safe_read(ptr, buffer, bodylen) != MEMCACHED_SUCCESS)
383 return MEMCACHED_UNKNOWN_READ_FAILURE;
384 }
385 break;
386 case PROTOCOL_BINARY_CMD_FLUSH:
387 case PROTOCOL_BINARY_CMD_QUIT:
388 case PROTOCOL_BINARY_CMD_SET:
389 case PROTOCOL_BINARY_CMD_ADD:
390 case PROTOCOL_BINARY_CMD_REPLACE:
391 case PROTOCOL_BINARY_CMD_APPEND:
392 case PROTOCOL_BINARY_CMD_PREPEND:
393 case PROTOCOL_BINARY_CMD_DELETE:
394 {
395 WATCHPOINT_ASSERT(bodylen == 0);
396 return MEMCACHED_SUCCESS;
397 }
398 case PROTOCOL_BINARY_CMD_NOOP:
399 {
400 WATCHPOINT_ASSERT(bodylen == 0);
401 return MEMCACHED_END;
402 }
403 case PROTOCOL_BINARY_CMD_STAT:
404 {
405 if (bodylen == 0)
406 return MEMCACHED_END;
407 else if (bodylen + 1 > buffer_length)
408 /* not enough space in buffer.. should not happen... */
409 return MEMCACHED_UNKNOWN_READ_FAILURE;
410 else
411 {
412 size_t keylen= header.response.keylen;
413 memset(buffer, 0, buffer_length);
414 if (memcached_safe_read(ptr, buffer, keylen) != MEMCACHED_SUCCESS ||
415 memcached_safe_read(ptr, buffer + keylen + 1,
416 bodylen - keylen) != MEMCACHED_SUCCESS)
417 return MEMCACHED_UNKNOWN_READ_FAILURE;
418 }
419 }
420 break;
421 default:
422 {
423 /* Command not implemented yet! */
424 WATCHPOINT_ASSERT(0);
425 return MEMCACHED_PROTOCOL_ERROR;
426 }
427 }
428 }
429 else if (header.response.bodylen)
430 {
431 /* What should I do with the error message??? just discard it for now */
432 char hole[SMALL_STRING_LEN];
433 while (bodylen > 0)
434 {
435 size_t nr= (bodylen > SMALL_STRING_LEN) ? SMALL_STRING_LEN : bodylen;
436 if (memcached_safe_read(ptr, hole, nr) != MEMCACHED_SUCCESS)
437 return MEMCACHED_UNKNOWN_READ_FAILURE;
438 bodylen -= nr;
439 }
440
441 /* This might be an error from one of the quiet commands.. if
442 * so, just throw it away and get the next one. What about creating
443 * a callback to the user with the error information?
444 */
445 switch (header.response.opcode)
446 {
447 case PROTOCOL_BINARY_CMD_SETQ:
448 case PROTOCOL_BINARY_CMD_ADDQ:
449 case PROTOCOL_BINARY_CMD_REPLACEQ:
450 case PROTOCOL_BINARY_CMD_APPENDQ:
451 case PROTOCOL_BINARY_CMD_PREPENDQ:
452 return binary_read_one_response(ptr, buffer, buffer_length, result);
453 default:
454 break;
455 }
456 }
457
458 memcached_return rc= MEMCACHED_SUCCESS;
459 unlikely(header.response.status != 0)
460 switch (header.response.status)
461 {
462 case PROTOCOL_BINARY_RESPONSE_KEY_ENOENT:
463 rc= MEMCACHED_NOTFOUND;
464 break;
465 case PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS:
466 rc= MEMCACHED_DATA_EXISTS;
467 break;
468 case PROTOCOL_BINARY_RESPONSE_E2BIG:
469 case PROTOCOL_BINARY_RESPONSE_EINVAL:
470 case PROTOCOL_BINARY_RESPONSE_NOT_STORED:
471 case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND:
472 case PROTOCOL_BINARY_RESPONSE_ENOMEM:
473 default:
474 /* @todo fix the error mappings */
475 rc= MEMCACHED_PROTOCOL_ERROR;
476 break;
477 }
478
479 return rc;
480 }