Moved apache code into a folder to help prepare for packaging where we dont want...
[httpd-crcsyncproxy.git] / apache / modules / cache / mod_socache_memcache.c
blob0ac1c0019abe3005f3845950541c47675e09759d
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.
18 #include "httpd.h"
19 #include "http_config.h"
21 #include "apr.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
28 #endif
30 #ifdef HAVE_APU_MEMCACHE
32 #include "ap_socache.h"
33 #include "ap_mpm.h"
34 #include "http_log.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
42 #endif
45 #ifndef MC_DEFAULT_SERVER_MIN
46 #define MC_DEFAULT_SERVER_MIN 0
47 #endif
49 #ifndef MC_DEFAULT_SERVER_SMAX
50 #define MC_DEFAULT_SERVER_SMAX 1
51 #endif
53 #ifndef MC_DEFAULT_SERVER_TTL
54 #define MC_DEFAULT_SERVER_TTL 600
55 #endif
57 struct ap_socache_instance_t {
58 const char *servers;
59 apr_memcache_t *mc;
60 const char *tag;
61 apr_size_t taglen; /* strlen(tag) + 1 */
64 static const char *socache_mc_create(ap_socache_instance_t **context,
65 const char *arg,
66 apr_pool_t *tmp, apr_pool_t *p)
68 ap_socache_instance_t *ctx;
70 *context = ctx = apr_palloc(p, sizeof *ctx);
72 if (!arg || !*arg) {
73 return "List of server names required to create memcache socache.";
76 ctx->servers = apr_pstrdup(p, arg);
78 return NULL;
81 static apr_status_t socache_mc_init(ap_socache_instance_t *ctx,
82 const char *namespace,
83 const struct ap_socache_hints *hints,
84 server_rec *s, apr_pool_t *p)
86 apr_status_t rv;
87 int thread_limit = 0;
88 int nservers = 0;
89 char *cache_config;
90 char *split;
91 char *tok;
93 ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit);
95 /* Find all the servers in the first run to get a total count */
96 cache_config = apr_pstrdup(p, ctx->servers);
97 split = apr_strtok(cache_config, ",", &tok);
98 while (split) {
99 nservers++;
100 split = apr_strtok(NULL,",", &tok);
103 rv = apr_memcache_create(p, nservers, 0, &ctx->mc);
104 if (rv != APR_SUCCESS) {
105 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
106 "socache: Failed to create Memcache Object of '%d' size.",
107 nservers);
108 return rv;
111 /* Now add each server to the memcache */
112 cache_config = apr_pstrdup(p, ctx->servers);
113 split = apr_strtok(cache_config, ",", &tok);
114 while (split) {
115 apr_memcache_server_t *st;
116 char *host_str;
117 char *scope_id;
118 apr_port_t port;
120 rv = apr_parse_addr_port(&host_str, &scope_id, &port, split, p);
121 if (rv != APR_SUCCESS) {
122 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
123 "socache: Failed to Parse memcache Server: '%s'", split);
124 return rv;
127 if (host_str == NULL) {
128 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
129 "socache: Failed to Parse Server, "
130 "no hostname specified: '%s'", split);
131 return APR_EINVAL;
134 if (port == 0) {
135 port = MC_DEFAULT_SERVER_PORT;
138 rv = apr_memcache_server_create(p,
139 host_str, port,
140 MC_DEFAULT_SERVER_MIN,
141 MC_DEFAULT_SERVER_SMAX,
142 thread_limit,
143 MC_DEFAULT_SERVER_TTL,
144 &st);
145 if (rv != APR_SUCCESS) {
146 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
147 "socache: Failed to Create memcache Server: %s:%d",
148 host_str, port);
149 return rv;
152 rv = apr_memcache_add_server(ctx->mc, st);
153 if (rv != APR_SUCCESS) {
154 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
155 "socache: Failed to Add memcache Server: %s:%d",
156 host_str, port);
157 return rv;
160 split = apr_strtok(NULL,",", &tok);
163 ctx->tag = apr_pstrcat(p, namespace, ":", NULL);
164 ctx->taglen = strlen(ctx->tag) + 1;
166 /* socache API constraint: */
167 AP_DEBUG_ASSERT(ctx->taglen <= 16);
169 return APR_SUCCESS;
172 static void socache_mc_kill(ap_socache_instance_t *context, server_rec *s)
174 /* noop. */
177 /* Converts (binary) id into a key prefixed by the predetermined
178 * namespace tag; writes output to key buffer. Returns non-zero if
179 * the id won't fit in the key buffer. */
180 static int socache_mc_id2key(ap_socache_instance_t *ctx,
181 const unsigned char *id, unsigned int idlen,
182 char *key, apr_size_t keylen)
184 char *cp;
185 unsigned int n;
187 if (idlen * 2 + ctx->taglen >= keylen)
188 return 1;
190 cp = apr_cpystrn(key, ctx->tag, ctx->taglen);
192 for (n = 0; n < idlen; n++) {
193 apr_snprintf(cp, 3, "%02X", (unsigned) id[n]);
194 cp += 2;
197 *cp = '\0';
198 return 0;
201 static apr_status_t socache_mc_store(ap_socache_instance_t *ctx, server_rec *s,
202 const unsigned char *id, unsigned int idlen,
203 time_t timeout,
204 unsigned char *ucaData, unsigned int nData,
205 apr_pool_t *p)
207 char buf[MC_KEY_LEN];
208 apr_status_t rv;
210 if (socache_mc_id2key(ctx, id, idlen, buf, sizeof buf)) {
211 return APR_EINVAL;
214 rv = apr_memcache_set(ctx->mc, buf, (char*)ucaData, nData, timeout, 0);
216 if (rv != APR_SUCCESS) {
217 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
218 "scache_mc: error setting key '%s' "
219 "with %d bytes of data", buf, nData);
220 return rv;
223 return APR_SUCCESS;
226 static apr_status_t socache_mc_retrieve(ap_socache_instance_t *ctx, server_rec *s,
227 const unsigned char *id, unsigned int idlen,
228 unsigned char *dest, unsigned int *destlen,
229 apr_pool_t *p)
231 apr_size_t data_len;
232 char buf[MC_KEY_LEN], *data;
233 apr_status_t rv;
235 if (socache_mc_id2key(ctx, id, idlen, buf, sizeof buf)) {
236 return APR_EINVAL;
239 /* ### this could do with a subpool, but _getp looks like it will
240 * eat memory like it's going out of fashion anyway. */
242 rv = apr_memcache_getp(ctx->mc, p, buf, &data, &data_len, NULL);
243 if (rv) {
244 if (rv != APR_NOTFOUND) {
245 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
246 "scache_mc: 'retrieve' FAIL");
248 return rv;
250 else if (data_len > *destlen) {
251 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
252 "scache_mc: 'retrieve' OVERFLOW");
253 return APR_ENOMEM;
256 memcpy(dest, data, data_len);
257 *destlen = data_len;
259 return APR_SUCCESS;
262 static apr_status_t socache_mc_remove(ap_socache_instance_t *ctx, server_rec *s,
263 const unsigned char *id,
264 unsigned int idlen, apr_pool_t *p)
266 char buf[MC_KEY_LEN];
267 apr_status_t rv;
269 if (socache_mc_id2key(ctx, id, idlen, buf, sizeof buf)) {
270 return APR_EINVAL;
273 rv = apr_memcache_delete(ctx->mc, buf, 0);
275 if (rv != APR_SUCCESS) {
276 ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s,
277 "scache_mc: error deleting key '%s' ",
278 buf);
281 return rv;
284 static void socache_mc_status(ap_socache_instance_t *ctx, request_rec *r, int flags)
286 /* SSLModConfigRec *mc = myModConfig(r->server); */
287 /* TODO: Make a mod_status handler. meh. */
290 static const ap_socache_provider_t socache_mc = {
291 "memcache",
293 socache_mc_create,
294 socache_mc_init,
295 socache_mc_kill,
296 socache_mc_store,
297 socache_mc_retrieve,
298 socache_mc_remove,
299 socache_mc_status
302 #endif /* HAVE_APU_MEMCACHE */
304 static void register_hooks(apr_pool_t *p)
306 #ifdef HAVE_APU_MEMCACHE
307 ap_register_provider(p, AP_SOCACHE_PROVIDER_GROUP, "mc",
308 AP_SOCACHE_PROVIDER_VERSION,
309 &socache_mc);
310 #endif
313 module AP_MODULE_DECLARE_DATA socache_memcache_module = {
314 STANDARD20_MODULE_STUFF,
315 NULL, NULL, NULL, NULL, NULL,
316 register_hooks