upload http
[bottlenecks.git] / rubbos / app / httpd-2.0.64 / modules / experimental / mod_mem_cache.c
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
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 /*
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.
22  *
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.
26  * 
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
30  *
31  * refcount can be atomically decremented w/o protection of the sconf->lock
32  *   by worker threads.
33  *
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 
36  * it.
37  */
38 #define CORE_PRIVATE
39 #include "mod_cache.h"
40 #include "cache_pqueue.h"
41 #include "cache_cache.h"
42 #include "ap_provider.h"
43 #include "ap_mpm.h"
44 #include "apr_thread_mutex.h"
45 #if APR_HAVE_UNISTD_H
46 #include <unistd.h>
47 #endif
48
49 #if !APR_HAS_THREADS
50 #error This module does not currently compile unless you have a thread-capable APR. Sorry!
51 #endif
52
53 module AP_MODULE_DECLARE_DATA mem_cache_module;
54
55 typedef enum {
56     CACHE_TYPE_FILE = 1,
57     CACHE_TYPE_HEAP,
58     CACHE_TYPE_MMAP
59 } cache_type_e;
60
61 typedef struct {
62     char* hdr;
63     char* val;
64 } cache_header_tbl_t;
65
66 typedef struct mem_cache_object {
67     cache_type_e type;
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 */
78     apr_size_t m_len;
79     void *m;
80     apr_os_file_t fd;
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 */
84
85     apr_uint32_t pos;   /**< the position of this entry in the cache */
86
87 } mem_cache_object_t;
88
89 typedef struct {
90     apr_thread_mutex_t *lock;
91     cache_cache_t *cache_cache;
92
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;
99
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;
103 } mem_cache_conf;
104 static mem_cache_conf *sconf;
105
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
112
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);
119
120 static void cleanup_cache_object(cache_object_t *obj);
121
122 static long memcache_get_priority(void*a)
123 {
124     cache_object_t *obj = (cache_object_t *)a;
125     mem_cache_object_t *mobj = obj->vobj;
126
127     return  mobj->priority;
128 }
129
130 static void memcache_inc_frequency(void*a)
131 {
132     cache_object_t *obj = (cache_object_t *)a;
133     mem_cache_object_t *mobj = obj->vobj;
134
135     mobj->total_refs++;
136     mobj->priority = 0;
137 }
138
139 static void memcache_set_pos(void *a, apr_ssize_t pos)
140 {
141     cache_object_t *obj = (cache_object_t *)a;
142     mem_cache_object_t *mobj = obj->vobj;
143
144     apr_atomic_set(&mobj->pos, pos);
145 }
146 static apr_ssize_t memcache_get_pos(void *a)
147 {
148     cache_object_t *obj = (cache_object_t *)a;
149     mem_cache_object_t *mobj = obj->vobj;
150
151     return apr_atomic_read(&mobj->pos);
152 }
153
154 static apr_size_t memcache_cache_get_size(void*a)
155 {
156     cache_object_t *obj = (cache_object_t *)a;
157     mem_cache_object_t *mobj = obj->vobj;
158     return mobj->m_len;
159 }
160 /** callback to get the key of a item */
161 static const char* memcache_cache_get_key(void*a)
162 {
163     cache_object_t *obj = (cache_object_t *)a;
164     return obj->key;
165 }
166 /** 
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.
173  */
174 static void memcache_cache_free(void*a)
175 {
176     cache_object_t *obj = (cache_object_t *)a;
177
178     /* Decrement the refcount to account for the object being ejected
179      * from the cache. If the refcount is 0, free the object.
180      */
181     if (!apr_atomic_dec(&obj->refcount)) {
182         cleanup_cache_object(obj);
183     }
184 }
185 /*
186  * functions return a 'negative' score since priority queues
187  * dequeue the object with the highest value first
188  */
189 static long memcache_lru_algorithm(long queue_clock, void *a) 
190 {
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;
195
196     /*  
197      * a 'proper' LRU function would just be
198      *  mobj->priority = mobj->total_refs; 
199      */
200     return mobj->priority;
201 }
202
203 static long memcache_gdsf_algorithm(long queue_clock, void *a) 
204 {
205     cache_object_t *obj = (cache_object_t *)a;
206     mem_cache_object_t *mobj = obj->vobj;
207
208     if (mobj->priority == 0)
209         mobj->priority = queue_clock -
210                            (long)(mobj->total_refs*1000 / mobj->m_len);
211
212     return mobj->priority;
213 }
214
215 static void cleanup_cache_object(cache_object_t *obj)
216 {
217     mem_cache_object_t *mobj = obj->vobj;
218
219     /* TODO:
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 
222      * cache object...
223      */
224
225     /* Cleanup the cache_object_t */
226     if (obj->key) {
227         free(obj->key);
228     }
229     if (obj->info.content_type) {
230         free(obj->info.content_type);
231     }
232     if (obj->info.etag) {
233         free(obj->info.etag);
234     }
235     if (obj->info.lastmods) {
236         free(obj->info.lastmods);
237     }
238     if (obj->info.filename) {
239         free(obj->info.filename);
240     }
241
242     free(obj);
243     
244     /* Cleanup the mem_cache_object_t */
245     if (mobj) {
246         if (mobj->type == CACHE_TYPE_HEAP && mobj->m) {
247             free(mobj->m);
248         }
249         if (mobj->type == CACHE_TYPE_FILE && mobj->fd) {
250 #ifdef WIN32
251             CloseHandle(mobj->fd);
252 #else
253             close(mobj->fd);
254 #endif
255         }
256         if (mobj->header_out) {
257             if (mobj->header_out[0].hdr) 
258                 free(mobj->header_out[0].hdr);
259             free(mobj->header_out);
260         }
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);
265         }
266         if (mobj->subprocess_env) {
267             if (mobj->subprocess_env[0].hdr) 
268                 free(mobj->subprocess_env[0].hdr);
269             free(mobj->subprocess_env);
270         }
271         if (mobj->notes) {
272             if (mobj->notes[0].hdr) 
273                 free(mobj->notes[0].hdr);
274             free(mobj->notes);
275         }
276         if (mobj->req_hdrs) {
277             if (mobj->req_hdrs[0].hdr)
278                 free(mobj->req_hdrs[0].hdr);
279             free(mobj->req_hdrs);
280         }
281         free(mobj);
282     }
283 }
284 static apr_status_t decrement_refcount(void *arg) 
285 {
286     cache_object_t *obj = (cache_object_t *) arg;
287
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.
293      */
294     if (!obj->complete) {
295         cache_object_t *tobj = NULL;
296         if (sconf->lock) {
297             apr_thread_mutex_lock(sconf->lock);
298         }
299         tobj = cache_find(sconf->cache_cache, obj->key);
300         if (tobj == obj) {
301             cache_remove(sconf->cache_cache, obj);
302             apr_atomic_dec(&obj->refcount);
303         }
304         if (sconf->lock) {
305             apr_thread_mutex_unlock(sconf->lock);
306         }
307     } 
308
309     /* If the refcount drops to 0, cleanup the cache object */
310     if (!apr_atomic_dec(&obj->refcount)) {
311         cleanup_cache_object(obj);
312     }
313     return APR_SUCCESS;
314 }
315 static apr_status_t cleanup_cache_mem(void *sconfv)
316 {
317     cache_object_t *obj;
318     mem_cache_conf *co = (mem_cache_conf*) sconfv;
319
320     if (!co) {
321         return APR_SUCCESS;
322     }
323     if (!co->cache_cache) {
324         return APR_SUCCESS;
325     }
326
327     if (sconf->lock) {
328         apr_thread_mutex_lock(sconf->lock);
329     }
330     obj = cache_pop(co->cache_cache);
331     while (obj) {         
332         /* Iterate over the cache and clean up each unreferenced entry */
333         if (!apr_atomic_dec(&obj->refcount)) {
334             cleanup_cache_object(obj);
335         }
336         obj = cache_pop(co->cache_cache);
337     }
338
339     /* Cache is empty, free the cache table */        
340     cache_free(co->cache_cache);
341
342     if (sconf->lock) {
343         apr_thread_mutex_unlock(sconf->lock);
344     }
345     return APR_SUCCESS;
346 }
347 /*
348  * TODO: enable directives to be overridden in various containers
349  */
350 static void *create_cache_config(apr_pool_t *p, server_rec *s)
351 {
352     sconf = apr_pcalloc(p, sizeof(mem_cache_conf));
353
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;
363
364     return sconf;
365 }
366
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) 
369 {
370     cache_object_t *obj, *tmp_obj;
371     mem_cache_object_t *mobj;
372     apr_size_t key_len;
373
374     if (len == -1) {
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.
379          */
380         len = sconf->max_streaming_buffer_size;
381     }
382
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.
387      */
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.",
392                      key);
393         return DECLINED;
394     }
395
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
399          * local or not.
400          */
401         if (!r->filename) {
402             return DECLINED;
403         }
404     }
405
406     /* Allocate and initialize cache_object_t */
407     obj = calloc(1, sizeof(*obj));
408     if (!obj) {
409         return DECLINED;
410     }
411     key_len = strlen(key) + 1;
412     obj->key = malloc(key_len);
413     if (!obj->key) {
414         cleanup_cache_object(obj);
415         return DECLINED;
416     }
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;
420
421     /* Allocate and init mem_cache_object_t */
422     mobj = calloc(1, sizeof(*mobj));
423     if (!mobj) {
424         cleanup_cache_object(obj);
425         return DECLINED;
426     }
427
428     /* Finish initing the cache object */
429     apr_atomic_set(&obj->refcount, 1);
430     mobj->total_refs = 1;
431     obj->complete = 0;
432     obj->vobj = mobj;
433     /* Safe cast: We tested < sconf->max_cache_object_size above */
434     mobj->m_len = (apr_size_t)len;
435     mobj->type = type_e;
436
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
446      * initialized...
447      * XXX Need a way to insert into the cache w/o such coarse grained locking 
448      */
449     if (sconf->lock) {
450         apr_thread_mutex_lock(sconf->lock);
451     }
452     tmp_obj = (cache_object_t *) cache_find(sconf->cache_cache, key);
453
454     if (!tmp_obj) {
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.
459          */
460         apr_atomic_inc(&obj->refcount);
461     }
462     if (sconf->lock) {
463         apr_thread_mutex_unlock(sconf->lock);
464     }
465
466     if (tmp_obj) {
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 
469          * is further along.
470          */
471         cleanup_cache_object(obj);
472         return DECLINED;
473     }
474
475     apr_pool_cleanup_register(r->pool, obj, decrement_refcount, 
476                               apr_pool_cleanup_null);
477
478     /* Populate the cache handle */
479     h->cache_obj = obj;
480
481     return OK;
482 }
483
484 static int create_mem_entity(cache_handle_t *h, request_rec *r,
485                              const char *key, apr_off_t len) 
486 {
487     return create_entity(h, CACHE_TYPE_HEAP, r, key, len);
488 }
489
490 static int create_fd_entity(cache_handle_t *h, request_rec *r,
491                             const char *key, apr_off_t len) 
492 {
493     return create_entity(h, CACHE_TYPE_FILE, r, key, len);
494 }
495
496 static int open_entity(cache_handle_t *h, request_rec *r, const char *key) 
497 {
498     cache_object_t *obj;
499
500     /* Look up entity keyed to 'url' */
501     if (sconf->lock) {
502         apr_thread_mutex_lock(sconf->lock);
503     }
504     obj = (cache_object_t *) cache_find(sconf->cache_cache, key);
505     if (obj) {
506         if (obj->complete) {
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);
511
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.
516              */
517             rtmp = r;
518             while (rtmp) {
519                 rmain = rtmp;
520                 rtmp = rmain->main;
521             }
522             apr_pool_cleanup_register(rmain->pool, obj, decrement_refcount,
523                                       apr_pool_cleanup_null);
524         }
525         else {
526             obj = NULL;
527         }
528     }
529
530     if (sconf->lock) {
531         apr_thread_mutex_unlock(sconf->lock);
532     }
533
534     if (!obj) {
535         return DECLINED;
536     }
537
538     /* Initialize the cache_handle */
539     h->cache_obj = obj;
540     h->req_hdrs = NULL;  /* Pick these up in recall_headers() */
541     return OK;
542 }
543
544 /* remove_entity()
545  * Notes: 
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.
550  */
551 static int remove_entity(cache_handle_t *h) 
552 {
553     cache_object_t *obj = h->cache_obj;
554     cache_object_t *tobj = NULL;
555
556     if (sconf->lock) {
557         apr_thread_mutex_lock(sconf->lock);
558     }
559
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.
564      */
565     tobj = cache_find(sconf->cache_cache, obj->key);
566     if (tobj == obj) {
567         cache_remove(sconf->cache_cache, obj);
568         apr_atomic_dec(&obj->refcount);
569     }
570     
571     if (sconf->lock) {
572         apr_thread_mutex_unlock(sconf->lock);
573     }
574
575     return OK;
576 }
577 static apr_status_t serialize_table(cache_header_tbl_t **obj, 
578                                     apr_ssize_t *nelts, 
579                                     apr_table_t *table)
580 {
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;
583     apr_ssize_t i;
584     apr_size_t len = 0;
585     apr_size_t idx = 0;
586     char *buf;
587    
588     *nelts = elts_arr->nelts;
589     if (*nelts == 0 ) {
590         *obj=NULL;
591         return APR_SUCCESS;
592     }
593     *obj = malloc(sizeof(cache_header_tbl_t) * elts_arr->nelts);
594     if (NULL == *obj) {
595         return APR_ENOMEM;
596     }
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 */
601     }
602
603     /* Transfer the headers into a contiguous memory block */
604     buf = malloc(len);
605     if (!buf) {
606         *obj = NULL;
607         return APR_ENOMEM;
608     }
609
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);
614         idx+=len;
615
616         (*obj)[i].val = &buf[idx];
617         len = strlen(elts[i].val) + 1;
618         memcpy(&buf[idx], elts[i].val, len);
619         idx+=len;
620     }
621     return APR_SUCCESS;
622 }
623 static int unserialize_table( cache_header_tbl_t *ctbl, 
624                               int num_headers, 
625                               apr_table_t *t )
626 {
627     int i;
628
629     for (i = 0; i < num_headers; ++i) {
630         apr_table_addn(t, ctbl[i].hdr, ctbl[i].val);
631     } 
632
633     return APR_SUCCESS;
634 }
635 /* Define request processing hook handlers */
636 /* remove_url()
637  * Notes:
638  */
639 static int remove_url(const char *key) 
640 {
641     cache_object_t *obj;
642     int cleanup = 0;
643
644     if (sconf->lock) {
645         apr_thread_mutex_lock(sconf->lock);
646     }
647   
648     obj = cache_find(sconf->cache_cache, key);       
649     if (obj) {
650         cache_remove(sconf->cache_cache, obj);
651         /* For performance, cleanup cache object after releasing the lock */
652         cleanup = !apr_atomic_dec(&obj->refcount);
653     }
654     if (sconf->lock) {
655         apr_thread_mutex_unlock(sconf->lock);
656     }
657
658     if (cleanup) {
659         cleanup_cache_object(obj);
660     }
661
662     return OK;
663 }
664
665 static apr_status_t recall_headers(cache_handle_t *h, request_rec *r) 
666 {
667     int rc;
668     mem_cache_object_t *mobj = (mem_cache_object_t*) h->cache_obj->vobj;
669  
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);
676
677     rc = unserialize_table(mobj->req_hdrs,
678                            mobj->num_req_hdrs,
679                            h->req_hdrs);
680     rc = unserialize_table( mobj->header_out,
681                             mobj->num_header_out, 
682                             h->resp_hdrs);
683     rc = unserialize_table( mobj->err_header_out,
684                             mobj->num_err_header_out, 
685                             h->resp_err_hdrs);
686     rc = unserialize_table( mobj->subprocess_env, 
687                             mobj->num_subprocess_env, 
688                             r->subprocess_env);
689     rc = unserialize_table( mobj->notes,
690                             mobj->num_notes,
691                             r->notes);
692
693     /* Content-Type: header may not be set if content is local since
694      * CACHE_IN runs before header filters....
695      */
696     h->content_type = h->cache_obj->info.content_type;
697     h->status = h->cache_obj->info.status;
698
699     return rc;
700 }
701
702 static apr_status_t recall_body(cache_handle_t *h, apr_pool_t *p, apr_bucket_brigade *bb) 
703 {
704     apr_bucket *b;
705     mem_cache_object_t *mobj = (mem_cache_object_t*) h->cache_obj->vobj;
706
707     if (mobj->type == CACHE_TYPE_FILE) {
708         /* CACHE_TYPE_FILE */
709         apr_file_t *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);
712     }
713     else {
714         /* CACHE_TYPE_HEAP */
715         b = apr_bucket_immortal_create(mobj->m, mobj->m_len, bb->bucket_alloc);
716     }
717     APR_BRIGADE_INSERT_TAIL(bb, b);
718     b = apr_bucket_eos_create(bb->bucket_alloc);
719     APR_BRIGADE_INSERT_TAIL(bb, b);
720
721     return APR_SUCCESS;
722 }
723
724
725 static apr_status_t store_headers(cache_handle_t *h, request_rec *r, cache_info *info)
726 {
727     cache_object_t *obj = h->cache_obj;
728     mem_cache_object_t *mobj = (mem_cache_object_t*) obj->vobj;
729     int rc;
730  
731     /*
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
737      */
738     rc = serialize_table(&mobj->req_hdrs,
739                          &mobj->num_req_hdrs,
740                          r->headers_in);
741     if (rc != APR_SUCCESS) {
742         return rc;
743     }
744
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,
749                                                      r->server));
750     if (rc != APR_SUCCESS) {
751         return rc;
752     }
753     rc = serialize_table(&mobj->err_header_out, 
754                          &mobj->num_err_header_out, 
755                          ap_cache_cacheable_hdrs_out(r->pool,
756                                                      r->err_headers_out,
757                                                      r->server));
758     if (rc != APR_SUCCESS) {
759         return rc;
760     }
761     rc = serialize_table(&mobj->subprocess_env,
762                          &mobj->num_subprocess_env, 
763                          r->subprocess_env );
764     if (rc != APR_SUCCESS) {
765         return rc;
766     }
767
768     rc = serialize_table(&mobj->notes, &mobj->num_notes, r->notes);
769     if (rc != APR_SUCCESS) {
770         return rc;
771     }
772  
773     /* Init the info struct */
774     obj->info.status = info->status;
775     if (info->date) {
776         obj->info.date = info->date;
777     }
778     if (info->lastmod) {
779         obj->info.lastmod = info->lastmod;
780     }
781     if (info->response_time) {
782         obj->info.response_time = info->response_time;
783     }
784     if (info->request_time) {
785         obj->info.request_time = info->request_time;
786     }
787     if (info->expire) {
788         obj->info.expire = info->expire;
789     }
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) {
794             return APR_ENOMEM;
795         }
796         memcpy(obj->info.content_type, info->content_type, len);
797     }
798     if (info->etag) {
799         apr_size_t len = strlen(info->etag) + 1;
800         obj->info.etag = (char*) malloc(len);
801         if (!obj->info.etag) {
802             return APR_ENOMEM;
803         }
804         memcpy(obj->info.etag, info->etag, len);
805     }
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) {
810             return APR_ENOMEM;
811         }
812         memcpy(obj->info.lastmods, info->lastmods, len);
813     }
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 ) {
818             return APR_ENOMEM;
819         }
820         memcpy(obj->info.filename, info->filename, len);
821     }
822
823     return APR_SUCCESS;
824 }
825
826 static apr_status_t store_body(cache_handle_t *h, request_rec *r, apr_bucket_brigade *b) 
827 {
828     apr_status_t rv;
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;
833     apr_bucket *e;
834     char *cur;
835     int eos = 0;
836
837     if (mobj->type == CACHE_TYPE_FILE) {
838         apr_file_t *file = NULL;
839         int fd = 0;
840         int other = 0;
841
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
846          */
847         for (e = APR_BRIGADE_FIRST(b);
848              e != APR_BRIGADE_SENTINEL(b);
849              e = APR_BUCKET_NEXT(e))
850         {
851             if (APR_BUCKET_IS_EOS(e)) {
852                 eos = 1;
853             }
854             else if (APR_BUCKET_IS_FILE(e)) {
855                 apr_bucket_file *a = e->data;
856                 fd++;
857                 file = a->fd;
858             }
859             else {
860                 other++;
861             }
862         }
863         if (fd == 1 && !other && eos) {
864             apr_file_t *tmpfile;
865             const char *name;
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) {
873                 return rv;
874             }
875             apr_file_inherit_unset(tmpfile);
876             apr_os_file_get(&(mobj->fd), tmpfile);
877
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);
881             obj->complete = 1;
882             return APR_SUCCESS;
883         }
884
885         /* Content not suitable for fd caching. Cache in-memory instead. */
886         mobj->type = CACHE_TYPE_HEAP;
887     }
888
889     /* 
890      * FD cacheing is not enabled or the content was not
891      * suitable for fd caching.
892      */  
893     if (mobj->m == NULL) {
894         mobj->m = malloc(mobj->m_len);
895         if (mobj->m == NULL) {
896             return APR_ENOMEM;
897         }
898         obj->count = 0;
899     }
900     cur = (char*) mobj->m + obj->count;
901
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))
906     {
907         const char *s;
908         apr_size_t len;
909
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 
914                  * buffer */
915                 char *buf = malloc(obj->count);
916                 if (!buf) {
917                     return APR_ENOMEM;
918                 }
919                 memcpy(buf, mobj->m, obj->count);
920                 free(mobj->m);
921                 mobj->m = buf;
922
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.
927                  */
928                 if (sconf->lock) {
929                     apr_thread_mutex_lock(sconf->lock);
930                 }
931                 /* Has the object been ejected from the cache?
932                  */
933                 tobj = (cache_object_t *) cache_find(sconf->cache_cache, obj->key);
934                 if (tobj == obj) {
935                     /* Object is still in the cache, remove it, update the len field then
936                      * replace it under protection of sconf->lock.
937                      */
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); 
942                      */
943                     mobj->m_len = obj->count;
944
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); 
949                      */
950                 }
951                 else if (tobj) {
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.
955                      */
956
957                 } else {
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); 
962                 }
963
964                 if (sconf->lock) {
965                     apr_thread_mutex_unlock(sconf->lock);
966                 }
967             }
968             /* Open for business */
969             ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
970                          "mem_cache: Cached url: %s", obj->key);
971             obj->complete = 1;
972             break;
973         }
974         rv = apr_bucket_read(e, &s, &len, eblock);
975         if (rv != APR_SUCCESS) {
976             return rv;
977         }
978         if (len) {
979             /* Check for buffer overflow */
980            if ((obj->count + len) > mobj->m_len) {
981                return APR_ENOMEM;
982            }
983            else {
984                memcpy(cur, s, len);
985                cur+=len;
986                obj->count+=len;
987            }
988         }
989         /* This should not fail, but if it does, we are in BIG trouble
990          * cause we just stomped all over the heap.
991          */
992         AP_DEBUG_ASSERT(obj->count <= mobj->m_len);
993     }
994     return APR_SUCCESS;
995 }
996 /**
997  * Configuration and start-up
998  */
999 static int mem_cache_post_config(apr_pool_t *p, apr_pool_t *plog,
1000                                  apr_pool_t *ptemp, server_rec *s)
1001 {
1002     int threaded_mpm;
1003
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");
1008         return DONE;
1009     }
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");
1013         return DONE;
1014     }
1015     if (sconf->max_streaming_buffer_size > sconf->max_cache_object_size) {
1016         /* Issue a notice only if something other than the default config 
1017          * is being used */
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.");
1023         }
1024         sconf->max_streaming_buffer_size = sconf->max_cache_object_size;
1025     }
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;
1031     }
1032     ap_mpm_query(AP_MPMQ_IS_THREADED, &threaded_mpm);
1033     if (threaded_mpm) {
1034         apr_thread_mutex_create(&sconf->lock, APR_THREAD_MUTEX_DEFAULT, p);
1035     }
1036
1037     sconf->cache_cache = cache_init(sconf->max_object_cnt,
1038                                     sconf->max_cache_size,                                   
1039                                     memcache_get_priority,
1040                                     sconf->cache_remove_algorithm,
1041                                     memcache_get_pos,
1042                                     memcache_set_pos,
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);
1048
1049     if (sconf->cache_cache)
1050         return OK;
1051
1052     return -1;
1053
1054 }
1055  
1056 static const char 
1057 *set_max_cache_size(cmd_parms *parms, void *in_struct_ptr, const char *arg)
1058 {
1059     apr_size_t val;
1060
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.";
1063     }
1064     sconf->max_cache_size = val*1024;
1065     return NULL;
1066 }
1067 static const char 
1068 *set_min_cache_object_size(cmd_parms *parms, void *in_struct_ptr, const char *arg)
1069 {
1070     apr_size_t val;
1071
1072     if (sscanf(arg, "%" APR_SIZE_T_FMT, &val) != 1) {
1073         return "MCacheMinObjectSize value must be an integer (bytes)";
1074     }
1075     sconf->min_cache_object_size = val;
1076     return NULL;
1077 }
1078 static const char 
1079 *set_max_cache_object_size(cmd_parms *parms, void *in_struct_ptr, const char *arg)
1080 {
1081     apr_size_t val;
1082
1083     if (sscanf(arg, "%" APR_SIZE_T_FMT, &val) != 1) {
1084         return "MCacheMaxObjectSize value must be an integer (bytes)";
1085     }
1086     sconf->max_cache_object_size = val;
1087     return NULL;
1088 }
1089 static const char 
1090 *set_max_object_count(cmd_parms *parms, void *in_struct_ptr, const char *arg)
1091 {
1092     apr_size_t val;
1093
1094     if (sscanf(arg, "%" APR_SIZE_T_FMT, &val) != 1) {
1095         return "MCacheMaxObjectCount value must be an integer";
1096     }
1097     sconf->max_object_cnt = val;
1098     return NULL;
1099 }
1100
1101 static const char 
1102 *set_cache_removal_algorithm(cmd_parms *parms, void *name, const char *arg)
1103 {
1104     if (strcasecmp("LRU", arg)) {
1105         sconf->cache_remove_algorithm = memcache_lru_algorithm;
1106     }
1107     else {
1108         if (strcasecmp("GDSF", arg)) {
1109             sconf->cache_remove_algorithm = memcache_gdsf_algorithm;
1110         }
1111         else {
1112             return "currently implemented algorithms are LRU and GDSF";
1113         }
1114     }
1115     return NULL;
1116 }
1117
1118 static const char *set_max_streaming_buffer(cmd_parms *parms, void *dummy,
1119                                             const char *arg)
1120 {
1121 #if 0
1122     char *err;
1123     if (apr_strtoff(&sconf->max_streaming_buffer_size, arg, &err, 10) || *err) {
1124         return "MCacheMaxStreamingBuffer value must be a number";
1125     }
1126 #else
1127     sconf->max_streaming_buffer_size = apr_atoi64(arg);
1128 #endif
1129     return NULL;
1130 }
1131
1132 static const command_rec cache_cmds[] =
1133 {
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"),
1146     {NULL}
1147 };
1148
1149 static const cache_provider cache_mem_provider =
1150 {
1151     &remove_entity,
1152     &store_headers,
1153     &store_body,
1154     &recall_headers,
1155     &recall_body,
1156     &create_mem_entity,
1157     &open_entity,
1158     &remove_url,
1159 };
1160
1161 static const cache_provider cache_fd_provider =
1162 {
1163     &remove_entity,
1164     &store_headers,
1165     &store_body,
1166     &recall_headers,
1167     &recall_body,
1168     &create_fd_entity,
1169     &open_entity,
1170     &remove_url,
1171 };
1172
1173 static void register_hooks(apr_pool_t *p)
1174 {
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);  */
1178     /*
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);
1182     */
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);
1187 }
1188
1189 module AP_MODULE_DECLARE_DATA mem_cache_module =
1190 {
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 */
1197     register_hooks
1198 };