1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include "http_config.h"
22 #include "apu_version.h"
24 /* apr_memcache support requires >= 1.3 */
25 #if APU_MAJOR_VERSION > 1 || \
26 (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION > 2)
27 #define HAVE_APU_MEMCACHE 1
30 #ifdef HAVE_APU_MEMCACHE
32 #include "ap_socache.h"
35 #include "apr_memcache.h"
37 /* The underlying apr_memcache system is thread safe.. */
38 #define MC_KEY_LEN 254
40 #ifndef MC_DEFAULT_SERVER_PORT
41 #define MC_DEFAULT_SERVER_PORT 11211
45 #ifndef MC_DEFAULT_SERVER_MIN
46 #define MC_DEFAULT_SERVER_MIN 0
49 #ifndef MC_DEFAULT_SERVER_SMAX
50 #define MC_DEFAULT_SERVER_SMAX 1
53 #ifndef MC_DEFAULT_SERVER_TTL
54 #define MC_DEFAULT_SERVER_TTL (15*1000*1000) /* 15 seconds */
57 module AP_MODULE_DECLARE_DATA socache_memcache_module;
63 struct ap_socache_instance_t {
67 apr_size_t taglen; /* strlen(tag) + 1 */
70 static const char *socache_mc_create(ap_socache_instance_t **context,
72 apr_pool_t *tmp, apr_pool_t *p)
74 ap_socache_instance_t *ctx;
76 *context = ctx = apr_palloc(p, sizeof *ctx);
79 return "List of server names required to create memcache socache.";
82 ctx->servers = apr_pstrdup(p, arg);
87 static apr_status_t socache_mc_init(ap_socache_instance_t *ctx,
88 const char *namespace,
89 const struct ap_socache_hints *hints,
90 server_rec *s, apr_pool_t *p)
94 apr_uint16_t nservers = 0;
99 socache_mc_svr_cfg *sconf = ap_get_module_config(s->module_config,
100 &socache_memcache_module);
102 ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit);
104 /* Find all the servers in the first run to get a total count */
105 cache_config = apr_pstrdup(p, ctx->servers);
106 split = apr_strtok(cache_config, ",", &tok);
109 split = apr_strtok(NULL,",", &tok);
112 rv = apr_memcache_create(p, nservers, 0, &ctx->mc);
113 if (rv != APR_SUCCESS) {
114 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(00785)
115 "Failed to create Memcache Object of '%d' size.",
120 /* Now add each server to the memcache */
121 cache_config = apr_pstrdup(p, ctx->servers);
122 split = apr_strtok(cache_config, ",", &tok);
124 apr_memcache_server_t *st;
129 rv = apr_parse_addr_port(&host_str, &scope_id, &port, split, p);
130 if (rv != APR_SUCCESS) {
131 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(00786)
132 "Failed to Parse memcache Server: '%s'", split);
136 if (host_str == NULL) {
137 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(00787)
138 "Failed to Parse Server, "
139 "no hostname specified: '%s'", split);
144 port = MC_DEFAULT_SERVER_PORT;
147 rv = apr_memcache_server_create(p,
149 MC_DEFAULT_SERVER_MIN,
150 MC_DEFAULT_SERVER_SMAX,
154 if (rv != APR_SUCCESS) {
155 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(00788)
156 "Failed to Create memcache Server: %s:%d",
161 rv = apr_memcache_add_server(ctx->mc, st);
162 if (rv != APR_SUCCESS) {
163 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(00789)
164 "Failed to Add memcache Server: %s:%d",
169 split = apr_strtok(NULL,",", &tok);
172 ctx->tag = apr_pstrcat(p, namespace, ":", NULL);
173 ctx->taglen = strlen(ctx->tag) + 1;
175 /* socache API constraint: */
176 AP_DEBUG_ASSERT(ctx->taglen <= 16);
181 static void socache_mc_destroy(ap_socache_instance_t *context, server_rec *s)
186 /* Converts (binary) id into a key prefixed by the predetermined
187 * namespace tag; writes output to key buffer. Returns non-zero if
188 * the id won't fit in the key buffer. */
189 static int socache_mc_id2key(ap_socache_instance_t *ctx,
190 const unsigned char *id, unsigned int idlen,
191 char *key, apr_size_t keylen)
195 if (idlen * 2 + ctx->taglen >= keylen)
198 cp = apr_cpystrn(key, ctx->tag, ctx->taglen);
199 ap_bin2hex(id, idlen, cp);
204 static apr_status_t socache_mc_store(ap_socache_instance_t *ctx, server_rec *s,
205 const unsigned char *id, unsigned int idlen,
207 unsigned char *ucaData, unsigned int nData,
210 char buf[MC_KEY_LEN];
213 if (socache_mc_id2key(ctx, id, idlen, buf, sizeof buf)) {
217 /* memcache needs time in seconds till expiry; fail if this is not
218 * positive *before* casting to unsigned (apr_uint32_t). */
219 expiry -= apr_time_now();
220 if (apr_time_sec(expiry) <= 0) {
223 rv = apr_memcache_set(ctx->mc, buf, (char*)ucaData, nData,
224 apr_time_sec(expiry), 0);
226 if (rv != APR_SUCCESS) {
227 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(00790)
228 "scache_mc: error setting key '%s' "
229 "with %d bytes of data", buf, nData);
236 static apr_status_t socache_mc_retrieve(ap_socache_instance_t *ctx, server_rec *s,
237 const unsigned char *id, unsigned int idlen,
238 unsigned char *dest, unsigned int *destlen,
242 char buf[MC_KEY_LEN], *data;
245 if (socache_mc_id2key(ctx, id, idlen, buf, sizeof buf)) {
249 /* ### this could do with a subpool, but _getp looks like it will
250 * eat memory like it's going out of fashion anyway. */
252 rv = apr_memcache_getp(ctx->mc, p, buf, &data, &data_len, NULL);
254 if (rv != APR_NOTFOUND) {
255 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00791)
256 "scache_mc: 'retrieve' FAIL");
260 else if (data_len > *destlen) {
261 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00792)
262 "scache_mc: 'retrieve' OVERFLOW");
266 memcpy(dest, data, data_len);
272 static apr_status_t socache_mc_remove(ap_socache_instance_t *ctx, server_rec *s,
273 const unsigned char *id,
274 unsigned int idlen, apr_pool_t *p)
276 char buf[MC_KEY_LEN];
279 if (socache_mc_id2key(ctx, id, idlen, buf, sizeof buf)) {
283 rv = apr_memcache_delete(ctx->mc, buf, 0);
285 if (rv != APR_SUCCESS) {
286 ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(00793)
287 "scache_mc: error deleting key '%s' ",
294 static void socache_mc_status(ap_socache_instance_t *ctx, request_rec *r, int flags)
296 /* TODO: Make a mod_status handler. meh. */
299 static apr_status_t socache_mc_iterate(ap_socache_instance_t *instance,
300 server_rec *s, void *userctx,
301 ap_socache_iterator_t *iterator,
307 static const ap_socache_provider_t socache_mc = {
320 #endif /* HAVE_APU_MEMCACHE */
322 static void *create_server_config(apr_pool_t *p, server_rec *s)
324 socache_mc_svr_cfg *sconf = apr_pcalloc(p, sizeof(socache_mc_svr_cfg));
326 sconf->ttl = MC_DEFAULT_SERVER_TTL;
331 static const char *socache_mc_set_ttl(cmd_parms *cmd, void *dummy,
334 socache_mc_svr_cfg *sconf = ap_get_module_config(cmd->server->module_config,
335 &socache_memcache_module);
340 if ((i < 1) || (i > 1800)) {
341 return apr_pstrcat(cmd->pool, cmd->cmd->name,
342 " must be a number between 1 and 1800.", NULL);
345 /* apr_memcache_server_create needs a ttl in usec. */
346 sconf->ttl = i * 1000 * 1000;
351 static void register_hooks(apr_pool_t *p)
353 #ifdef HAVE_APU_MEMCACHE
354 ap_register_provider(p, AP_SOCACHE_PROVIDER_GROUP, "memcache",
355 AP_SOCACHE_PROVIDER_VERSION,
360 static const command_rec socache_memcache_cmds[] = {
361 AP_INIT_TAKE1("MemcacheConnTTL", socache_mc_set_ttl, NULL, RSRC_CONF,
362 "TTL used for the connection with the memcache server(s), in seconds"),
366 AP_DECLARE_MODULE(socache_memcache) = {
367 STANDARD20_MODULE_STUFF,
368 NULL, /* create per-dir config structures */
369 NULL, /* merge per-dir config structures */
370 create_server_config, /* create per-server config structures */
371 NULL, /* merge per-server config structures */
372 socache_memcache_cmds, /* table of config file commands */
373 register_hooks /* register hooks */