Commit 00065c81 authored by Bill Stoddard's avatar Bill Stoddard
Browse files

Introduce the notion of a multi part cache object. Part of the cache_object

is common across any cache implementation, the other part is private to
the particular implementation (eg, mem_cache_object_t/mod_mem_cache).
Use a cache_handle_t allocated out of the request pool to hold references
to the callback functions and common cache object.

The cache_handle_t contains implementation specific callback functions and
a reference to a common cache_object_t.  The cache_object_t contains
a reference to an implementation specific cache object extension (mem_cache_object_t
for example).

All this simplifies managing the callback function pointers (don't want to
save them in each cache entry) and collections of cache_object_t keyed
to a single url.


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@90994 13f79535-47bb-0310-9956-ffa450edef68
parent 03a87338
Loading
Loading
Loading
Loading
+25 −19
Original line number Diff line number Diff line
@@ -101,7 +101,7 @@ 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)
{
    cache_handle *h;
    cache_handle_t *h = apr_pcalloc(r->pool, sizeof(h));
    const char *next = types;
    const char *type;
    apr_status_t rv;
@@ -111,7 +111,7 @@ int cache_create_entity(request_rec *r, const char *types, char *url, apr_size_t
    /* 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)) {
        switch (rv = cache_run_create_entity(h, type, url, size)) {
        case OK: {
            cache->handle = h;
            return OK;
@@ -134,7 +134,7 @@ int cache_create_entity(request_rec *r, const char *types, char *url, apr_size_t
 * from the cache, and the cache_handle is closed.
 */
/* XXX Don't think we need to pass in request_rec or types ... */
int cache_remove_entity(request_rec *r, const char *types, cache_handle *h)
int cache_remove_entity(request_rec *r, const char *types, cache_handle_t *h)
{
    h->remove_entity(h);
    return 1;
@@ -160,16 +160,24 @@ int cache_select_url(request_rec *r, const char *types, char *url)
                                                                          &cache_module);

    /* go through the cache types till we get a match */
    cache->handle = apr_palloc(r->pool, sizeof(cache_handle));
    cache->handle = apr_palloc(r->pool, sizeof(cache_handle_t));

    while (next) {
        type = ap_cache_tokstr(r->pool, next, &next);
        switch ((rv = cache_run_open_entity(cache->handle, type, url))) {
        case OK: {
            /* cool bananas! */
/*** loop through returned entities */
/*** do freshness calculation here */
            /* XXX:
             * Handle being returned a collection of entities.
             */

            /* Has the cache entry expired? */
#if 0
            if (r->request_time > cache->handle... need to get info out of the cache... info.expire)
                cache->fresh = 0;
            else
#endif
                cache->fresh = 1;

            /*** do content negotiation here */
            return OK;
        }
@@ -188,13 +196,13 @@ int cache_select_url(request_rec *r, const char *types, char *url)
    return DECLINED;
}

apr_status_t cache_write_entity_headers(cache_handle *h, request_rec *r, cache_info *info,
apr_status_t cache_write_entity_headers(cache_handle_t *h, request_rec *r, cache_info *info,
                                        apr_table_t *headers)
{
    h->write_headers(h, r, info, headers);
    return APR_SUCCESS;
}
apr_status_t cache_write_entity_body(cache_handle *h, apr_bucket_brigade *b) 
apr_status_t cache_write_entity_body(cache_handle_t *h, apr_bucket_brigade *b) 
{
    apr_status_t rv = APR_SUCCESS;
    if (h->write_body(h, b) != OK) {
@@ -202,29 +210,27 @@ apr_status_t cache_write_entity_body(cache_handle *h, apr_bucket_brigade *b)
    return rv;
}

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

    /* Build the header table from info in the info struct */
    *headers = apr_table_make(r->pool, 15);

    h->read_headers(h, r, &info, *headers);
    h->read_headers(h, r, *headers);

    return APR_SUCCESS;
}
apr_status_t cache_read_entity_body(cache_handle *h, apr_bucket_brigade *b) 
apr_status_t cache_read_entity_body(cache_handle_t *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)
                                      (cache_handle_t *h, const char *type, 
                                      char *url, apr_size_t len),(h,type,url,len),DECLINED)
APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(cache, CACHE, int, open_entity,  
                                      (cache_handle *h, const char *type, 
                                      (cache_handle_t *h, const char *type, 
                                      char *url),(h,type,url),DECLINED)
APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(cache, CACHE, int, remove_url, 
                                    (const char *type, char *url),(type,url),OK,DECLINED)
+5 −5
Original line number Diff line number Diff line
@@ -186,13 +186,13 @@ int ap_url_cache_handler(request_rec *r)

            /* We are in the quick handler hook, which means that no output
             * filters have been set. So lets run the insert_filter hook.
             * Humm... Probably should not go through most of these hooks
             * for a proxy request, so take out all but the basics.
             * XXX - Should we be inserting filters in the output stream
             * for proxy requests? Certainly we need the core filters
             * (byterange, chunking, etc.).  I can also see the need to
             * conditionally insert tag processing filters (e.g. INCLUDES).
             */
            ap_run_insert_filter(r);
            if (r->proxyreq) {
                ap_cache_reset_output_filters(r);
            }

            /* Now add the cache_out filter. cache_out is a FTYPE_CONTENT
             * which means it will be inserted first in the stream, which
             * is exactly what we need.
+24 −22
Original line number Diff line number Diff line
@@ -146,7 +146,6 @@ typedef struct {
    int factor_set;
    int complete;			/* Force cache completion after this point */
    int complete_set;

} cache_server_conf;

/* cache info information */
@@ -165,21 +164,24 @@ struct cache_info {
};

/* 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 */
typedef struct cache_object cache_object_t;
struct cache_object {
    char *key;
    cache_object_t *next;
    cache_info info;
    void *vobj;         /* Opaque portion (specific to the cache implementation) of the cache object */
    apr_size_t count;   /* Number of body bytes written to the cache so far */
    int complete;
};

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

typedef struct cache_handle cache_handle_t;
struct cache_handle {
    cache_object_t *cache_obj;
    int (*remove_entity) (cache_handle_t *h);
    int (*write_headers)(cache_handle_t *h, request_rec *r, cache_info *i, apr_table_t *headers);
    int (*write_body)(cache_handle_t *h, apr_bucket_brigade *b);
    int (*read_headers) (cache_handle_t *h, request_rec *r, apr_table_t *headers);
    int (*read_body) (cache_handle_t *h, apr_bucket_brigade *bb); 
};

/* per request cache information */
@@ -187,7 +189,7 @@ 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 */
    cache_handle_t *handle;		/* current cache handle */
    int in_checked;			/* CACHE_IN must cache the entity */
} cache_request_rec;

@@ -204,15 +206,15 @@ const char *ap_cache_tokstr(apr_pool_t *p, const char *list, const char **str);
 */
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_remove_entity(request_rec *r, const char *types, cache_handle_t *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_status_t cache_write_entity_headers(cache_handle_t *h, request_rec *r, cache_info *info, 
                                        apr_table_t *headers);
apr_status_t cache_write_entity_body(cache_handle *h, apr_bucket_brigade *bb);
apr_status_t cache_write_entity_body(cache_handle_t *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);
apr_status_t cache_read_entity_headers(cache_handle_t *h, request_rec *r, apr_table_t **headers);
apr_status_t cache_read_entity_body(cache_handle_t *h, apr_bucket_brigade *bb);


/* hooks */
@@ -239,10 +241,10 @@ apr_status_t cache_read_entity_body(cache_handle *h, apr_bucket_brigade *bb);
#endif

APR_DECLARE_EXTERNAL_HOOK(cache, CACHE, int, create_entity, 
                          (cache_handle **hp, const char *type,
                          (cache_handle_t *h, const char *type,
                           char *url, apr_size_t len))
APR_DECLARE_EXTERNAL_HOOK(cache, CACHE, int, open_entity,  
                          (cache_handle *h, const char *type,
                          (cache_handle_t *h, const char *type,
                           char *url))
APR_DECLARE_EXTERNAL_HOOK(cache, CACHE, int, remove_url, 
                          (const char *type, char *url))
+93 −105
Original line number Diff line number Diff line
@@ -85,16 +85,13 @@ typedef struct {
    char* val;
} cache_header_tbl_t;

typedef struct {
typedef struct mem_cache_object {
    cache_type_e type;
    char *key;
    apr_ssize_t num_headers;
    cache_header_tbl_t *tbl;
    apr_size_t m_len;
    void *m;
    cache_info info;
    int complete;
} cache_object_t;
} mem_cache_object_t;

typedef struct {
    apr_lock_t *lock;
@@ -109,16 +106,17 @@ static mem_cache_conf *sconf;
#define CACHEFILE_LEN 20

/* Forward declarations */
static int remove_entity(cache_handle *h);
static int write_headers(cache_handle *h, request_rec *r, cache_info *i,
                         apr_table_t *headers);
static int write_body(cache_handle *h, apr_bucket_brigade *b);
static int read_headers(cache_handle *h, request_rec *r, cache_info **info, 
static int remove_entity(cache_handle_t *h);
static int write_headers(cache_handle_t *h, request_rec *r, cache_info *i,
                         apr_table_t *headers);
static int read_body(cache_handle *h, apr_bucket_brigade *bb);
static int write_body(cache_handle_t *h, apr_bucket_brigade *b);
static int read_headers(cache_handle_t *h, request_rec *r, apr_table_t *headers);
static int read_body(cache_handle_t *h, apr_bucket_brigade *bb);

static void cleanup_cache_object(cache_object_t *obj)
{
    mem_cache_object_t *mobj = obj->vobj;

    /* The cache object has been removed from the cache. Now clean
     * it up, freeing any storage, closing file descriptors, etc.
     */
@@ -149,18 +147,25 @@ static void cleanup_cache_object(cache_object_t *obj)
       }
    */

    if (obj->info.content_type)
        free((char*) obj->info.content_type);
    if (obj->key)
    /* Cleanup the cache_object_t */
    if (obj->key) {
        free(obj->key);
    if (obj->m)
        free(obj->m);
    }
    free(obj);
    
    /* Cleanup the mem_cache_object_t */
    if (!mobj) {
        return;
    }
    if (mobj->m) {
        free(mobj->m);
    }

    /* XXX Cleanup the headers */
    if (obj->num_headers) {
    if (mobj->num_headers) {
        
    }
    free(obj);
    free(mobj);
}

static apr_status_t cleanup_cache_mem(void *sconfv)
@@ -203,100 +208,88 @@ static void *create_cache_config(apr_pool_t *p, server_rec *s)
    return sconf;
}

static int create_entity(cache_handle **hp, const char *type, char *key, apr_size_t len) 
static int create_entity(cache_handle_t *h, const char *type, char *key, apr_size_t len) 
{
    cache_object_t *obj, *eobj = NULL;
    cache_handle *h;
    cache_object_t *obj, *tmp_obj;
    mem_cache_object_t *mobj;

    /* Create the cache handle and begin populating it.
     */
    if (strcasecmp(type, "mem")) {
        return DECLINED;
    }

    /* Check len to see if it is withing acceptable bounds 
     * XXX max cache check should be configurable variable.
    /* XXX Check len to see if it is withing acceptable bounds 
     * max cache check should be configurable variable.
     */
    if (len < 0 || len > MAX_CACHE) {
        return DECLINED;
    }
    /* Check total cache size and number of entries. Are they within the
    /* XXX Check total cache size and number of entries. Are they within the
     * configured limits? If not, kick off garbage collection thread.
     */

    /* Allocate the cache_handle and set up call back functions specific to 
     * this cache handler.
     */
    h = malloc(sizeof(cache_handle));
    *hp = h;
    if (!h) {
        /* handle the error */
        return DECLINED;
    }
    h->read_body = &read_body;
    h->read_headers = &read_headers;
    h->write_body = &write_body;
    h->write_headers = &write_headers;

    /* Allocate and initialize the cache object. The cache object is
     * unique to this implementation.
     */
    /* Allocate and initialize cache_object_t */
    obj = malloc(sizeof(*obj));
    if (!obj) {
        /* Handle ther error */
        free(h);
        return DECLINED;
    }
    memset(obj,'\0', sizeof(*obj));

    obj->key = malloc(strlen(key) + 1);
    if (!obj->key) {
        /* XXX Uuugh, there has got to be a better way to manage memory.
         */
        free(h);
        free(obj);
        return DECLINED;
    }
    obj->m_len = len;     /* One of these len fields can go */
    obj->info.len = len;
    strncpy(obj->key, key, strlen(key) + 1);
    h->cache_obj = (void *) obj;
    obj->info.len = len;
    obj->complete = 0;   /* Cache object is not complete */


    /* Allocate and init mem_cache_object_t */
    mobj = malloc(sizeof(*mobj));
    if (!mobj) {
        /* XXX: Cleanup */
        cleanup_cache_object(obj);
    }
    memset(mobj,'\0', sizeof(*mobj));
    obj->vobj = mobj;    /* Reference the mem_cache_object_t out of cache_object_t */
    mobj->m_len = len;    /* Duplicates info in cache_object_t info */

    /* Mark the cache object as incomplete and put it into the cache */
    obj->complete = 0;

    /* XXX Need a way to insert into the cache w/o such coarse grained locking */
    /* Place the cache_object_t into the hash table
     * XXX Need a way to insert into the cache w/o such coarse grained locking 
     * XXX Need to enable caching multiple cache objects (representing different
     * views of the same content) under a single search key
     */
    if (sconf->lock) {
        apr_lock_acquire(sconf->lock);
    }
    /* Do not allow the new cache object to replace an existing cache object.
     * We should find eobj only when another thread is in the process of
     * caching the same object as this thread. If we hit this case, decline
     * the request.
     */
    eobj = (cache_object_t *) apr_hash_get(sconf->cacheht, key, APR_HASH_KEY_STRING);
    if (!eobj) {
    tmp_obj = (cache_object_t *) apr_hash_get(sconf->cacheht, key, APR_HASH_KEY_STRING);
    if (!tmp_obj) {
        apr_hash_set(sconf->cacheht, obj->key, strlen(obj->key), obj);
    }
    if (sconf->lock) {
        apr_lock_release(sconf->lock);
    }

    if (eobj) {
    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.
         */
        cleanup_cache_object(obj);
        free(h);
        *hp = NULL;
        return DECLINED;
    }

    /* Populate the cache handle */
    h->cache_obj = obj;
    h->read_body = &read_body;
    h->read_headers = &read_headers;
    h->write_body = &write_body;
    h->write_headers = &write_headers;

    return OK;
}

static int open_entity(cache_handle *h, const char *type, char *key) 
static int open_entity(cache_handle_t *h, const char *type, char *key) 
{
    cache_object_t *obj;

@@ -322,15 +315,13 @@ static int open_entity(cache_handle *h, const char *type, char *key)
    h->write_body = &write_body;
    h->write_headers = &write_headers;
    h->cache_obj = obj;
    if (!obj || !(obj->complete)) {
        return DECLINED;
    }

    return OK;
}

static int remove_entity(cache_handle *h) 
static int remove_entity(cache_handle_t *h) 
{
    cache_object_t *obj = (cache_object_t *) h->cache_obj;
    cache_object_t *obj = h->cache_obj;

    if (sconf->lock) {
        apr_lock_acquire(sconf->lock);
@@ -342,9 +333,6 @@ static int remove_entity(cache_handle *h)

    cleanup_cache_object(obj);
    
    /* Reinit the cache_handle fields? */
    h->cache_obj = NULL;

    return OK;
}

@@ -390,26 +378,24 @@ static int remove_url(const char *type, char *key)
    return OK;
}

static int read_headers(cache_handle *h, request_rec *r, cache_info **info, 
                        apr_table_t *headers) 
static int read_headers(cache_handle_t *h, request_rec *r, apr_table_t *headers) 
{
    cache_object_t *obj = (cache_object_t*) h->cache_obj;
    mem_cache_object_t *mobj = (mem_cache_object_t*) h->cache_obj->vobj;
    int i;

    for (i = 0; i < obj->num_headers; ++i) {
        apr_table_setn(headers, obj->tbl[i].hdr, obj->tbl[i].val);
    for (i = 0; i < mobj->num_headers; ++i) {
        apr_table_setn(headers, mobj->tbl[i].hdr, mobj->tbl[i].val);
    } 
    *info = &(obj->info);

    return OK;
}

static int read_body(cache_handle *h, apr_bucket_brigade *bb) 
static int read_body(cache_handle_t *h, apr_bucket_brigade *bb) 
{
    apr_bucket *b;
    cache_object_t *obj = h->cache_obj;
    mem_cache_object_t *mobj = (mem_cache_object_t*) h->cache_obj->vobj;
    
    b = apr_bucket_immortal_create(obj->m, obj->m_len);
    b = apr_bucket_immortal_create(mobj->m, mobj->m_len);
    APR_BRIGADE_INSERT_TAIL(bb, b);
    b = apr_bucket_eos_create();
    APR_BRIGADE_INSERT_TAIL(bb, b);
@@ -417,9 +403,9 @@ static int read_body(cache_handle *h, apr_bucket_brigade *bb)
    return OK;
}

static int write_headers(cache_handle *h, request_rec *r, cache_info *info, apr_table_t *headers)
static int write_headers(cache_handle_t *h, request_rec *r, cache_info *info, apr_table_t *headers)
{
    cache_object_t *obj = (cache_object_t*) h->cache_obj;
    mem_cache_object_t *mobj = (mem_cache_object_t*) h->cache_obj->vobj;
    apr_table_entry_t *elts = (apr_table_entry_t *) headers->a.elts;
    apr_ssize_t i;
    apr_size_t len = 0;
@@ -427,8 +413,9 @@ static int write_headers(cache_handle *h, request_rec *r, cache_info *info, apr_
    char *buf;

    /* Precompute how much storage we need to hold the headers */
    obj->tbl = malloc(sizeof(cache_header_tbl_t) * headers->a.nelts);
    if (NULL == obj->tbl) {
    mobj->tbl = malloc(sizeof(cache_header_tbl_t) * headers->a.nelts);
    if (NULL == mobj->tbl) {
        /* cleanup_cache_obj(h->cache_obj); */
        return DECLINED;
    }
    for (i = 0; i < headers->a.nelts; ++i) {
@@ -440,18 +427,19 @@ static int write_headers(cache_handle *h, request_rec *r, cache_info *info, apr_
    /* Transfer the headers into a contiguous memory block */
    buf = malloc(len);
    if (!buf) {
        free(obj->tbl);
        obj->tbl = NULL;
        free(mobj->tbl);
        mobj->tbl = NULL;
        /* cleanup_cache_obj(h->cache_obj); */
        return DECLINED;
    }
    obj->num_headers = headers->a.nelts;
    for (i = 0; i < obj->num_headers; ++i) {
        obj->tbl[i].hdr = &buf[idx];
    mobj->num_headers = headers->a.nelts;
    for (i = 0; i < mobj->num_headers; ++i) {
        mobj->tbl[i].hdr = &buf[idx];
        len = strlen(elts[i].key) + 1;              /* Include NULL terminator */
        strncpy(&buf[idx], elts[i].key, len);
        idx+=len;

        obj->tbl[i].val = &buf[idx];
        mobj->tbl[i].val = &buf[idx];
        len = strlen(elts[i].val) + 1;
        strncpy(&buf[idx], elts[i].val, len);
        idx+=len;
@@ -476,10 +464,10 @@ static int write_headers(cache_handle *h, request_rec *r, cache_info *info, apr_
    return OK;
}

static int write_body(cache_handle *h, apr_bucket_brigade *b) 
static int write_body(cache_handle_t *h, apr_bucket_brigade *b) 
{
    apr_status_t rv;
    cache_object_t *obj = (cache_object_t *) h->cache_obj;
    mem_cache_object_t *mobj = (mem_cache_object_t*) h->cache_obj->vobj;
    apr_read_type_e eblock = APR_BLOCK_READ;
    apr_bucket *e;
    char *cur;
@@ -488,15 +476,15 @@ static int write_body(cache_handle *h, apr_bucket_brigade *b)
     * Enable this decision to be configured....
     * XXX cache buckets...
     */
    if (obj->m == NULL) {
        obj->m = malloc(obj->m_len);
        if (obj->m == NULL) {
    if (mobj->m == NULL) {
        mobj->m = malloc(mobj->m_len);
        if (mobj->m == NULL) {
            /* Cleanup cache entry and return */
        }
        obj->type = CACHE_TYPE_HEAP;
        h->count = 0;
        mobj->type = CACHE_TYPE_HEAP;
        h->cache_obj->count = 0;
    }
    cur = (char*) obj->m + h->count;
    cur = (char*) mobj->m + h->cache_obj->count;

    /* Iterate accross the brigade and populate the cache storage */
    APR_BRIGADE_FOREACH(e, b) {
@@ -504,7 +492,7 @@ static int write_body(cache_handle *h, apr_bucket_brigade *b)
        apr_size_t len;

        if (APR_BUCKET_IS_EOS(e)) {
            obj->complete = 1;
            h->cache_obj->complete = 1;
            break;
        }
        rv = apr_bucket_read(e, &s, &len, eblock);
@@ -515,12 +503,12 @@ static int write_body(cache_handle *h, apr_bucket_brigade *b)
        if (len ) {
            memcpy(cur, s, len);
            cur+=len;
            h->count+=len;
            h->cache_obj->count+=len;
        }
        /* This should not happen, but if it does, we are in BIG trouble
         * cause we just stomped all over the heap.
         */
        AP_DEBUG_ASSERT(h->count > obj->m_len);
        AP_DEBUG_ASSERT(h->cache_object->count > mobj->m_len);
    }

    return OK;