src/libmemcached: apply clang-format
[awesomized/libmemcached] / src / libmemcached / response.cc
1 /*
2 +--------------------------------------------------------------------+
3 | libmemcached - C/C++ Client Library for memcached |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted under the terms of the BSD license. |
7 | You should have received a copy of the license in a bundled file |
8 | named LICENSE; in case you did not receive a copy you can review |
9 | the terms online at: https://opensource.org/licenses/BSD-3-Clause |
10 +--------------------------------------------------------------------+
11 | Copyright (c) 2006-2014 Brian Aker https://datadifferential.com/ |
12 | Copyright (c) 2020 Michael Wallner <mike@php.net> |
13 +--------------------------------------------------------------------+
14 */
15
16 #include "libmemcached/common.h"
17 #include "libmemcached/string.hpp"
18
19 static memcached_return_t textual_value_fetch(memcached_instance_st *instance, char *buffer,
20 memcached_result_st *result) {
21 char *next_ptr;
22 ssize_t read_length = 0;
23 size_t value_length;
24
25 WATCHPOINT_ASSERT(instance->root);
26 char *end_ptr = buffer + MEMCACHED_DEFAULT_COMMAND_SIZE;
27
28 memcached_result_reset(result);
29
30 char *string_ptr = buffer;
31 string_ptr += 6; /* "VALUE " */
32
33 // Just used for cases of AES decrypt currently
34 memcached_return_t rc = MEMCACHED_SUCCESS;
35
36 /* We load the key */
37 {
38 char *key = result->item_key;
39 result->key_length = 0;
40
41 for (ptrdiff_t prefix_length = memcached_array_size(instance->root->_namespace);
42 !(iscntrl(*string_ptr) || isspace(*string_ptr)); string_ptr++)
43 {
44 if (prefix_length == 0) {
45 *key = *string_ptr;
46 key++;
47 result->key_length++;
48 } else
49 prefix_length--;
50 }
51 result->item_key[result->key_length] = 0;
52 }
53
54 if (end_ptr == string_ptr) {
55 goto read_error;
56 }
57
58 /* Flags fetch move past space */
59 string_ptr++;
60 if (end_ptr == string_ptr) {
61 goto read_error;
62 }
63
64 for (next_ptr = string_ptr; isdigit(*string_ptr); string_ptr++) {
65 };
66 errno = 0;
67 result->item_flags = (uint32_t) strtoul(next_ptr, &string_ptr, 10);
68
69 if (errno != 0 or end_ptr == string_ptr) {
70 goto read_error;
71 }
72
73 /* Length fetch move past space*/
74 string_ptr++;
75 if (end_ptr == string_ptr) {
76 goto read_error;
77 }
78
79 for (next_ptr = string_ptr; isdigit(*string_ptr); string_ptr++) {
80 };
81 errno = 0;
82 value_length = (size_t) strtoull(next_ptr, &string_ptr, 10);
83
84 if (errno != 0 or end_ptr == string_ptr) {
85 goto read_error;
86 }
87
88 /* Skip spaces */
89 if (*string_ptr == '\r') {
90 /* Skip past the \r\n */
91 string_ptr += 2;
92 } else {
93 string_ptr++;
94 for (next_ptr = string_ptr; isdigit(*string_ptr); string_ptr++) {
95 };
96 errno = 0;
97 result->item_cas = strtoull(next_ptr, &string_ptr, 10);
98 }
99
100 if (errno != 0 or end_ptr < string_ptr) {
101 goto read_error;
102 }
103
104 /* We add two bytes so that we can walk the \r\n */
105 if (memcached_failed(memcached_string_check(&result->value, value_length + 2))) {
106 return memcached_set_error(*instance, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT);
107 }
108
109 {
110 char *value_ptr = memcached_string_value_mutable(&result->value);
111 /*
112 We read the \r\n into the string since not doing so is more
113 cycles then the waster of memory to do so.
114
115 We are null terminating through, which will most likely make
116 some people lazy about using the return length.
117 */
118 size_t to_read = (value_length) + 2;
119 memcached_return_t rrc = memcached_io_read(instance, value_ptr, to_read, read_length);
120 if (memcached_failed(rrc) and rrc == MEMCACHED_IN_PROGRESS) {
121 memcached_quit_server(instance, true);
122 return memcached_set_error(*instance, MEMCACHED_IN_PROGRESS, MEMCACHED_AT);
123 } else if (memcached_failed(rrc)) {
124 return rrc;
125 }
126 }
127
128 if (read_length != (ssize_t)(value_length + 2)) {
129 goto read_error;
130 }
131
132 /* This next bit blows the API, but this is internal....*/
133 {
134 char *char_ptr;
135 char_ptr = memcached_string_value_mutable(&result->value);
136 ;
137 char_ptr[value_length] = 0;
138 char_ptr[value_length + 1] = 0;
139 memcached_string_set_length(&result->value, value_length);
140 }
141
142 if (memcached_is_encrypted(instance->root) and memcached_result_length(result)) {
143 hashkit_string_st *destination;
144
145 if ((destination = hashkit_decrypt(&instance->root->hashkit, memcached_result_value(result),
146 memcached_result_length(result)))
147 == NULL)
148 {
149 rc = memcached_set_error(*instance->root, MEMCACHED_FAILURE, MEMCACHED_AT,
150 memcached_literal_param("hashkit_decrypt() failed"));
151 } else {
152 memcached_result_reset_value(result);
153 if (memcached_failed(memcached_result_set_value(result, hashkit_string_c_str(destination),
154 hashkit_string_length(destination))))
155 {
156 rc = memcached_set_error(*instance->root, MEMCACHED_FAILURE, MEMCACHED_AT,
157 memcached_literal_param("hashkit_decrypt() failed"));
158 }
159 }
160
161 if (memcached_failed(rc)) {
162 memcached_result_reset(result);
163 }
164 hashkit_string_free(destination);
165 }
166
167 return rc;
168
169 read_error:
170 memcached_io_reset(instance);
171
172 return MEMCACHED_PARTIAL_READ;
173 }
174
175 static memcached_return_t textual_read_one_response(memcached_instance_st *instance, char *buffer,
176 const size_t buffer_length,
177 memcached_result_st *result) {
178 size_t total_read;
179 memcached_return_t rc = memcached_io_readline(instance, buffer, buffer_length, total_read);
180
181 if (memcached_failed(rc)) {
182 return rc;
183 }
184 assert(total_read);
185
186 switch (buffer[0]) {
187 case 'V': {
188 // VALUE
189 if (buffer[1] == 'A' and buffer[2] == 'L' and buffer[3] == 'U' and buffer[4] == 'E') /* VALUE */
190 {
191 /* We add back in one because we will need to search for END */
192 memcached_server_response_increment(instance);
193 return textual_value_fetch(instance, buffer, result);
194 }
195 // VERSION
196 else if (buffer[1] == 'E' and buffer[2] == 'R' and buffer[3] == 'S' and buffer[4] == 'I'
197 and buffer[5] == 'O' and buffer[6] == 'N') /* VERSION */
198 {
199 /* Find the space, and then move one past it to copy version */
200 char *response_ptr = index(buffer, ' ');
201
202 char *endptr;
203 errno = 0;
204 long int version = strtol(response_ptr, &endptr, 10);
205 if (errno != 0 or version == LONG_MIN or version == LONG_MAX or version > UINT8_MAX
206 or version == 0) {
207 instance->major_version = instance->minor_version = instance->micro_version = UINT8_MAX;
208 return memcached_set_error(
209 *instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
210 memcached_literal_param("strtol() failed to parse major version"));
211 }
212 instance->major_version = uint8_t(version);
213
214 endptr++;
215 errno = 0;
216 version = strtol(endptr, &endptr, 10);
217 if (errno != 0 or version == LONG_MIN or version == LONG_MAX or version > UINT8_MAX) {
218 instance->major_version = instance->minor_version = instance->micro_version = UINT8_MAX;
219 return memcached_set_error(
220 *instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
221 memcached_literal_param("strtol() failed to parse minor version"));
222 }
223 instance->minor_version = uint8_t(version);
224
225 endptr++;
226 errno = 0;
227 version = strtol(endptr, &endptr, 10);
228 if (errno != 0 or version == LONG_MIN or version == LONG_MAX or version > UINT8_MAX) {
229 instance->major_version = instance->minor_version = instance->micro_version = UINT8_MAX;
230 return memcached_set_error(
231 *instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
232 memcached_literal_param("strtol() failed to parse micro version"));
233 }
234 instance->micro_version = uint8_t(version);
235
236 return MEMCACHED_SUCCESS;
237 }
238 } break;
239
240 case 'O': {
241 // OK
242 if (buffer[1] == 'K') {
243 return MEMCACHED_SUCCESS;
244 }
245 } break;
246
247 case 'S': {
248 // STAT
249 if (buffer[1] == 'T' and buffer[2] == 'A' and buffer[3] == 'T') /* STORED STATS */ {
250 memcached_server_response_increment(instance);
251 return MEMCACHED_STAT;
252 }
253 // SERVER_ERROR
254 else if (buffer[1] == 'E' and buffer[2] == 'R' and buffer[3] == 'V' and buffer[4] == 'E'
255 and buffer[5] == 'R' and buffer[6] == '_' and buffer[7] == 'E' and buffer[8] == 'R'
256 and buffer[9] == 'R' and buffer[10] == 'O' and buffer[11] == 'R')
257 {
258 if (total_read == memcached_literal_param_size("SERVER_ERROR")) {
259 return MEMCACHED_SERVER_ERROR;
260 }
261
262 if (total_read >= memcached_literal_param_size("SERVER_ERROR object too large for cache")
263 and (memcmp(buffer, memcached_literal_param("SERVER_ERROR object too large for cache"))
264 == 0))
265 {
266 return MEMCACHED_E2BIG;
267 }
268
269 if (total_read >= memcached_literal_param_size("SERVER_ERROR out of memory")
270 and ((memcmp(buffer, memcached_literal_param("SERVER_ERROR out of memory")) == 0)
271 or (memcmp(buffer, memcached_literal_param("SERVER_ERROR Out of memory")) == 0)))
272 {
273 return MEMCACHED_SERVER_MEMORY_ALLOCATION_FAILURE;
274 }
275
276 // Move past the basic error message and whitespace
277 char *startptr = buffer + memcached_literal_param_size("SERVER_ERROR");
278 if (startptr[0] == ' ') {
279 startptr++;
280 }
281
282 char *endptr = startptr;
283 while (*endptr != '\r' && *endptr != '\n') endptr++;
284
285 return memcached_set_error(*instance, MEMCACHED_SERVER_ERROR, MEMCACHED_AT, startptr,
286 size_t(endptr - startptr));
287 }
288 // STORED
289 else if (buffer[1] == 'T' and buffer[2] == 'O'
290 and buffer[3] == 'R') // and buffer[4] == 'E' and buffer[5] == 'D')
291 {
292 return MEMCACHED_STORED;
293 }
294 } break;
295
296 case 'D': {
297 // DELETED
298 if (buffer[1] == 'E' and buffer[2] == 'L' and buffer[3] == 'E' and buffer[4] == 'T'
299 and buffer[5] == 'E' and buffer[6] == 'D')
300 {
301 return MEMCACHED_DELETED;
302 }
303 } break;
304
305 case 'N': {
306 // NOT_FOUND
307 if (buffer[1] == 'O' and buffer[2] == 'T' and buffer[3] == '_' and buffer[4] == 'F'
308 and buffer[5] == 'O' and buffer[6] == 'U' and buffer[7] == 'N' and buffer[8] == 'D')
309 {
310 return MEMCACHED_NOTFOUND;
311 }
312 // NOT_STORED
313 else if (buffer[1] == 'O' and buffer[2] == 'T' and buffer[3] == '_' and buffer[4] == 'S'
314 and buffer[5] == 'T' and buffer[6] == 'O' and buffer[7] == 'R' and buffer[8] == 'E'
315 and buffer[9] == 'D')
316 {
317 return MEMCACHED_NOTSTORED;
318 }
319 } break;
320
321 case 'E': /* PROTOCOL ERROR or END */
322 {
323 // END
324 if (buffer[1] == 'N' and buffer[2] == 'D') {
325 return MEMCACHED_END;
326 }
327 #if 0
328 // PROTOCOL_ERROR
329 else if (buffer[1] == 'R' and buffer[2] == 'O' and buffer[3] == 'T' and buffer[4] == 'O' and buffer[5] == 'C' and buffer[6] == 'O' and buffer[7] == 'L'
330 and buffer[8] == '_'
331 and buffer[9] == 'E' and buffer[10] == 'R' and buffer[11] == 'R' and buffer[12] == 'O' and buffer[13] == 'R')
332 {
333 return MEMCACHED_PROTOCOL_ERROR;
334 }
335 #endif
336 // ERROR
337 else if (buffer[1] == 'R' and buffer[2] == 'R' and buffer[3] == 'O' and buffer[4] == 'R')
338 {
339 return MEMCACHED_ERROR;
340 }
341 // EXISTS
342 else if (buffer[1] == 'X' and buffer[2] == 'I' and buffer[3] == 'S' and buffer[4] == 'T'
343 and buffer[5] == 'S')
344 {
345 return MEMCACHED_DATA_EXISTS;
346 }
347 } break;
348
349 case 'T': /* TOUCHED */
350 {
351 // TOUCHED
352 if (buffer[1] == 'O' and buffer[2] == 'U' and buffer[3] == 'C' and buffer[4] == 'H'
353 and buffer[5] == 'E' and buffer[6] == 'D')
354 {
355 return MEMCACHED_SUCCESS;
356 }
357 } break;
358
359 case 'I': /* ITEM */
360 {
361 // ITEM
362 if (buffer[1] == 'T' and buffer[2] == 'E' and buffer[3] == 'M') {
363 /* We add back in one because we will need to search for END */
364 memcached_server_response_increment(instance);
365 return MEMCACHED_ITEM;
366 }
367 } break;
368
369 case 'C': /* CLIENT ERROR */
370 {
371 // CLIENT_ERROR
372 if (buffer[1] == 'L' and buffer[2] == 'I' and buffer[3] == 'E' and buffer[4] == 'N'
373 and buffer[5] == 'T' and buffer[6] == '_' and buffer[7] == 'E' and buffer[8] == 'R'
374 and buffer[9] == 'R' and buffer[10] == 'O' and buffer[11] == 'R')
375 {
376 // Move past the basic error message and whitespace
377 char *startptr = buffer + memcached_literal_param_size("CLIENT_ERROR");
378 if (startptr[0] == ' ') {
379 startptr++;
380 }
381
382 char *endptr = startptr;
383 while (*endptr != '\r' && *endptr != '\n') endptr++;
384
385 return memcached_set_error(*instance, MEMCACHED_CLIENT_ERROR, MEMCACHED_AT, startptr,
386 size_t(endptr - startptr));
387 }
388 } break;
389
390 case '0': /* INCR/DECR response */
391 case '1': /* INCR/DECR response */
392 case '2': /* INCR/DECR response */
393 case '3': /* INCR/DECR response */
394 case '4': /* INCR/DECR response */
395 case '5': /* INCR/DECR response */
396 case '6': /* INCR/DECR response */
397 case '7': /* INCR/DECR response */
398 case '8': /* INCR/DECR response */
399 case '9': /* INCR/DECR response */
400 {
401 errno = 0;
402 unsigned long long int auto_return_value = strtoull(buffer, (char **) NULL, 10);
403
404 if (auto_return_value == ULLONG_MAX and errno == ERANGE) {
405 result->numeric_value = UINT64_MAX;
406 return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
407 memcached_literal_param("Numeric response was out of range"));
408 } else if (errno == EINVAL) {
409 result->numeric_value = UINT64_MAX;
410 return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
411 memcached_literal_param("Numeric response was out of range"));
412 } else if (errno != 0) {
413 result->numeric_value = UINT64_MAX;
414 return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
415 memcached_literal_param("Numeric response was out of range"));
416 }
417
418 result->numeric_value = uint64_t(auto_return_value);
419
420 WATCHPOINT_STRING(buffer);
421 return MEMCACHED_SUCCESS;
422 }
423
424 default: break;
425 }
426
427 buffer[total_read] = 0;
428 #if 0
429 if (total_read >= sizeof("STORSTORED") -1)
430 {
431 fprintf(stderr, "%s:%d '%s', %.*s\n", __FILE__, __LINE__,
432 buffer, MEMCACHED_MAX_BUFFER, instance->read_buffer);
433 assert(memcmp(buffer,"STORSTORED", sizeof("STORSTORED") -1));
434 }
435 #endif
436 return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT, buffer,
437 total_read);
438 }
439
440 static memcached_return_t binary_read_one_response(memcached_instance_st *instance, char *buffer,
441 const size_t buffer_length,
442 memcached_result_st *result) {
443 memcached_return_t rc;
444 protocol_binary_response_header header;
445
446 assert(memcached_is_binary(instance->root));
447
448 if ((rc = memcached_safe_read(instance, &header.bytes, sizeof(header.bytes)))
449 != MEMCACHED_SUCCESS) {
450 WATCHPOINT_ERROR(rc);
451 return rc;
452 }
453
454 if (header.response.magic != PROTOCOL_BINARY_RES) {
455 return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
456 }
457
458 /*
459 ** Convert the header to host local endian!
460 */
461 header.response.keylen = ntohs(header.response.keylen);
462 header.response.status = ntohs(header.response.status);
463 header.response.bodylen = ntohl(header.response.bodylen);
464 header.response.cas = memcached_ntohll(header.response.cas);
465 uint32_t bodylen = header.response.bodylen;
466
467 if (header.response.status == PROTOCOL_BINARY_RESPONSE_SUCCESS
468 or header.response.status == PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE)
469 {
470 switch (header.response.opcode) {
471 case PROTOCOL_BINARY_CMD_GETKQ:
472 /*
473 * We didn't increment the response counter for the GETKQ packet
474 * (only the final NOOP), so we need to increment the counter again.
475 */
476 memcached_server_response_increment(instance);
477 /* fall through */
478 case PROTOCOL_BINARY_CMD_GETK: {
479 uint16_t keylen = header.response.keylen;
480 memcached_result_reset(result);
481 result->item_cas = header.response.cas;
482
483 if ((rc = memcached_safe_read(instance, &result->item_flags, sizeof(result->item_flags)))
484 != MEMCACHED_SUCCESS)
485 {
486 WATCHPOINT_ERROR(rc);
487 return MEMCACHED_UNKNOWN_READ_FAILURE;
488 }
489
490 result->item_flags = ntohl(result->item_flags);
491 bodylen -= header.response.extlen;
492
493 result->key_length = keylen;
494 if (memcached_failed(rc = memcached_safe_read(instance, result->item_key, keylen))) {
495 WATCHPOINT_ERROR(rc);
496 return MEMCACHED_UNKNOWN_READ_FAILURE;
497 }
498
499 // Only bother with doing this if key_length > 0
500 if (result->key_length) {
501 if (memcached_array_size(instance->root->_namespace)
502 and memcached_array_size(instance->root->_namespace) >= result->key_length)
503 {
504 return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
505 }
506
507 if (memcached_array_size(instance->root->_namespace)) {
508 result->key_length -= memcached_array_size(instance->root->_namespace);
509 memmove(result->item_key,
510 result->item_key + memcached_array_size(instance->root->_namespace),
511 result->key_length);
512 }
513 }
514
515 bodylen -= keylen;
516 if (memcached_failed(memcached_string_check(&result->value, bodylen))) {
517 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
518 }
519
520 char *vptr = memcached_string_value_mutable(&result->value);
521 if (memcached_failed(rc = memcached_safe_read(instance, vptr, bodylen))) {
522 WATCHPOINT_ERROR(rc);
523 return MEMCACHED_UNKNOWN_READ_FAILURE;
524 }
525
526 memcached_string_set_length(&result->value, bodylen);
527 } break;
528
529 case PROTOCOL_BINARY_CMD_INCREMENT:
530 case PROTOCOL_BINARY_CMD_DECREMENT: {
531 if (bodylen != sizeof(uint64_t)) {
532 result->numeric_value = UINT64_MAX;
533 return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
534 }
535
536 uint64_t val;
537 if ((rc = memcached_safe_read(instance, &val, sizeof(val))) != MEMCACHED_SUCCESS) {
538 result->numeric_value = UINT64_MAX;
539 return MEMCACHED_UNKNOWN_READ_FAILURE;
540 }
541
542 result->numeric_value = memcached_ntohll(val);
543 } break;
544
545 case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS: {
546 if (header.response.keylen != 0 || bodylen + 1 > buffer_length) {
547 return MEMCACHED_UNKNOWN_READ_FAILURE;
548 } else {
549 if ((rc = memcached_safe_read(instance, buffer, bodylen)) != MEMCACHED_SUCCESS) {
550 return MEMCACHED_UNKNOWN_READ_FAILURE;
551 }
552 }
553 } break;
554
555 case PROTOCOL_BINARY_CMD_VERSION: {
556 char version_buffer[32]; // @todo document this number
557 memset(version_buffer, 0, sizeof(version_buffer));
558
559 if (memcached_safe_read(instance, version_buffer, bodylen) != MEMCACHED_SUCCESS) {
560 return MEMCACHED_UNKNOWN_READ_FAILURE;
561 }
562
563 char *endptr;
564 errno = 0;
565 long int version = strtol(version_buffer, &endptr, 10);
566 if (errno != 0 or version == LONG_MIN or version == LONG_MAX or version > UINT8_MAX
567 or version == 0) {
568 instance->major_version = instance->minor_version = instance->micro_version = UINT8_MAX;
569 return memcached_set_error(
570 *instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
571 memcached_literal_param("strtol() failed to parse major version"));
572 }
573 instance->major_version = uint8_t(version);
574
575 endptr++;
576 errno = 0;
577 version = strtol(endptr, &endptr, 10);
578 if (errno != 0 or version == LONG_MIN or version == LONG_MAX or version > UINT8_MAX) {
579 instance->major_version = instance->minor_version = instance->micro_version = UINT8_MAX;
580 return memcached_set_error(
581 *instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
582 memcached_literal_param("strtol() failed to parse minor version"));
583 }
584 instance->minor_version = uint8_t(version);
585
586 endptr++;
587 errno = 0;
588 version = strtol(endptr, &endptr, 10);
589 if (errno != 0 or version == LONG_MIN or version == LONG_MAX or version > UINT8_MAX) {
590 instance->major_version = instance->minor_version = instance->micro_version = UINT8_MAX;
591 return memcached_set_error(
592 *instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
593 memcached_literal_param("strtol() failed to parse micro version"));
594 }
595 instance->micro_version = uint8_t(version);
596 } break;
597
598 case PROTOCOL_BINARY_CMD_TOUCH: {
599 rc = MEMCACHED_SUCCESS;
600 if (bodylen == 4) // The four byte read is a bug?
601 {
602 char touch_buffer[4]; // @todo document this number
603 rc = memcached_safe_read(instance, touch_buffer, sizeof(touch_buffer));
604 #if 0
605 fprintf(stderr, "%s:%d %d %d %d %d %.*s(%d)\n", __FILE__, __LINE__,
606 int(touch_buffer[0]),
607 int(touch_buffer[1]),
608 int(touch_buffer[2]),
609 int(touch_buffer[3]),
610 int(bodylen), touch_buffer, int(bodylen));
611 #endif
612 }
613 return memcached_set_error(*instance, rc, MEMCACHED_AT);
614 }
615
616 case PROTOCOL_BINARY_CMD_FLUSH:
617 case PROTOCOL_BINARY_CMD_QUIT:
618 case PROTOCOL_BINARY_CMD_SET:
619 case PROTOCOL_BINARY_CMD_ADD:
620 case PROTOCOL_BINARY_CMD_REPLACE:
621 case PROTOCOL_BINARY_CMD_APPEND:
622 case PROTOCOL_BINARY_CMD_PREPEND:
623 case PROTOCOL_BINARY_CMD_DELETE: {
624 WATCHPOINT_ASSERT(bodylen == 0);
625 return MEMCACHED_SUCCESS;
626 }
627
628 case PROTOCOL_BINARY_CMD_NOOP: {
629 WATCHPOINT_ASSERT(bodylen == 0);
630 return MEMCACHED_END;
631 }
632
633 case PROTOCOL_BINARY_CMD_STAT: {
634 if (bodylen == 0) {
635 return MEMCACHED_END;
636 } else if (bodylen + 1 > buffer_length) {
637 /* not enough space in buffer.. should not happen... */
638 return MEMCACHED_UNKNOWN_READ_FAILURE;
639 } else {
640 size_t keylen = header.response.keylen;
641 memset(buffer, 0, buffer_length);
642 if ((rc = memcached_safe_read(instance, buffer, keylen)) != MEMCACHED_SUCCESS
643 || (rc = memcached_safe_read(instance, buffer + keylen + 1, bodylen - keylen))
644 != MEMCACHED_SUCCESS)
645 {
646 WATCHPOINT_ERROR(rc);
647 return MEMCACHED_UNKNOWN_READ_FAILURE;
648 }
649 }
650 } break;
651
652 case PROTOCOL_BINARY_CMD_SASL_AUTH:
653 case PROTOCOL_BINARY_CMD_SASL_STEP: {
654 memcached_result_reset(result);
655 result->item_cas = header.response.cas;
656
657 if (memcached_string_check(&result->value, bodylen) != MEMCACHED_SUCCESS)
658 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
659
660 char *vptr = memcached_string_value_mutable(&result->value);
661 if ((rc = memcached_safe_read(instance, vptr, bodylen)) != MEMCACHED_SUCCESS) {
662 WATCHPOINT_ERROR(rc);
663 return MEMCACHED_UNKNOWN_READ_FAILURE;
664 }
665
666 memcached_string_set_length(&result->value, bodylen);
667 } break;
668 default: {
669 /* Command not implemented yet! */
670 return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
671 }
672 }
673 } else if (header.response.bodylen) {
674 /* What should I do with the error message??? just discard it for now */
675 char hole[SMALL_STRING_LEN];
676 while (bodylen > 0) {
677 size_t nr = (bodylen > SMALL_STRING_LEN) ? SMALL_STRING_LEN : bodylen;
678 if ((rc = memcached_safe_read(instance, hole, nr)) != MEMCACHED_SUCCESS) {
679 WATCHPOINT_ERROR(rc);
680 return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
681 }
682 bodylen -= (uint32_t) nr;
683 }
684
685 /* This might be an error from one of the quiet commands.. if
686 * so, just throw it away and get the next one. What about creating
687 * a callback to the user with the error information?
688 */
689 switch (header.response.opcode) {
690 case PROTOCOL_BINARY_CMD_SETQ:
691 case PROTOCOL_BINARY_CMD_ADDQ:
692 case PROTOCOL_BINARY_CMD_REPLACEQ:
693 case PROTOCOL_BINARY_CMD_APPENDQ:
694 case PROTOCOL_BINARY_CMD_PREPENDQ: return MEMCACHED_FETCH_NOTFINISHED;
695
696 default: break;
697 }
698 }
699
700 rc = MEMCACHED_SUCCESS;
701 if (header.response.status != 0) {
702 switch (header.response.status) {
703 case PROTOCOL_BINARY_RESPONSE_KEY_ENOENT: rc = MEMCACHED_NOTFOUND; break;
704
705 case PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS: rc = MEMCACHED_DATA_EXISTS; break;
706
707 case PROTOCOL_BINARY_RESPONSE_NOT_STORED: rc = MEMCACHED_NOTSTORED; break;
708
709 case PROTOCOL_BINARY_RESPONSE_E2BIG: rc = MEMCACHED_E2BIG; break;
710
711 case PROTOCOL_BINARY_RESPONSE_ENOMEM: rc = MEMCACHED_MEMORY_ALLOCATION_FAILURE; break;
712
713 case PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE: rc = MEMCACHED_AUTH_CONTINUE; break;
714
715 case PROTOCOL_BINARY_RESPONSE_AUTH_ERROR: rc = MEMCACHED_AUTH_FAILURE; break;
716
717 case PROTOCOL_BINARY_RESPONSE_EINVAL:
718 case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND:
719 default:
720 return memcached_set_error(*instance, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT);
721 break;
722 }
723 }
724
725 return rc;
726 }
727
728 static memcached_return_t _read_one_response(memcached_instance_st *instance, char *buffer,
729 const size_t buffer_length,
730 memcached_result_st *result) {
731 memcached_server_response_decrement(instance);
732
733 if (result == NULL) {
734 Memcached *root = (Memcached *) instance->root;
735 result = &root->result;
736 }
737
738 memcached_return_t rc;
739 if (memcached_is_binary(instance->root)) {
740 do {
741 rc = binary_read_one_response(instance, buffer, buffer_length, result);
742 } while (rc == MEMCACHED_FETCH_NOTFINISHED);
743 } else {
744 rc = textual_read_one_response(instance, buffer, buffer_length, result);
745 }
746
747 if (memcached_fatal(rc) && rc != MEMCACHED_TIMEOUT) {
748 memcached_io_reset(instance);
749 }
750
751 return rc;
752 }
753
754 memcached_return_t memcached_read_one_response(memcached_instance_st *instance,
755 memcached_result_st *result) {
756 char buffer[SMALL_STRING_LEN];
757
758 if (memcached_is_udp(instance->root)) {
759 return memcached_set_error(*instance, MEMCACHED_NOT_SUPPORTED, MEMCACHED_AT);
760 }
761
762 return _read_one_response(instance, buffer, sizeof(buffer), result);
763 }
764
765 memcached_return_t memcached_response(memcached_instance_st *instance,
766 memcached_result_st *result) {
767 char buffer[1024];
768
769 return memcached_response(instance, buffer, sizeof(buffer), result);
770 }
771
772 memcached_return_t memcached_response(memcached_instance_st *instance, char *buffer,
773 size_t buffer_length, memcached_result_st *result) {
774 if (memcached_is_udp(instance->root)) {
775 return memcached_set_error(*instance, MEMCACHED_NOT_SUPPORTED, MEMCACHED_AT);
776 }
777
778 /* We may have old commands in the buffer not sent, first purge */
779 if ((instance->root->flags.no_block) and (memcached_is_processing_input(instance->root) == false))
780 {
781 (void) memcached_io_write(instance);
782 }
783
784 /* Before going into loop wait to see if we have any IO waiting for us */
785 if (0) {
786 memcached_return_t read_rc = memcached_io_wait_for_read(instance);
787 fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, memcached_strerror(NULL, read_rc));
788 }
789
790 /*
791 * The previous implementation purged all pending requests and just
792 * returned the last one. Purge all pending messages to ensure backwards
793 * compatibility.
794 */
795 if (memcached_is_binary(instance->root) == false
796 and memcached_server_response_count(instance) > 1) {
797 memcached_result_st junked_result;
798 memcached_result_st *junked_result_ptr =
799 memcached_result_create(instance->root, &junked_result);
800
801 assert(junked_result_ptr);
802
803 while (memcached_server_response_count(instance) > 1) {
804 memcached_return_t rc =
805 _read_one_response(instance, buffer, buffer_length, junked_result_ptr);
806
807 // @TODO should we return an error on another but a bad read case?
808 if (memcached_fatal(rc)) {
809 memcached_result_free(junked_result_ptr);
810 return rc;
811 }
812 }
813 memcached_result_free(junked_result_ptr);
814 }
815
816 return _read_one_response(instance, buffer, buffer_length, result);
817 }