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 600
57 struct ap_socache_instance_t
{
61 apr_size_t taglen
; /* strlen(tag) + 1 */
64 static const char *socache_mc_create(ap_socache_instance_t
**context
,
66 apr_pool_t
*tmp
, apr_pool_t
*p
)
68 ap_socache_instance_t
*ctx
;
70 *context
= ctx
= apr_palloc(p
, sizeof *ctx
);
73 return "List of server names required to create memcache socache.";
76 ctx
->servers
= apr_pstrdup(p
, arg
);
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
)
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
);
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.",
111 /* Now add each server to the memcache */
112 cache_config
= apr_pstrdup(p
, ctx
->servers
);
113 split
= apr_strtok(cache_config
, ",", &tok
);
115 apr_memcache_server_t
*st
;
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
);
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
);
135 port
= MC_DEFAULT_SERVER_PORT
;
138 rv
= apr_memcache_server_create(p
,
140 MC_DEFAULT_SERVER_MIN
,
141 MC_DEFAULT_SERVER_SMAX
,
143 MC_DEFAULT_SERVER_TTL
,
145 if (rv
!= APR_SUCCESS
) {
146 ap_log_error(APLOG_MARK
, APLOG_CRIT
, rv
, s
,
147 "socache: Failed to Create memcache Server: %s:%d",
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",
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);
172 static void socache_mc_kill(ap_socache_instance_t
*context
, server_rec
*s
)
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
)
187 if (idlen
* 2 + ctx
->taglen
>= keylen
)
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
]);
201 static apr_status_t
socache_mc_store(ap_socache_instance_t
*ctx
, server_rec
*s
,
202 const unsigned char *id
, unsigned int idlen
,
204 unsigned char *ucaData
, unsigned int nData
,
207 char buf
[MC_KEY_LEN
];
210 if (socache_mc_id2key(ctx
, id
, idlen
, buf
, sizeof buf
)) {
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
);
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
,
232 char buf
[MC_KEY_LEN
], *data
;
235 if (socache_mc_id2key(ctx
, id
, idlen
, buf
, sizeof buf
)) {
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
);
244 if (rv
!= APR_NOTFOUND
) {
245 ap_log_error(APLOG_MARK
, APLOG_ERR
, rv
, s
,
246 "scache_mc: 'retrieve' FAIL");
250 else if (data_len
> *destlen
) {
251 ap_log_error(APLOG_MARK
, APLOG_ERR
, rv
, s
,
252 "scache_mc: 'retrieve' OVERFLOW");
256 memcpy(dest
, data
, data_len
);
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
];
269 if (socache_mc_id2key(ctx
, id
, idlen
, buf
, sizeof buf
)) {
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' ",
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
= {
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
,
313 module AP_MODULE_DECLARE_DATA socache_memcache_module
= {
314 STANDARD20_MODULE_STUFF
,
315 NULL
, NULL
, NULL
, NULL
, NULL
,