~ubuntu-branches/ubuntu/feisty/apache2/feisty

« back to all changes in this revision

Viewing changes to modules/cache/mod_mem_cache.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Barth
  • Date: 2006-12-09 21:05:45 UTC
  • mfrom: (0.6.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20061209210545-h70s0xaqc2v8vqr2
Tags: 2.2.3-3.2
* Non-maintainer upload.
* 043_ajp_connection_reuse: Patch from upstream Bugzilla, fixing a critical
  issue with regard to connection reuse in mod_proxy_ajp.
  Closes: #396265

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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_req_hdrs;
 
70
    cache_header_tbl_t *header_out;
 
71
    cache_header_tbl_t *req_hdrs; /* for Vary negotiation */
 
72
    apr_size_t m_len;
 
73
    void *m;
 
74
    apr_os_file_t fd;
 
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 */
 
78
 
 
79
    apr_uint32_t pos;   /**< the position of this entry in the cache */
 
80
 
 
81
} mem_cache_object_t;
 
82
 
 
83
typedef struct {
 
84
    apr_thread_mutex_t *lock;
 
85
    cache_cache_t *cache_cache;
 
86
 
 
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;
 
93
 
 
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;
 
97
} mem_cache_conf;
 
98
static mem_cache_conf *sconf;
 
99
 
 
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
 
106
 
 
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);
 
113
 
 
114
static void cleanup_cache_object(cache_object_t *obj);
 
115
 
 
116
static long memcache_get_priority(void*a)
 
117
{
 
118
    cache_object_t *obj = (cache_object_t *)a;
 
119
    mem_cache_object_t *mobj = obj->vobj;
 
120
 
 
121
    return  mobj->priority;
 
122
}
 
123
 
 
124
static void memcache_inc_frequency(void*a)
 
125
{
 
126
    cache_object_t *obj = (cache_object_t *)a;
 
127
    mem_cache_object_t *mobj = obj->vobj;
 
128
 
 
129
    mobj->total_refs++;
 
130
    mobj->priority = 0;
 
131
}
 
132
 
 
133
static void memcache_set_pos(void *a, apr_ssize_t pos)
 
134
{
 
135
    cache_object_t *obj = (cache_object_t *)a;
 
136
    mem_cache_object_t *mobj = obj->vobj;
 
137
 
 
138
    apr_atomic_set32(&mobj->pos, pos);
 
139
}
 
140
static apr_ssize_t memcache_get_pos(void *a)
 
141
{
 
142
    cache_object_t *obj = (cache_object_t *)a;
 
143
    mem_cache_object_t *mobj = obj->vobj;
 
144
 
 
145
    return apr_atomic_read32(&mobj->pos);
 
146
}
 
147
 
 
148
static apr_size_t memcache_cache_get_size(void*a)
 
149
{
 
150
    cache_object_t *obj = (cache_object_t *)a;
 
151
    mem_cache_object_t *mobj = obj->vobj;
 
152
    return mobj->m_len;
 
153
}
 
154
/** callback to get the key of a item */
 
155
static const char* memcache_cache_get_key(void*a)
 
156
{
 
157
    cache_object_t *obj = (cache_object_t *)a;
 
158
    return obj->key;
 
159
}
 
160
/**
 
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.
 
167
 */
 
168
static void memcache_cache_free(void*a)
 
169
{
 
170
    cache_object_t *obj = (cache_object_t *)a;
 
171
 
 
172
    /* Decrement the refcount to account for the object being ejected
 
173
     * from the cache. If the refcount is 0, free the object.
 
174
     */
 
175
    if (!apr_atomic_dec32(&obj->refcount)) {
 
176
        cleanup_cache_object(obj);
 
177
    }
 
178
}
 
179
/*
 
180
 * functions return a 'negative' score since priority queues
 
181
 * dequeue the object with the highest value first
 
182
 */
 
183
static long memcache_lru_algorithm(long queue_clock, void *a)
 
