mod_mem_cache.c revision 12901074f5d6b36d08be84d8637b6f2c21e0da26
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000-2002 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
*
* Portions of this software are based upon public domain software
* originally written at the National Center for Supercomputing Applications,
* University of Illinois, Urbana-Champaign.
*/
#define CORE_PRIVATE
#include "mod_cache.h"
#include "cache_hash.h"
#include "ap_mpm.h"
#include "apr_thread_mutex.h"
#include <unistd.h>
#endif
#if !APR_HAS_THREADS
#endif
typedef enum {
CACHE_TYPE_FILE = 1,
} cache_type_e;
typedef struct {
char* hdr;
char* val;
typedef struct mem_cache_object {
void *m;
typedef struct {
/* Fields set by config directives */
static mem_cache_conf *sconf;
#define DEFAULT_MIN_CACHE_OBJECT_SIZE 0
#define DEFAULT_MAX_CACHE_OBJECT_SIZE 10000
#define DEFAULT_MAX_OBJECT_CNT 1000
#define CACHEFILE_LEN 20
/* Forward declarations */
static int remove_entity(cache_handle_t *h);
{
/* TODO:
* We desperately need a more efficient way of allocating objects. We're
* making way too many malloc calls to create a fully populated
* cache object...
*/
/* Cleanup the cache_object_t */
}
}
}
}
}
/* Cleanup the mem_cache_object_t */
if (mobj) {
}
#ifdef WIN32
#else
#endif
}
if (mobj->header_out) {
}
if (mobj->subprocess_env) {
}
}
}
return;
}
{
/* If obj->complete is not set, the cache update failed and the
* object needs to be removed from the cache then cleaned up.
*/
}
/* Remember, objects marked for cleanup are, by design, already
* removed from the cache. remove_url() could have already
* removed the object from the cache (and set obj->cleanup)
*/
sconf->object_cnt--;
}
}
}
/* Cleanup the cache object */
#ifdef USE_ATOMICS
}
}
#else
}
/* If the object is marked for cleanup and the refcount
* has dropped to zero, cleanup the object
*/
}
}
#endif
return APR_SUCCESS;
}
{
if (!co) {
return APR_SUCCESS;
}
}
/* Iterate over the cache and clean up each entry */
/* Fetch the object from the cache */
if (obj) {
/* Remove the object from the cache */
/* Free the object if the recount == 0 */
#ifdef USE_ATOMICS
#else
#endif
}
}
}
}
return APR_SUCCESS;
}
/*
* TODO: enable directives to be overridden in various containers
*/
{
int threaded_mpm;
if (threaded_mpm) {
}
/* Todo: determine hash table size from max_cache_object_cnt */
/* Number of objects in the cache */
sconf->object_cnt = 0;
/* Size of the cache in bytes */
sconf->cache_size = 0;
return sconf;
}
const char *type,
const char *key,
{
}
}
else {
return DECLINED;
}
/*
* TODO: Get smarter about managing the cache size. If the cache is
* full, we need to garbage collect stale/infrequently referenced
* objects.
*/
return DECLINED;
}
if (type_e == CACHE_TYPE_HEAP) {
/* We can safely ignore these measures when caching open fds */
return DECLINED;
}
return DECLINED;
}
} else {
/* CACHE_TYPE_FILE is only valid for local content
* handled by the default handler?
* This is not the right check...
*/
if (!r->filename) {
return DECLINED;
}
}
/* Allocate and initialize cache_object_t */
if (!obj) {
return DECLINED;
}
return DECLINED;
}
/* Allocate and init mem_cache_object_t */
if (!mobj) {
return DECLINED;
}
/* Finish initing the cache object */
/* Place the cache_object_t into the hash table.
* Note: Perhaps we should wait to put the object in the
* hash table when the object is complete? I add the object here to
* avoid multiple threads attempting to cache the same content only
* to discover at the very end that only one of them will suceed.
* Furthermore, adding the cache object to the table at the end could
* open up a subtle but easy to exploit DoS hole: someone could request
* a very large file with multiple requests. Better to detect this here
* rather than after the cache object has been completely built and
* initialized...
* XXX Need a way to insert into the cache w/o such coarse grained locking
*/
}
key,
if (!tmp_obj) {
sconf->object_cnt++;
}
}
if (tmp_obj) {
/* This thread collided with another thread loading the same object
* into the cache at the same time. Defer to the other thread which
* is further along.
*/
return DECLINED;
}
/* Populate the cache handle */
h->read_headers = &read_headers;
h->write_body = &write_body;
h->write_headers = &write_headers;
h->remove_entity = &remove_entity;
return OK;
}
{
/* Look up entity keyed to 'url' */
return DECLINED;
}
}
if (obj) {
#ifdef USE_ATOMICS
#else
#endif
/* If this is a subrequest, register the cleanup against
* the main request. This will prevent the cache object
* from being cleaned up from under the request after the
* subrequest is destroyed.
*/
rtmp = r;
while (rtmp) {
}
}
else {
}
}
}
if (!obj) {
return DECLINED;
}
/* Initialize the cache_handle */
h->read_headers = &read_headers;
h->write_body = &write_body;
h->write_headers = &write_headers;
h->remove_entity = &remove_entity;
return OK;
}
static int remove_entity(cache_handle_t *h)
{
/* Remove the cache object from the cache under protection */
}
/* If the object is not already marked for cleanup, remove
* it from the cache and mark it for cleanup. Remember,
* an object marked for cleanup is by design not in the
* hash table.
*/
sconf->object_cnt--;
}
}
return OK;
}
{
apr_ssize_t i;
apr_size_t len = 0;
apr_size_t idx = 0;
char *buf;
if (*nelts == 0 ) {
return APR_SUCCESS;
}
return APR_ENOMEM;
}
}
/* Transfer the headers into a contiguous memory block */
if (!buf) {
return APR_ENOMEM;
}
for (i = 0; i < *nelts; ++i) {
}
return APR_SUCCESS;
}
int num_headers,
apr_table_t *t )
{
int i;
for (i = 0; i < num_headers; ++i) {
}
return APR_SUCCESS;
}
/* Define request processing hook handlers */
{
return DECLINED;
}
/* Order of the operations is important to avoid race conditions.
* First, remove the object from the cache. Remember, all additions
* deletions from the cache are protected by sconf->lock.
* Increment the ref count on the object to indicate our thread
* is accessing the object. Then set the cleanup flag on the
* object. Remember, the cleanup flag is NEVER set on an
* object in the hash table. If an object has the cleanup
* flag set, it is guaranteed to NOT be in the hash table.
*/
}
/* This call will return a pointer to the cache_object just removed */
if (obj) {
/* obj has been removed from the cache */
sconf->object_cnt--;
#ifdef USE_ATOMICS
/* Refcount increment in this case MUST be made under
* protection of the lock
*/
#else
}
#endif
if (obj) {
}
}
}
#ifdef USE_ATOMICS
if (obj) {
}
}
#endif
return OK;
}
{
int rc;
r->headers_out);
r->subprocess_env);
r->notes);
return rc;
}
{
apr_bucket *b;
/* CACHE_TYPE_FILE */
}
else {
/* CACHE_TYPE_HEAP */
}
return APR_SUCCESS;
}
{
int rc;
/* Precompute how much storage we need to hold the headers */
r->headers_out);
if (rc != APR_SUCCESS) {
return rc;
}
r->subprocess_env );
if (rc != APR_SUCCESS) {
return rc;
}
if (rc != APR_SUCCESS) {
return rc;
}
/* Init the info struct */
}
}
}
if (info->content_type) {
return APR_ENOMEM;
}
}
return APR_ENOMEM;
}
}
return APR_SUCCESS;
}
{
apr_bucket *e;
char *cur;
int eos = 0;
int fd = 0;
int other = 0;
/* We can cache an open file descriptor if:
* - the brigade contains one and only one file_bucket &&
* - the brigade is complete &&
* - the file_bucket is the last data bucket in the brigade
*/
APR_BRIGADE_FOREACH(e, b) {
if (APR_BUCKET_IS_EOS(e)) {
eos = 1;
}
else if (APR_BUCKET_IS_FILE(e)) {
apr_bucket_file *a = e->data;
fd++;
}
else {
other++;
}
}
const char *name;
/* Open a new XTHREAD handle to the file */
APR_OS_DEFAULT, r->pool);
if (rv != APR_SUCCESS) {
return rv;
}
/* Open for business */
return APR_SUCCESS;
}
/* Content not suitable for fd caching. Cache in-memory instead. */
/* Check to make sure the object will not exceed configured thresholds */
return APR_ENOMEM; /* ?? DECLINED; */
}
return APR_ENOMEM; /* ?? DECLINED; */
}
}
/*
* FD cacheing is not enabled or the content was not
* suitable for fd caching.
*/
return APR_ENOMEM;
}
}
/* Iterate accross the brigade and populate the cache storage */
APR_BRIGADE_FOREACH(e, b) {
const char *s;
if (APR_BUCKET_IS_EOS(e)) {
/* Open for business */
break;
}
if (rv != APR_SUCCESS) {
return rv;
}
if (len) {
/* Check for buffer overflow */
return APR_ENOMEM;
}
else {
}
}
/* This should not happen, but if it does, we are in BIG trouble
* cause we just stomped all over the heap.
*/
}
return APR_SUCCESS;
}
static const char
{
return "MCacheSize argument must be an integer representing the max cache size in KBytes.";
}
return NULL;
}
static const char
{
return "MCacheMinObjectSize value must be an integer (bytes)";
}
return NULL;
}
static const char
{
return "MCacheMaxObjectSize value must be an integer (bytes)";
}
return NULL;
}
static const char
{
return "MCacheMaxObjectCount value must be an integer";
}
return NULL;
}
static const command_rec cache_cmds[] =
{
"The maximum amount of memory used by the cache in KBytes"),
"The maximum number of objects allowed to be placed in the cache"),
"The minimum size (in bytes) of an object to be placed in the cache"),
"The maximum size (in bytes) of an object to be placed in the cache"),
{NULL}
};
static void register_hooks(apr_pool_t *p)
{
/* cache initializer */
/* cache_hook_cache_init(cache_init, NULL, NULL, AP_HOOK_FIRST); */
}
{
NULL, /* create per-directory config structure */
NULL, /* merge per-directory config structures */
create_cache_config, /* create per-server config structure */
NULL, /* merge per-server config structures */
cache_cmds, /* command apr_table_t */
};