OSDN Git Service

* Fixed BUG.
[modchxj/mod_chxj.git] / src / chxj_memcache.c
1 /*
2  * Copyright (C) 2005-2011 Atsushi Konno All rights reserved.
3  * Copyright (C) 2005 QSDN,Inc. All rights reserved.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 #ifdef USE_MEMCACHE_COOKIE
18 #include "mod_chxj.h"
19 #include "chxj_cookie.h"
20 #include "chxj_url_encode.h"
21 #include "chxj_apply_convrule.h"
22 #include "chxj_str_util.h"
23
24 #include "ap_release.h"
25
26 #include "apu.h"
27 #include "apr_uuid.h"
28 #include "apr_md5.h"
29 #include "apr_base64.h"
30 #include "apr_uri.h"
31
32 #include <unistd.h>
33
34 /* for memcache */
35 #include <libmemcached/memcached.h>
36
37 #define MEMCACHE_MIN_CONNECTION (0)
38 #define MEMCACHE_SMAX_CONNECTION (1)
39 #define MEMCACHE_MAX_CONNECTION (1)
40 #define MEMCACHE_CONNECTION_TIMEOUT (60)
41 #define MEMCACHE_POLL_TIMEOUT       (60)
42 #define MEMCACHE_RETRY_TIMEOUT      (60)
43
44 #define MEMCACHE_LOCK_KEY "chxj::lock"
45 #define MEMCACHE_WAIT_MICRO_SECOND (5000)
46 #define MEMCACHE_LOCK_RETRY_COUNT (100)
47
48
49 #define MEMCACHE_MAX_SERVER (10)
50 #define MEMCACHE_FLAGS (0)
51
52 #define DEFAULT_MEMCACHE_TIMEOUT (1000)
53 #define DEFAULT_DELETE_TIMEOUT (0)
54
55 #define DEFAULT_COOKIE_DB_NAME "chxj_cookie"
56 #define DEFAULT_COOKIE_EXPIRE_DB_NAME "chxj_cookie_expire"
57
58 /* The underlying memcache system is thread safe. */
59 static memcached_st *memc = NULL;
60 static memcached_server_st *servers = NULL;
61
62 static apr_status_t
63 _memcache_cleanup(void *UNUSED(unused))
64 {
65   if (servers) {
66     memcached_server_list_free(servers);
67     servers = NULL;
68   }
69   if (memc) {
70     memcached_free(memc);
71     memc = NULL;
72   }
73   return APR_SUCCESS;
74 }
75
76 int
77 chxj_memcache_init(request_rec *r, mod_chxj_config *m)
78 {
79   DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
80   if (! memc) {
81     if (!chxj_memcache_and_memcache_server_create(r, m)) {
82       ERR(r, "%s:%d end chxj_memcache_init() failed: chxj_memcache_and_memcache_server_create()", APLOG_MARK);
83       return CHXJ_FALSE;
84     }
85     apr_pool_cleanup_register(r->pool, NULL, _memcache_cleanup, _memcache_cleanup);
86   }
87   DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
88   return CHXJ_TRUE;
89 }
90
91 int
92 chxj_memcache_and_memcache_server_create(request_rec *r, mod_chxj_config *m)
93 {
94   memcached_return rc;
95   DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
96   
97   memc = memcached_create(NULL);
98   if (! memc) {
99     ERR(r, "%s:%d end chxj_memcache_server_create(): failed allocation of memcached_st.", APLOG_MARK);
100     return CHXJ_FALSE;
101   }
102   servers = memcached_server_list_append(NULL, m->memcache.host, m->memcache.port, &rc);
103   if (servers == NULL || rc != MEMCACHED_SUCCESS) {
104     ERR(r, "%s:%d end chxj_memcache_server_create(): host:[%s] port:[%d]: %s", APLOG_MARK, m->memcache.host, m->memcache.port, memcached_strerror(memc, rc));
105     return CHXJ_FALSE;
106   }
107   rc = memcached_server_push(memc, servers);
108   if (rc != MEMCACHED_SUCCESS) {
109     ERR(r, "%s:%d end chxj_memcache_server_create(): host:[%s] port:[%d]: %s\n", APLOG_MARK, m->memcache.host, m->memcache.port, memcached_strerror(memc, rc));
110     return CHXJ_FALSE;
111   }
112
113   rc = memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_POLL_TIMEOUT, (uint64_t)MEMCACHE_POLL_TIMEOUT);
114   if (rc != MEMCACHED_SUCCESS) {
115     ERR(r, "%s:%d end chxj_memcache_server_create(): memcached_behavior_set(MEMCACHED_BEHAVIOR_POLL_TIMEOUT): %s", APLOG_MARK, memcached_strerror(memc, rc));
116     return CHXJ_FALSE;
117   }
118   rc = memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT, (uint64_t)MEMCACHE_CONNECTION_TIMEOUT);
119   if (rc != MEMCACHED_SUCCESS) {
120     ERR(r, "%s:%d end chxj_memcache_server_create(): memcached_behavior_set(MEMCACHED_BEHAVIOR_CONNECTION_TIMEOUT): %s", APLOG_MARK, memcached_strerror(memc, rc));
121     return CHXJ_FALSE;
122   }
123   rc = memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_RETRY_TIMEOUT, (uint64_t)MEMCACHE_RETRY_TIMEOUT);
124   if (rc != MEMCACHED_SUCCESS) {
125     ERR(r, "%s:%d end chxj_memcache_server_create(): memcached_behavior_set(MEMCACHED_BEHAVIOR_RETRY_TIMEOUT): %s", APLOG_MARK, memcached_strerror(memc, rc));
126     return CHXJ_FALSE;
127   }
128   
129   DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
130   return CHXJ_TRUE;
131 }
132
133
134 int
135 chxj_memcache_set_cookie(request_rec *r, mod_chxj_config *m, const char *cookie_id, const char *store_string)
136 {
137   memcached_return rc;
138   time_t timeout = (time_t) ((m->cookie_timeout) ? m->cookie_timeout : DEFAULT_COOKIE_TIMEOUT);
139   DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
140
141   rc= memcached_set(memc, (char *)cookie_id, strlen(cookie_id),
142                     (char *)store_string, strlen(store_string),
143                     (time_t)timeout, (uint32_t)0);
144   if (rc != MEMCACHED_SUCCESS && rc != MEMCACHED_BUFFERED) {
145     ERR(r, "%s:%d end chxj_memcache_set_cookie(): failed memcache_set(): %s", APLOG_MARK, memcached_strerror(memc, rc));
146     return CHXJ_FALSE;
147   }
148
149   DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
150   return CHXJ_TRUE;
151 }
152
153
154 int
155 chxj_memcache_reset_cookie(request_rec *r, mod_chxj_config *m, const char *cookie_id)
156 {
157   char *store_string;
158   DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
159
160   if (! (store_string = chxj_memcache_get_cookie(r, m, cookie_id))) {
161     ERR(r, "%s:%d end chxj_memcache_reset_cookie() failed: chxj_memcache_get_cookie() cookie_id:[%s]", APLOG_MARK, cookie_id);
162     return CHXJ_FALSE;
163   }
164
165   if (! chxj_memcache_set_cookie(r, m, cookie_id, store_string)) {
166     ERR(r, "%s:%d end chxj_memcache_reset_cookie() failed: chxj_memcache_set_cookie() cookie_id:[%s]", APLOG_MARK, cookie_id);
167     return CHXJ_FALSE;
168   }
169
170   DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
171   return CHXJ_TRUE;
172 }
173
174
175 char *
176 chxj_memcache_get_cookie(request_rec *r, mod_chxj_config *UNUSED(m), const char *cookie_id)
177 {
178   char *load_string;
179   char *ret;
180   size_t value_length;
181   uint32_t flags;
182   memcached_return rc;
183
184   DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
185
186   load_string = memcached_get(memc, (char *)cookie_id, strlen(cookie_id), &value_length, &flags, &rc);
187   if (rc != MEMCACHED_SUCCESS) {
188     ERR(r, "%s:%d end chxj_memcache_get_cookie(): failed memcached_get(): %s", APLOG_MARK, memcached_strerror(memc, rc));
189     return NULL;
190   }
191   ret = apr_pstrdup(r->pool, load_string);
192   free(load_string);
193   
194   DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
195   return ret;
196 }
197
198
199 int
200 chxj_memcache_delete_cookie(request_rec *r, mod_chxj_config *UNUSED(m),  const char *cookie_id)
201 {
202   memcached_return rc;
203
204   DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
205
206   rc = memcached_delete(memc, (char *)cookie_id, strlen(cookie_id), (time_t)0);
207   if (rc != MEMCACHED_SUCCESS && rc == MEMCACHED_BUFFERED) {
208     ERR(r,"%s:%d end chxj_memcache_delete_cookie(): failed memcached_delete(): %s", APLOG_MARK, memcached_strerror(memc, rc));
209     return CHXJ_FALSE;
210   }
211
212   DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
213   return CHXJ_TRUE;
214 }
215
216
217
218 int
219 chxj_save_cookie_memcache(request_rec *r, mod_chxj_config *m, const char *cookie_id, const char *store_string)
220 {
221   DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
222
223   if (! chxj_memcache_init(r, m)) {
224     ERR(r, "%s:%d end chxj_save_cookie_memcache(): Cannot create memcache server: cookie_id:[%s] store_string:[%s]", APLOG_MARK, cookie_id, store_string);
225     return CHXJ_FALSE;
226   }
227
228   if (! chxj_memcache_set_cookie(r, m, cookie_id, store_string)) {
229     ERR(r, "%s:%d end chxj_save_cookie_memcache(): cannot store to memcache host:[%s] port:[%d] cookie_id:[%s] store_string:[%s]", 
230         APLOG_MARK, m->memcache.host, m->memcache.port, cookie_id, store_string);
231     return CHXJ_FALSE;
232   }
233
234   DBG(r,"REQ[%X] stored DATA:[%s]", TO_ADDR(r),chxj_memcache_get_cookie(r, m, cookie_id));
235   DBG(r,"REQ[%X] cookie_id:[%s]", TO_ADDR(r),cookie_id);
236   DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
237   return CHXJ_TRUE;
238 }
239
240
241 int
242 chxj_update_cookie_memcache(request_rec *r, mod_chxj_config *m, const char *cookie_id, const char *store_string)
243 {
244   DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
245   DBG(r,"REQ[%X] cookie_id:[%s]", TO_ADDR(r),cookie_id);
246   if (! chxj_memcache_init(r, m)) {
247     ERR(r, "%s:%d end chxj_update_cookie_memcache(): Cannot create memcache server: cookie_id:[%s] store_string:[%s]",APLOG_MARK,cookie_id, store_string);
248     return CHXJ_FALSE;
249   }
250
251   if (! chxj_memcache_set_cookie(r, m, cookie_id, store_string)) {
252     ERR(r, 
253         "%s:%d end chxj_update_cookie_memcache(): cannot store to memcache host:[%s] port:[%d] cookie_id:[%s] store_string:[%s]", 
254         APLOG_MARK, m->memcache.host, m->memcache.port, cookie_id, store_string);
255     return CHXJ_FALSE;
256   }
257   DBG(r,"REQ[%X] cookie_id:[%s]", TO_ADDR(r),cookie_id);
258   DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
259   return CHXJ_TRUE;
260 }
261
262
263 char *
264 chxj_load_cookie_memcache(request_rec *r, mod_chxj_config *m, const char *cookie_id)
265 {
266   char *load_string;
267   DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
268   DBG(r,"REQ[%X] cookie_id:[%s]", TO_ADDR(r),cookie_id);
269
270   if (! chxj_memcache_init(r, m)) {
271     ERR(r, "%s:%d end chxj_load_cookie_memcache(): Cannot create memcache server: cookie_id:[%s]",APLOG_MARK, cookie_id);
272     return NULL;
273   }
274
275   if (! (load_string = chxj_memcache_get_cookie(r, m, cookie_id))) {
276     ERR(r, "%s:%d end chxj_load_cookie_memcache(): cannot store to memcache host:[%s] port:[%d] cookie_id:[%s]", APLOG_MARK, 
277         m->memcache.host, m->memcache.port, cookie_id);
278     return NULL;
279   }
280   DBG(r,"REQ[%X] cookie_id:[%s]", TO_ADDR(r),cookie_id);
281   DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
282   return load_string;
283 }
284
285
286 int
287 chxj_delete_cookie_memcache(request_rec *r, mod_chxj_config *m, const char *cookie_id)
288 {
289   DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
290   DBG(r,"REQ[%X] cookie_id:[%s]", TO_ADDR(r),cookie_id);
291   if (! chxj_memcache_init(r, m)) {
292     ERR(r, "%s:%d end chxj_delete_cookie_memcache(): Cannot create memcache server: cookie_id:[%s]",APLOG_MARK, cookie_id);
293     return CHXJ_FALSE;
294   }
295
296   if (! chxj_memcache_delete_cookie(r, m, cookie_id)) {
297     ERR(r, "%s:%d end chxj_delete_cookie_memcache(): Cannot store to memcache host:[%s] port:[%d] cookie_id:[%s]", APLOG_MARK,
298         m->memcache.host, m->memcache.port, cookie_id);
299     return CHXJ_FALSE;
300   }
301   DBG(r,"REQ[%X] cookie_id:[%s]", TO_ADDR(r),cookie_id);
302   DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
303   return CHXJ_TRUE;
304 }
305
306
307 int
308 chxj_save_cookie_expire_memcache(request_rec *r, mod_chxj_config *m, const char *cookie_id)
309 {
310   DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
311   DBG(r,"REQ[%X] cookie_id:[%s]", TO_ADDR(r),cookie_id);
312   if (! chxj_memcache_init(r, m)) {
313     ERR(r, "%s:%d end chxj_save_cookie_expire_memcache(): Cannot create memcache server: cookie_id:[%s]",APLOG_MARK,cookie_id);
314     return CHXJ_FALSE;
315   }
316
317   if (! chxj_memcache_reset_cookie(r, m, cookie_id)) {
318     ERR(r, "%s:%d end chxj_save_cookie_expire_memcache(): Cannot store to memcache host:[%s] port:[%d] cookie_id:[%s]", APLOG_MARK, 
319         m->memcache.host, m->memcache.port, cookie_id);
320     return CHXJ_FALSE;
321   }
322   DBG(r,"REQ[%X] cookie_id:[%s]", TO_ADDR(r),cookie_id);
323   DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
324   return CHXJ_TRUE;
325 }
326
327
328 int
329 chxj_delete_cookie_expire_memcache(request_rec *r, mod_chxj_config *UNUSED(m), const char *cookie_id)
330 {
331   DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
332   DBG(r,"REQ[%X] cookie_id:[%s]", TO_ADDR(r),cookie_id);
333   /* PASS */
334   DBG(r,"REQ[%X] cookie_id:[%s]", TO_ADDR(r),cookie_id);
335   DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
336   return CHXJ_TRUE;
337 }
338
339
340 int
341 chxj_cookie_expire_gc_memcache(request_rec *r, mod_chxj_config *UNUSED(m))
342 {
343   DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
344   /* PASS */
345   DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
346   return CHXJ_TRUE;
347 }
348
349
350 int
351 chxj_cookie_lock_memcache(request_rec *r, mod_chxj_config *m)
352 {
353   char baton[256];
354   int retry_count = 0;
355   apr_uint32_t timeout = (apr_uint32_t) ((m->cookie_timeout) ? m->cookie_timeout : DEFAULT_COOKIE_TIMEOUT);
356   apr_interval_time_t wait_time = MEMCACHE_WAIT_MICRO_SECOND;
357
358   DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
359
360   if (! chxj_memcache_init(r, m)) {
361     ERR(r, "%s:%d end chxj_cookie_lock_memcache(): Cannot create memcache server", APLOG_MARK);
362     return CHXJ_FALSE;
363   }
364
365   apr_snprintf(baton, sizeof(baton)-1, "dummy");
366   while(1) {
367     memcached_return rc;
368     rc = memcached_add(memc, MEMCACHE_LOCK_KEY, sizeof(MEMCACHE_LOCK_KEY)-1, baton, strlen(baton), (time_t)timeout, (uint32_t)0);
369     if (rc != MEMCACHED_SUCCESS && rc != MEMCACHED_STORED && rc != MEMCACHED_NOTSTORED) {
370       ERR(r, "%s:%d end chxj_cookie_lock_memcache(): failed memcached_add(): %s\n", APLOG_MARK, memcached_strerror(memc, rc));
371       return CHXJ_FALSE;
372     }
373     if (rc == MEMCACHED_SUCCESS || rc == MEMCACHED_STORED) {
374       /* got lock */
375       DBG(r,"REQ[%X] got lock",TO_ADDR(r));
376       break;
377     }
378     retry_count++;
379     if (retry_count >= MEMCACHE_LOCK_RETRY_COUNT) {
380       ERR(r, "%s:%d end chxj_cookie_lock_memcache(): retry over.",APLOG_MARK);
381       return CHXJ_FALSE;
382     }
383     apr_sleep(wait_time);
384   }
385
386   DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
387   return CHXJ_TRUE;
388 }
389
390
391 int
392 chxj_cookie_unlock_memcache(request_rec *r, mod_chxj_config *m)
393 {
394   DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
395   if (! memc) {
396     ERR(r, "%s:%d end chxj_cookie_unlock_memcache(): runtime exception: programing failure.", APLOG_MARK);
397     return CHXJ_FALSE;
398   }
399
400   if (! chxj_memcache_delete_cookie(r, m, MEMCACHE_LOCK_KEY)) {
401     ERR(r, "%s:%d end chxj_cookie_unlock_memcache(): failed: chxj_memcache_delete_cookie() (lock data)", APLOG_MARK);
402     return CHXJ_FALSE;
403   }
404   DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
405   return CHXJ_TRUE;
406 }
407
408
409 #endif
410 /*
411  * vim:ts=2 et
412  */