184
{
 
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;
 
189
 
 
190
    /*
 
191
     * a 'proper' LRU function would just be
 
192
     *  mobj->priority = mobj->total_refs;
 
193
     */
 
194
    return mobj->priority;
 
195
}
 
196
 
 
197
static long memcache_gdsf_algorithm(long queue_clock, void *a)
 
198
{
 
199
    cache_object_t *obj = (cache_object_t *)a;
 
200
    mem_cache_object_t *mobj = obj->vobj;
 
201
 
 
202
    if (mobj->priority == 0)
 
203
        mobj->priority = queue_clock -
 
204
                           (long)(mobj->total_refs*1000 / mobj->m_len);
 
205
 
 
206
    return mobj->priority;
 
207
}
 
208
 
 
209
static void cleanup_cache_object(cache_object_t *obj)
 
210
{
 
211
    mem_cache_object_t *mobj = obj->vobj;
 
212
 
 
213
    /* TODO:
 
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
 
216
     * cache object...
 
217
     */
 
218
 
 
219
    /* Cleanup the cache_object_t */
 
220
    if (obj->key) {
 
221
        free((void*)obj->key);
 
222
    }
 
223
 
 
224
    free(obj);
 
225
 
 
226
    /* Cleanup the mem_cache_object_t */
 
227
    if (mobj) {
 
228
        if (mobj->type == CACHE_TYPE_HEAP && mobj->m) {
 
229
            free(mobj->m);
 
230
        }
 
231
        if (mobj->type == CACHE_TYPE_FILE && mobj->fd) {
 
232
#ifdef WIN32
 
233
            CloseHandle(mobj->fd);
 
234
#else
 
235
            close(mobj->fd);
 
236
#endif
 
237
        }
 
238
        if (mobj->header_out) {
 
239
            if (mobj->header_out[0].hdr)
 
240
                free(mobj->header_out[0].hdr);
 
241
            free(mobj->header_out);
 
242
        }
 
243
        if (mobj->req_hdrs) {
 
244
            if (mobj->req_hdrs[0].hdr)
 
245
                free(mobj->req_hdrs[0].hdr);
 
246
            free(mobj->req_hdrs);
 
247
        }
 
248
        free(mobj);
 
249
    }
 
250
}
 
251
static apr_status_t decrement_refcount(void *arg)
 
252
{
 
253
    cache_object_t *obj = (cache_object_t *) arg;
 
254
 
 
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.
 
260
     */
 
261
    if (!obj->complete) {
 
262
        cache_object_t *tobj = NULL;
 
263
        if (sconf->lock) {
 
264
            apr_thread_mutex_lock(sconf->lock);
 
265
        }
 
266
        tobj = cache_find(sconf->cache_cache, obj->key);
 
267
        if (tobj == obj) {
 
268
            cache_remove(sconf->cache_cache, obj);
 
269
            apr_atomic_dec32(&obj->refcount);
 
270
        }
 
271
        if (sconf->lock) {
 
272
            apr_thread_mutex_unlock(sconf->lock);
 
273
        }
 
274
    }
 
275
 
 
276
    /* If the refcount drops to 0, cleanup the cache object */
 
277
    if (!apr_atomic_dec32(&obj->refcount)) {
 
278
        cleanup_cache_object(obj);
 
279
    }
 
280
    return APR_SUCCESS;
 
281
}
 
282
static apr_status_t cleanup_cache_mem(void *sconfv)
 
