mod_socache_memcache.c revision d6347d2d215db87038d0aff55c690c30f7ccccb6
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering/* Licensed to the Apache Software Foundation (ASF) under one or more
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering* contributor license agreements. See the NOTICE file distributed with
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering* this work for additional information regarding copyright ownership.
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering* The ASF licenses this file to You under the Apache License, Version 2.0
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering* (the "License"); you may not use this file except in compliance with
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering* the License. You may obtain a copy of the License at
c91faef3b3facfdf13282aee957af25dd555890bLennart Poettering*
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering* http://www.apache.org/licenses/LICENSE-2.0
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering*
5430f7f2bc7330f3088b894166bf3524a067e3d8Lennart Poettering* Unless required by applicable law or agreed to in writing, software
5430f7f2bc7330f3088b894166bf3524a067e3d8Lennart Poettering* distributed under the License is distributed on an "AS IS" BASIS,
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering* See the License for the specific language governing permissions and
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering* limitations under the License.
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering*/
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering
5430f7f2bc7330f3088b894166bf3524a067e3d8Lennart Poettering
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering#include "httpd.h"
5430f7f2bc7330f3088b894166bf3524a067e3d8Lennart Poettering#include "http_config.h"
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering#include "apr.h"
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering#include "apu_version.h"
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering/* apr_memcache support requires >= 1.3 */
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering#if APU_MAJOR_VERSION > 1 || \
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION > 2)
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering#define HAVE_APU_MEMCACHE 1
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering#endif
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering#ifdef HAVE_APU_MEMCACHE
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering#include "ap_socache.h"
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering#include "ap_mpm.h"
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering#include "http_log.h"
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering#include "apr_memcache.h"
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering/* The underlying apr_memcache system is thread safe.. */
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering#define MC_KEY_LEN 254
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering#ifndef MC_DEFAULT_SERVER_PORT
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering#define MC_DEFAULT_SERVER_PORT 11211
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering#endif
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering#ifndef MC_DEFAULT_SERVER_MIN
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering#define MC_DEFAULT_SERVER_MIN 0
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering#endif
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering#ifndef MC_DEFAULT_SERVER_SMAX
db1413d7380acacc4e50faf801ca0d401da89764Kay Sievers#define MC_DEFAULT_SERVER_SMAX 1
fc1a2e06a2eab6ca16664adb83b61fe958f00598Lennart Poettering#endif
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering#ifndef MC_DEFAULT_SERVER_TTL
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering#define MC_DEFAULT_SERVER_TTL 600
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering#endif
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poetteringstruct ap_socache_instance_t {
db1413d7380acacc4e50faf801ca0d401da89764Kay Sievers const char *servers;
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering apr_memcache_t *mc;
772f83719e3c2262d948a4c4e70fe9babc4c4610Kay Sievers const char *tag;
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering apr_size_t taglen; /* strlen(tag) + 1 */
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering};
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poetteringstatic const char *socache_mc_create(ap_socache_instance_t **context,
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering const char *arg,
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering apr_pool_t *tmp, apr_pool_t *p)
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering{
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering ap_socache_instance_t *ctx;
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering *context = ctx = apr_palloc(p, sizeof *ctx);
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering if (!arg || !*arg) {
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering return "List of server names required to create memcache socache.";
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering }
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering
772f83719e3c2262d948a4c4e70fe9babc4c4610Kay Sievers ctx->servers = apr_pstrdup(p, arg);
772f83719e3c2262d948a4c4e70fe9babc4c4610Kay Sievers
772f83719e3c2262d948a4c4e70fe9babc4c4610Kay Sievers return NULL;
db1413d7380acacc4e50faf801ca0d401da89764Kay Sievers}
db1413d7380acacc4e50faf801ca0d401da89764Kay Sievers
db1413d7380acacc4e50faf801ca0d401da89764Kay Sieversstatic apr_status_t socache_mc_init(ap_socache_instance_t *ctx,
db1413d7380acacc4e50faf801ca0d401da89764Kay Sievers const char *namespace,
db1413d7380acacc4e50faf801ca0d401da89764Kay Sievers const struct ap_socache_hints *hints,
db1413d7380acacc4e50faf801ca0d401da89764Kay Sievers server_rec *s, apr_pool_t *p)
db1413d7380acacc4e50faf801ca0d401da89764Kay Sievers{
db1413d7380acacc4e50faf801ca0d401da89764Kay Sievers apr_status_t rv;
db1413d7380acacc4e50faf801ca0d401da89764Kay Sievers int thread_limit = 0;
db1413d7380acacc4e50faf801ca0d401da89764Kay Sievers int nservers = 0;
db1413d7380acacc4e50faf801ca0d401da89764Kay Sievers char *cache_config;
db1413d7380acacc4e50faf801ca0d401da89764Kay Sievers char *split;
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering char *tok;
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit);
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering /* Find all the servers in the first run to get a total count */
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering cache_config = apr_pstrdup(p, ctx->servers);
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering split = apr_strtok(cache_config, ",", &tok);
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering while (split) {
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering nservers++;
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering split = apr_strtok(NULL,",", &tok);
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering }
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering rv = apr_memcache_create(p, nservers, 0, &ctx->mc);
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering if (rv != APR_SUCCESS) {
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering "SSLSessionCache: Failed to create Memcache Object of '%d' size.",
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering nservers);
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering return rv;
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering }
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering
151b190e79e64824552e01849352ca8f6ac7dedbLennart Poettering /* Now add each server to the memcache */
cache_config = apr_pstrdup(p, ctx->servers);
split = apr_strtok(cache_config, ",", &tok);
while (split) {
apr_memcache_server_t *st;
char *host_str;
char *scope_id;
apr_port_t port;
rv = apr_parse_addr_port(&host_str, &scope_id, &port, split, p);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
"SSLSessionCache: Failed to Parse Server: '%s'", split);
return rv;
}
if (host_str == NULL) {
ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
"SSLSessionCache: Failed to Parse Server, "
"no hostname specified: '%s'", split);
return APR_EINVAL;
}
if (port == 0) {
port = MC_DEFAULT_SERVER_PORT;
}
rv = apr_memcache_server_create(p,
host_str, port,
MC_DEFAULT_SERVER_MIN,
MC_DEFAULT_SERVER_SMAX,
thread_limit,
MC_DEFAULT_SERVER_TTL,
&st);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
"SSLSessionCache: Failed to Create Server: %s:%d",
host_str, port);
return rv;
}
rv = apr_memcache_add_server(ctx->mc, st);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
"SSLSessionCache: Failed to Add Server: %s:%d",
host_str, port);
return rv;
}
split = apr_strtok(NULL,",", &tok);
}
ctx->tag = apr_pstrcat(p, namespace, ":", NULL);
ctx->taglen = strlen(ctx->tag) + 1;
/* socache API constraint: */
AP_DEBUG_ASSERT(ctx->taglen <= 16);
return APR_SUCCESS;
}
static void socache_mc_kill(ap_socache_instance_t *context, server_rec *s)
{
/* noop. */
}
/* Converts (binary) id into a key prefixed by the predetermined
* namespace tag; writes output to key buffer. Returns non-zero if
* the id won't fit in the key buffer. */
static int socache_mc_id2key(ap_socache_instance_t *ctx,
const unsigned char *id, unsigned int idlen,
char *key, apr_size_t keylen)
{
char *cp;
unsigned int n;
if (idlen * 2 + ctx->taglen >= keylen)
return 1;
cp = apr_cpystrn(key, ctx->tag, ctx->taglen);
for (n = 0; n < idlen; n++) {
apr_snprintf(cp, 3, "%02X", (unsigned) id[n]);
cp += 2;
}
*cp = '\0';
return 0;
}
static apr_status_t socache_mc_store(ap_socache_instance_t *ctx, server_rec *s,
const unsigned char *id, unsigned int idlen,
time_t timeout,
unsigned char *ucaData, unsigned int nData)
{
char buf[MC_KEY_LEN];
apr_status_t rv;
if (socache_mc_id2key(ctx, id, idlen, buf, sizeof buf)) {
return APR_EINVAL;
}
rv = apr_memcache_set(ctx->mc, buf, (char*)ucaData, nData, timeout, 0);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
"scache_mc: error setting key '%s' "
"with %d bytes of data", buf, nData);
return rv;
}
return APR_SUCCESS;
}
static apr_status_t socache_mc_retrieve(ap_socache_instance_t *ctx,
server_rec *s,
const unsigned char *id, unsigned int idlen,
unsigned char *dest, unsigned int *destlen,
apr_pool_t *p)
{
apr_size_t der_len;
char buf[MC_KEY_LEN], *der;
apr_status_t rv;
if (socache_mc_id2key(ctx, id, idlen, buf, sizeof buf)) {
return APR_EINVAL;
}
/* ### this could do with a subpool, but _getp looks like it will
* eat memory like it's going out of fashion anyway. */
rv = apr_memcache_getp(ctx->mc, p, buf,
&der, &der_len, NULL);
if (rv) {
if (rv != APR_NOTFOUND) {
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
"scache_mc: 'get_session' FAIL");
}
return rv;
}
else if (der_len > *destlen) {
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
"scache_mc: 'get_session' OVERFLOW");
return rv;
}
memcpy(dest, der, der_len);
*destlen = der_len;
return APR_SUCCESS;
}
static void socache_mc_remove(ap_socache_instance_t *ctx, server_rec *s,
const unsigned char *id, unsigned int idlen,
apr_pool_t *p)
{
char buf[MC_KEY_LEN];
apr_status_t rv;
if (socache_mc_id2key(ctx, id, idlen, buf, sizeof buf)) {
return;
}
rv = apr_memcache_delete(ctx->mc, buf, 0);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s,
"scache_mc: error deleting key '%s' ",
buf);
return;
}
}
static void socache_mc_status(ap_socache_instance_t *ctx, request_rec *r, int flags)
{
/* SSLModConfigRec *mc = myModConfig(r->server); */
/* TODO: Make a mod_status handler. meh. */
}
static const ap_socache_provider_t socache_mc = {
"memcache",
0,
socache_mc_create,
socache_mc_init,
socache_mc_kill,
socache_mc_store,
socache_mc_retrieve,
socache_mc_remove,
socache_mc_status
};
#endif /* HAVE_APU_MEMCACHE */
static void register_hooks(apr_pool_t *p)
{
#ifdef HAVE_APU_MEMCACHE
ap_register_provider(p, AP_SOCACHE_PROVIDER_GROUP, "mc",
AP_SOCACHE_PROVIDER_VERSION,
&socache_mc);
#endif
}
module AP_MODULE_DECLARE_DATA socache_memcache_module = {
STANDARD20_MODULE_STUFF,
NULL, NULL, NULL, NULL, NULL,
register_hooks
};