key = cache_create_key(r);
while (next) {
type = ap_cache_tokstr(r->pool, next, &next);
- switch (rv = cache_run_create_entity(h, type, key, size)) {
+ switch (rv = cache_run_create_entity(h, r, type, key, size)) {
case OK: {
cache->handle = h;
return OK;
while (next) {
type = ap_cache_tokstr(r->pool, next, &next);
- switch ((rv = cache_run_open_entity(cache->handle, type, key))) {
+ switch ((rv = cache_run_open_entity(cache->handle, r->pool, type, key))) {
case OK: {
info = &(cache->handle->cache_obj->info);
/* XXX:
h->write_headers(h, r, info);
return APR_SUCCESS;
}
-apr_status_t cache_write_entity_body(cache_handle_t *h, apr_bucket_brigade *b)
+apr_status_t cache_write_entity_body(cache_handle_t *h, request_rec *r, apr_bucket_brigade *b)
{
apr_status_t rv = APR_SUCCESS;
- if (h->write_body(h, b) != OK) {
+ if (h->write_body(h, r, b) != OK) {
}
return rv;
}
return APR_SUCCESS;
}
-apr_status_t cache_read_entity_body(cache_handle_t *h, apr_bucket_brigade *b)
+apr_status_t cache_read_entity_body(cache_handle_t *h, apr_pool_t *p, apr_bucket_brigade *b)
{
- h->read_body(h, b);
+ h->read_body(h, p, b);
return APR_SUCCESS;
}
return r->uri;
}
APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(cache, CACHE, int, create_entity,
- (cache_handle_t *h, const char *type,
+ (cache_handle_t *h, request_rec *r, const char *type,
const char *urlkey, apr_size_t len),
- (h,type,urlkey,len),DECLINED)
+ (h, r, type,urlkey,len),DECLINED)
APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(cache, CACHE, int, open_entity,
- (cache_handle_t *h, const char *type,
- const char *urlkey),(h,type,urlkey),
+ (cache_handle_t *h, apr_pool_t *p, const char *type,
+ const char *urlkey),(h,p,type,urlkey),
DECLINED)
APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(cache, CACHE, int, remove_url,
(const char *type, const char *urlkey),
(type,urlkey),OK,DECLINED)
+
+
return NULL;
}
+
+/*
+ * XXX TODO:
+ * These functions were lifted from mod_proxy
+ * Consider putting them in APR or some other common accessable
+ * location.
+ */
+/*
+ * Converts apr_time_t hex digits to a time integer
+ */
+static apr_time_t ap_cache_hex2msec(const char *x)
+{
+ int i, ch;
+ apr_time_t j;
+ for (i = 0, j = 0; i < sizeof(j) * 2; i++) {
+ ch = x[i];
+ j <<= 4;
+ if (apr_isdigit(ch))
+ j |= ch - '0';
+ else if (apr_isupper(ch))
+ j |= ch - ('A' - 10);
+ else
+ j |= ch - ('a' - 10);
+ }
+ return j;
+}
+
+/*
+ * Converts a time integer to apr_time_t hex digits
+ */
+static void ap_cache_msec2hex(apr_time_t j, char *y)
+{
+ int i, ch;
+
+ for (i = (sizeof(j) * 2)-1; i >= 0; i--) {
+ ch = j & 0xF;
+ j >>= 4;
+ if (ch >= 10)
+ y[i] = ch + ('A' - 10);
+ else
+ y[i] = ch + '0';
+ }
+ y[sizeof(j) * 2] = '\0';
+}
+
+int mkdir_structure(char *file, const char *root)
+{
+
+ /* XXX TODO: Use APR to make a root directory. Do some sanity checking... */
+ return 0;
+}
+
+cache_info * create_cache_el(apr_pool_t *p, cache_handle_t *h, const char *name)
+{
+ cache_info *info = apr_pcalloc(p, sizeof(cache_info));
+ memset(info, '\0', sizeof(cache_info));
+ info->name = (char *)name;
+ return info;
+}
+
+/* These two functions get and put state information into the data
+ * file for an ap_cache_el, this state information will be read
+ * and written transparent to clients of this module
+ */
+int file_cache_read_mydata(apr_file_t *fd, cache_info *info, request_rec *r)
+{
+ apr_status_t rv;
+ char urlbuff[1034];
+ int urllen = sizeof(urlbuff);
+ int offset=0;
+ char * temp;
+
+ if(!info->hdrsfile) {
+ return APR_NOTFOUND;
+ }
+
+ /* read the data from the cache file */
+ /* format
+ * date SP expire SP count CRLF
+ * dates are stored as hex seconds since 1970
+ */
+ rv = apr_file_gets(&urlbuff[0], urllen, fd);
+ if (rv != APR_SUCCESS)
+ {
+ return rv;
+ }
+
+ if ((temp = strchr(&urlbuff[0], '\n')) != NULL) /* trim off new line character */
+ *temp = '\0'; /* overlay it with the null terminator */
+
+ if (!apr_date_checkmask(urlbuff, "&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&"))
+ {
+ return APR_EGENERAL;
+ }
+
+ info->date = ap_cache_hex2msec(urlbuff + offset);
+ offset += (sizeof(info->date)*2) + 1;
+ info->expire = ap_cache_hex2msec(urlbuff + offset);
+ offset += (sizeof(info->expire)*2) + 1;
+ info->version = ap_cache_hex2msec(urlbuff + offset);
+
+ /* check that we have the same URL */
+ rv = apr_file_gets(&urlbuff[0], urllen, fd);
+ if (rv != APR_SUCCESS)
+ {
+ return rv;
+ }
+
+ if ((temp = strchr(&urlbuff[0], '\n')) != NULL) /* trim off new line character */
+ *temp = '\0'; /* overlay it with the null terminator */
+
+ if (strncmp(urlbuff, "X-NAME: ", 7) != 0)
+ {
+ return APR_EGENERAL;
+ }
+ if (strcmp(urlbuff + 8, info->name) != 0)
+ {
+ return APR_EGENERAL;
+ }
+
+ return APR_SUCCESS;
+}
+
+static void cache_hash(const char *it, char *val, int ndepth, int nlength)
+{
+ apr_md5_ctx_t context;
+ unsigned char digest[16];
+ char tmp[22];
+ int i, k, d;
+ unsigned int x;
+ static const char enc_table[64] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_@";
+
+ apr_md5_init(&context);
+ apr_md5_update(&context, (const unsigned char *) it, strlen(it));
+ apr_md5_final(digest, &context);
+
+ /* encode 128 bits as 22 characters, using a modified uuencoding
+ * the encoding is 3 bytes -> 4 characters* i.e. 128 bits is
+ * 5 x 3 bytes + 1 byte -> 5 * 4 characters + 2 characters
+ */
+ for (i = 0, k = 0; i < 15; i += 3) {
+ x = (digest[i] << 16) | (digest[i + 1] << 8) | digest[i + 2];
+ tmp[k++] = enc_table[x >> 18];
+ tmp[k++] = enc_table[(x >> 12) & 0x3f];
+ tmp[k++] = enc_table[(x >> 6) & 0x3f];
+ tmp[k++] = enc_table[x & 0x3f];
+ }
+
+ /* one byte left */
+ x = digest[15];
+ tmp[k++] = enc_table[x >> 2]; /* use up 6 bits */
+ tmp[k++] = enc_table[(x << 4) & 0x3f];
+
+ /* now split into directory levels */
+ for (i = k = d = 0; d < ndepth; ++d) {
+ memcpy(&val[i], &tmp[k], nlength);
+ k += nlength;
+ val[i + nlength] = '/';
+ i += nlength + 1;
+ }
+ memcpy(&val[i], &tmp[k], 22 - k);
+ val[i + 22 - k] = '\0';
+}
+
+static char *generate_name(apr_pool_t *p, cache_handle_t *h, const char *name)
+{
+ char hashfile[66], *filebase;
+ cache_hash(name, hashfile, h->dirlevels, h->dirlength);
+ filebase = apr_pstrcat(p, h->root, "/", hashfile, "%s", NULL);
+ return filebase;
+}
+char *header_file(cache_handle_t *h, apr_pool_t *p, const char *name)
+{
+ return apr_psprintf(p, generate_name(p, h, name), CACHE_HEADER_SUFFIX);
+}
+char *data_file(cache_handle_t *h, apr_pool_t *p, const char *name)
+{
+ return apr_psprintf(p, generate_name(p, h, name), CACHE_DATA_SUFFIX);
+}
* that stored headers and the entity body in seperate files.
*/
cache_read_entity_headers(cache->handle, r);
- cache_read_entity_body(cache->handle, bb);
+ cache_read_entity_body(cache->handle, r->pool, bb);
/* This filter is done once it has served up its content */
ap_remove_output_filter(f);
/* pass the brigades into the cache, then pass them
* up the filter stack
*/
- cache_write_entity_body(cache->handle, in);
+ cache_write_entity_body(cache->handle, r, in);
return ap_pass_brigade(f->next, in);
}
* Write away header information to cache.
*/
cache_write_entity_headers(cache->handle, r, info);
- cache_write_entity_body(cache->handle, in);
+ cache_write_entity_body(cache->handle, r, in);
return ap_pass_brigade(f->next, in);
}
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
+/* Used by mod_disk_cache
+ * XXX: Perhaps these should be moved to a mod_disk_cache header
+ * file?
+ */
+#define CACHE_HEADER_SUFFIX ".header"
+#define CACHE_DATA_SUFFIX ".data"
/* default completion is 60% */
#define DEFAULT_CACHE_COMPLETION (60)
apr_time_t request_time;
apr_time_t response_time;
apr_size_t len;
+
+ /* Field used by mod_disk_cache */
+ char *datafile; /* where the data will go */
+ char *hdrsfile; /* where the hdrs will go */
+ char *name;
+ int version; /* update count of the file */
+ apr_file_t *fd; /* pointer to apr_file_t structure for the data file */
+ apr_off_t file_size; /* File size of the cached data file */
};
/* cache handle information */
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;
+ /* Used by mod_disk_cache: name of the temporary file,
+ * used for cache element creation
+ */
+ char *tempfile;
};
typedef struct cache_handle cache_handle_t;
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);
- int (*write_body)(cache_handle_t *h, apr_bucket_brigade *b);
+ int (*write_body)(cache_handle_t *h, request_rec *r, apr_bucket_brigade *b);
int (*read_headers) (cache_handle_t *h, request_rec *r);
- int (*read_body) (cache_handle_t *h, apr_bucket_brigade *bb);
+ int (*read_body) (cache_handle_t *h, apr_pool_t *p, apr_bucket_brigade *bb);
+
+ /* These fields were added for mod_disk_cache but just
+ * use unnecessary storage in mod_mem_cache.
+ */
+ const char *root; /* the location of the cache directory */
+ int dirlevels; /* Number of levels of subdirectories */
+ int dirlength; /* Length of subdirectory names */
};
/* per request cache information */
/* cache_util.c */
+cache_info *create_cache_el(apr_pool_t *p, cache_handle_t *h, const char *name);
+char *data_file(cache_handle_t *h, apr_pool_t *p, const char *name);
+char *header_file(cache_handle_t *h, apr_pool_t *p, const char *name);
+int file_cache_read_mydata(apr_file_t *fd, cache_info *info, request_rec *r);
+apr_time_t ap_cache_hex2msec(const char *x);
+void ap_cache_msec2hex(apr_time_t j, char *y);
+
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);
const char* cache_create_key( request_rec*r );
apr_status_t cache_write_entity_headers(cache_handle_t *h, request_rec *r, cache_info *info);
-apr_status_t cache_write_entity_body(cache_handle_t *h, apr_bucket_brigade *bb);
+apr_status_t cache_write_entity_body(cache_handle_t *h, request_rec *r, apr_bucket_brigade *bb);
apr_status_t cache_read_entity_headers(cache_handle_t *h, request_rec *r);
-apr_status_t cache_read_entity_body(cache_handle_t *h, apr_bucket_brigade *bb);
+apr_status_t cache_read_entity_body(cache_handle_t *h, apr_pool_t *p, apr_bucket_brigade *bb);
/* hooks */
#endif
APR_DECLARE_EXTERNAL_HOOK(cache, CACHE, int, create_entity,
- (cache_handle_t *h, const char *type,
+ (cache_handle_t *h, request_rec *r, const char *type,
const char *urlkey, apr_size_t len))
APR_DECLARE_EXTERNAL_HOOK(cache, CACHE, int, open_entity,
- (cache_handle_t *h, const char *type,
+ (cache_handle_t *h, apr_pool_t *p, const char *type,
const char *urlkey))
APR_DECLARE_EXTERNAL_HOOK(cache, CACHE, int, remove_url,
(const char *type, const char *urlkey))
/* Forward declarations */
static int remove_entity(cache_handle_t *h);
static int write_headers(cache_handle_t *h, request_rec *r, cache_info *i);
-static int write_body(cache_handle_t *h, apr_bucket_brigade *b);
+static int write_body(cache_handle_t *h, request_rec *r, apr_bucket_brigade *b);
static int read_headers(cache_handle_t *h, request_rec *r);
-static int read_body(cache_handle_t *h, apr_bucket_brigade *bb);
+static int read_body(cache_handle_t *h, apr_pool_t *p, apr_bucket_brigade *bb);
static void cleanup_cache_object(cache_object_t *obj)
{
return sconf;
}
-static int create_entity(cache_handle_t *h,
+static int create_entity(cache_handle_t *h, request_rec *r,
const char *type,
const char *key,
apr_size_t len)
return OK;
}
-static int open_entity(cache_handle_t *h, const char *type, const char *key)
+static int open_entity(cache_handle_t *h, apr_pool_t *p, const char *type, const char *key)
{
cache_object_t *obj;
}
-static int read_body(cache_handle_t *h, apr_bucket_brigade *bb)
+static int read_body(cache_handle_t *h, apr_pool_t *p, apr_bucket_brigade *bb)
{
apr_bucket *b;
mem_cache_object_t *mobj = (mem_cache_object_t*) h->cache_obj->vobj;
return OK;
}
-static int write_body(cache_handle_t *h, apr_bucket_brigade *b)
+static int write_body(cache_handle_t *h, request_rec *r, apr_bucket_brigade *b)
{
apr_status_t rv;
mem_cache_object_t *mobj = (mem_cache_object_t*) h->cache_obj->vobj;