cb5a867209bd3efaf8a272860e65449eb12bf975
[awesomized/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_read_one_response(memcached_server_st *ptr,
75 char *buffer, size_t buffer_length,
76 memcached_result_st *result)
77 {
78 memcached_return rc= memcached_io_readline(ptr, buffer, buffer_length);
79 if (rc != MEMCACHED_SUCCESS)
80 return rc;
81
82 switch(buffer[0])
83 {
84 case 'V': /* VALUE || VERSION */
85 if (buffer[1] == 'A') /* VALUE */
86 {
87 /* We add back in one because we will need to search for END */
88 memcached_server_response_increment(ptr);
89 return value_fetch(ptr, buffer, result);
90 }
91 else if (buffer[1] == 'E') /* VERSION */
92 {
93 return MEMCACHED_SUCCESS;
94 }
95 else
96 {
97 WATCHPOINT_STRING(buffer);
98 WATCHPOINT_ASSERT(0);
99 return MEMCACHED_UNKNOWN_READ_FAILURE;
100 }
101 case 'O': /* OK */
102 return MEMCACHED_SUCCESS;
103 case 'S': /* STORED STATS SERVER_ERROR */
104 {
105 if (buffer[2] == 'A') /* STORED STATS */
106 {
107 memcached_server_response_increment(ptr);
108 return MEMCACHED_STAT;
109 }
110 else if (buffer[1] == 'E')
111 return MEMCACHED_SERVER_ERROR;
112 else if (buffer[1] == 'T')
113 return MEMCACHED_STORED;
114 else
115 {
116 WATCHPOINT_STRING(buffer);
117 WATCHPOINT_ASSERT(0);
118 return MEMCACHED_UNKNOWN_READ_FAILURE;
119 }
120 }
121 case 'D': /* DELETED */
122 return MEMCACHED_DELETED;
123 case 'N': /* NOT_FOUND */
124 {
125 if (buffer[4] == 'F')
126 return MEMCACHED_NOTFOUND;
127 else if (buffer[4] == 'S')
128 return MEMCACHED_NOTSTORED;
129 else
130 return MEMCACHED_UNKNOWN_READ_FAILURE;
131 }
132 case 'E': /* PROTOCOL ERROR or END */
133 {
134 if (buffer[1] == 'N')
135 return MEMCACHED_END;
136 else if (buffer[1] == 'R')
137 return MEMCACHED_PROTOCOL_ERROR;
138 else if (buffer[1] == 'X')
139 return MEMCACHED_DATA_EXISTS;
140 else
141 return MEMCACHED_UNKNOWN_READ_FAILURE;
142 }
143 case 'C': /* CLIENT ERROR */
144 return MEMCACHED_CLIENT_ERROR;
145 default:
146 {
147 unsigned long long auto_return_value;
148
149 if (sscanf(buffer, "%llu", &auto_return_value) == 1)
150 return MEMCACHED_SUCCESS;
151
152 return MEMCACHED_UNKNOWN_READ_FAILURE;
153 }
154 }
155
156 /* NOTREACHED */
157 }
158
159 char *memcached_result_value(memcached_result_st *ptr)
160 {
161 memcached_string_st *sptr= &ptr->value;
162 return memcached_string_value(sptr);
163 }
164
165 size_t memcached_result_length(memcached_result_st *ptr)
166 {
167 memcached_string_st *sptr= &ptr->value;
168 return memcached_string_length(sptr);
169 }
170
171 static memcached_return binary_read_one_response(memcached_server_st *ptr,
172 char *buffer, size_t buffer_length,
173 memcached_result_st *result)
174 {
175 protocol_binary_response_header header;
176
177 unlikely (memcached_safe_read(ptr, &header.bytes,
178 sizeof(header.bytes)) != MEMCACHED_SUCCESS)
179 return MEMCACHED_UNKNOWN_READ_FAILURE;
180
181 unlikely (header.response.magic != PROTOCOL_BINARY_RES)
182 return MEMCACHED_PROTOCOL_ERROR;
183
184 /*
185 ** Convert the header to host local endian!
186 */
187 header.response.keylen= ntohs(header.response.keylen);
188 header.response.status= ntohs(header.response.status);
189 header.response.bodylen= ntohl(header.response.bodylen);
190 header.response.cas= ntohll(header.response.cas);
191 uint32_t bodylen= header.response.bodylen;
192
193 if (header.response.status == 0)
194 {
195 switch (header.response.opcode)
196 {
197 case PROTOCOL_BINARY_CMD_GETK:
198 case PROTOCOL_BINARY_CMD_GETKQ:
199 {
200 uint16_t keylen= header.response.keylen;
201 memcached_result_reset(result);
202 result->cas= header.response.cas;
203
204 if (memcached_safe_read(ptr, &result->flags,
205 sizeof (result->flags)) != MEMCACHED_SUCCESS)
206 return MEMCACHED_UNKNOWN_READ_FAILURE;
207
208 result->flags= ntohl(result->flags);
209 bodylen -= header.response.extlen;
210
211 result->key_length= keylen;
212 if (memcached_safe_read(ptr, result->key, keylen) != MEMCACHED_SUCCESS)
213 return MEMCACHED_UNKNOWN_READ_FAILURE;
214
215 bodylen -= keylen;
216 if (memcached_string_check(&result->value,
217 bodylen) != MEMCACHED_SUCCESS)
218 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
219
220 char *vptr= memcached_string_value(&result->value);
221 if (memcached_safe_read(ptr, vptr, bodylen) != MEMCACHED_SUCCESS)
222 return MEMCACHED_UNKNOWN_READ_FAILURE;
223
224 memcached_string_set_length(&result->value, bodylen);
225 }
226 break;
227 case PROTOCOL_BINARY_CMD_INCREMENT:
228 case PROTOCOL_BINARY_CMD_DECREMENT:
229 {
230 if (bodylen != sizeof(uint64_t) || buffer_length != sizeof(uint64_t))
231 return MEMCACHED_PROTOCOL_ERROR;
232
233 WATCHPOINT_ASSERT(bodylen == buffer_length);
234 uint64_t val;
235 if (memcached_safe_read(ptr, &val, sizeof(val)) != MEMCACHED_SUCCESS)
236 return MEMCACHED_UNKNOWN_READ_FAILURE;
237
238 val= ntohll(val);
239 memcpy(buffer, &val, sizeof(val));
240 }
241 break;
242 case PROTOCOL_BINARY_CMD_VERSION:
243 {
244 memset(buffer, 0, buffer_length);
245 if (bodylen >= buffer_length)
246 /* not enough space in buffer.. should not happen... */
247 return MEMCACHED_UNKNOWN_READ_FAILURE;
248 else if (memcached_safe_read(ptr, buffer, bodylen) != MEMCACHED_SUCCESS)
249 return MEMCACHED_UNKNOWN_READ_FAILURE;
250 }
251 break;
252 case PROTOCOL_BINARY_CMD_FLUSH:
253 case PROTOCOL_BINARY_CMD_QUIT:
254 case PROTOCOL_BINARY_CMD_SET:
255 case PROTOCOL_BINARY_CMD_ADD:
256 case PROTOCOL_BINARY_CMD_REPLACE:
257 case PROTOCOL_BINARY_CMD_APPEND:
258 case PROTOCOL_BINARY_CMD_PREPEND:
259 case PROTOCOL_BINARY_CMD_DELETE:
260 {
261 WATCHPOINT_ASSERT(bodylen == 0);
262 return MEMCACHED_SUCCESS;
263 }
264 break;
265 case PROTOCOL_BINARY_CMD_NOOP:
266 {
267 WATCHPOINT_ASSERT(bodylen == 0);
268 return MEMCACHED_END;
269 }
270 break;
271 case PROTOCOL_BINARY_CMD_STAT:
272 {
273 if (bodylen == 0)
274 return MEMCACHED_END;
275 else if (bodylen + 1 > buffer_length)
276 /* not enough space in buffer.. should not happen... */
277 return MEMCACHED_UNKNOWN_READ_FAILURE;
278 else
279 {
280 size_t keylen= header.response.keylen;
281 memset(buffer, 0, buffer_length);
282 if (memcached_safe_read(ptr, buffer, keylen) != MEMCACHED_SUCCESS ||
283 memcached_safe_read(ptr, buffer + keylen + 1,
284 bodylen - keylen) != MEMCACHED_SUCCESS)
285 return MEMCACHED_UNKNOWN_READ_FAILURE;
286 }
287 }
288 break;
289 default:
290 {
291 /* Command not implemented yet! */
292 WATCHPOINT_ASSERT(0);
293 return MEMCACHED_PROTOCOL_ERROR;
294 }
295 }
296 }
297 else if (header.response.bodylen)
298 {
299 /* What should I do with the error message??? just discard it for now */
300 char hole[SMALL_STRING_LEN];
301 while (bodylen > 0)
302 {
303 size_t nr= (bodylen > SMALL_STRING_LEN) ? SMALL_STRING_LEN : bodylen;
304 if (memcached_safe_read(ptr, hole, nr) != MEMCACHED_SUCCESS)
305 return MEMCACHED_UNKNOWN_READ_FAILURE;
306 bodylen -= nr;
307 }
308
309 /* This might be an error from one of the quiet commands.. if
310 * so, just throw it away and get the next one. What about creating
311 * a callback to the user with the error information?
312 */
313 switch (header.response.opcode)
314 {
315 case PROTOCOL_BINARY_CMD_SETQ:
316 case PROTOCOL_BINARY_CMD_ADDQ:
317 case PROTOCOL_BINARY_CMD_REPLACEQ:
318 case PROTOCOL_BINARY_CMD_APPENDQ:
319 case PROTOCOL_BINARY_CMD_PREPENDQ:
320 return binary_read_one_response(ptr, buffer, buffer_length, result);
321 default:
322 break;
323 }
324 }
325
326 memcached_return rc= MEMCACHED_SUCCESS;
327 unlikely(header.response.status != 0)
328 switch (header.response.status)
329 {
330 case PROTOCOL_BINARY_RESPONSE_KEY_ENOENT:
331 rc= MEMCACHED_NOTFOUND;
332 break;
333 case PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS:
334 rc= MEMCACHED_DATA_EXISTS;
335 break;
336 case PROTOCOL_BINARY_RESPONSE_E2BIG:
337 case PROTOCOL_BINARY_RESPONSE_EINVAL:
338 case PROTOCOL_BINARY_RESPONSE_NOT_STORED:
339 case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND:
340 case PROTOCOL_BINARY_RESPONSE_ENOMEM:
341 default:
342 /* @todo fix the error mappings */
343 rc= MEMCACHED_PROTOCOL_ERROR;
344 break;
345 }
346
347 return rc;
348 }