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