Commit 77848c0b authored by Bill Stoddard's avatar Bill Stoddard
Browse files

Experimental cache based on Graham Leggett's layered cache design. mod_cache

implements a quick handler, and three filters. The filters are
CACHE_IN for loading the cache, CACHE_OUT for serving content out of the cache
and CACHE_CONDITIONAL, which handles stale entries in the cache.

mod_cache implements code that makes RFC compliant caching decisions. It
interfaces with the actual storage mechanism via calls to functions defined in
cache_storage.c.  This commit includes a simple in memory (malloc'ed memory)
cache implementation that demonstrates autoloading and serving files
keyed on URL.

This is not even close to production ready. You have been warned :-)


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@90549 13f79535-47bb-0310-9956-ffa450edef68
parent e96b012c
Loading
Loading
Loading
Loading
+276 −0
Original line number Diff line number Diff line
/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2000-2001 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
 *    the documentation and/or other materials provided with the
 *    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
 * <http://www.apache.org/>.
 *
 * 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"

APR_HOOK_STRUCT(
	APR_HOOK_LINK(remove_url)
	APR_HOOK_LINK(create_entity)
	APR_HOOK_LINK(open_entity)
)

module AP_MODULE_DECLARE_DATA tcache_module;

/* -------------------------------------------------------------- */

/*
 * delete all URL entities from the cache
 *
 */
int cache_remove_url(request_rec *r, const char *types, char *url)
{
    const char *next = types;
    const char *type;

    /* for each specified cache type, delete the URL */
    while ((type = ap_cache_tokstr(r->pool, next, &next))) {
	cache_run_remove_url(type, url);
    }
    return OK;
}


/*
 * create a new URL entity in the cache
 *
 * It is possible to store more than once entity per URL. This
 * function will always create a new entity, regardless of whether
 * other entities already exist for the same URL.
 *
 * The size of the entity is provided so that a cache module can
 * decide whether or not it wants to cache this particular entity.
 * If the size is unknown, a size of -1 should be set.
 */
int cache_create_entity(request_rec *r, const char *types, char *url, apr_size_t size)
{
    cache_handle *h;
    const char *next = types;
    const char *type;
    apr_status_t rv;
    cache_request_rec *cache = (cache_request_rec *) ap_get_module_config(r->request_config, 
                                                                          &tcache_module);

    /* for each specified cache type, delete the URL */
    while (next) {
        type = ap_cache_tokstr(r->pool, next, &next);
        switch (rv = cache_run_create_entity(&h, type, url, size)) {
        case OK: {
            cache->handle = h;
            return OK;
        }
        case DECLINED: {
            continue;
        }
        default: {
            return rv;
        }
        }
    }
    return DECLINED;
}

/*
 * remove a specific URL entity from the cache
 *
 * The specific entity referenced by the cache_handle is removed
 * from the cache, and the cache_handle is closed.
 */
int cache_remove_entity(request_rec *r, const char *types, cache_handle *h)
{
    const char *next = types;
    const char *type;

    while (next) {
        type = ap_cache_tokstr(r->pool, next, &next);
    }
    return 1;
}

/*
 * select a specific URL entity in the cache
 *
 * It is possible to store more than one entity per URL. Content
 * negotiation is used to select an entity. Once an entity is
 * selected, details of it are stored in the per request
 * config to save time when serving the request later.
 *
 * This function returns OK if successful, DECLINED if no
 * cached entity fits the bill.
 */
int cache_select_url(request_rec *r, const char *types, char *url)
{
    cache_handle *h;
    const char *next = types;
    const char *type;
    apr_status_t rv;
    cache_request_rec *cache = (cache_request_rec *) ap_get_module_config(r->request_config, 
                                                                          &tcache_module);

    /* go through the cache types till we get a match */
    while (next) {
        type = ap_cache_tokstr(r->pool, next, &next);
        switch ((rv = cache_run_open_entity(&h, type, url))) {
        case OK: {
            /* cool bananas! */
            cache->handle = h;
/*** loop through returned entities */
/*** do freshness calculation here */
            cache->fresh = 1;
/*** do content negotiation here */
            return OK;
        }
        case DECLINED: {
            /* try again with next cache type */
            continue;
        }
        default: {
            /* oo-er! an error */
            return rv;
        }
        }
    }
    return DECLINED;
}

