2 * Copyright (C) 2005-2011 Atsushi Konno All rights reserved.
3 * Copyright (C) 2005 QSDN,Inc. All rights reserved.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #ifdef USE_MEMCACHE_COOKIE
19 #include "chxj_cookie.h"
20 #include "chxj_url_encode.h"
21 #include "chxj_apply_convrule.h"
22 #include "chxj_str_util.h"
24 #include "ap_release.h"
29 #include "apr_base64.h"
35 #include <libmemcached/memcached.h>
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)
44 #define MEMCACHE_LOCK_KEY "chxj::lock"
45 #define MEMCACHE_WAIT_MICRO_SECOND (5000)
46 #define MEMCACHE_LOCK_RETRY_COUNT (100)
49 #define MEMCACHE_MAX_SERVER (10)
50 #define MEMCACHE_FLAGS (0)
52 #define DEFAULT_MEMCACHE_TIMEOUT (1000)
53 #define DEFAULT_DELETE_TIMEOUT (0)
55 #define DEFAULT_COOKIE_DB_NAME "chxj_cookie"
56 #define DEFAULT_COOKIE_EXPIRE_DB_NAME "chxj_cookie_expire"
58 /* The underlying memcache system is thread safe. */
59 static memcached_st *memc = NULL;
60 static memcached_server_st *servers = NULL;
63 _memcache_cleanup(void *UNUSED(unused))
66 memcached_server_list_free(servers);
77 chxj_memcache_init(request_rec *r, mod_chxj_config *m)
79 DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
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);
85 apr_pool_cleanup_register(r->pool, NULL, _memcache_cleanup, _memcache_cleanup);
87 DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
92 chxj_memcache_and_memcache_server_create(request_rec *r, mod_chxj_config *m)
95 DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
97 memc = memcached_create(NULL);
99 ERR(r, "%s:%d end chxj_memcache_server_create(): failed allocation of memcached_st.", APLOG_MARK);
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));
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));
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));
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));
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));
129 DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
135 chxj_memcache_set_cookie(request_rec *r, mod_chxj_config *m, const char *cookie_id, const char *store_string)
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__);
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));
149 DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
155 chxj_memcache_reset_cookie(request_rec *r, mod_chxj_config *m, const char *cookie_id)
158 DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
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);
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);
170 DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
176 chxj_memcache_get_cookie(request_rec *r, mod_chxj_config *UNUSED(m), const char *cookie_id)
184 DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
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));
191 ret = apr_pstrdup(r->pool, load_string);
194 DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
200 chxj_memcache_delete_cookie(request_rec *r, mod_chxj_config *UNUSED(m), const char *cookie_id)
204 DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
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));
212 DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
219 chxj_save_cookie_memcache(request_rec *r, mod_chxj_config *m, const char *cookie_id, const char *store_string)
221 DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
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);
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);
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__);
242 chxj_update_cookie_memcache(request_rec *r, mod_chxj_config *m, const char *cookie_id, const char *store_string)
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);
251 if (! chxj_memcache_set_cookie(r, m, cookie_id, store_string)) {
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);
257 DBG(r,"REQ[%X] cookie_id:[%s]", TO_ADDR(r),cookie_id);
258 DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
264 chxj_load_cookie_memcache(request_rec *r, mod_chxj_config *m, const char *cookie_id)
267 DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
268 DBG(r,"REQ[%X] cookie_id:[%s]", TO_ADDR(r),cookie_id);
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);
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);
280 DBG(r,"REQ[%X] cookie_id:[%s]", TO_ADDR(r),cookie_id);
281 DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
287 chxj_delete_cookie_memcache(request_rec *r, mod_chxj_config *m, const char *cookie_id)
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);
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);
301 DBG(r,"REQ[%X] cookie_id:[%s]", TO_ADDR(r),cookie_id);
302 DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
308 chxj_save_cookie_expire_memcache(request_rec *r, mod_chxj_config *m, const char *cookie_id)
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);
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);
322 DBG(r,"REQ[%X] cookie_id:[%s]", TO_ADDR(r),cookie_id);
323 DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
329 chxj_delete_cookie_expire_memcache(request_rec *r, mod_chxj_config *UNUSED(m), const char *cookie_id)
331 DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
332 DBG(r,"REQ[%X] cookie_id:[%s]", TO_ADDR(r),cookie_id);
334 DBG(r,"REQ[%X] cookie_id:[%s]", TO_ADDR(r),cookie_id);
335 DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
341 chxj_cookie_expire_gc_memcache(request_rec *r, mod_chxj_config *UNUSED(m))
343 DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
345 DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
351 chxj_cookie_lock_memcache(request_rec *r, mod_chxj_config *m)
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;
358 DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
360 if (! chxj_memcache_init(r, m)) {
361 ERR(r, "%s:%d end chxj_cookie_lock_memcache(): Cannot create memcache server", APLOG_MARK);
365 apr_snprintf(baton, sizeof(baton)-1, "dummy");
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));
373 if (rc == MEMCACHED_SUCCESS || rc == MEMCACHED_STORED) {
375 DBG(r,"REQ[%X] got lock",TO_ADDR(r));
379 if (retry_count >= MEMCACHE_LOCK_RETRY_COUNT) {
380 ERR(r, "%s:%d end chxj_cookie_lock_memcache(): retry over.",APLOG_MARK);
383 apr_sleep(wait_time);
386 DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
392 chxj_cookie_unlock_memcache(request_rec *r, mod_chxj_config *m)
394 DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
396 ERR(r, "%s:%d end chxj_cookie_unlock_memcache(): runtime exception: programing failure.", APLOG_MARK);
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);
404 DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);