283
{
 
284
    cache_object_t *obj;
 
285
    mem_cache_conf *co = (mem_cache_conf*) sconfv;
 
286
 
 
287
    if (!co) {
 
288
        return APR_SUCCESS;
 
289
    }
 
290
    if (!co->cache_cache) {
 
291
        return APR_SUCCESS;
 
292
    }
 
293
 
 
294
    if (sconf->lock) {
 
295
        apr_thread_mutex_lock(sconf->lock);
 
296
    }
 
297
    obj = cache_pop(co->cache_cache);
 
298
    while (obj) {
 
299
        /* Iterate over the cache and clean up each unreferenced entry */
 
300
        if (!apr_atomic_dec32(&obj->refcount)) {
 
301
            cleanup_cache_object(obj);
 
302
        }
 
303
        obj = cache_pop(co->cache_cache);
 
304
    }
 
305
 
 
306
    /* Cache is empty, free the cache table */
 
307
    cache_free(co->cache_cache);
 
308
 
 
309
    if (sconf->lock) {
 
310
        apr_thread_mutex_unlock(sconf->lock);
 
311
    }
 
312
    return APR_SUCCESS;
 
313
}
 
314
/*
 
315
 * TODO: enable directives to be overridden in various containers
 
316
 */
 
317
static void *create_cache_config(apr_pool_t *p, server_rec *s)
 
318
{
 
319
    sconf = apr_pcalloc(p, sizeof(mem_cache_conf));
 
320
 
 
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;
 
330
 
 
331
    return sconf;
 
332
}
 
333
 
 
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)
 
336
{
 
337
    cache_object_t *obj, *tmp_obj;
 
338
    mem_cache_object_t *mobj;
 
339
    apr_size_t key_len;
 
340
 
 
341
    if (len == -1) {
 
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.
 
346
         */
 
347
        len = sconf->max_streaming_buffer_size;
 
348
    }
 
349
 
 
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.
 
354
     */
 
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.",
 
359
                     key);
 
360
        return DECLINED;
 
361
    }
 
362
 
 
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
 
366
         * local or not.
 
367
         */
 
368
        if (!r->filename) {
 
369
            return DECLINED;
 
370
        }
 
371
    }
 
372
 
 
373
    /* Allocate and initialize cache_object_t */
 
374
    obj = calloc(1, sizeof(*obj));
 
375
    if (!obj) {
 
376
        return DECLINED;
 
377
    }
 
378
    key_len = strlen(key) + 1;
 
379
    obj->key = malloc(key_len);
 
380
    if (!obj->key) {
 
381
        cleanup_cache_object(obj);
 
382
        return DECLINED;
 
383
    }
 
384
    memcpy((void*)obj->key, key, key_len);
 
385
 
 
386
    /* Allocate and init mem_cache_object_t */
 
387
    mobj = calloc(1, sizeof(*mobj));
 
388
    if (!mobj) {
 
389
        cleanup_cache_object(obj);
 
390
        return DECLINED;
 
391
    }
 
392
 
 
393
    /* Finish initing the cache object */
 
394
    apr_atomic_set32(&obj->refcount, 1);
 
395
    mobj->total_refs = 1;
 
396
    obj->complete = 0;
 
397
    obj->vobj = mobj;
 
398
    /* Safe cast: We tested < sconf->max_cache_object_size above */
 
399
    mobj->m_len = (apr_size_t)len;
 
400
    mobj->type = type_e;
 
401
 
 
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
 
411
     * initialized...
 
412
     * XXX Need a way to insert into the cache w/o such coarse grained locking
 
413
     */
 
414
    if (sconf->lock) {
 
415
        apr_thread_mutex_lock(sconf->lock);
 
416
    }
 
417
    tmp_obj = (cache_object_t *) cache_find(sconf->cache_cache, key);
 
418
 
 
419
    if (!tmp_obj) {
 
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.
 
424
         */
 
425
        apr_atomic_inc32(&obj->refcount);
 
426
    }
 
427
    if (sconf->lock) {
 
428
        apr_thread_mutex_unlock(sconf->lock);
 
429
    }
 
430
 
 
431
    if (tmp_obj) {
 
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
 
434
         * is further along.
 
435
         */
 
436
        cleanup_cache_object(obj);
 
437
        return DECLINED;
 
438
    }
 
439
 
 
440
    apr_pool_cleanup_register(r->pool, obj, decrement_refcount,
 
441
                              apr_pool_cleanup_null);
 
442
 
 
443
    /* Populate the cache handle */
 
