1
/* Licensed to the Apache Software Foundation (ASF) under one or more
2
* contributor license agreements. See the NOTICE file distributed with
3
* this work for additional information regarding copyright ownership.
4
* The ASF licenses this file to You under the Apache License, Version 2.0
5
* (the "License"); you may not use this file except in compliance with
6
* the License. You may obtain a copy of the License at
8
* http://www.apache.org/licenses/LICENSE-2.0
10
* Unless required by applicable law or agreed to in writing, software
11
* distributed under the License is distributed on an "AS IS" BASIS,
12
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
* See the License for the specific language governing permissions and
14
* limitations under the License.
18
* Rules for managing obj->refcount:
19
* refcount should be incremented when an object is placed in the cache. Insertion
20
* of an object into the cache and the refcount increment should happen under
21
* protection of the sconf->lock.
23
* refcount should be decremented when the object is removed from the cache.
24
* Object should be removed from the cache and the refcount decremented while
25
* under protection of the sconf->lock.
27
* refcount should be incremented when an object is retrieved from the cache
28
* by a worker thread. The retrieval/find operation and refcount increment
29
* should occur under protection of the sconf->lock
31
* refcount can be atomically decremented w/o protection of the sconf->lock
34
* Any object whose refcount drops to 0 should be freed/cleaned up. A refcount
35
* of 0 means the object is not in the cache and no worker threads are accessing
39
#include "mod_cache.h"
40
#include "cache_pqueue.h"
41
#include "cache_cache.h"
42
#include "ap_provider.h"
44
#include "apr_thread_mutex.h"
50
#error This module does not currently compile unless you have a thread-capable APR. Sorry!
53
module AP_MODULE_DECLARE_DATA mem_cache_module;
66
typedef struct mem_cache_object {
68
apr_ssize_t num_header_out;
69
apr_ssize_t num_req_hdrs;
70
cache_header_tbl_t *header_out;
71
cache_header_tbl_t *req_hdrs; /* for Vary negotiation */
75
apr_int32_t flags; /* File open flags */
76
long priority; /**< the priority of this entry */
77
long total_refs; /**< total number of references this entry has had */
79
apr_uint32_t pos; /**< the position of this entry in the cache */
84
apr_thread_mutex_t *lock;
85
cache_cache_t *cache_cache;
87
/* Fields set by config directives */
88
apr_size_t min_cache_object_size; /* in bytes */
89
apr_size_t max_cache_object_size; /* in bytes */
90
apr_size_t max_cache_size; /* in bytes */
91
apr_size_t max_object_cnt;
92
cache_pqueue_set_priority cache_remove_algorithm;
94
/* maximum amount of data to buffer on a streamed response where
95
* we haven't yet seen EOS */
96
apr_off_t max_streaming_buffer_size;
98
static mem_cache_conf *sconf;
100
#define DEFAULT_MAX_CACHE_SIZE 100*1024
101
#define DEFAULT_MIN_CACHE_OBJECT_SIZE 0
102
#define DEFAULT_MAX_CACHE_OBJECT_SIZE 10000
103
#define DEFAULT_MAX_OBJECT_CNT 1009
104
#define DEFAULT_MAX_STREAMING_BUFFER_SIZE 100000
105
#define CACHEFILE_LEN 20
107
/* Forward declarations */
108
static int remove_entity(cache_handle_t *h);
109
static apr_status_t store_headers(cache_handle_t *h, request_rec *r, cache_info *i);
110
static apr_status_t store_body(cache_handle_t *h, request_rec *r, apr_bucket_brigade *b);
111
static apr_status_t recall_headers(cache_handle_t *h, request_rec *r);
112
static apr_status_t recall_body(cache_handle_t *h, apr_pool_t *p, apr_bucket_brigade *bb);
114
static void cleanup_cache_object(cache_object_t *obj);
116
static long memcache_get_priority(void*a)
118
cache_object_t *obj = (cache_object_t *)a;
119
mem_cache_object_t *mobj = obj->vobj;
121
return mobj->priority;
124
static void memcache_inc_frequency(void*a)
126
cache_object_t *obj = (cache_object_t *)a;
127
mem_cache_object_t *mobj = obj->vobj;
133
static void memcache_set_pos(void *a, apr_ssize_t pos)
135
cache_object_t *obj = (cache_object_t *)a;
136
mem_cache_object_t *mobj = obj->vobj;
138
apr_atomic_set32(&mobj->pos, pos);
140
static apr_ssize_t memcache_get_pos(void *a)
142
cache_object_t *obj = (cache_object_t *)a;
143
mem_cache_object_t *mobj = obj->vobj;
145
return apr_atomic_read32(&mobj->pos);
148
static apr_size_t memcache_cache_get_size(void*a)
150
cache_object_t *obj = (cache_object_t *)a;
151
mem_cache_object_t *mobj = obj->vobj;
154
/** callback to get the key of a item */
155
static const char* memcache_cache_get_key(void*a)
157
cache_object_t *obj = (cache_object_t *)a;
161
* memcache_cache_free()
162
* memcache_cache_free is a callback that is only invoked by a thread
163
* running in cache_insert(). cache_insert() runs under protection
164
* of sconf->lock. By the time this function has been entered, the cache_object
165
* has been ejected from the cache. decrement the refcount and if the refcount drops
166
* to 0, cleanup the cache object.
168
static void memcache_cache_free(void*a)
170
cache_object_t *obj = (cache_object_t *)a;
172
/* Decrement the refcount to account for the object being ejected
173
* from the cache. If the refcount is 0, free the object.
175
if (!apr_atomic_dec32(&obj->refcount)) {
176
cleanup_cache_object(obj);
180
* functions return a 'negative' score since priority queues
181
* dequeue the object with the highest value first
183
static long memcache_lru_algorithm(long queue_clock, void *a)
185
cache_object_t *obj = (cache_object_t *)a;
186
mem_cache_object_t *mobj = obj->vobj;
187
if (mobj->priority == 0)
188
mobj->priority = queue_clock - mobj->total_refs;
191
* a 'proper' LRU function would just be
192
* mobj->priority = mobj->total_refs;
194
return mobj->priority;
197
static long memcache_gdsf_algorithm(long queue_clock, void *a)
199
cache_object_t *obj = (cache_object_t *)a;
200
mem_cache_object_t *mobj = obj->vobj;
202
if (mobj->priority == 0)
203
mobj->priority = queue_clock -
204
(long)(mobj->total_refs*1000 / mobj->m_len);
206
return mobj->priority;
209
static void cleanup_cache_object(cache_object_t *obj)
211
mem_cache_object_t *mobj = obj->vobj;
214
* We desperately need a more efficient way of allocating objects. We're
215
* making way too many malloc calls to create a fully populated
219
/* Cleanup the cache_object_t */
221
free((void*)obj->key);
226
/* Cleanup the mem_cache_object_t */
228
if (mobj->type == CACHE_TYPE_HEAP && mobj->m) {
231
if (mobj->type == CACHE_TYPE_FILE && mobj->fd) {
233
CloseHandle(mobj->fd);
238
if (mobj->header_out) {
239
if (mobj->header_out[0].hdr)
240
free(mobj->header_out[0].hdr);
241
free(mobj->header_out);
243
if (mobj->req_hdrs) {
244
if (mobj->req_hdrs[0].hdr)
245
free(mobj->req_hdrs[0].hdr);
246
free(mobj->req_hdrs);
251
static apr_status_t decrement_refcount(void *arg)
253
cache_object_t *obj = (cache_object_t *) arg;
255
/* If obj->complete is not set, the cache update failed and the
256
* object needs to be removed from the cache then cleaned up.
257
* The garbage collector may have ejected the object from the
258
* cache already, so make sure it is really still in the cache
259
* before attempting to remove it.
261
if (!obj->complete) {
262
cache_object_t *tobj = NULL;
264
apr_thread_mutex_lock(sconf->lock);
266
tobj = cache_find(sconf->cache_cache, obj->key);
268
cache_remove(sconf->cache_cache, obj);
269
apr_atomic_dec32(&obj->refcount);
272
apr_thread_mutex_unlock(sconf->lock);
276
/* If the refcount drops to 0, cleanup the cache object */
277
if (!apr_atomic_dec32(&obj->refcount)) {
278
cleanup_cache_object(obj);
282
static apr_status_t cleanup_cache_mem(void *sconfv)
285
mem_cache_conf *co = (mem_cache_conf*) sconfv;
290
if (!co->cache_cache) {
295
apr_thread_mutex_lock(sconf->lock);
297
obj = cache_pop(co->cache_cache);
299
/* Iterate over the cache and clean up each unreferenced entry */
300
if (!apr_atomic_dec32(&obj->refcount)) {
301
cleanup_cache_object(obj);
303
obj = cache_pop(co->cache_cache);
306
/* Cache is empty, free the cache table */
307
cache_free(co->cache_cache);
310
apr_thread_mutex_unlock(sconf->lock);
315
* TODO: enable directives to be overridden in various containers
317
static void *create_cache_config(apr_pool_t *p, server_rec *s)
319
sconf = apr_pcalloc(p, sizeof(mem_cache_conf));
321
sconf->min_cache_object_size = DEFAULT_MIN_CACHE_OBJECT_SIZE;
322
sconf->max_cache_object_size = DEFAULT_MAX_CACHE_OBJECT_SIZE;
323
/* Number of objects in the cache */
324
sconf->max_object_cnt = DEFAULT_MAX_OBJECT_CNT;
325
/* Size of the cache in bytes */
326
sconf->max_cache_size = DEFAULT_MAX_CACHE_SIZE;
327
sconf->cache_cache = NULL;
328
sconf->cache_remove_algorithm = memcache_gdsf_algorithm;
329
sconf->max_streaming_buffer_size = DEFAULT_MAX_STREAMING_BUFFER_SIZE;
334
static int create_entity(cache_handle_t *h, cache_type_e type_e,
335
request_rec *r, const char *key, apr_off_t len)
337
cache_object_t *obj, *tmp_obj;
338
mem_cache_object_t *mobj;
342
/* Caching a streaming response. Assume the response is
343
* less than or equal to max_streaming_buffer_size. We will
344
* correct all the cache size counters in store_body once
345
* we know exactly know how much we are caching.
347
len = sconf->max_streaming_buffer_size;
350
/* Note: cache_insert() will automatically garbage collect
351
* objects from the cache if the max_cache_size threshold is
352
* exceeded. This means mod_mem_cache does not need to implement
353
* max_cache_size checks.
355
if (len < sconf->min_cache_object_size ||
356
len > sconf->max_cache_object_size) {
357
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
358
"mem_cache: URL %s failed the size check and will not be cached.",
363
if (type_e == CACHE_TYPE_FILE) {
364
/* CACHE_TYPE_FILE is only valid for local content handled by the
365
* default handler. Need a better way to check if the file is
373
/* Allocate and initialize cache_object_t */
374
obj = calloc(1, sizeof(*obj));
378
key_len = strlen(key) + 1;
379
obj->key = malloc(key_len);
381
cleanup_cache_object(obj);
384
memcpy((void*)obj->key, key, key_len);
386
/* Allocate and init mem_cache_object_t */
387
mobj = calloc(1, sizeof(*mobj));
389
cleanup_cache_object(obj);
393
/* Finish initing the cache object */
394
apr_atomic_set32(&obj->refcount, 1);
395
mobj->total_refs = 1;
398
/* Safe cast: We tested < sconf->max_cache_object_size above */
399
mobj->m_len = (apr_size_t)len;
402
/* Place the cache_object_t into the hash table.
403
* Note: Perhaps we should wait to put the object in the
404
* hash table when the object is complete? I add the object here to
405
* avoid multiple threads attempting to cache the same content only
406
* to discover at the very end that only one of them will succeed.
407
* Furthermore, adding the cache object to the table at the end could
408
* open up a subtle but easy to exploit DoS hole: someone could request
409
* a very large file with multiple requests. Better to detect this here
410
* rather than after the cache object has been completely built and
412
* XXX Need a way to insert into the cache w/o such coarse grained locking
415
apr_thread_mutex_lock(sconf->lock);
417
tmp_obj = (cache_object_t *) cache_find(sconf->cache_cache, key);
420
cache_insert(sconf->cache_cache, obj);
421
/* Add a refcount to account for the reference by the
422
* hashtable in the cache. Refcount should be 2 now, one
423
* for this thread, and one for the cache.
425
apr_atomic_inc32(&obj->refcount);
428
apr_thread_mutex_unlock(sconf->lock);
432
/* This thread collided with another thread loading the same object
433
* into the cache at the same time. Defer to the other thread which
436
cleanup_cache_object(obj);
440
apr_pool_cleanup_register(r->pool, obj, decrement_refcount,
441
apr_pool_cleanup_null);
443
/* Populate the cache handle */
449
static int create_mem_entity(cache_handle_t *h, request_rec *r,
450
const char *key, apr_off_t len)
452
return create_entity(h, CACHE_TYPE_HEAP, r, key, len);
455
static int create_fd_entity(cache_handle_t *h, request_rec *r,
456
const char *key, apr_off_t len)
458
return create_entity(h, CACHE_TYPE_FILE, r, key, len);
461
static int open_entity(cache_handle_t *h, request_rec *r, const char *key)
465
/* Look up entity keyed to 'url' */
467
apr_thread_mutex_lock(sconf->lock);
469
obj = (cache_object_t *) cache_find(sconf->cache_cache, key);
472
request_rec *rmain=r, *rtmp;
473
apr_atomic_inc32(&obj->refcount);
474
/* cache is worried about overall counts, not 'open' ones */
475
cache_update(sconf->cache_cache, obj);
477
/* If this is a subrequest, register the cleanup against
478
* the main request. This will prevent the cache object
479
* from being cleaned up from under the request after the
480
* subrequest is destroyed.
487
apr_pool_cleanup_register(rmain->pool, obj, decrement_refcount,
488
apr_pool_cleanup_null);
496
apr_thread_mutex_unlock(sconf->lock);
503
/* Initialize the cache_handle */
505
h->req_hdrs = NULL; /* Pick these up in recall_headers() */
511
* refcount should be at least 1 upon entry to this function to account
512
* for this thread's reference to the object. If the refcount is 1, then
513
* object has been removed from the cache by another thread and this thread
514
* is the last thread accessing the object.
516
static int remove_entity(cache_handle_t *h)
518
cache_object_t *obj = h->cache_obj;
519
cache_object_t *tobj = NULL;
522
apr_thread_mutex_lock(sconf->lock);
525
/* If the entity is still in the cache, remove it and decrement the
526
* refcount. If the entity is not in the cache, do nothing. In both cases
527
* decrement_refcount called by the last thread referencing the object will
528
* trigger the cleanup.
530
tobj = cache_find(sconf->cache_cache, obj->key);
532
cache_remove(sconf->cache_cache, obj);
533
apr_atomic_dec32(&obj->refcount);
537
apr_thread_mutex_unlock(sconf->lock);
542
static apr_status_t serialize_table(cache_header_tbl_t **obj,
546
const apr_array_header_t *elts_arr = apr_table_elts(table);
547
apr_table_entry_t *elts = (apr_table_entry_t *) elts_arr->elts;
553
*nelts = elts_arr->nelts;
558
*obj = malloc(sizeof(cache_header_tbl_t) * elts_arr->nelts);
562
for (i = 0; i < elts_arr->nelts; ++i) {
563
len += strlen(elts[i].key);
564
len += strlen(elts[i].val);
565
len += 2; /* Extra space for NULL string terminator for key and val */
568
/* Transfer the headers into a contiguous memory block */
575
for (i = 0; i < *nelts; ++i) {
576
(*obj)[i].hdr = &buf[idx];
577
len = strlen(elts[i].key) + 1; /* Include NULL terminator */
578
memcpy(&buf[idx], elts[i].key, len);
581
(*obj)[i].val = &buf[idx];
582
len = strlen(elts[i].val) + 1;
583
memcpy(&buf[idx], elts[i].val, len);
588
static int unserialize_table( cache_header_tbl_t *ctbl,
594
for (i = 0; i < num_headers; ++i) {
595
apr_table_addn(t, ctbl[i].hdr, ctbl[i].val);
600
/* Define request processing hook handlers */
604
static int remove_url(cache_handle_t *h, apr_pool_t *p)
610
apr_thread_mutex_lock(sconf->lock);
615
cache_remove(sconf->cache_cache, obj);
616
/* For performance, cleanup cache object after releasing the lock */
617
cleanup = !apr_atomic_dec32(&obj->refcount);
620
apr_thread_mutex_unlock(sconf->lock);
624
cleanup_cache_object(obj);
630
static apr_status_t recall_headers(cache_handle_t *h, request_rec *r)
633
mem_cache_object_t *mobj = (mem_cache_object_t*) h->cache_obj->vobj;
635
h->req_hdrs = apr_table_make(r->pool, mobj->num_req_hdrs);
636
h->resp_hdrs = apr_table_make(r->pool, mobj->num_header_out);
638
rc = unserialize_table(mobj->req_hdrs, mobj->num_req_hdrs, h->req_hdrs);
639
rc = unserialize_table(mobj->header_out, mobj->num_header_out,
645
static apr_status_t recall_body(cache_handle_t *h, apr_pool_t *p, apr_bucket_brigade *bb)
648
mem_cache_object_t *mobj = (mem_cache_object_t*) h->cache_obj->vobj;
650
if (mobj->type == CACHE_TYPE_FILE) {
651
/* CACHE_TYPE_FILE */
653
apr_os_file_put(&file, &mobj->fd, mobj->flags, p);
654
b = apr_bucket_file_create(file, 0, mobj->m_len, p, bb->bucket_alloc);
657
/* CACHE_TYPE_HEAP */
658
b = apr_bucket_immortal_create(mobj->m, mobj->m_len, bb->bucket_alloc);
660
APR_BRIGADE_INSERT_TAIL(bb, b);
661
b = apr_bucket_eos_create(bb->bucket_alloc);
662
APR_BRIGADE_INSERT_TAIL(bb, b);
668
static apr_status_t store_headers(cache_handle_t *h, request_rec *r, cache_info *info)
670
cache_object_t *obj = h->cache_obj;
671
mem_cache_object_t *mobj = (mem_cache_object_t*) obj->vobj;
673
apr_table_t *headers_out;
676
* The cache needs to keep track of the following information:
677
* - Date, LastMod, Version, ReqTime, RespTime, ContentLength
678
* - The original request headers (for Vary)
679
* - The original response headers (for returning with a cached response)
680
* - The body of the message
682
rc = serialize_table(&mobj->req_hdrs,
685
if (rc != APR_SUCCESS) {
689
/* Precompute how much storage we need to hold the headers */
690
headers_out = ap_cache_cacheable_hdrs_out(r->pool, r->headers_out,
693
/* If not set in headers_out, set Content-Type */
694
if (!apr_table_get(headers_out, "Content-Type")
695
&& r->content_type) {
696
apr_table_setn(headers_out, "Content-Type",
697
ap_make_content_type(r, r->content_type));
700
headers_out = apr_table_overlay(r->pool, headers_out, r->err_headers_out);
702
rc = serialize_table(&mobj->header_out, &mobj->num_header_out,
704
if (rc != APR_SUCCESS) {
708
/* Init the info struct */
709
obj->info.status = info->status;
711
obj->info.date = info->date;
713
if (info->response_time) {
714
obj->info.response_time = info->response_time;
716
if (info->request_time) {
717
obj->info.request_time = info->request_time;
720
obj->info.expire = info->expire;
726
static apr_status_t store_body(cache_handle_t *h, request_rec *r, apr_bucket_brigade *b)
729
cache_object_t *obj = h->cache_obj;
730
cache_object_t *tobj = NULL;
731
mem_cache_object_t *mobj = (mem_cache_object_t*) obj->vobj;
732
apr_read_type_e eblock = APR_BLOCK_READ;
737
if (mobj->type == CACHE_TYPE_FILE) {
738
apr_file_t *file = NULL;
742
/* We can cache an open file descriptor if:
743
* - the brigade contains one and only one file_bucket &&
744
* - the brigade is complete &&
745
* - the file_bucket is the last data bucket in the brigade
747
for (e = APR_BRIGADE_FIRST(b);
748
e != APR_BRIGADE_SENTINEL(b);
749
e = APR_BUCKET_NEXT(e))
751
if (APR_BUCKET_IS_EOS(e)) {
754
else if (APR_BUCKET_IS_FILE(e)) {
755
apr_bucket_file *a = e->data;
763
if (fd == 1 && !other && eos) {
766
/* Open a new XTHREAD handle to the file */
767
apr_file_name_get(&name, file);
768
mobj->flags = ((APR_SENDFILE_ENABLED & apr_file_flags_get(file))
769
| APR_READ | APR_BINARY | APR_XTHREAD | APR_FILE_NOCLEANUP);
770
rv = apr_file_open(&tmpfile, name, mobj->flags,
771
APR_OS_DEFAULT, r->pool);
772
if (rv != APR_SUCCESS) {
775
apr_file_inherit_unset(tmpfile);
776
apr_os_file_get(&(mobj->fd), tmpfile);
778
/* Open for business */
779
ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
780
"mem_cache: Cached file: %s with key: %s", name, obj->key);
785
/* Content not suitable for fd caching. Cache in-memory instead. */
786
mobj->type = CACHE_TYPE_HEAP;
790
* FD cacheing is not enabled or the content was not
791
* suitable for fd caching.
793
if (mobj->m == NULL) {
794
mobj->m = malloc(mobj->m_len);
795
if (mobj->m == NULL) {
800
cur = (char*) mobj->m + obj->count;
802
/* Iterate accross the brigade and populate the cache storage */
803
for (e = APR_BRIGADE_FIRST(b);
804
e != APR_BRIGADE_SENTINEL(b);
805
e = APR_BUCKET_NEXT(e))
810
if (APR_BUCKET_IS_EOS(e)) {
811
if (mobj->m_len > obj->count) {
812
/* Caching a streamed response. Reallocate a buffer of the
813
* correct size and copy the streamed response into that
815
char *buf = malloc(obj->count);
819
memcpy(buf, mobj->m, obj->count);
823
/* Now comes the crufty part... there is no way to tell the
824
* cache that the size of the object has changed. We need
825
* to remove the object, update the size and re-add the
826
* object, all under protection of the lock.
829
apr_thread_mutex_lock(sconf->lock);
831
/* Has the object been ejected from the cache?
833
tobj = (cache_object_t *) cache_find(sconf->cache_cache, obj->key);
835
/* Object is still in the cache, remove it, update the len field then
836
* replace it under protection of sconf->lock.
838
cache_remove(sconf->cache_cache, obj);
839
/* For illustration, cache no longer has reference to the object
840
* so decrement the refcount
841
* apr_atomic_dec32(&obj->refcount);
843
mobj->m_len = obj->count;
845
cache_insert(sconf->cache_cache, obj);
846
/* For illustration, cache now has reference to the object, so
847
* increment the refcount
848
* apr_atomic_inc32(&obj->refcount);
852
/* Different object with the same key found in the cache. Doing nothing
853
* here will cause the object refcount to drop to 0 in decrement_refcount
854
* and the object will be cleaned up.
858
/* Object has been ejected from the cache, add it back to the cache */
859
mobj->m_len = obj->count;
860
cache_insert(sconf->cache_cache, obj);
861
apr_atomic_inc32(&obj->refcount);
865
apr_thread_mutex_unlock(sconf->lock);
868
/* Open for business */
869
ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
870
"mem_cache: Cached url: %s", obj->key);
874
rv = apr_bucket_read(e, &s, &len, eblock);
875
if (rv != APR_SUCCESS) {
879
/* Check for buffer overflow */
880
if ((obj->count + len) > mobj->m_len) {
889
/* This should not fail, but if it does, we are in BIG trouble
890
* cause we just stomped all over the heap.
892
AP_DEBUG_ASSERT(obj->count <= mobj->m_len);
897
* Configuration and start-up
899
static int mem_cache_post_config(apr_pool_t *p, apr_pool_t *plog,
900
apr_pool_t *ptemp, server_rec *s)
904
/* Sanity check the cache configuration */
905
if (sconf->min_cache_object_size >= sconf->max_cache_object_size) {
906
ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s,
907
"MCacheMaxObjectSize must be greater than MCacheMinObjectSize");
910
if (sconf->max_cache_object_size >= sconf->max_cache_size) {
911
ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s,
912
"MCacheSize must be greater than MCacheMaxObjectSize");
915
if (sconf->max_streaming_buffer_size > sconf->max_cache_object_size) {
916
/* Issue a notice only if something other than the default config
918
if (sconf->max_streaming_buffer_size != DEFAULT_MAX_STREAMING_BUFFER_SIZE &&
919
sconf->max_cache_object_size != DEFAULT_MAX_CACHE_OBJECT_SIZE) {
920
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s,
921
"MCacheMaxStreamingBuffer must be less than or equal to MCacheMaxObjectSize. "
922
"Resetting MCacheMaxStreamingBuffer to MCacheMaxObjectSize.");
924
sconf->max_streaming_buffer_size = sconf->max_cache_object_size;
926
if (sconf->max_streaming_buffer_size < sconf->min_cache_object_size) {
927
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
928
"MCacheMaxStreamingBuffer must be greater than or equal to MCacheMinObjectSize. "
929
"Resetting MCacheMaxStreamingBuffer to MCacheMinObjectSize.");
930
sconf->max_streaming_buffer_size = sconf->min_cache_object_size;
932
ap_mpm_query(AP_MPMQ_IS_THREADED, &threaded_mpm);
934
apr_thread_mutex_create(&sconf->lock, APR_THREAD_MUTEX_DEFAULT, p);
937
sconf->cache_cache = cache_init(sconf->max_object_cnt,
938
sconf->max_cache_size,
939
memcache_get_priority,
940
sconf->cache_remove_algorithm,
943
memcache_inc_frequency,
944
memcache_cache_get_size,
945
memcache_cache_get_key,
946
memcache_cache_free);
947
apr_pool_cleanup_register(p, sconf, cleanup_cache_mem, apr_pool_cleanup_null);
949
if (sconf->cache_cache)
957
*set_max_cache_size(cmd_parms *parms, void *in_struct_ptr, const char *arg)
961
if (sscanf(arg, "%" APR_SIZE_T_FMT, &val) != 1) {
962
return "MCacheSize argument must be an integer representing the max cache size in KBytes.";
964
sconf->max_cache_size = val*1024;
968
*set_min_cache_object_size(cmd_parms *parms, void *in_struct_ptr, const char *arg)
972
if (sscanf(arg, "%" APR_SIZE_T_FMT, &val) != 1) {
973
return "MCacheMinObjectSize value must be an integer (bytes)";
975
sconf->min_cache_object_size = val;
979
*set_max_cache_object_size(cmd_parms *parms, void *in_struct_ptr, const char *arg)
983
if (sscanf(arg, "%" APR_SIZE_T_FMT, &val) != 1) {
984
return "MCacheMaxObjectSize value must be an integer (bytes)";
986
sconf->max_cache_object_size = val;
990
*set_max_object_count(cmd_parms *parms, void *in_struct_ptr, const char *arg)
994
if (sscanf(arg, "%" APR_SIZE_T_FMT, &val) != 1) {
995
return "MCacheMaxObjectCount value must be an integer";
997
sconf->max_object_cnt = val;
1002
*set_cache_removal_algorithm(cmd_parms *parms, void *name, const char *arg)
1004
if (strcasecmp("LRU", arg)) {
1005
sconf->cache_remove_algorithm = memcache_lru_algorithm;
1008
if (strcasecmp("GDSF", arg)) {
1009
sconf->cache_remove_algorithm = memcache_gdsf_algorithm;
1012
return "currently implemented algorithms are LRU and GDSF";
1018
static const char *set_max_streaming_buffer(cmd_parms *parms, void *dummy,
1022
if (apr_strtoff(&sconf->max_streaming_buffer_size, arg, &err, 10) || *err) {
1023
return "MCacheMaxStreamingBuffer value must be a number";
1029
static const command_rec cache_cmds[] =
1031
AP_INIT_TAKE1("MCacheSize", set_max_cache_size, NULL, RSRC_CONF,
1032
"The maximum amount of memory used by the cache in KBytes"),
1033
AP_INIT_TAKE1("MCacheMaxObjectCount", set_max_object_count, NULL, RSRC_CONF,
1034
"The maximum number of objects allowed to be placed in the cache"),
1035
AP_INIT_TAKE1("MCacheMinObjectSize", set_min_cache_object_size, NULL, RSRC_CONF,
1036
"The minimum size (in bytes) of an object to be placed in the cache"),
1037
AP_INIT_TAKE1("MCacheMaxObjectSize", set_max_cache_object_size, NULL, RSRC_CONF,
1038
"The maximum size (in bytes) of an object to be placed in the cache"),
1039
AP_INIT_TAKE1("MCacheRemovalAlgorithm", set_cache_removal_algorithm, NULL, RSRC_CONF,
1040
"The algorithm used to remove entries from the cache (default: GDSF)"),
1041
AP_INIT_TAKE1("MCacheMaxStreamingBuffer", set_max_streaming_buffer, NULL, RSRC_CONF,
1042
"Maximum number of bytes of content to buffer for a streamed response"),
1046
static const cache_provider cache_mem_provider =
1058
static const cache_provider cache_fd_provider =
1070
static void register_hooks(apr_pool_t *p)
1072
ap_hook_post_config(mem_cache_post_config, NULL, NULL, APR_HOOK_MIDDLE);
1073
/* cache initializer */
1074
/* cache_hook_init(cache_mem_init, NULL, NULL, APR_HOOK_MIDDLE); */
1076
cache_hook_create_entity(create_entity, NULL, NULL, APR_HOOK_MIDDLE);
1077
cache_hook_open_entity(open_entity, NULL, NULL, APR_HOOK_MIDDLE);
1078
cache_hook_remove_url(remove_url, NULL, NULL, APR_HOOK_MIDDLE);
1080
ap_register_provider(p, CACHE_PROVIDER_GROUP, "mem", "0",
1081
&cache_mem_provider);
1082
ap_register_provider(p, CACHE_PROVIDER_GROUP, "fd", "0",
1083
&cache_fd_provider);
1086
module AP_MODULE_DECLARE_DATA mem_cache_module =
1088
STANDARD20_MODULE_STUFF,
1089
NULL, /* create per-directory config structure */
1090
NULL, /* merge per-directory config structures */
1091
create_cache_config, /* create per-server config structure */
1092
NULL, /* merge per-server config structures */
1093
cache_cmds, /* command apr_table_t */