apr_status_t cache_write_entity_headers(cache_handle *h, request_rec *r, cache_info *info,
                                        apr_table_t *headers_in, apr_table_t *headers_out) 
{
    const char *ct;

    ct = ap_table_get(r->headers_out, "Content-Type");
    info->content_type = ct;
    h->write_headers(h, r, info);
    return APR_SUCCESS;
}
apr_status_t cache_write_entity_body(cache_handle *h, apr_bucket_brigade *b) 
{
    apr_status_t rv = APR_SUCCESS;
    if (h->write_body(h, b) != OK) {
    }
    return rv;
}

apr_status_t cache_read_entity_headers(cache_handle *h, request_rec *r, 
                                       apr_table_t **headers)
{
    cache_info *info;

    /* Be careful to not modify info. */
    h->read_headers(h, r, &info);

    /* Build the header table from info in the info struct */
    *headers = apr_table_make(r->pool, 15);
    /* Content-Length */
    if (info->len)
        apr_table_set(*headers, "Content-Length", apr_psprintf(r->pool, "%lu", info->len));

    /* Last-Modified */
    if (info->lastmod) {
    }
    /* Expires */
    if (info->expire) {
    }
    if (info->content_type) {
        r->content_type = apr_pstrdup(r->pool, info->content_type);
    }
    /* Date */
    
    return APR_SUCCESS;
}
apr_status_t cache_read_entity_body(cache_handle *h, apr_bucket_brigade *b) 
{
    h->read_body(h, b);
    return APR_SUCCESS;
}

APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(cache, CACHE, int, create_entity, 
                                      (cache_handle **hp, const char *type, 
                                      char *url, apr_size_t len),(hp,type,url,len),DECLINED)
APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(cache, CACHE, int, open_entity,  
                                      (cache_handle **hp, const char *type, 
                                      char *url),(hp,type,url),DECLINED)
APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(cache, CACHE, int, remove_url, 
                                    (const char *type, char *url),(type,url),OK,DECLINED)
#if 0
/* BillS doesn't think these should be hooks.
 * All functions which accept a cache_handle * argument should use
 * function pointers in the cache_handle. Leave them here for now as 
 * points for discussion...
 */

APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(cache, CACHE, int, remove_entity, 
                                     (cache_handle *h),(h),DECLINED)

APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(cache, CACHE, int, read_entity_headers, 
                                     (cache_handle *h, request_rec *r,
                                      apr_table_t **headers),
                                      (h,info,headers_in,headers_out),DECLINED)
APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(cache, CACHE, int, read_entity_body, 
                                     (cache_handle *h,
                                      apr_bucket_brigade *out),(h,out),DECLINED)
APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(cache, CACHE, int, write_entity_headers, 
                                     (cache_handle *h, cache_info *info,
                                      apr_table_t *headers_in, 
                                      apr_table_t *headers_out),
                                      (h,info,headers_in,headers_out),DECLINED)
APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(cache, CACHE, int, write_entity_body, 
                                     (cache_handle *h,
                                      apr_bucket_brigade *in),(h,in),DECLINED)
#endif
+206 −0
Original line number Diff line number Diff line
/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2000-2001 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
 *    the documentation and/or other materials provided with the
 *    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
 * <http://www.apache.org/>.
 *
 * 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"



/* -------------------------------------------------------------- */

