| >| PURGE http://cachehost/someuri HTTP/1.1
| >| ....
| >
| >Isn't it better to do it as a uri - say - /cache-control - hosted on the 
| >proxy?
| >so the above would look like
| >
| >POST /cache-control?purge
| >
| >http://cachehost/someuri1
| >http://cachehost/someuri2
| >http://cachehost/someuri3
| >
| >
| >the advantage is that standard http clients like wget can be used to purge 
| >a
| >number of URLs (or based on wild cards) and it does not use any non-rfc 
| >methods.
| 
| If implemented as a handler it would also give you ACL/auth for free. OTOH 
| it gets more difficult to implement the actual deleting from the cache.
| Furthermore you cannot delete specific variants.

A simple minded cache purge (POC) is attached.
    1) does not understand variants.
    2) takes input as a post request with a number of URLs one per line
    3) generates our own request, so should be possible to add vary &
        headers
configuration
-------------
<Location /cache>
    SetHandler meta_cache
</Location>

invoke e.g

$cat > url.file
/index.html
http://webproxy.india.sun.com/index.html
^D

$wget --post-file url.file 'http://agneyam.india.sun.com:8080/cache?purge'

do let me know how I can improve this.

                                    rahul
/* 
**  mod_meta_cache.c -- Apache sample meta_cache module
**  A simple minded module to purge URLs from cache.
**  conf:
**  <Location /cache>
**    SetHandler meta_cache
**  </Location>
**  
**  Usage:
**  $cat > url.file
**  /index.html
**  http://webproxy.india.sun.com/index.html
**  ^D
**  $wget --post-file url.file 'http://agneyam.india.sun.com:8080/cache?purge'
** 
**  License : Apache V2
*/ 

#include "httpd.h"
#include "http_config.h"
#include "http_request.h"
#include "http_protocol.h"
#include "apr_tables.h"
#include "ap_config.h"
#include "mod_cache.h"
#include "mod_proxy.h"

static const char s_metacache[] = "meta-cache";
extern module AP_MODULE_DECLARE_DATA cache_module;

typedef int (apply_fn)(const char *url, request_rec *r);

static int process_post(request_rec* r,apply_fn url_fn)
{
    apr_status_t rv;
    apr_bucket_brigade *bb_in;
    apr_bucket_brigade *temp_brigade;

    bb_in = apr_brigade_create(r->pool, r->connection->bucket_alloc);

    rv = ap_get_brigade(r->input_filters, bb_in, AP_MODE_READBYTES,
            APR_BLOCK_READ, HUGE_STRING_LEN);
    if (rv != APR_SUCCESS) {
        ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
                "Error reading request entity data");
        return HTTP_INTERNAL_SERVER_ERROR;
    }

    temp_brigade = apr_brigade_create(r->pool, r->connection->bucket_alloc);

    while(!APR_BRIGADE_EMPTY(bb_in)) {
        apr_bucket *bkt_in = APR_BRIGADE_FIRST(bb_in);
        apr_bucket *bkt_out;
        char line[AP_IOBUFSIZE];
        apr_size_t line_len = sizeof(line) - 1;

        if(APR_BUCKET_IS_EOS(bkt_in)) {
            APR_BUCKET_REMOVE(bkt_in);
            break;
        }

        rv = apr_brigade_split_line(temp_brigade, bb_in,
                APR_BLOCK_READ, AP_IOBUFSIZE);
        if (rv != APR_SUCCESS) return rv;

        rv = apr_brigade_flatten(temp_brigade, line, &line_len);
        if (rv != APR_SUCCESS) return rv;

        line[line_len] = 0;
        if(line[line_len-1] == '\n')
            line[line_len-1] = 0;
        if (strlen(line)) {
            rv = url_fn(line, r);
            ap_rputs(line, r);
            if (rv != APR_SUCCESS) {
                ap_rputs(" : notfound\n", r);
            } else {
                ap_rputs(" : purged\n", r);
            }
        }

        apr_brigade_cleanup(temp_brigade);
    }
    apr_brigade_destroy(temp_brigade);
    apr_brigade_cleanup(bb_in);
}

