1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2007 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.0 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_0.txt. |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Antony Dovgal <tony2001@phpclub.net> |
16 | Mikael Johansson <mikael AT synd DOT info> |
17 +----------------------------------------------------------------------+
18 */
19
20 /* $Id$ */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include "memcache_pool.h"
27 #include "ext/standard/php_smart_string.h"
28
29 typedef struct mmc_ascii_request {
30 mmc_request_t base; /* enable cast to mmc_request_t */
31 struct { /* stores value info while the body is being read */
32 char key[MMC_MAX_KEY_LEN + 1];
33 unsigned int flags;
34 unsigned long length;
35 unsigned long cas; /* CAS counter */
36 } value;
37 } mmc_ascii_request_t;
38
39 static int mmc_server_read_value(mmc_t *, mmc_request_t *);
40
mmc_stream_get_line(mmc_stream_t * io,char ** line)41 static int mmc_stream_get_line(mmc_stream_t *io, char **line) /*
42 attempts to read a line from server, returns the line size or 0 if no complete line was available {{{ */
43 {
44 size_t returned_len = 0;
45 io->readline(io, io->input.value + io->input.idx, MMC_BUFFER_SIZE - io->input.idx, &returned_len);
46 io->input.idx += returned_len;
47
48 if (io->input.idx && io->input.value[io->input.idx - 1] == '\n') {
49 int result = io->input.idx;
50 *line = io->input.value;
51 io->input.idx = 0;
52 return result;
53 }
54
55 return 0;
56 }
57 /* }}} */
58
mmc_request_check_response(char * line,int line_len)59 static int mmc_request_check_response(char *line, int line_len) /*
60 checks for response status and error codes {{{ */
61 {
62 int response;
63
64 // remove newline and thus prevent passing it to userland
65 if (line_len >= 2 && line[line_len - 2] == '\r' && line[line_len - 1] == '\n') {
66 line[line_len - 2] = '\0';
67 }
68
69 if (mmc_str_left(line, "OK", line_len, sizeof("OK")-1) ||
70 mmc_str_left(line, "STORED", line_len, sizeof("STORED")-1) ||
71 mmc_str_left(line, "DELETED", line_len, sizeof("DELETED")-1))
72 {
73 response = MMC_OK;
74 }
75 else if (mmc_str_left(line, "NOT_FOUND", line_len, sizeof("NOT_FOUND")-1)) {
76 response = MMC_RESPONSE_NOT_FOUND;
77 }
78 else if (
79 mmc_str_left(line, "NOT_STORED", line_len, sizeof("NOT_STORED")-1) ||
80 mmc_str_left(line, "EXISTS", line_len, sizeof("EXISTS")-1))
81 {
82 response = MMC_RESPONSE_EXISTS;
83 }
84 else if (mmc_str_left(line, "SERVER_ERROR out of memory", line_len, sizeof("SERVER_ERROR out of memory")-1)) {
85 response = MMC_RESPONSE_OUT_OF_MEMORY;
86 }
87 else if (mmc_str_left(line, "SERVER_ERROR object too large", line_len, sizeof("SERVER_ERROR object too large")-1)) {
88 response = MMC_RESPONSE_TOO_LARGE;
89 }
90 else if (
91 mmc_str_left(line, "ERROR", line_len, sizeof("ERROR")-1) ||
92 mmc_str_left(line, "SERVER_ERROR", line_len, sizeof("SERVER_ERROR")-1))
93 {
94 response = MMC_RESPONSE_ERROR;
95 }
96 else if (mmc_str_left(line, "CLIENT_ERROR", line_len, sizeof("CLIENT_ERROR")-1)) {
97 response = MMC_RESPONSE_CLIENT_ERROR;
98 }
99 else {
100 response = MMC_RESPONSE_UNKNOWN;
101 }
102
103 return response;
104 }
105
mmc_request_parse_response(mmc_t * mmc,mmc_request_t * request)106 static int mmc_request_parse_response(mmc_t *mmc, mmc_request_t *request) /*
107 reads a generic response header and delegates it to response_handler {{{ */
108 {
109 char *line;
110 int line_len = mmc_stream_get_line(request->io, &line);
111
112 if (line_len > 0) {
113 int response = mmc_request_check_response(line, line_len);
114 return request->response_handler(mmc, request, response, line, line_len - (sizeof("\r\n")-1), request->response_handler_param);
115 }
116
117 return MMC_REQUEST_MORE;
118 }
119 /* }}}*/
120
mmc_request_parse_mutate(mmc_t * mmc,mmc_request_t * request)121 static int mmc_request_parse_mutate(mmc_t *mmc, mmc_request_t *request) /*
122 reads and parses the <long-value> response header {{{ */
123 {
124 char *line;
125 int line_len;
126
127 line_len = mmc_stream_get_line(request->io, &line);
128 if (line_len > 0) {
129 long lval;
130 zval value;
131
132 int response = mmc_request_check_response(line, line_len);
133 if (response != MMC_RESPONSE_UNKNOWN) {
134 return request->response_handler(mmc, request, response, line, line_len - (sizeof("\r\n")-1), request->response_handler_param);
135 }
136
137 if (sscanf(line, "%ld", &lval) < 1) {
138 return mmc_server_failure(mmc, request->io, "Malformed VALUE header", 0);
139 }
140
141 ZVAL_LONG(&value, lval);
142 return request->value_handler(request->key, request->key_len, &value, 0, 0, request->value_handler_param);
143 }
144
145 return MMC_REQUEST_MORE;
146 }
147 /* }}}*/
148
mmc_request_parse_value(mmc_t * mmc,mmc_request_t * request)149 static int mmc_request_parse_value(mmc_t *mmc, mmc_request_t *request) /*
150 reads and parses the VALUE <key> <flags> <size> <cas> response header and then reads the value body {{{ */
151 {
152 char *line;
153 int line_len;
154 mmc_ascii_request_t *req = (mmc_ascii_request_t *)request;
155
156 line_len = mmc_stream_get_line(request->io, &line);
157 if (line_len > 0) {
158 if (mmc_str_left(line, "END", line_len, sizeof("END")-1)) {
159 return MMC_REQUEST_DONE;
160 }
161
162 if (sscanf(line, MMC_VALUE_HEADER, req->value.key, &(req->value.flags), &(req->value.length), &(req->value.cas)) < 3) {
163 return mmc_server_failure(mmc, request->io, "Malformed VALUE header", 0);
164 }
165
166 /* memory for data + \r\n */
167 mmc_buffer_alloc(&(request->readbuf), req->value.length + 2);
168
169 /* allow read_value handler to read the value body */
170 request->parse = mmc_server_read_value;
171
172 /* read more, php streams buffer input which must be read if available */
173 return MMC_REQUEST_AGAIN;
174 }
175
176 return MMC_REQUEST_MORE;
177 }
178 /* }}}*/
179
mmc_server_read_value(mmc_t * mmc,mmc_request_t * request)180 static int mmc_server_read_value(mmc_t *mmc, mmc_request_t *request) /*
181 read the value body into the buffer {{{ */
182 {
183 mmc_ascii_request_t *req = (mmc_ascii_request_t *)request;
184 request->readbuf.idx +=
185 request->io->read(request->io, request->readbuf.value.c + request->readbuf.idx, req->value.length + 2 - request->readbuf.idx);
186
187 /* done reading? */
188 if (request->readbuf.idx >= req->value.length + 2) {
189 int result;
190
191 /* allow parse_value to read next VALUE or END line */
192 request->parse = mmc_request_parse_value;
193 mmc_buffer_reset(&(request->readbuf));
194
195 result = mmc_unpack_value(
196 mmc, request, &(request->readbuf), req->value.key, strlen(req->value.key),
197 req->value.flags, req->value.cas, req->value.length);
198
199 /* request more data (END line) */
200 if (result == MMC_REQUEST_DONE) {
201 return MMC_REQUEST_AGAIN;
202 }
203
204 return result;
205 }
206
207 return MMC_REQUEST_MORE;
208 }
209 /* }}}*/
210
mmc_ascii_create_request()211 static mmc_request_t *mmc_ascii_create_request() /* {{{ */
212 {
213 mmc_ascii_request_t *request = emalloc(sizeof(mmc_ascii_request_t));
214 ZEND_SECURE_ZERO(request, sizeof(*request));
215 return (mmc_request_t *)request;
216 }
217 /* }}} */
218
mmc_ascii_clone_request(mmc_request_t * clone,mmc_request_t * request)219 static void mmc_ascii_clone_request(mmc_request_t *clone, mmc_request_t *request) /* {{{ */
220 {}
221 /* }}} */
222
mmc_ascii_reset_request(mmc_request_t * request)223 static void mmc_ascii_reset_request(mmc_request_t *request) /* {{{ */
224 {
225 mmc_ascii_request_t *req = (mmc_ascii_request_t *)request;
226 req->value.cas = 0;
227 mmc_request_reset(request);
228 }
229 /* }}} */
230
mmc_ascii_begin_get(mmc_request_t * request,int op)231 static void mmc_ascii_begin_get(mmc_request_t *request, int op) /* {{{ */
232 {
233 request->parse = mmc_request_parse_value;
234
235 if (op == MMC_OP_GETS) {
236 smart_string_appendl(&(request->sendbuf.value), "gets", sizeof("gets")-1);
237 }
238 else {
239 smart_string_appendl(&(request->sendbuf.value), "get", sizeof("get")-1);
240 }
241 }
242 /* }}} */
243
mmc_ascii_append_get(mmc_request_t * request,zval * zkey,const char * key,unsigned int key_len)244 static void mmc_ascii_append_get(mmc_request_t *request, zval *zkey, const char *key, unsigned int key_len) /* {{{ */
245 {
246 smart_string_appendl(&(request->sendbuf.value), " ", 1);
247 smart_string_appendl(&(request->sendbuf.value), key, key_len);
248 }
249 /* }}} */
250
mmc_ascii_end_get(mmc_request_t * request)251 static void mmc_ascii_end_get(mmc_request_t *request) /* {{{ */
252 {
253 smart_string_appendl(&(request->sendbuf.value), "\r\n", sizeof("\r\n")-1);
254 }
255 /* }}} */
256
mmc_ascii_get(mmc_request_t * request,int op,zval * zkey,const char * key,unsigned int key_len)257 static void mmc_ascii_get(mmc_request_t *request, int op, zval *zkey, const char *key, unsigned int key_len) /* {{{ */
258 {
259 mmc_ascii_begin_get(request, op);
260 mmc_ascii_append_get(request, zkey, key, key_len);
261 mmc_ascii_end_get(request);
262 }
263 /* }}} */
264
mmc_ascii_store(mmc_pool_t * pool,mmc_request_t * request,int op,const char * key,unsigned int key_len,unsigned int flags,unsigned int exptime,unsigned long cas,zval * value)265 static int mmc_ascii_store(
266 mmc_pool_t *pool, mmc_request_t *request, int op, const char *key, unsigned int key_len,
267 unsigned int flags, unsigned int exptime, unsigned long cas, zval *value) /* {{{ */
268 {
269 int status;
270 mmc_buffer_t buffer;
271 request->parse = mmc_request_parse_response;
272
273 ZEND_SECURE_ZERO(&buffer, sizeof(buffer));
274 status = mmc_pack_value(pool, &buffer, value, &flags);
275
276 if (status != MMC_OK) {
277 return status;
278 }
279
280 switch (op) {
281 case MMC_OP_SET:
282 smart_string_appendl(&(request->sendbuf.value), "set", sizeof("set")-1);
283 break;
284 case MMC_OP_ADD:
285 smart_string_appendl(&(request->sendbuf.value), "add", sizeof("add")-1);
286 break;
287 case MMC_OP_REPLACE:
288 smart_string_appendl(&(request->sendbuf.value), "replace", sizeof("replace")-1);
289 break;
290 case MMC_OP_CAS:
291 smart_string_appendl(&(request->sendbuf.value), "cas", sizeof("cas")-1);
292 break;
293 case MMC_OP_APPEND:
294 smart_string_appendl(&(request->sendbuf.value), "append", sizeof("append")-1);
295 break;
296 case MMC_OP_PREPEND:
297 smart_string_appendl(&(request->sendbuf.value), "prepend", sizeof("prepend")-1);
298 break;
299 default:
300 return MMC_REQUEST_FAILURE;
301 }
302
303 smart_string_appendl(&(request->sendbuf.value), " ", 1);
304 smart_string_appendl(&(request->sendbuf.value), key, key_len);
305 smart_string_appendl(&(request->sendbuf.value), " ", 1);
306 smart_string_append_unsigned(&(request->sendbuf.value), flags);
307 smart_string_appendl(&(request->sendbuf.value), " ", 1);
308 smart_string_append_unsigned(&(request->sendbuf.value), exptime);
309 smart_string_appendl(&(request->sendbuf.value), " ", 1);
310 smart_string_append_unsigned(&(request->sendbuf.value), buffer.value.len);
311
312 if (op == MMC_OP_CAS) {
313 smart_string_appendl(&(request->sendbuf.value), " ", 1);
314 smart_string_append_unsigned(&(request->sendbuf.value), cas);
315 }
316
317 smart_string_appendl(&(request->sendbuf.value), "\r\n", sizeof("\r\n")-1);
318 smart_string_appendl(&(request->sendbuf.value), buffer.value.c, buffer.value.len);
319 smart_string_appendl(&(request->sendbuf.value), "\r\n", sizeof("\r\n")-1);
320
321 mmc_buffer_free(&buffer);
322 return MMC_OK;
323 }
324 /* }}} */
325
mmc_ascii_delete(mmc_request_t * request,const char * key,unsigned int key_len,unsigned int exptime)326 static void mmc_ascii_delete(mmc_request_t *request, const char *key, unsigned int key_len, unsigned int exptime) /* {{{ */
327 {
328 request->parse = mmc_request_parse_response;
329
330 smart_string_appendl(&(request->sendbuf.value), "delete", sizeof("delete")-1);
331 smart_string_appendl(&(request->sendbuf.value), " ", 1);
332 smart_string_appendl(&(request->sendbuf.value), key, key_len);
333
334 if (exptime > 0) {
335 smart_string_appendl(&(request->sendbuf.value), " ", 1);
336 smart_string_append_unsigned(&(request->sendbuf.value), exptime);
337 }
338
339 smart_string_appendl(&(request->sendbuf.value), "\r\n", sizeof("\r\n")-1);
340 }
341 /* }}} */
342
mmc_ascii_mutate(mmc_request_t * request,zval * zkey,const char * key,unsigned int key_len,long value,long defval,int defval_used,unsigned int exptime)343 static void mmc_ascii_mutate(mmc_request_t *request, zval *zkey, const char *key, unsigned int key_len, long value, long defval, int defval_used, unsigned int exptime) /* {{{ */
344 {
345 request->parse = mmc_request_parse_mutate;
346
347 if (value >= 0) {
348 smart_string_appendl(&(request->sendbuf.value), "incr", sizeof("incr")-1);
349 }
350 else {
351 smart_string_appendl(&(request->sendbuf.value), "decr", sizeof("decr")-1);
352 }
353
354 smart_string_appendl(&(request->sendbuf.value), " ", 1);
355 smart_string_appendl(&(request->sendbuf.value), key, key_len);
356 smart_string_appendl(&(request->sendbuf.value), " ", 1);
357 smart_string_append_unsigned(&(request->sendbuf.value), value >= 0 ? value : -value);
358 smart_string_appendl(&(request->sendbuf.value), "\r\n", sizeof("\r\n")-1);
359 }
360 /* }}} */
361
mmc_ascii_flush(mmc_request_t * request,unsigned int exptime)362 static void mmc_ascii_flush(mmc_request_t *request, unsigned int exptime) /* {{{ */
363 {
364 request->parse = mmc_request_parse_response;
365 smart_string_appendl(&(request->sendbuf.value), "flush_all", sizeof("flush_all")-1);
366
367 if (exptime > 0) {
368 smart_string_appendl(&(request->sendbuf.value), " ", 1);
369 smart_string_append_unsigned(&(request->sendbuf.value), exptime);
370 }
371
372 smart_string_appendl(&(request->sendbuf.value), "\r\n", sizeof("\r\n")-1);
373 }
374 /* }}} */
375
mmc_ascii_version(mmc_request_t * request)376 static void mmc_ascii_version(mmc_request_t *request) /* {{{ */
377 {
378 request->parse = mmc_request_parse_response;
379 smart_string_appendl(&(request->sendbuf.value), "version\r\n", sizeof("version\r\n")-1);
380 }
381 /* }}} */
382
mmc_ascii_stats(mmc_request_t * request,const char * type,long slabid,long limit)383 static void mmc_ascii_stats(mmc_request_t *request, const char *type, long slabid, long limit) /* {{{ */
384 {
385 char *cmd;
386 unsigned int cmd_len;
387 request->parse = mmc_request_parse_response;
388
389 if (slabid) {
390 cmd_len = spprintf(&cmd, 0, "stats %s %ld %ld\r\n", type, slabid, limit);
391 }
392 else if (type) {
393 cmd_len = spprintf(&cmd, 0, "stats %s\r\n", type);
394 }
395 else {
396 cmd_len = spprintf(&cmd, 0, "stats\r\n");
397 }
398
399 smart_string_appendl(&(request->sendbuf.value), cmd, cmd_len);
400 efree(cmd);
401 }
402 /* }}} */
403
mmc_set_sasl_auth_data(mmc_pool_t * pool,mmc_request_t * request,const char * user,const char * password)404 static void mmc_set_sasl_auth_data(mmc_pool_t *pool, mmc_request_t *request, const char *user, const char *password) /* {{{ */
405 {
406 /* stats not supported */
407 }
408 /* }}} */
409
410 mmc_protocol_t mmc_ascii_protocol = {
411 mmc_ascii_create_request,
412 mmc_ascii_clone_request,
413 mmc_ascii_reset_request,
414 mmc_request_free,
415 mmc_ascii_get,
416 mmc_ascii_begin_get,
417 mmc_ascii_append_get,
418 mmc_ascii_end_get,
419 mmc_ascii_store,
420 mmc_ascii_delete,
421 mmc_ascii_mutate,
422 mmc_ascii_flush,
423 mmc_ascii_version,
424 mmc_ascii_stats,
425 mmc_set_sasl_auth_data
426 };
427
428 /*
429 * Local variables:
430 * tab-width: 4
431 * c-basic-offset: 4
432 * End:
433 * vim600: noet sw=4 ts=4 fdm=marker
434 * vim<600: noet sw=4 ts=4
435 */
436