444
    h->cache_obj = obj;
 
445
 
 
446
    return OK;
 
447
}
 
448
 
 
449
static int create_mem_entity(cache_handle_t *h, request_rec *r,
 
450
                             const char *key, apr_off_t len)
 
451
{
 
452
    return create_entity(h, CACHE_TYPE_HEAP, r, key, len);
 
453
}
 
454
 
 
455
static int create_fd_entity(cache_handle_t *h, request_rec *r,
 
456
                            const char *key, apr_off_t len)
 
457
{
 
458
    return create_entity(h, CACHE_TYPE_FILE, r, key, len);
 
459
}
 
460
 
 
461
static int open_entity(cache_handle_t *h, request_rec *r, const char *key)
 
462
{
 
463
    cache_object_t *obj;
 
464
 
 
465
    /* Look up entity keyed to 'url' */
 
466
    if (sconf->lock) {
 
467
        apr_thread_mutex_lock(sconf->lock);
 
468
    }
 
469
    obj = (cache_object_t *) cache_find(sconf->cache_cache, key);
 
470
    if (obj) {
 
471
        if (obj->complete) {
 
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);
 
476
 
 
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.
 
481
             */
 
482
            rtmp = r;
 
483
            while (rtmp) {
 
484
                rmain = rtmp;
 
485
                rtmp = rmain->main;
 
486
            }
 
487
            apr_pool_cleanup_register(rmain->pool, obj, decrement_refcount,
 
488
                                      apr_pool_cleanup_null);
 
489
        }
 
490
        else {
 
491
            obj = NULL;
 
492
        }
 
493
    }
 
494
 
 
495
    if (sconf->lock) {
 
496
        apr_thread_mutex_unlock(sconf->lock);
 
497
    }
 
498
 
 
499
    if (!obj) {
 
500
        return DECLINED;
 
501
    }
 
502
 
 
503
    /* Initialize the cache_handle */
 
504
    h->cache_obj = obj;
 
505
    h->req_hdrs = NULL;  /* Pick these up in recall_headers() */
 
506
    return OK;
 
507
}
 
508
 
 
509
/* remove_entity()
 
510
 * Notes:
 
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.
 
515
 */
 
516
static int remove_entity(cache_handle_t *h)
 
517
{
 
518
    cache_object_t *obj = h->cache_obj;
 
519
    cache_object_t *tobj = NULL;
 
520
 
 
521
    if (sconf->lock) {
 
522
        apr_thread_mutex_lock(sconf->lock);
 
523
    }
 
524
 
 
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.
 
529
     */
 
530
    tobj = cache_find(sconf->cache_cache, obj->key);
 
531
    if (tobj == obj) {
 
532
        cache_remove(sconf->cache_cache, obj);
 
533
        apr_atomic_dec32(&obj->refcount);
 
534
    }
 
535
 
 
536
    if (sconf->lock) {
 
537
        apr_thread_mutex_unlock(sconf->lock);
 
538
    }
 
539
 
 
540
    return OK;
 
541
}
 
542
static apr_status_t serialize_table(cache_header_tbl_t **obj,
 
543
                                    apr_ssize_t *nelts,
 
544
                                    apr_table_t *table)
 
545
{
 
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;
 
548
    apr_ssize_t i;
 
549
    apr_size_t len = 0;
 
550
    apr_size_t idx = 0;
 
551
    char *buf;
 
552
 
 
553
    *nelts = elts_arr->nelts;
 
554
    if (*nelts == 0 ) {
 
555
        *obj=NULL;
 
556
        return APR_SUCCESS;
 
557
    }
 
558
    *obj = malloc(sizeof(cache_header_tbl_t) * elts_arr->nelts);
 
559
    if (NULL == *obj) {
 
560
        return APR_ENOMEM;
 
561
    }
 
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 */
 
566
    }
 
567
 
 
568
    /* Transfer the headers into a contiguous memory block */
 
569
    buf = malloc(len);
 