/* return true if the request is conditional */
int ap_cache_request_is_conditional(request_rec *r)
{
    if (apr_table_get(r->headers_in, "If-Match") ||
        apr_table_get(r->headers_in, "If-None-Match") ||
        apr_table_get(r->headers_in, "If-Modified-Since") ||
        apr_table_get(r->headers_in, "If-Unmodified-Since")) {

	return 1;
    }
    return 0;
}


/* remove other filters from filter stack */
void ap_cache_reset_output_filters(request_rec *r)
{
    ap_filter_t *f = r->output_filters;

    while (f) {
        if (!strcasecmp(f->frec->name, "CORE") ||
            !strcasecmp(f->frec->name, "CONTENT_LENGTH") ||
            !strcasecmp(f->frec->name, "HTTP_HEADER")) {
            f = f->next;
            continue;
        }
        else {
            ap_remove_output_filter(f);
            f = f->next;
        }
    }
}

const char *ap_cache_get_cachetype(request_rec *r, cache_server_conf *conf, const char *url)
{
    const char *type = NULL;
    int i;

    /* loop through all the cacheenable entries */
    for (i = 0; i < conf->cacheenable->nelts; i++) {
	struct cache_enable *ent = (struct cache_enable *) conf->cacheenable->elts;
	const char *thisurl = ent[i].url;
	const char *thistype = ent[i].type;
	if ((thisurl) && !strncasecmp(thisurl, url, strlen(thisurl))) {
	    if (!type) {
		type = thistype;
	    }
	    else {
		type = apr_pstrcat(r->pool, type, ",", thistype, NULL);
	    }
	}
    }

    /* then loop through all the cachedisable entries */
    for (i = 0; i < conf->cachedisable->nelts; i++) {
	struct cache_disable *ent = (struct cache_disable *) conf->cachedisable->elts;
	const char *thisurl = ent[i].url;
	if ((thisurl) && !strncasecmp(thisurl, url, strlen(thisurl))) {
	    type = NULL;
	}
    }

    return type;
}

/*
 * list is a comma-separated list of case-insensitive tokens, with
 * optional whitespace around the tokens.
 * The return returns 1 if the token val is found in the list, or 0
 * otherwise.
 */
int ap_cache_liststr(const char *list, const char *key, char **val)
{
    int len, i;
    char *p;
    char valbuf[HUGE_STRING_LEN];
    valbuf[sizeof(valbuf)-1] = 0; /* safety terminating zero */

    len = strlen(key);

    while (list != NULL) {
	p = strchr((char *) list, ',');
	if (p != NULL) {
	    i = p - list;
	    do
		p++;
	    while (ap_isspace(*p));
	}
	else
	    i = strlen(list);

	while (i > 0 && ap_isspace(list[i - 1]))
	    i--;
	if (i == len && strncasecmp(list, key, len) == 0) {
	    if (val) {
		p = strchr((char *) list, ',');
		while (ap_isspace(*list)) {
		    list++;
		}
		if ('=' == list[0])
		    list++;
		while (ap_isspace(*list)) {
		    list++;
		}
		strncpy(valbuf, list, MIN(p-list, sizeof(valbuf)-1));
		*val = valbuf;
	    }
	    return 1;
	}
	list = p;
    }
    return 0;
}

/* return each comma separated token, one at a time */
const char *ap_cache_tokstr(apr_pool_t *p, const char *list, const char **str)
{
    apr_off_t len, i;
    const char *s;

    s = ap_strchr_c(list, ',');
    if (s != NULL) {
	i = s - list;
	do
	    s++;
	while (apr_isspace(*s));
    }
    else
	i = strlen(list);

    while (i > 0 && apr_isspace(list[i - 1]))
	i--;

    *str = s;
    if (len)
	return apr_pstrndup(p, list, len);
    else
	return NULL;

}
+725 −44

File changed.

Preview size limit exceeded, changes collapsed.

+203 −3
Original line number Diff line number Diff line
@@ -56,10 +56,210 @@
 * University of Illinois, Urbana-Champaign.
 */