int remove_uri(const char *url, request_rec *r);

static int meta_cache_handler(request_rec *r)
{
    if (strcmp(r->handler, "meta_cache")) return DECLINED;

    r->content_type = "text/html";
    if(r->args) {
        if (!strncmp(r->args, "purge", sizeof("purge"))) {
            process_post(r, remove_uri);
        }
    }
    ap_rputs("meta_cache executed successfully. \n", r);
    return OK;
}

/* All this does is to copy over relevant fields of our original reques_rec
 * structure to new one, and stick a new URI
 * Required as a request_rec is used as a key in the cache interface.
 */
static request_rec *fake_request(const char *new_uri, request_rec *r)
{
    request_rec *new = (request_rec *) apr_pcalloc(r->pool, sizeof(request_rec));

    new->connection = r->connection;
    new->server     = r->server;
    new->pool       = r->pool;

    new->method          = r->method;
    new->method_number   = r->method_number;
    new->allowed_methods = ap_make_method_list(new->pool, 2);
    ap_parse_uri(new, new_uri);

    new->request_config = ap_create_request_config(r->pool);
    new->per_dir_config = r->server->lookup_defaults;

    new->the_request = r->the_request;
    new->allowed         = r->allowed;
    new->status          = r->status;
    new->assbackwards    = r->assbackwards;
    new->header_only     = r->header_only;
    new->protocol        = r->protocol;
    new->proto_num       = r->proto_num;
    new->hostname        = r->hostname;
    new->request_time    = r->request_time;
    new->main            = r->main;
    new->headers_in      = r->headers_in;
    new->headers_out     = apr_table_make(r->pool, 12);
    new->err_headers_out = r->err_headers_out;
    new->subprocess_env  = r->subprocess_env;
    new->notes           = apr_table_make(r->pool, 5);
    new->allowed_methods = ap_make_method_list(new->pool, 2);
    new->htaccess        = r->htaccess;
    new->no_cache        = r->no_cache;
    new->expecting_100   = r->expecting_100;
    new->no_local_copy   = r->no_local_copy;
    new->read_length     = r->read_length;
    new->vlist_validator = r->vlist_validator;
    new->proto_output_filters  = r->proto_output_filters;
    new->proto_input_filters   = r->proto_input_filters;
    new->output_filters  = new->proto_output_filters;
    new->input_filters   = new->proto_input_filters;
    
    /* cache_select uses parsed_uri.hostname if it is proxyreq */
    if (new->parsed_uri.hostname)
        new->proxyreq = 1;

    return new;
}

static int remove_uri(const char *url, request_rec *r)
{
    apr_status_t rv;
    request_rec *preq = fake_request(url, r);

    /* make space for the per request config */
    cache_provider_list *providers;
    cache_server_conf *conf;
    cache_request_rec *cache = apr_pcalloc(r->pool, sizeof(cache_request_rec));
    ap_set_module_config(preq->request_config, &cache_module, cache);

    conf = (cache_server_conf *) ap_get_module_config(r->server->module_config,
            &cache_module);

    if (!(providers = ap_cache_get_providers(preq, conf, preq->parsed_uri)))
        return DECLINED;
    cache->providers = providers;
    /* Select one version of URI in the db. the version selected depends on the 
     * pseudo request that we create. this function updates the request_config with
     * required cache data.
     * */
    rv = cache_select(preq);
    if (rv == APR_SUCCESS)
        cache_remove_url(cache, r->pool);
    return rv;
}

static void meta_cache_register_hooks(apr_pool_t *p)
{
    ap_hook_handler(meta_cache_handler, NULL, NULL, APR_HOOK_MIDDLE);
}

/* Dispatch list for API hooks */
module AP_MODULE_DECLARE_DATA meta_cache_module = {
    STANDARD20_MODULE_STUFF, 
    NULL,                  /* create per-dir    config structures */
    NULL,                  /* merge  per-dir    config structures */
    NULL,                  /* create per-server config structures */
    NULL,                  /* merge  per-server config structures */
    NULL,                  /* table of config file commands       */
    meta_cache_register_hooks  /* register hooks                      */
};

Reply via email to