570
    if (!buf) {
 
571
        *obj = NULL;
 
572
        return APR_ENOMEM;
 
573
    }
 
574
 
 
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);
 
579
        idx+=len;
 
580
 
 
581
        (*obj)[i].val = &buf[idx];
 
582
        len = strlen(elts[i].val) + 1;
 
583
        memcpy(&buf[idx], elts[i].val, len);
 
584
        idx+=len;
 
585
    }
 
586
    return APR_SUCCESS;
 
587
}
 
588
static int unserialize_table( cache_header_tbl_t *ctbl,
 
589
                              int num_headers,
 
590
                              apr_table_t *t )
 
591
{
 
592
    int i;
 
593
 
 
594
    for (i = 0; i < num_headers; ++i) {
 
595
        apr_table_addn(t, ctbl[i].hdr, ctbl[i].val);
 
596
    }
 
597
 
 
598
    return APR_SUCCESS;
 
599
}
 
600
/* Define request processing hook handlers */
 
601
/* remove_url()
 
602
 * Notes:
 
603
 */
 
604
static int remove_url(cache_handle_t *h, apr_pool_t *p)
 
605
{
 
606
    cache_object_t *obj;
 
607
    int cleanup = 0;
 
608
 
 
609
    if (sconf->lock) {
 
610
        apr_thread_mutex_lock(sconf->lock);
 
611
    }
 
612
 
 
613
    obj = h->cache_obj;
 
614
    if (obj) {
 
615
        cache_remove(sconf->cache_cache, obj);
 
616
        /* For performance, cleanup cache object after releasing the lock */
 
617
        cleanup = !apr_atomic_dec32(&obj->refcount);
 
618
    }
 
619
    if (sconf->lock) {
 
620
        apr_thread_mutex_unlock(sconf->lock);
 
621
    }
 
622
 
 
623
    if (cleanup) {
 
624
        cleanup_cache_object(obj);
 
625
    }
 
626
 
 
627
    return OK;
 
628
}
 
629
 
 
630
static apr_status_t recall_headers(cache_handle_t *h, request_rec *r)
 
631
{
 
632
    int rc;
 
633
    mem_cache_object_t *mobj = (mem_cache_object_t*) h->cache_obj->vobj;
 
634
 
 
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);
 
637
 
 
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,
 
640
                           h->resp_hdrs);
 
641
 
 
642
    return rc;
 
643
}
 
644
 
 
645
static apr_status_t recall_body(cache_handle_t *h, apr_pool_t *p, apr_bucket_brigade *bb)
 
646
{
 
647
    apr_bucket *b;
 
648
    mem_cache_object_t *mobj = (mem_cache_object_t*) h->cache_obj->vobj;
 
649
 
 
650
    if (mobj->type == CACHE_TYPE_FILE) {
 
651
        /* CACHE_TYPE_FILE */
 
652
        apr_file_t *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);
 
655
    }
 
656
    else {
 
657
        /* CACHE_TYPE_HEAP */
 
658
        b = apr_bucket_immortal_create(mobj->m, mobj->m_len, bb->bucket_alloc);
 
659
    }
 
660
    APR_BRIGADE_INSERT_TAIL(bb, b);
 
661
    b = apr_bucket_eos_create(bb->bucket_alloc);
 
662
    APR_BRIGADE_INSERT_TAIL(bb, b);
 
663
 
 
664
    return APR_SUCCESS;
 
665
}
 
666
 
 
667
 
 
668
static apr_status_t store_headers(cache_handle_t *h, request_rec *r, cache_info *info)
 
669
{
 
670
    cache_object_t *obj = h->cache_obj;
 
671
    mem_cache_object_t *mobj = (mem_cache_object_t*) obj->vobj;
 
672
    int rc;
 
673
    apr_table_t *headers_out;
 
674
 
 
675
    /*
 
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
 
681
     */
 
682
    rc = serialize_table(&mobj->req_hdrs,
 
683
                         &mobj->num_req_hdrs,
 
684
                         r->headers_in);
 
685
    if (rc != APR_SUCCESS) {
 
686
        return rc;
 
687
    }
 
688
 
 
689
    /* Precompute how much storage we need to hold the headers */
 
690
    headers_out = ap_cache_cacheable_hdrs_out(r->pool, r->headers_out,
 
691
                                              r->server);
 
692
 
 
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));
 