#include "apr_buckets.h"
#ifndef MOD_CACHE_H
#define MOD_CACHE_H 

/*
 * Main include file for the Apache Transparent Cache
 */

#define CORE_PRIVATE

#include "apr_hooks.h"
#include "apr.h"
#include "apr_compat.h"
#include "apr_lib.h"
#include "apr_strings.h"
#include "apr_buckets.h"
#include "apr_md5.h"
#include "apr_pools.h"
#include "apr_strings.h"

#include "httpd.h"
#include "http_config.h"
#include "ap_config.h"
#include "http_core.h"
#include "http_protocol.h"
#include "http_request.h"
#include "http_vhost.h"
#include "http_main.h"
#include "http_log.h"
#include "http_connection.h"
#include "util_filter.h"
#include "apr_date.h"
#include "apr_uri.h"

#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif

#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif

#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif

#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif

#ifndef MAX
#define MAX(a,b)                ((a) > (b) ? (a) : (b))
#endif
#ifndef MIN
#define MIN(a,b)                ((a) < (b) ? (a) : (b))
#endif


/* default completion is 60% */
#define DEFAULT_CACHE_COMPLETION (60)

#define MSEC_ONE_DAY ((apr_time_t)(86400*APR_USEC_PER_SEC)) /* one day, in microseconds */
#define MSEC_ONE_HR  ((apr_time_t)(3600*APR_USEC_PER_SEC))  /* one hour, in microseconds */

#define DEFAULT_CACHE_MAXEXPIRE MSEC_ONE_DAY
#define DEFAULT_CACHE_EXPIRE    MSEC_ONE_HR
#define DEFAULT_CACHE_LMFACTOR (0.1)

struct cache_enable {
    const char *url;
    const char *type;
};

struct cache_disable {
    const char *url;
};

/* static information about the local cache */
typedef struct {
    int cacheon;			/* Cache enabled? */
    int cacheon_set;
    apr_array_header_t *cacheenable;	/* URLs to cache */
    apr_array_header_t *cachedisable;	/* URLs not to cache */
    apr_time_t maxex;			/* Maximum time to keep cached files in msecs */
    int maxex_set;
    apr_time_t defex;			/* default time to keep cached file in msecs */
    int defex_set;
    double factor;			/* factor for estimating expires date */
    int factor_set;
    int complete;			/* Force cache completion after this point */
    int complete_set;

} cache_server_conf;

/* cache info information */
typedef struct cache_info cache_info;
struct cache_info {
    char *content_type;
    const char *etag;
    const char *lastmods;	/* last modified of cache entity */
    apr_time_t date;
    apr_time_t lastmod;
    char lastmod_str[APR_RFC822_DATE_LEN];
    apr_time_t expire;
    apr_time_t request_time;
    apr_time_t response_time;
    apr_size_t len;
};

/* cache handle information */
typedef struct cache_handle cache_handle;
struct cache_handle {
    cache_info *info;
    cache_handle *next;
    void *cache_obj;           /* Pointer to cache specific object */

    /* Cache call back functions */
    int (*remove_entity) (cache_handle *h);
    int (*write_headers)(cache_handle *h, request_rec *r, cache_info *i);
    int (*write_body)(cache_handle *h, apr_bucket_brigade *b);
    int (*read_headers) (cache_handle *h, request_rec *r, cache_info **i);
    int (*read_body) (cache_handle *h, apr_bucket_brigade *bb); 

};

/* per request cache information */
typedef struct {
    const char *types;			/* the types of caches allowed */
    const char *type;			/* the type of cache selected */
    int fresh;				/* is the entitey fresh? */
    cache_handle *handle;		/* current cache handle */
    int in_checked;			/* CACHE_IN must cache the entity */
} cache_request_rec;


