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_err_header_out;
70 apr_ssize_t num_subprocess_env;
71 apr_ssize_t num_notes;
72 apr_ssize_t num_req_hdrs;
73 cache_header_tbl_t *header_out;
74 cache_header_tbl_t *err_header_out;
75 cache_header_tbl_t *subprocess_env;
76 cache_header_tbl_t *notes;
77 cache_header_tbl_t *req_hdrs; /* for Vary negotiation */
81 apr_int32_t flags; /* File open flags */
82 long priority; /**< the priority of this entry */
83 long total_refs; /**< total number of references this entry has had */
85 apr_uint32_t pos; /**< the position of this entry in the cache */
90 apr_thread_mutex_t *lock;
91 cache_cache_t *cache_cache;
93 /* Fields set by config directives */
94 apr_size_t min_cache_object_size; /* in bytes */
95 apr_size_t max_cache_object_size; /* in bytes */
96 apr_size_t max_cache_size; /* in bytes */
97 apr_size_t max_object_cnt;
98 cache_pqueue_set_priority cache_remove_algorithm;
100 /* maximum amount of data to buffer on a streamed response where
101 * we haven't yet seen EOS */
102 apr_off_t max_streaming_buffer_size;
104 static mem_cache_conf *sconf;
106 #define DEFAULT_MAX_CACHE_SIZE 100*1024
107 #define DEFAULT_MIN_CACHE_OBJECT_SIZE 0
108 #define DEFAULT_MAX_CACHE_OBJECT_SIZE 10000
109 #define DEFAULT_MAX_OBJECT_CNT 1009
110 #define DEFAULT_MAX_STREAMING_BUFFER_SIZE 100000
111 #define CACHEFILE_LEN 20
113 /* Forward declarations */
114 static int remove_entity(cache_handle_t *h);
115 static apr_status_t store_headers(cache_handle_t *h, request_rec *r, cache_info *i);
116 static apr_status_t store_body(cache_handle_t *h, request_rec *r, apr_bucket_brigade *b);
117 static apr_status_t recall_headers(cache_handle_t *h, request_rec *r);
118 static apr_status_t recall_body(cache_handle_t *h, apr_pool_t *p, apr_bucket_brigade *bb);
120 static void cleanup_cache_object(cache_object_t *obj);
122 static long memcache_get_priority(void*a)
124 cache_object_t *obj = (cache_object_t *)a;
125 mem_cache_object_t *mobj = obj->vobj;
127 return mobj->priority;
130 static void memcache_inc_frequency(void*a)
132 cache_object_t *obj = (cache_object_t *)a;
133 mem_cache_object_t *mobj = obj->vobj;
139 static void memcache_set_pos(void *a, apr_ssize_t pos)
141 cache_object_t *obj = (cache_object_t *)a;
142 mem_cache_object_t *mobj = obj->vobj;
144 apr_atomic_set(&mobj->pos, pos);
146 static apr_ssize_t memcache_get_pos(void *a)
148 cache_object_t *obj = (cache_object_t *)a;
149 mem_cache_object_t *mobj = obj->vobj;
151 return apr_atomic_read(&mobj->pos);
154 static apr_size_t memcache_cache_get_size(void*a)
156 cache_object_t *obj = (cache_object_t *)a;
157 mem_cache_object_t *mobj = obj->vobj;
160 /** callback to get the key of a item */
161 static const char* memcache_cache_get_key(void*a)
163 cache_object_t *obj = (cache_object_t *)a;
167 * memcache_cache_free()
168 * memcache_cache_free is a callback that is only invoked by a thread
169 * running in cache_insert(). cache_insert() runs under protection
170 * of sconf->lock. By the time this function has been entered, the cache_object
171 * has been ejected from the cache. decrement the refcount and if the refcount drops
172 * to 0, cleanup the cache object.
174 static void memcache_cache_free(void*a)
176 cache_object_t *obj = (cache_object_t *)a;
178 /* Decrement the refcount to account for the object being ejected
179 * from the cache. If the refcount is 0, free the object.
181 if (!apr_atomic_dec(&obj->refcount)) {
182 cleanup_cache_object(obj);
186 * functions return a 'negative' score since priority queues
187 * dequeue the object with the highest value first
189 static long memcache_lru_algorithm(long queue_clock, void *a)
191 cache_object_t *obj = (cache_object_t *)a;
192 mem_cache_object_t *mobj = obj->vobj;
193 if (mobj->priority == 0)
194 mobj->priority = queue_clock - mobj->total_refs;
197 * a 'proper' LRU function would just be
198 * mobj->priority = mobj->total_refs;
200 return mobj->priority;
203 static long memcache_gdsf_algorithm(long queue_clock, void *a)
205 cache_object_t *obj = (cache_object_t *)a;
206 mem_cache_object_t *mobj = obj->vobj;
208 if (mobj->priority == 0)
209 mobj->priority = queue_clock -
210 (long)(mobj->total_refs*1000 / mobj->m_len);
212 return mobj->priority;
215 static void cleanup_cache_object(cache_object_t *obj)
217 mem_cache_object_t *mobj = obj->vobj;
220 * We desperately need a more efficient way of allocating objects. We're
221 * making way too many malloc calls to create a fully populated
225 /* Cleanup the cache_object_t */
229 if (obj->info.content_type) {
230 free(obj->info.content_type);
232 if (obj->info.etag) {
233 free(obj->info.etag);
235 if (obj->info.lastmods) {
236 free(obj->info.lastmods);
238 if (obj->info.filename) {
239 free(obj->info.filename);
244 /* Cleanup the mem_cache_object_t */
246 if (mobj->type == CACHE_TYPE_HEAP && mobj->m) {
249 if (mobj->type == CACHE_TYPE_FILE && mobj->fd) {
251 CloseHandle(mobj->fd);
256 if (mobj->header_out) {
257 if (mobj->header_out[0].hdr)
258 free(mobj->header_out[0].hdr);
259 free(mobj->header_out);
261 if (mobj->err_header_out) {
262 if (mobj->err_header_out[0].hdr)
263 free(mobj->err_header_out[0].hdr);
264 free(mobj->err_header_out);
266 if (mobj->subprocess_env) {
267 if (mobj->subprocess_env[0].hdr)
268 free(mobj->subprocess_env[0].hdr);
269 free(mobj->subprocess_env);
272 if (mobj->notes[0].hdr)
273 free(mobj->notes[0].hdr);
276 if (mobj->req_hdrs) {
277 if (mobj->req_hdrs[0].hdr)
278 free(mobj->req_hdrs[0].hdr);
279 free(mobj->req_hdrs);
284 static apr_status_t decrement_refcount(void *arg)
286 cache_object_t *obj = (cache_object_t *) arg;
288 /* If obj->complete is not set, the cache update failed and the
289 * object needs to be removed from the cache then cleaned up.
290 * The garbage collector may have ejected the object from the
291 * cache already, so make sure it is really still in the cache
292 * before attempting to remove it.
294 if (!obj->complete) {
295 cache_object_t *tobj = NULL;
297 apr_thread_mutex_lock(sconf->lock);
299 tobj = cache_find(sconf->cache_cache, obj->key);
301 cache_remove(sconf->cache_cache, obj);
302 apr_atomic_dec(&obj->refcount);
305 apr_thread_mutex_unlock(sconf->lock);
309 /* If the refcount drops to 0, cleanup the cache object */
310 if (!apr_atomic_dec(&obj->refcount)) {
311 cleanup_cache_object(obj);
315 static apr_status_t cleanup_cache_mem(void *sconfv)
318 mem_cache_conf *co = (mem_cache_conf*) sconfv;
323 if (!co->cache_cache) {
328 apr_thread_mutex_lock(sconf->lock);
330 obj = cache_pop(co->cache_cache);
332 /* Iterate over the cache and clean up each unreferenced entry */
333 if (!apr_atomic_dec(&obj->refcount)) {
334 cleanup_cache_object(obj);
336 obj = cache_pop(co->cache_cache);
339 /* Cache is empty, free the cache table */
340 cache_free(co->cache_cache);
343 apr_thread_mutex_unlock(sconf->lock);
348 * TODO: enable directives to be overridden in various containers
350 static void *create_cache_config(apr_pool_t *p, server_rec *s)
352 sconf = apr_pcalloc(p, sizeof(mem_cache_conf));
354 sconf->min_cache_object_size = DEFAULT_MIN_CACHE_OBJECT_SIZE;
355 sconf->max_cache_object_size = DEFAULT_MAX_CACHE_OBJECT_SIZE;
356 /* Number of objects in the cache */
357 sconf->max_object_cnt = DEFAULT_MAX_OBJECT_CNT;
358 /* Size of the cache in bytes */
359 sconf->max_cache_size = DEFAULT_MAX_CACHE_SIZE;
360 sconf->cache_cache = NULL;
361 sconf->cache_remove_algorithm = memcache_gdsf_algorithm;
362 sconf->max_streaming_buffer_size = DEFAULT_MAX_STREAMING_BUFFER_SIZE;
367 static int create_entity(cache_handle_t *h, cache_type_e type_e,
368 request_rec *r, const char *key, apr_off_t len)
370 cache_object_t *obj, *tmp_obj;
371 mem_cache_object_t *mobj;
375 /* Caching a streaming response. Assume the response is
376 * less than or equal to max_streaming_buffer_size. We will
377 * correct all the cache size counters in store_body once
378 * we know exactly know how much we are caching.
380 len = sconf->max_streaming_buffer_size;
383 /* Note: cache_insert() will automatically garbage collect
384 * objects from the cache if the max_cache_size threshold is
385 * exceeded. This means mod_mem_cache does not need to implement
386 * max_cache_size checks.
388 if (len < sconf->min_cache_object_size ||
389 len > sconf->max_cache_object_size) {
390 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
391 "mem_cache: URL %s failed the size check and will not be cached.",
396 if (type_e == CACHE_TYPE_FILE) {
397 /* CACHE_TYPE_FILE is only valid for local content handled by the
398 * default handler. Need a better way to check if the file is
406 /* Allocate and initialize cache_object_t */
407 obj = calloc(1, sizeof(*obj));
411 key_len = strlen(key) + 1;
412 obj->key = malloc(key_len);
414 cleanup_cache_object(obj);
417 memcpy(obj->key, key, key_len);
418 /* Safe cast: We tested < sconf->max_cache_object_size above */
419 obj->info.len = (apr_size_t)len;
421 /* Allocate and init mem_cache_object_t */
422 mobj = calloc(1, sizeof(*mobj));
424 cleanup_cache_object(obj);
428 /* Finish initing the cache object */
429 apr_atomic_set(&obj->refcount, 1);
430 mobj->total_refs = 1;
433 /* Safe cast: We tested < sconf->max_cache_object_size above */
434 mobj->m_len = (apr_size_t)len;
437 /* Place the cache_object_t into the hash table.
438 * Note: Perhaps we should wait to put the object in the
439 * hash table when the object is complete? I add the object here to
440 * avoid multiple threads attempting to cache the same content only
441 * to discover at the very end that only one of them will succeed.
442 * Furthermore, adding the cache object to the table at the end could
443 * open up a subtle but easy to exploit DoS hole: someone could request
444 * a very large file with multiple requests. Better to detect this here
445 * rather than after the cache object has been completely built and
447 * XXX Need a way to insert into the cache w/o such coarse grained locking
450 apr_thread_mutex_lock(sconf->lock);
452 tmp_obj = (cache_object_t *) cache_find(sconf->cache_cache, key);
455 cache_insert(sconf->cache_cache, obj);
456 /* Add a refcount to account for the reference by the
457 * hashtable in the cache. Refcount should be 2 now, one
458 * for this thread, and one for the cache.
460 apr_atomic_inc(&obj->refcount);
463 apr_thread_mutex_unlock(sconf->lock);
467 /* This thread collided with another thread loading the same object
468 * into the cache at the same time. Defer to the other thread which
471 cleanup_cache_object(obj);
475 apr_pool_cleanup_register(r->pool, obj, decrement_refcount,
476 apr_pool_cleanup_null);
478 /* Populate the cache handle */
484 static int create_mem_entity(cache_handle_t *h, request_rec *r,
485 const char *key, apr_off_t len)
487 return create_entity(h, CACHE_TYPE_HEAP, r, key, len);
490 static int create_fd_entity(cache_handle_t *h, request_rec *r,
491 const char *key, apr_off_t len)
493 return create_entity(h, CACHE_TYPE_FILE, r, key, len);
496 static int open_entity(cache_handle_t *h, request_rec *r, const char *key)
500 /* Look up entity keyed to 'url' */
502 apr_thread_mutex_lock(sconf->lock);
504 obj = (cache_object_t *) cache_find(sconf->cache_cache, key);
507 request_rec *rmain=r, *rtmp;
508 apr_atomic_inc(&obj->refcount);
509 /* cache is worried about overall counts, not 'open' ones */
510 cache_update(sconf->cache_cache, obj);
512 /* If this is a subrequest, register the cleanup against
513 * the main request. This will prevent the cache object
514 * from being cleaned up from under the request after the
515 * subrequest is destroyed.
522 apr_pool_cleanup_register(rmain->pool, obj, decrement_refcount,
523 apr_pool_cleanup_null);
531 apr_thread_mutex_unlock(sconf->lock);
538 /* Initialize the cache_handle */
540 h->req_hdrs = NULL; /* Pick these up in recall_headers() */
546 * refcount should be at least 1 upon entry to this function to account
547 * for this thread's reference to the object. If the refcount is 1, then
548 * object has been removed from the cache by another thread and this thread
549 * is the last thread accessing the object.
551 static int remove_entity(cache_handle_t *h)
553 cache_object_t *obj = h->cache_obj;
554 cache_object_t *tobj = NULL;
557 apr_thread_mutex_lock(sconf->lock);
560 /* If the entity is still in the cache, remove it and decrement the
561 * refcount. If the entity is not in the cache, do nothing. In both cases
562 * decrement_refcount called by the last thread referencing the object will
563 * trigger the cleanup.
565 tobj = cache_find(sconf->cache_cache, obj->key);
567 cache_remove(sconf->cache_cache, obj);
568 apr_atomic_dec(&obj->refcount);
572 apr_thread_mutex_unlock(sconf->lock);
577 static apr_status_t serialize_table(cache_header_tbl_t **obj,
581 const apr_array_header_t *elts_arr = apr_table_elts(table);
582 apr_table_entry_t *elts = (apr_table_entry_t *) elts_arr->elts;
588 *nelts = elts_arr->nelts;
593 *obj = malloc(sizeof(cache_header_tbl_t) * elts_arr->nelts);
597 for (i = 0; i < elts_arr->nelts; ++i) {
598 len += strlen(elts[i].key);
599 len += strlen(elts[i].val);
600 len += 2; /* Extra space for NULL string terminator for key and val */
603 /* Transfer the headers into a contiguous memory block */
610 for (i = 0; i < *nelts; ++i) {
611 (*obj)[i].hdr = &buf[idx];
612 len = strlen(elts[i].key) + 1; /* Include NULL terminator */
613 memcpy(&buf[idx], elts[i].key, len);
616 (*obj)[i].val = &buf[idx];
617 len = strlen(elts[i].val) + 1;
618 memcpy(&buf[idx], elts[i].val, len);
623 static int unserialize_table( cache_header_tbl_t *ctbl,
629 for (i = 0; i < num_headers; ++i) {
630 apr_table_addn(t, ctbl[i].hdr, ctbl[i].val);
635 /* Define request processing hook handlers */
639 static int remove_url(const char *key)
645 apr_thread_mutex_lock(sconf->lock);
648 obj = cache_find(sconf->cache_cache, key);
650 cache_remove(sconf->cache_cache, obj);
651 /* For performance, cleanup cache object after releasing the lock */
652 cleanup = !apr_atomic_dec(&obj->refcount);
655 apr_thread_mutex_unlock(sconf->lock);
659 cleanup_cache_object(obj);
665 static apr_status_t recall_headers(cache_handle_t *h, request_rec *r)
668 mem_cache_object_t *mobj = (mem_cache_object_t*) h->cache_obj->vobj;
670 h->req_hdrs = apr_table_make(r->pool, mobj->num_req_hdrs);
671 h->resp_hdrs = apr_table_make(r->pool, mobj->num_header_out);
672 h->resp_err_hdrs = apr_table_make(r->pool, mobj->num_err_header_out);
673 /* ### FIXME: These two items should not be saved. */
674 r->subprocess_env = apr_table_make(r->pool, mobj->num_subprocess_env);
675 r->notes = apr_table_make(r->pool, mobj->num_notes);
677 rc = unserialize_table(mobj->req_hdrs,
680 rc = unserialize_table( mobj->header_out,
681 mobj->num_header_out,
683 rc = unserialize_table( mobj->err_header_out,
684 mobj->num_err_header_out,
686 rc = unserialize_table( mobj->subprocess_env,
687 mobj->num_subprocess_env,
689 rc = unserialize_table( mobj->notes,
693 /* Content-Type: header may not be set if content is local since
694 * CACHE_IN runs before header filters....
696 h->content_type = h->cache_obj->info.content_type;
697 h->status = h->cache_obj->info.status;
702 static apr_status_t recall_body(cache_handle_t *h, apr_pool_t *p, apr_bucket_brigade *bb)
705 mem_cache_object_t *mobj = (mem_cache_object_t*) h->cache_obj->vobj;
707 if (mobj->type == CACHE_TYPE_FILE) {
708 /* CACHE_TYPE_FILE */
710 apr_os_file_put(&file, &mobj->fd, mobj->flags, p);
711 b = apr_bucket_file_create(file, 0, mobj->m_len, p, bb->bucket_alloc);
714 /* CACHE_TYPE_HEAP */
715 b = apr_bucket_immortal_create(mobj->m, mobj->m_len, bb->bucket_alloc);
717 APR_BRIGADE_INSERT_TAIL(bb, b);
718 b = apr_bucket_eos_create(bb->bucket_alloc);
719 APR_BRIGADE_INSERT_TAIL(bb, b);
725 static apr_status_t store_headers(cache_handle_t *h, request_rec *r, cache_info *info)
727 cache_object_t *obj = h->cache_obj;
728 mem_cache_object_t *mobj = (mem_cache_object_t*) obj->vobj;
732 * The cache needs to keep track of the following information:
733 * - Date, LastMod, Version, ReqTime, RespTime, ContentLength
734 * - The original request headers (for Vary)
735 * - The original response headers (for returning with a cached response)
736 * - The body of the message
738 rc = serialize_table(&mobj->req_hdrs,
741 if (rc != APR_SUCCESS) {
745 /* Precompute how much storage we need to hold the headers */
746 rc = serialize_table(&mobj->header_out,
747 &mobj->num_header_out,
748 ap_cache_cacheable_hdrs_out(r->pool, r->headers_out,
750 if (rc != APR_SUCCESS) {
753 rc = serialize_table(&mobj->err_header_out,
754 &mobj->num_err_header_out,
755 ap_cache_cacheable_hdrs_out(r->pool,
758 if (rc != APR_SUCCESS) {
761 rc = serialize_table(&mobj->subprocess_env,
762 &mobj->num_subprocess_env,
764 if (rc != APR_SUCCESS) {
768 rc = serialize_table(&mobj->notes, &mobj->num_notes, r->notes);
769 if (rc != APR_SUCCESS) {
773 /* Init the info struct */
774 obj->info.status = info->status;
776 obj->info.date = info->date;
779 obj->info.lastmod = info->lastmod;
781 if (info->response_time) {
782 obj->info.response_time = info->response_time;
784 if (info->request_time) {
785 obj->info.request_time = info->request_time;
788 obj->info.expire = info->expire;
790 if (info->content_type) {
791 apr_size_t len = strlen(info->content_type) + 1;
792 obj->info.content_type = (char*) malloc(len);
793 if (!obj->info.content_type) {
796 memcpy(obj->info.content_type, info->content_type, len);
799 apr_size_t len = strlen(info->etag) + 1;
800 obj->info.etag = (char*) malloc(len);
801 if (!obj->info.etag) {
804 memcpy(obj->info.etag, info->etag, len);
806 if (info->lastmods) {
807 apr_size_t len = strlen(info->lastmods) + 1;
808 obj->info.lastmods = (char*) malloc(len);
809 if (!obj->info.lastmods) {
812 memcpy(obj->info.lastmods, info->lastmods, len);
814 if ( info->filename) {
815 apr_size_t len = strlen(info->filename) + 1;
816 obj->info.filename = (char*) malloc(len);
817 if (!obj->info.filename ) {
820 memcpy(obj->info.filename, info->filename, len);
826 static apr_status_t store_body(cache_handle_t *h, request_rec *r, apr_bucket_brigade *b)
829 cache_object_t *obj = h->cache_obj;
830 cache_object_t *tobj = NULL;
831 mem_cache_object_t *mobj = (mem_cache_object_t*) obj->vobj;
832 apr_read_type_e eblock = APR_BLOCK_READ;
837 if (mobj->type == CACHE_TYPE_FILE) {
838 apr_file_t *file = NULL;
842 /* We can cache an open file descriptor if:
843 * - the brigade contains one and only one file_bucket &&
844 * - the brigade is complete &&
845 * - the file_bucket is the last data bucket in the brigade
847 for (e = APR_BRIGADE_FIRST(b);
848 e != APR_BRIGADE_SENTINEL(b);
849 e = APR_BUCKET_NEXT(e))
851 if (APR_BUCKET_IS_EOS(e)) {
854 else if (APR_BUCKET_IS_FILE(e)) {
855 apr_bucket_file *a = e->data;
863 if (fd == 1 && !other && eos) {
866 /* Open a new XTHREAD handle to the file */
867 apr_file_name_get(&name, file);
868 mobj->flags = ((APR_SENDFILE_ENABLED & apr_file_flags_get(file))
869 | APR_READ | APR_BINARY | APR_XTHREAD | APR_FILE_NOCLEANUP);
870 rv = apr_file_open(&tmpfile, name, mobj->flags,
871 APR_OS_DEFAULT, r->pool);
872 if (rv != APR_SUCCESS) {
875 apr_file_inherit_unset(tmpfile);
876 apr_os_file_get(&(mobj->fd), tmpfile);
878 /* Open for business */
879 ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
880 "mem_cache: Cached file: %s with key: %s", name, obj->key);
885 /* Content not suitable for fd caching. Cache in-memory instead. */
886 mobj->type = CACHE_TYPE_HEAP;
890 * FD cacheing is not enabled or the content was not
891 * suitable for fd caching.
893 if (mobj->m == NULL) {
894 mobj->m = malloc(mobj->m_len);
895 if (mobj->m == NULL) {
900 cur = (char*) mobj->m + obj->count;
902 /* Iterate accross the brigade and populate the cache storage */
903 for (e = APR_BRIGADE_FIRST(b);
904 e != APR_BRIGADE_SENTINEL(b);
905 e = APR_BUCKET_NEXT(e))
910 if (APR_BUCKET_IS_EOS(e)) {
911 if (mobj->m_len > obj->count) {
912 /* Caching a streamed response. Reallocate a buffer of the
913 * correct size and copy the streamed response into that
915 char *buf = malloc(obj->count);
919 memcpy(buf, mobj->m, obj->count);
923 /* Now comes the crufty part... there is no way to tell the
924 * cache that the size of the object has changed. We need
925 * to remove the object, update the size and re-add the
926 * object, all under protection of the lock.
929 apr_thread_mutex_lock(sconf->lock);
931 /* Has the object been ejected from the cache?
933 tobj = (cache_object_t *) cache_find(sconf->cache_cache, obj->key);
935 /* Object is still in the cache, remove it, update the len field then
936 * replace it under protection of sconf->lock.
938 cache_remove(sconf->cache_cache, obj);
939 /* For illustration, cache no longer has reference to the object
940 * so decrement the refcount
941 * apr_atomic_dec(&obj->refcount);
943 mobj->m_len = obj->count;
945 cache_insert(sconf->cache_cache, obj);
946 /* For illustration, cache now has reference to the object, so
947 * increment the refcount
948 * apr_atomic_inc(&obj->refcount);
952 /* Different object with the same key found in the cache. Doing nothing
953 * here will cause the object refcount to drop to 0 in decrement_refcount
954 * and the object will be cleaned up.
958 /* Object has been ejected from the cache, add it back to the cache */
959 mobj->m_len = obj->count;
960 cache_insert(sconf->cache_cache, obj);
961 apr_atomic_inc(&obj->refcount);
965 apr_thread_mutex_unlock(sconf->lock);
968 /* Open for business */
969 ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
970 "mem_cache: Cached url: %s", obj->key);
974 rv = apr_bucket_read(e, &s, &len, eblock);
975 if (rv != APR_SUCCESS) {
979 /* Check for buffer overflow */
980 if ((obj->count + len) > mobj->m_len) {
989 /* This should not fail, but if it does, we are in BIG trouble
990 * cause we just stomped all over the heap.
992 AP_DEBUG_ASSERT(obj->count <= mobj->m_len);
997 * Configuration and start-up
999 static int mem_cache_post_config(apr_pool_t *p, apr_pool_t *plog,
1000 apr_pool_t *ptemp, server_rec *s)
1004 /* Sanity check the cache configuration */
1005 if (sconf->min_cache_object_size >= sconf->max_cache_object_size) {
1006 ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s,
1007 "MCacheMaxObjectSize must be greater than MCacheMinObjectSize");
1010 if (sconf->max_cache_object_size >= sconf->max_cache_size) {
1011 ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s,
1012 "MCacheSize must be greater than MCacheMaxObjectSize");
1015 if (sconf->max_streaming_buffer_size > sconf->max_cache_object_size) {
1016 /* Issue a notice only if something other than the default config
1018 if (sconf->max_streaming_buffer_size != DEFAULT_MAX_STREAMING_BUFFER_SIZE &&
1019 sconf->max_cache_object_size != DEFAULT_MAX_CACHE_OBJECT_SIZE) {
1020 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s,
1021 "MCacheMaxStreamingBuffer must be less than or equal to MCacheMaxObjectSize. "
1022 "Resetting MCacheMaxStreamingBuffer to MCacheMaxObjectSize.");
1024 sconf->max_streaming_buffer_size = sconf->max_cache_object_size;
1026 if (sconf->max_streaming_buffer_size < sconf->min_cache_object_size) {
1027 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
1028 "MCacheMaxStreamingBuffer must be greater than or equal to MCacheMinObjectSize. "
1029 "Resetting MCacheMaxStreamingBuffer to MCacheMinObjectSize.");
1030 sconf->max_streaming_buffer_size = sconf->min_cache_object_size;
1032 ap_mpm_query(AP_MPMQ_IS_THREADED, &threaded_mpm);
1034 apr_thread_mutex_create(&sconf->lock, APR_THREAD_MUTEX_DEFAULT, p);
1037 sconf->cache_cache = cache_init(sconf->max_object_cnt,
1038 sconf->max_cache_size,
1039 memcache_get_priority,
1040 sconf->cache_remove_algorithm,
1043 memcache_inc_frequency,
1044 memcache_cache_get_size,
1045 memcache_cache_get_key,
1046 memcache_cache_free);
1047 apr_pool_cleanup_register(p, sconf, cleanup_cache_mem, apr_pool_cleanup_null);
1049 if (sconf->cache_cache)
1057 *set_max_cache_size(cmd_parms *parms, void *in_struct_ptr, const char *arg)
1061 if (sscanf(arg, "%" APR_SIZE_T_FMT, &val) != 1) {
1062 return "MCacheSize argument must be an integer representing the max cache size in KBytes.";
1064 sconf->max_cache_size = val*1024;
1068 *set_min_cache_object_size(cmd_parms *parms, void *in_struct_ptr, const char *arg)
1072 if (sscanf(arg, "%" APR_SIZE_T_FMT, &val) != 1) {
1073 return "MCacheMinObjectSize value must be an integer (bytes)";
1075 sconf->min_cache_object_size = val;
1079 *set_max_cache_object_size(cmd_parms *parms, void *in_struct_ptr, const char *arg)
1083 if (sscanf(arg, "%" APR_SIZE_T_FMT, &val) != 1) {
1084 return "MCacheMaxObjectSize value must be an integer (bytes)";
1086 sconf->max_cache_object_size = val;
1090 *set_max_object_count(cmd_parms *parms, void *in_struct_ptr, const char *arg)
1094 if (sscanf(arg, "%" APR_SIZE_T_FMT, &val) != 1) {
1095 return "MCacheMaxObjectCount value must be an integer";
1097 sconf->max_object_cnt = val;
1102 *set_cache_removal_algorithm(cmd_parms *parms, void *name, const char *arg)
1104 if (strcasecmp("LRU", arg)) {
1105 sconf->cache_remove_algorithm = memcache_lru_algorithm;
1108 if (strcasecmp("GDSF", arg)) {
1109 sconf->cache_remove_algorithm = memcache_gdsf_algorithm;
1112 return "currently implemented algorithms are LRU and GDSF";
1118 static const char *set_max_streaming_buffer(cmd_parms *parms, void *dummy,
1123 if (apr_strtoff(&sconf->max_streaming_buffer_size, arg, &err, 10) || *err) {
1124 return "MCacheMaxStreamingBuffer value must be a number";
1127 sconf->max_streaming_buffer_size = apr_atoi64(arg);
1132 static const command_rec cache_cmds[] =
1134 AP_INIT_TAKE1("MCacheSize", set_max_cache_size, NULL, RSRC_CONF,
1135 "The maximum amount of memory used by the cache in KBytes"),
1136 AP_INIT_TAKE1("MCacheMaxObjectCount", set_max_object_count, NULL, RSRC_CONF,
1137 "The maximum number of objects allowed to be placed in the cache"),
1138 AP_INIT_TAKE1("MCacheMinObjectSize", set_min_cache_object_size, NULL, RSRC_CONF,
1139 "The minimum size (in bytes) of an object to be placed in the cache"),
1140 AP_INIT_TAKE1("MCacheMaxObjectSize", set_max_cache_object_size, NULL, RSRC_CONF,
1141 "The maximum size (in bytes) of an object to be placed in the cache"),
1142 AP_INIT_TAKE1("MCacheRemovalAlgorithm", set_cache_removal_algorithm, NULL, RSRC_CONF,
1143 "The algorithm used to remove entries from the cache (default: GDSF)"),
1144 AP_INIT_TAKE1("MCacheMaxStreamingBuffer", set_max_streaming_buffer, NULL, RSRC_CONF,
1145 "Maximum number of bytes of content to buffer for a streamed response"),
1149 static const cache_provider cache_mem_provider =
1161 static const cache_provider cache_fd_provider =
1173 static void register_hooks(apr_pool_t *p)
1175 ap_hook_post_config(mem_cache_post_config, NULL, NULL, APR_HOOK_MIDDLE);
1176 /* cache initializer */
1177 /* cache_hook_init(cache_mem_init, NULL, NULL, APR_HOOK_MIDDLE); */
1179 cache_hook_create_entity(create_entity, NULL, NULL, APR_HOOK_MIDDLE);
1180 cache_hook_open_entity(open_entity, NULL, NULL, APR_HOOK_MIDDLE);
1181 cache_hook_remove_url(remove_url, NULL, NULL, APR_HOOK_MIDDLE);
1183 ap_register_provider(p, CACHE_PROVIDER_GROUP, "mem", "0",
1184 &cache_mem_provider);
1185 ap_register_provider(p, CACHE_PROVIDER_GROUP, "fd", "0",
1186 &cache_fd_provider);
1189 module AP_MODULE_DECLARE_DATA mem_cache_module =
1191 STANDARD20_MODULE_STUFF,
1192 NULL, /* create per-directory config structure */
1193 NULL, /* merge per-directory config structures */
1194 create_cache_config, /* create per-server config structure */
1195 NULL, /* merge per-server config structures */
1196 cache_cmds, /* command apr_table_t */