698
    }
 
699
 
 
700
    headers_out = apr_table_overlay(r->pool, headers_out, r->err_headers_out);
 
701
 
 
702
    rc = serialize_table(&mobj->header_out, &mobj->num_header_out,
 
703
                         headers_out);
 
704
    if (rc != APR_SUCCESS) {
 
705
        return rc;
 
706
    }
 
707
 
 
708
    /* Init the info struct */
 
709
    obj->info.status = info->status;
 
710
    if (info->date) {
 
711
        obj->info.date = info->date;
 
712
    }
 
713
    if (info->response_time) {
 
714
        obj->info.response_time = info->response_time;
 
715
    }
 
716
    if (info->request_time) {
 
717
        obj->info.request_time = info->request_time;
 
718
    }
 
719
    if (info->expire) {
 
720
        obj->info.expire = info->expire;
 
721
    }
 
722
 
 
723
    return APR_SUCCESS;
 
724
}
 
725
 
 
726
static apr_status_t store_body(cache_handle_t *h, request_rec *r, apr_bucket_brigade *b)
 
727
{
 
728
    apr_status_t rv;
 
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;
 
733
    apr_bucket *e;
 
734
    char *cur;
 
735
    int eos = 0;
 
736
 
 
737
    if (mobj->type == CACHE_TYPE_FILE) {
 
738
        apr_file_t *file = NULL;
 
739
        int fd = 0;
 
740
        int other = 0;
 
741
 
 
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
 
746
         */
 
747
        for (e = APR_BRIGADE_FIRST(b);
 
748
             e != APR_BRIGADE_SENTINEL(b);
 
749
             e = APR_BUCKET_NEXT(e))
 
750
        {
 
751
            if (APR_BUCKET_IS_EOS(e)) {
 
752
                eos = 1;
 
753
            }
 
754
            else if (APR_BUCKET_IS_FILE(e)) {
 
755
                apr_bucket_file *a = e->data;
 
756
                fd++;
 
757
                file = a->fd;
 
758
            }
 
759
            else {
 
760
                other++;
 
761
            }
 
762
        }
 
763
        if (fd == 1 && !other && eos) {
 
764
            apr_file_t *tmpfile;
 
765
            const char *name;
 
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) {
 
773
                return rv;
 
774
            }
 
775
            apr_file_inherit_unset(tmpfile);
 
776
            apr_os_file_get(&(mobj->fd), tmpfile);
 
777
 
 
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);
 
781
            obj->complete = 1;
 
782
            return APR_SUCCESS;
 
783
        }
 
784
 
 
785
        /* Content not suitable for fd caching. Cache in-memory instead. */
 
786
        mobj->type = CACHE_TYPE_HEAP;
 
787
    }
 
788
 
 
789
    /*
 
790
     * FD cacheing is not enabled or the content was not
 
791
     * suitable for fd caching.
 
792
     */
 
793
    if (mobj->m == NULL) {
 
794
        mobj->m = malloc(mobj->m_len);
 
795
        if (mobj->m == NULL) {
 
796
            return APR_ENOMEM;
 
797
        }
 
798
        obj->count = 0;
 
799
    }
 
800
    cur = (char*) mobj->m + obj->count;
 
801
 
 
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))
 