/* cache_util.c */
int ap_cache_request_is_conditional(request_rec *r);
void ap_cache_reset_output_filters(request_rec *r);
const char *ap_cache_get_cachetype(request_rec *r, cache_server_conf *conf, const char *url);
int ap_cache_liststr(const char *list, const char *key, char **val);
const char *ap_cache_tokstr(apr_pool_t *p, const char *list, const char **str);

/**
 * cache_storage.c
 */
int cache_remove_url(request_rec *r, const char *types, char *url);
int cache_create_entity(request_rec *r, const char *types, char *url, apr_size_t size);
int cache_remove_entity(request_rec *r, const char *types, cache_handle *h);
int cache_select_url(request_rec *r, const char *types, char *url);

apr_status_t cache_write_entity_headers(cache_handle *h, request_rec *r, cache_info *info, 
                                        apr_table_t *headers_in, apr_table_t *headers_out);
apr_status_t cache_write_entity_body(cache_handle *h, apr_bucket_brigade *bb);

apr_status_t cache_read_entity_headers(cache_handle *h, request_rec *r, apr_table_t **headers);
apr_status_t cache_read_entity_body(cache_handle *h, apr_bucket_brigade *bb);


/* hooks */

/* Create a set of CACHE_DECLARE(type), CACHE_DECLARE_NONSTD(type) and 
 * CACHE_DECLARE_DATA with appropriate export and import tags for the platform
 */
#if !defined(WIN32)
#define CACHE_DECLARE(type)            type
#define CACHE_DECLARE_NONSTD(type)     type
#define CACHE_DECLARE_DATA
#elif defined(CACHE_DECLARE_STATIC)
#define CACHE_DECLARE(type)            type __stdcall
#define CACHE_DECLARE_NONSTD(type)     type
#define CACHE_DECLARE_DATA
#elif defined(CACHE_DECLARE_EXPORT)
#define CACHE_DECLARE(type)            __declspec(dllexport) type __stdcall
#define CACHE_DECLARE_NONSTD(type)     __declspec(dllexport) type
#define CACHE_DECLARE_DATA             __declspec(dllexport)
#else
#define CACHE_DECLARE(type)            __declspec(dllimport) type __stdcall
#define CACHE_DECLARE_NONSTD(type)     __declspec(dllimport) type
#define CACHE_DECLARE_DATA             __declspec(dllimport)
#endif

APR_DECLARE_EXTERNAL_HOOK(cache, CACHE, int, create_entity, 
                          (cache_handle **hp, const char *type,
                           char *url, apr_size_t len))
APR_DECLARE_EXTERNAL_HOOK(cache, CACHE, int, open_entity,  
                          (cache_handle **hp, const char *type,
                           char *url))
APR_DECLARE_EXTERNAL_HOOK(cache, CACHE, int, remove_url, 
                          (const char *type, char *url))

AP_DECLARE_HOOK(int,serve_cache,(request_rec *r));
AP_DECLARE_HOOK(int,store_cache,(request_rec *r, apr_bucket_brigade *bb, void **cf));
#if 0
APR_DECLARE_EXTERNAL_HOOK(cache, CACHE, int, remove_entity, 
                          (cache_handle *h))
APR_DECLARE_EXTERNAL_HOOK(cache, CACHE, int, read_entity_headers, 
                          (cache_handle *h, cache_info **info,
                           apr_table_t **headers_in, apr_table_t **headers_out))
APR_DECLARE_EXTERNAL_HOOK(cache, CACHE, int, read_entity_body, 
                          (cache_handle *h,
                           apr_bucket_brigade *out))
APR_DECLARE_EXTERNAL_HOOK(cache, CACHE, int, write_entity_headers, 
                          (cache_handle *h, cache_info *info,
                           apr_table_t *headers_in, apr_table_t *headers_out))
APR_DECLARE_EXTERNAL_HOOK(cache, CACHE, int, write_entity_body, 
                          (cache_handle *h,
                           apr_bucket_brigade *in))
#endif

#endif /*MOD_CACHE_H*/
+509 −0

File added.

Preview size limit exceeded, changes collapsed.