806
    {
 
807
        const char *s;
 
808
        apr_size_t len;
 
809
 
 
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
 
814
                 * buffer */
 
815
                char *buf = malloc(obj->count);
 
816
                if (!buf) {
 
817
                    return APR_ENOMEM;
 
818
                }
 
819
                memcpy(buf, mobj->m, obj->count);
 
820
                free(mobj->m);
 
821
                mobj->m = buf;
 
822
 
 
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.
 
827
                 */
 
828
                if (sconf->lock) {
 
829
                    apr_thread_mutex_lock(sconf->lock);
 
830
                }
 
831
                /* Has the object been ejected from the cache?
 
832
                 */
 
833
                tobj = (cache_object_t *) cache_find(sconf->cache_cache, obj->key);
 
834
                if (tobj == obj) {
 
835
                    /* Object is still in the cache, remove it, update the len field then
 
836
                     * replace it under protection of sconf->lock.
 
837
                     */
 
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);
 
842
                     */
 
843
                    mobj->m_len = obj->count;
 
844
 
 
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);
 
849
                     */
 
850
                }
 
851
                else if (tobj) {
 
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.
 
855
                     */
 
856
 
 
857
                } else {
 
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);
 
862
                }
 
863
 
 
864
                if (sconf->lock) {
 
865
                    apr_thread_mutex_unlock(sconf->lock);
 
866
                }
 
867
            }
 
868
            /* Open for business */
 
869
            ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
 
870
                         "mem_cache: Cached url: %s", obj->key);
 
871
            obj->complete = 1;
 
872
            break;
 
873
        }
 
874
        rv = apr_bucket_read(e, &s, &len, eblock);
 
875
        if (rv != APR_SUCCESS) {
 
876
            return rv;
 
877
        }
 
878
        if (len) {
 
879
            /* Check for buffer overflow */
 
880
           if ((obj->count + len) > mobj->m_len) {
 
881
               return APR_ENOMEM;
 
882
           }
 
883
           else {
 
884
               memcpy(cur, s, len);
 
885
               cur+=len;
 
886
               obj->count+=len;
 
887
           }
 
888
        }
 
889
        /* This should not fail, but if it does, we are in BIG trouble
 
890
         * cause we just stomped all over the heap.
 
891
         */
 
892
        AP_DEBUG_ASSERT(obj->count <= mobj->m_len);
 
893
    }
 
894
    return APR_SUCCESS;
 
895
}
 
896
/**
 
897
 * Configuration and start-up
 
898
 */
 
899
static int mem_cache_post_config(apr_pool_t *p, apr_pool_t *plog,
 
900
                                 apr_pool_t *ptemp, server_rec *s)
 
901
{
 
902
    int threaded_mpm;
 
903
 
 
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");
 
908
        return DONE;
 
909
    }
 
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");
 
913
        return DONE;
 
914
    }
 
915
    if (sconf->max_streaming_buffer_size > sconf->max_cache_object_size) {
 
916
        /* Issue a notice only if something other than the default config
 
917
         * is being used */
 
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.");
 
923
        }
 
924
        sconf->max_streaming_buffer_size = sconf->max_cache_object_size;
 
925
    }
 
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;
 
931
    }
 
932
    ap_mpm_query(AP_MPMQ_IS_THREADED, &threaded_mpm);
 
933
    if (threaded_mpm) {
 
934
        apr_thread_mutex_create(&sconf->lock, APR_THREAD_MUTEX_DEFAULT, p);
 
935
    }
 
936
 
 
937
    sconf->cache_cache = cache_init(sconf->max_object_cnt,
 
938
                                    sconf->max_cache_size,
 
939
                                    memcache_get_priority,
 
940
                                    sconf->cache_remove_algorithm,
 
941
                                    memcache_get_pos,
 
942
                                    memcache_set_pos,
 
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);
 
948
 
 
949
    if (sconf->cache_cache)
 
950
        return OK;
 
951
 
 
952
    return -1;
 
953
 
 
954
}
 
955
 
 
956
static const char
 
957
*set_max_cache_size(cmd_parms *parms, void *in_struct_ptr, const char *arg)
 
958
{
 
959
    apr_size_t val;
 
960
 
 
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.";
 
963
    }
 
964
    sconf->max_cache_size = val*1024;
 
965
    return NULL;
 
966
}
 
967
static const char
 
968
*set_min_cache_object_size(cmd_parms *parms, void *in_struct_ptr, const char *arg)
 
969
{
 
970
    apr_size_t val;
 
971
 
 
972
    if (sscanf(arg, "%" APR_SIZE_T_FMT, &val) != 1) {
 
973
        return "MCacheMinObjectSize value must be an integer (bytes)";
 
974
    }
 
975
    sconf->min_cache_object_size = val;
 
976
    return NULL;
 
977
}
 
978
static const char
 
979
*set_max_cache_object_size(cmd_parms *parms, void *in_struct_ptr, const char *arg)
 
980
{
 
981
    apr_size_t val;
 
982
 
 
983
    if (sscanf(arg, "%" APR_SIZE_T_FMT, &val) != 1) {
 
984
        return "MCacheMaxObjectSize value must be an integer (bytes)";
 
985
    }
 
986
    sconf->max_cache_object_size = val;
 
987
    return NULL;
 
988
}
 
989
static const char
 
990
*set_max_object_count(cmd_parms *parms, void *in_struct_ptr, const char *arg)
 
991
{
 
992
    apr_size_t val;
 
993
 
 
994
    if (sscanf(arg, "%" APR_SIZE_T_FMT, &val) != 1) {
 
995
        return "MCacheMaxObjectCount value must be an integer";
 
996
    }
 
997
    sconf->max_object_cnt = val;
 
998
    return NULL;
 
999
}
 
1000
 
 
1001
static const char
 
1002
*set_cache_removal_algorithm(cmd_parms *parms, void *name, const char *arg)
 
1003
{
 
1004
    if (strcasecmp("LRU", arg)) {
 
1005
        sconf->cache_remove_algorithm = memcache_lru_algorithm;
 
1006
    }
 
1007
    else {
 
1008
        if (strcasecmp("GDSF", arg)) {
 
1009
            sconf->cache_remove_algorithm = memcache_gdsf_algorithm;
 
1010
        }
 
1011
        else {
 
1012
            return "currently implemented algorithms are LRU and GDSF";
 
1013
        }
 
1014
    }
 
1015
    return NULL;
 
1016
}
 
1017
 
 
1018
static const char *set_max_streaming_buffer(cmd_parms *parms, void *dummy,
 
1019
                                            const char *arg)
 
1020
{
 
1021
    char *err;
 
1022
    if (apr_strtoff(&sconf->max_streaming_buffer_size, arg, &err, 10) || *err) {
 
1023
        return "MCacheMaxStreamingBuffer value must be a number";
 
1024
    }
 
1025
 
 
1026
    return NULL;
 
1027
}
 
1028
 
 
1029
static const command_rec cache_cmds[] =
 
1030
{
 
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"),
 
1043
    {NULL}
 
1044
};
 
1045
 
 
1046
static const cache_provider cache_mem_provider =
 
1047
{
 
1048
    &remove_entity,
 
1049
    &store_headers,
 
1050
    &store_body,
 
1051
    &recall_headers,
 
1052
    &recall_body,
 
1053
    &create_mem_entity,
 
1054
    &open_entity,
 
1055
    &remove_url,
 
1056
};
 
1057
 
 
1058
static const cache_provider cache_fd_provider =
 
1059
{
 
1060
    &remove_entity,
 
1061
    &store_headers,
 
1062
    &store_body,
 
1063
    &recall_headers,
 
1064
    &recall_body,
 
1065
    &create_fd_entity,
 
1066
    &open_entity,
 
1067
    &remove_url,
 
1068
};
 
1069
 
 
1070
static void register_hooks(apr_pool_t *p)
 
1071
{
 
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);  */
 
1075
    /*
 
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);
 
1079
    */
 
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);
 
1084
}
 
1085
 
 
1086
module AP_MODULE_DECLARE_DATA mem_cache_module =
 
1087
{
 
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 */
 
1094
    register_hooks
 
1095
};