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 #include "apr_file_io.h"
19 #include "apr_strings.h"
20 #include "apr_buckets.h"
22 #include "http_config.h"
24 #include "http_core.h"
25 #include "http_protocol.h"
26 #include "ap_provider.h"
27 #include "ap_socache.h"
28 #include "util_filter.h"
29 #include "util_script.h"
30 #include "util_charset.h"
31 #include "util_mutex.h"
33 #include "mod_cache.h"
34 #include "mod_status.h"
36 #include "cache_socache_common.h"
39 * mod_cache_socache: Shared Object Cache Based HTTP 1.1 Cache.
41 * Flow to Find the entry:
42 * Incoming client requests URI /foo/bar/baz
43 * Fetch URI key (may contain Format #1 or Format #2)
44 * If format #1 (Contains a list of Vary Headers):
45 * Use each header name (from .header) with our request values (headers_in) to
46 * regenerate key using HeaderName+HeaderValue+.../foo/bar/baz
47 * re-read in key (must be format #2)
50 * apr_uint32_t format;
52 * apr_array_t vary_headers (delimited by CRLF)
55 * cache_socache_info_t (first sizeof(apr_uint32_t) bytes is the format)
56 * entity name (sobj->name) [length is in cache_socache_info_t->name_len]
57 * r->headers_out (delimited by CRLF)
59 * r->headers_in (delimited by CRLF)
63 module AP_MODULE_DECLARE_DATA cache_socache_module;
66 * cache_socache_object_t
67 * Pointed to by cache_object_t::vobj
69 typedef struct cache_socache_object_t
71 apr_pool_t *pool; /* pool */
72 unsigned char *buffer; /* the cache buffer */
73 apr_size_t buffer_len; /* size of the buffer */
74 apr_bucket_brigade *body; /* brigade containing the body, if any */
75 apr_table_t *headers_in; /* Input headers to save */
76 apr_table_t *headers_out; /* Output headers to save */
77 cache_socache_info_t socache_info; /* Header information. */
78 apr_size_t body_offset; /* offset to the start of the body */
79 apr_off_t body_length; /* length of the cached entity body */
80 apr_time_t expire; /* when to expire the entry */
82 const char *name; /* Requested URI without vary bits - suitable for mortals. */
83 const char *key; /* On-disk prefix; URI with Vary bits (if present) */
84 apr_off_t offset; /* Max size to set aside */
85 apr_time_t timeout; /* Max time to set aside */
86 unsigned int newbody :1; /* whether a new body is present */
87 unsigned int done :1; /* Is the attempt to cache complete? */
88 } cache_socache_object_t;
91 * mod_cache_socache configuration
93 #define DEFAULT_MAX_FILE_SIZE 100*1024
94 #define DEFAULT_MAXTIME 86400
95 #define DEFAULT_MINTIME 600
96 #define DEFAULT_READSIZE 0
97 #define DEFAULT_READTIME 0
99 typedef struct cache_socache_provider_conf
102 ap_socache_provider_t *socache_provider;
103 ap_socache_instance_t *socache_instance;
104 } cache_socache_provider_conf;
106 typedef struct cache_socache_conf
108 cache_socache_provider_conf *provider;
109 } cache_socache_conf;
111 typedef struct cache_socache_dir_conf
113 apr_off_t max; /* maximum file size for cached files */
114 apr_time_t maxtime; /* maximum expiry time */
115 apr_time_t mintime; /* minimum expiry time */
116 apr_off_t readsize; /* maximum data to attempt to cache in one go */
117 apr_time_t readtime; /* maximum time taken to cache in one go */
118 unsigned int max_set :1;
119 unsigned int maxtime_set :1;
120 unsigned int mintime_set :1;
121 unsigned int readsize_set :1;
122 unsigned int readtime_set :1;
123 } cache_socache_dir_conf;
125 /* Shared object cache and mutex */
126 static const char * const cache_socache_id = "cache-socache";
127 static apr_global_mutex_t *socache_mutex = NULL;
130 * Local static functions
133 static apr_status_t read_array(request_rec *r, apr_array_header_t *arr,
134 unsigned char *buffer, apr_size_t buffer_len, apr_size_t *slider)
136 apr_size_t val = *slider;
138 while (*slider < buffer_len) {
139 if (buffer[*slider] == '\r') {
140 if (val == *slider) {
144 *((const char **) apr_array_push(arr)) = apr_pstrndup(r->pool,
145 (const char *) buffer + val, *slider - val);
147 if (buffer[*slider] == '\n') {
152 else if (buffer[*slider] == '\0') {
164 static apr_status_t store_array(apr_array_header_t *arr, unsigned char *buffer,
165 apr_size_t buffer_len, apr_size_t *slider)
170 elts = (const char **) arr->elts;
172 for (i = 0; i < arr->nelts; i++) {
173 apr_size_t e_len = strlen(elts[i]);
174 if (e_len + 3 >= buffer_len - *slider) {
177 len = apr_snprintf(buffer ? (char *) buffer + *slider : NULL,
178 buffer ? buffer_len - *slider : 0, "%s" CRLF, elts[i]);
182 memcpy(buffer + *slider, CRLF, sizeof(CRLF) - 1);
184 *slider += sizeof(CRLF) - 1;
189 static apr_status_t read_table(cache_handle_t *handle, request_rec *r,
190 apr_table_t *table, unsigned char *buffer, apr_size_t buffer_len,
193 apr_size_t key = *slider, colon = 0, len = 0;
195 while (*slider < buffer_len) {
196 if (buffer[*slider] == ':') {
202 else if (buffer[*slider] == '\r') {
204 if (key == *slider) {
206 if (buffer[*slider] == '\n') {
211 if (!colon || buffer[colon++] != ':') {
212 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02344)
213 "Premature end of cache headers.");
216 /* Do not go past the \r from above as apr_isspace('\r') is true */
217 while (apr_isspace(buffer[colon]) && (colon < *slider)) {
220 apr_table_addn(table, apr_pstrmemdup(r->pool, (const char *) buffer
221 + key, len - key), apr_pstrmemdup(r->pool,
222 (const char *) buffer + colon, *slider - colon));
224 if (buffer[*slider] == '\n') {
230 else if (buffer[*slider] == '\0') {
242 static apr_status_t store_table(apr_table_t *table, unsigned char *buffer,
243 apr_size_t buffer_len, apr_size_t *slider)
246 apr_table_entry_t *elts;
248 elts = (apr_table_entry_t *) apr_table_elts(table)->elts;
249 for (i = 0; i < apr_table_elts(table)->nelts; ++i) {
250 if (elts[i].key != NULL) {
251 apr_size_t key_len = strlen(elts[i].key);
252 apr_size_t val_len = strlen(elts[i].val);
253 if (key_len + val_len + 5 >= buffer_len - *slider) {
256 len = apr_snprintf(buffer ? (char *) buffer + *slider : NULL,
257 buffer ? buffer_len - *slider : 0, "%s: %s" CRLF,
258 elts[i].key, elts[i].val);
262 if (3 >= buffer_len - *slider) {
266 memcpy(buffer + *slider, CRLF, sizeof(CRLF) - 1);
268 *slider += sizeof(CRLF) - 1;
273 static const char* regen_key(apr_pool_t *p, apr_table_t *headers,
274 apr_array_header_t *varray, const char *oldkey,
275 apr_size_t *newkeylen)
283 nvec = (varray->nelts * 2) + 1;
284 iov = apr_palloc(p, sizeof(struct iovec) * nvec);
285 elts = (const char **) varray->elts;
288 * - Handle multiple-value headers better. (sort them?)
289 * - Handle Case in-sensitive Values better.
290 * This isn't the end of the world, since it just lowers the cache
291 * hit rate, but it would be nice to fix.
293 * The majority are case insenstive if they are values (encoding etc).
294 * Most of rfc2616 is case insensitive on header contents.
296 * So the better solution may be to identify headers which should be
297 * treated case-sensitive?
298 * HTTP URI's (3.2.3) [host and scheme are insensitive]
299 * HTTP method (5.1.1)
300 * HTTP-date values (3.3.1)
301 * 3.7 Media Types [exerpt]
302 * The type, subtype, and parameter attribute names are case-
303 * insensitive. Parameter values might or might not be case-sensitive,
304 * depending on the semantics of the parameter name.
305 * 4.20 Except [exerpt]
306 * Comparison of expectation values is case-insensitive for unquoted
307 * tokens (including the 100-continue token), and is case-sensitive for
308 * quoted-string expectation-extensions.
311 for (i = 0, k = 0; i < varray->nelts; i++) {
312 header = apr_table_get(headers, elts[i]);
316 iov[k].iov_base = (char*) elts[i];
317 iov[k].iov_len = strlen(elts[i]);
319 iov[k].iov_base = (char*) header;
320 iov[k].iov_len = strlen(header);
323 iov[k].iov_base = (char*) oldkey;
324 iov[k].iov_len = strlen(oldkey);
327 return apr_pstrcatv(p, iov, k, newkeylen);
330 static int array_alphasort(const void *fn1, const void *fn2)
332 return strcmp(*(char**) fn1, *(char**) fn2);
335 static void tokens_to_array(apr_pool_t *p, const char *data,
336 apr_array_header_t *arr)
340 while ((token = ap_get_list_item(p, &data)) != NULL) {
341 *((const char **) apr_array_push(arr)) = token;
344 /* Sort it so that "Vary: A, B" and "Vary: B, A" are stored the same. */
345 qsort((void *) arr->elts, arr->nelts, sizeof(char *), array_alphasort);
349 * Hook and mod_cache callback functions
351 static int create_entity(cache_handle_t *h, request_rec *r, const char *key,
352 apr_off_t len, apr_bucket_brigade *bb)
354 cache_socache_dir_conf *dconf =
355 ap_get_module_config(r->per_dir_config, &cache_socache_module);
356 cache_socache_conf *conf = ap_get_module_config(r->server->module_config,
357 &cache_socache_module);
359 cache_socache_object_t *sobj;
362 if (conf->provider == NULL) {
366 /* we don't support caching of range requests (yet) */
367 /* TODO: but we could */
368 if (r->status == HTTP_PARTIAL_CONTENT) {
369 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02345)
370 "URL %s partial content response not cached",
376 * We have a chicken and egg problem. We don't know until we
377 * attempt to store_headers just how big the response will be
378 * and whether it will fit in the cache limits set. But we
379 * need to make a decision now as to whether we plan to try.
380 * If we make the wrong decision, we could prevent another
381 * cache implementation, such as cache_disk, from getting the
382 * opportunity to cache, and that would be unfortunate.
384 * In a series of tests, from cheapest to most expensive,
385 * decide whether or not to ignore this attempt to cache,
386 * with a small margin just to be sure.
389 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02346)
390 "URL '%s' had no explicit size, ignoring", key);
393 if (len > dconf->max) {
394 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02347)
395 "URL '%s' body larger than limit, ignoring "
396 "(%" APR_OFF_T_FMT " > %" APR_OFF_T_FMT ")",
397 key, len, dconf->max);
401 /* estimate the total cached size, given current headers */
402 total = len + sizeof(cache_socache_info_t) + strlen(key);
403 if (APR_SUCCESS != store_table(r->headers_out, NULL, dconf->max, &total)
404 || APR_SUCCESS != store_table(r->headers_in, NULL, dconf->max,
406 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02348)
407 "URL '%s' estimated headers size larger than limit, ignoring "
408 "(%" APR_SIZE_T_FMT " > %" APR_OFF_T_FMT ")",
409 key, total, dconf->max);
413 if (total >= dconf->max) {
414 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02349)
415 "URL '%s' body and headers larger than limit, ignoring "
416 "(%" APR_OFF_T_FMT " > %" APR_OFF_T_FMT ")",
417 key, len, dconf->max);
421 /* Allocate and initialize cache_object_t and cache_socache_object_t */
422 h->cache_obj = obj = apr_pcalloc(r->pool, sizeof(*obj));
423 obj->vobj = sobj = apr_pcalloc(r->pool, sizeof(*sobj));
425 obj->key = apr_pstrdup(r->pool, key);
426 sobj->key = obj->key;
427 sobj->name = obj->key;
432 static apr_status_t sobj_body_pre_cleanup(void *baton)
434 cache_socache_object_t *sobj = baton;
435 apr_brigade_cleanup(sobj->body);
440 static int open_entity(cache_handle_t *h, request_rec *r, const char *key)
442 cache_socache_dir_conf *dconf =
443 ap_get_module_config(r->per_dir_config, &cache_socache_module);
444 cache_socache_conf *conf = ap_get_module_config(r->server->module_config,
445 &cache_socache_module);
448 unsigned int buffer_len;
453 cache_socache_object_t *sobj;
459 if (!conf->provider || !conf->provider->socache_instance) {
463 /* Create and init the cache object */
464 obj = apr_pcalloc(r->pool, sizeof(cache_object_t));
465 sobj = apr_pcalloc(r->pool, sizeof(cache_socache_object_t));
469 /* Create a temporary pool for the buffer, and destroy it if something
470 * goes wrong so we don't have large buffers of unused memory hanging
471 * about for the lifetime of the response.
473 apr_pool_create(&sobj->pool, r->pool);
475 sobj->buffer = apr_palloc(sobj->pool, dconf->max);
476 sobj->buffer_len = dconf->max;
478 /* attempt to retrieve the cached entry */
480 apr_status_t status = apr_global_mutex_lock(socache_mutex);
481 if (status != APR_SUCCESS) {
482 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02350)
483 "could not acquire lock, ignoring: %s", obj->key);
484 apr_pool_destroy(sobj->pool);
489 buffer_len = sobj->buffer_len;
490 rc = conf->provider->socache_provider->retrieve(
491 conf->provider->socache_instance, r->server, (unsigned char *) key,
492 strlen(key), sobj->buffer, &buffer_len, r->pool);
494 apr_status_t status = apr_global_mutex_unlock(socache_mutex);
495 if (status != APR_SUCCESS) {
496 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02351)
497 "could not release lock, ignoring: %s", obj->key);
498 apr_pool_destroy(sobj->pool);
503 if (rc != APR_SUCCESS) {
504 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rc, r, APLOGNO(02352)
505 "Key not found in cache: %s", key);
506 apr_pool_destroy(sobj->pool);
510 if (buffer_len >= sobj->buffer_len) {
511 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rc, r, APLOGNO(02353)
512 "Key found in cache but too big, ignoring: %s", key);
513 apr_pool_destroy(sobj->pool);
518 /* read the format from the cache file */
519 memcpy(&format, sobj->buffer, sizeof(format));
520 slider = sizeof(format);
522 if (format == CACHE_SOCACHE_VARY_FORMAT_VERSION) {
523 apr_array_header_t* varray;
526 memcpy(&expire, sobj->buffer + slider, sizeof(expire));
527 slider += sizeof(expire);
529 varray = apr_array_make(r->pool, 5, sizeof(char*));
530 rc = read_array(r, varray, sobj->buffer, buffer_len, &slider);
531 if (rc != APR_SUCCESS) {
532 ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, APLOGNO(02354)
533 "Cannot parse vary entry for key: %s", key);
534 apr_pool_destroy(sobj->pool);
539 nkey = regen_key(r->pool, r->headers_in, varray, key, &len);
541 /* attempt to retrieve the cached entry */
543 apr_status_t status = apr_global_mutex_lock(socache_mutex);
544 if (status != APR_SUCCESS) {
545 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02355)
546 "could not acquire lock, ignoring: %s", obj->key);
547 apr_pool_destroy(sobj->pool);
552 buffer_len = sobj->buffer_len;
553 rc = conf->provider->socache_provider->retrieve(
554 conf->provider->socache_instance, r->server,
555 (unsigned char *) nkey, len, sobj->buffer,
556 &buffer_len, r->pool);
558 apr_status_t status = apr_global_mutex_unlock(socache_mutex);
559 if (status != APR_SUCCESS) {
560 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02356)
561 "could not release lock, ignoring: %s", obj->key);
562 apr_pool_destroy(sobj->pool);
567 if (rc != APR_SUCCESS) {
568 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rc, r, APLOGNO(02357)
569 "Key not found in cache: %s", key);
570 apr_pool_destroy(sobj->pool);
574 if (buffer_len >= sobj->buffer_len) {
575 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rc, r, APLOGNO(02358)
576 "Key found in cache but too big, ignoring: %s", key);
581 else if (format != CACHE_SOCACHE_DISK_FORMAT_VERSION) {
582 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02359)
583 "Key '%s' found in cache has version %d, expected %d, ignoring",
584 key, format, CACHE_SOCACHE_DISK_FORMAT_VERSION);
595 if (buffer_len >= sizeof(cache_socache_info_t)) {
596 memcpy(&sobj->socache_info, sobj->buffer, sizeof(cache_socache_info_t));
599 ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, APLOGNO(02360)
600 "Cache entry for key '%s' too short, removing", nkey);
603 slider = sizeof(cache_socache_info_t);
605 /* Store it away so we can get it later. */
606 info->status = sobj->socache_info.status;
607 info->date = sobj->socache_info.date;
608 info->expire = sobj->socache_info.expire;
609 info->request_time = sobj->socache_info.request_time;
610 info->response_time = sobj->socache_info.response_time;
612 memcpy(&info->control, &sobj->socache_info.control, sizeof(cache_control_t));
614 if (sobj->socache_info.name_len <= buffer_len - slider) {
615 if (strncmp((const char *) sobj->buffer + slider, sobj->name,
616 sobj->socache_info.name_len)) {
617 ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, APLOGNO(02361)
618 "Cache entry for key '%s' URL mismatch, ignoring", nkey);
619 apr_pool_destroy(sobj->pool);
623 slider += sobj->socache_info.name_len;
626 ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, APLOGNO(02362)
627 "Cache entry for key '%s' too short, removing", nkey);
631 /* Is this a cached HEAD request? */
632 if (sobj->socache_info.header_only && !r->header_only) {
633 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(02363)
634 "HEAD request cached, non-HEAD requested, ignoring: %s",
636 apr_pool_destroy(sobj->pool);
641 h->req_hdrs = apr_table_make(r->pool, 20);
642 h->resp_hdrs = apr_table_make(r->pool, 20);
644 /* Call routine to read the header lines/status line */
645 if (APR_SUCCESS != read_table(h, r, h->resp_hdrs, sobj->buffer, buffer_len,
647 ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, APLOGNO(02364)
648 "Cache entry for key '%s' response headers unreadable, removing", nkey);
651 if (APR_SUCCESS != read_table(h, r, h->req_hdrs, sobj->buffer, buffer_len,
653 ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, APLOGNO(02365)
654 "Cache entry for key '%s' request headers unreadable, removing", nkey);
658 /* Retrieve the body if we have one */
659 len = buffer_len - slider;
662 /* Create the body brigade later concatenated to the output filters'
663 * brigade by recall_body(). Since sobj->buffer (the data) point to
664 * sobj->pool (a subpool of r->pool), be safe by using a pool bucket
665 * which can morph to heap if sobj->pool is destroyed while the bucket
666 * is still alive. But if sobj->pool gets destroyed while the bucket is
667 * still in sobj->body (i.e. recall_body() was never called), we don't
668 * need to morph to something just about to be freed, so a pre_cleanup
669 * will take care of cleaning up sobj->body before this happens (and is
672 sobj->body = apr_brigade_create(sobj->pool, r->connection->bucket_alloc);
673 apr_pool_pre_cleanup_register(sobj->pool, sobj, sobj_body_pre_cleanup);
674 e = apr_bucket_pool_create((const char *) sobj->buffer + slider, len,
675 sobj->pool, r->connection->bucket_alloc);
676 APR_BRIGADE_INSERT_TAIL(sobj->body, e);
679 /* make the configuration stick */
687 apr_status_t status = apr_global_mutex_lock(socache_mutex);
688 if (status != APR_SUCCESS) {
689 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02366)
690 "could not acquire lock, ignoring: %s", obj->key);
691 apr_pool_destroy(sobj->pool);
696 conf->provider->socache_provider->remove(
697 conf->provider->socache_instance, r->server,
698 (unsigned char *) nkey, strlen(nkey), r->pool);
700 apr_status_t status = apr_global_mutex_unlock(socache_mutex);
701 if (status != APR_SUCCESS) {
702 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02367)
703 "could not release lock, ignoring: %s", obj->key);
706 apr_pool_destroy(sobj->pool);
711 static int remove_entity(cache_handle_t *h)
713 /* Null out the cache object pointer so next time we start from scratch */
718 static int remove_url(cache_handle_t *h, request_rec *r)
720 cache_socache_conf *conf = ap_get_module_config(r->server->module_config,
721 &cache_socache_module);
722 cache_socache_object_t *sobj;
724 sobj = (cache_socache_object_t *) h->cache_obj->vobj;
729 /* Remove the key from the cache */
731 apr_status_t status = apr_global_mutex_lock(socache_mutex);
732 if (status != APR_SUCCESS) {
733 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02368)
734 "could not acquire lock, ignoring: %s", sobj->key);
735 apr_pool_destroy(sobj->pool);
740 conf->provider->socache_provider->remove(conf->provider->socache_instance,
741 r->server, (unsigned char *) sobj->key, strlen(sobj->key), r->pool);
743 apr_status_t status = apr_global_mutex_unlock(socache_mutex);
744 if (status != APR_SUCCESS) {
745 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02369)
746 "could not release lock, ignoring: %s", sobj->key);
747 apr_pool_destroy(sobj->pool);
756 static apr_status_t recall_headers(cache_handle_t *h, request_rec *r)
758 /* we recalled the headers during open_entity, so do nothing */
762 static apr_status_t recall_body(cache_handle_t *h, apr_pool_t *p,
763 apr_bucket_brigade *bb)
765 cache_socache_object_t *sobj = (cache_socache_object_t*) h->cache_obj->vobj;
768 APR_BRIGADE_CONCAT(bb, sobj->body);
774 static apr_status_t store_headers(cache_handle_t *h, request_rec *r,
777 cache_socache_dir_conf *dconf =
778 ap_get_module_config(r->per_dir_config, &cache_socache_module);
779 cache_socache_conf *conf = ap_get_module_config(r->server->module_config,
780 &cache_socache_module);
783 cache_object_t *obj = h->cache_obj;
784 cache_socache_object_t *sobj = (cache_socache_object_t*) obj->vobj;
785 cache_socache_info_t *socache_info;
787 memcpy(&h->cache_obj->info, info, sizeof(cache_info));
789 if (r->headers_out) {
790 sobj->headers_out = ap_cache_cacheable_headers_out(r);
794 sobj->headers_in = ap_cache_cacheable_headers_in(r);
798 = obj->info.expire > r->request_time + dconf->maxtime ? r->request_time
800 : obj->info.expire + dconf->mintime;
802 apr_pool_create(&sobj->pool, r->pool);
804 sobj->buffer = apr_palloc(sobj->pool, dconf->max);
805 sobj->buffer_len = dconf->max;
806 socache_info = (cache_socache_info_t *) sobj->buffer;
808 if (sobj->headers_out) {
811 vary = apr_table_get(sobj->headers_out, "Vary");
814 apr_array_header_t* varray;
815 apr_uint32_t format = CACHE_SOCACHE_VARY_FORMAT_VERSION;
817 memcpy(sobj->buffer, &format, sizeof(format));
818 slider = sizeof(format);
820 memcpy(sobj->buffer + slider, &obj->info.expire,
821 sizeof(obj->info.expire));
822 slider += sizeof(obj->info.expire);
824 varray = apr_array_make(r->pool, 6, sizeof(char*));
825 tokens_to_array(r->pool, vary, varray);
827 if (APR_SUCCESS != (rv = store_array(varray, sobj->buffer,
828 sobj->buffer_len, &slider))) {
829 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(02370)
830 "buffer too small for Vary array, caching aborted: %s",
832 apr_pool_destroy(sobj->pool);
837 apr_status_t status = apr_global_mutex_lock(socache_mutex);
838 if (status != APR_SUCCESS) {
839 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02371)
840 "could not acquire lock, ignoring: %s", obj->key);
841 apr_pool_destroy(sobj->pool);
846 rv = conf->provider->socache_provider->store(
847 conf->provider->socache_instance, r->server,
848 (unsigned char *) obj->key, strlen(obj->key), sobj->expire,
849 (unsigned char *) sobj->buffer, (unsigned int) slider,
852 apr_status_t status = apr_global_mutex_unlock(socache_mutex);
853 if (status != APR_SUCCESS) {
854 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02372)
855 "could not release lock, ignoring: %s", obj->key);
858 if (rv != APR_SUCCESS) {
859 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO(02373)
860 "Vary not written to cache, ignoring: %s", obj->key);
861 apr_pool_destroy(sobj->pool);
866 obj->key = sobj->key = regen_key(r->pool, sobj->headers_in, varray,
871 socache_info->format = CACHE_SOCACHE_DISK_FORMAT_VERSION;
872 socache_info->date = obj->info.date;
873 socache_info->expire = obj->info.expire;
874 socache_info->entity_version = sobj->socache_info.entity_version++;
875 socache_info->request_time = obj->info.request_time;
876 socache_info->response_time = obj->info.response_time;
877 socache_info->status = obj->info.status;
879 if (r->header_only && r->status != HTTP_NOT_MODIFIED) {
880 socache_info->header_only = 1;
883 socache_info->header_only = sobj->socache_info.header_only;
886 socache_info->name_len = strlen(sobj->name);
888 memcpy(&socache_info->control, &obj->info.control, sizeof(cache_control_t));
889 slider = sizeof(cache_socache_info_t);
891 if (slider + socache_info->name_len >= sobj->buffer_len) {
892 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(02374)
893 "cache buffer too small for name: %s",
895 apr_pool_destroy(sobj->pool);
899 memcpy(sobj->buffer + slider, sobj->name, socache_info->name_len);
900 slider += socache_info->name_len;
902 if (sobj->headers_out) {
903 if (APR_SUCCESS != store_table(sobj->headers_out, sobj->buffer,
904 sobj->buffer_len, &slider)) {
905 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(02375)
906 "out-headers didn't fit in buffer: %s", sobj->name);
907 apr_pool_destroy(sobj->pool);
913 /* Parse the vary header and dump those fields from the headers_in. */
914 /* TODO: Make call to the same thing cache_select calls to crack Vary. */
915 if (sobj->headers_in) {
916 if (APR_SUCCESS != store_table(sobj->headers_in, sobj->buffer,
917 sobj->buffer_len, &slider)) {
918 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(02376)
919 "in-headers didn't fit in buffer %s",
921 apr_pool_destroy(sobj->pool);
927 sobj->body_offset = slider;
932 static apr_status_t store_body(cache_handle_t *h, request_rec *r,
933 apr_bucket_brigade *in, apr_bucket_brigade *out)
936 apr_status_t rv = APR_SUCCESS;
937 cache_socache_object_t *sobj =
938 (cache_socache_object_t *) h->cache_obj->vobj;
939 cache_socache_dir_conf *dconf =
940 ap_get_module_config(r->per_dir_config, &cache_socache_module);
944 sobj->offset = dconf->readsize;
946 if (!sobj->timeout && dconf->readtime) {
947 sobj->timeout = apr_time_now() + dconf->readtime;
950 if (!sobj->newbody) {
951 sobj->body_length = 0;
955 apr_brigade_partition(in, sobj->offset, &e);
958 while (APR_SUCCESS == rv && !APR_BRIGADE_EMPTY(in)) {
962 e = APR_BRIGADE_FIRST(in);
964 /* are we done completely? if so, pass any trailing buckets right through */
965 if (sobj->done || !sobj->pool) {
966 APR_BUCKET_REMOVE(e);
967 APR_BRIGADE_INSERT_TAIL(out, e);
971 /* have we seen eos yet? */
972 if (APR_BUCKET_IS_EOS(e)) {
975 APR_BUCKET_REMOVE(e);
976 APR_BRIGADE_INSERT_TAIL(out, e);
980 /* honour flush buckets, we'll get called again */
981 if (APR_BUCKET_IS_FLUSH(e)) {
982 APR_BUCKET_REMOVE(e);
983 APR_BRIGADE_INSERT_TAIL(out, e);
987 /* metadata buckets are preserved as is */
988 if (APR_BUCKET_IS_METADATA(e)) {
989 APR_BUCKET_REMOVE(e);
990 APR_BRIGADE_INSERT_TAIL(out, e);
994 /* read the bucket, write to the cache */
995 rv = apr_bucket_read(e, &str, &length, APR_BLOCK_READ);
996 APR_BUCKET_REMOVE(e);
997 APR_BRIGADE_INSERT_TAIL(out, e);
998 if (rv != APR_SUCCESS) {
999 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02377)
1000 "Error when reading bucket for URL %s",
1002 /* Remove the intermediate cache file and return non-APR_SUCCESS */
1003 apr_pool_destroy(sobj->pool);
1008 /* don't write empty buckets to the cache */
1013 sobj->body_length += length;
1014 if (sobj->body_length >= sobj->buffer_len - sobj->body_offset) {
1015 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02378)
1016 "URL %s failed the buffer size check "
1017 "(%" APR_OFF_T_FMT ">=%" APR_SIZE_T_FMT ")",
1018 h->cache_obj->key, sobj->body_length,
1019 sobj->buffer_len - sobj->body_offset);
1020 apr_pool_destroy(sobj->pool);
1022 return APR_EGENERAL;
1024 memcpy(sobj->buffer + sobj->body_offset + sobj->body_length - length,
1027 /* have we reached the limit of how much we're prepared to write in one
1028 * go? If so, leave, we'll get called again. This prevents us from trying
1029 * to swallow too much data at once, or taking so long to write the data
1030 * the client times out.
1032 sobj->offset -= length;
1033 if (sobj->offset <= 0) {
1037 if ((dconf->readtime && apr_time_now() > sobj->timeout)) {
1044 /* Was this the final bucket? If yes, perform sanity checks.
1047 const char *cl_header = apr_table_get(r->headers_out, "Content-Length");
1049 if (r->connection->aborted || r->no_cache) {
1050 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02380)
1051 "Discarding body for URL %s "
1052 "because connection has been aborted.",
1054 apr_pool_destroy(sobj->pool);
1056 return APR_EGENERAL;
1061 if (apr_strtoff(&cl, cl_header, &cl_endp, 10) != APR_SUCCESS
1062 || *cl_endp != '\0' || cl != sobj->body_length) {
1063 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02381)
1064 "URL %s didn't receive complete response, not caching",
1066 apr_pool_destroy(sobj->pool);
1068 return APR_EGENERAL;
1072 /* All checks were fine, we're good to go when the commit comes */
1079 static apr_status_t commit_entity(cache_handle_t *h, request_rec *r)
1081 cache_socache_conf *conf = ap_get_module_config(r->server->module_config,
1082 &cache_socache_module);
1083 cache_object_t *obj = h->cache_obj;
1084 cache_socache_object_t *sobj = (cache_socache_object_t *) obj->vobj;
1087 if (socache_mutex) {
1088 apr_status_t status = apr_global_mutex_lock(socache_mutex);
1089 if (status != APR_SUCCESS) {
1090 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02384)
1091 "could not acquire lock, ignoring: %s", obj->key);
1092 apr_pool_destroy(sobj->pool);
1097 rv = conf->provider->socache_provider->store(
1098 conf->provider->socache_instance, r->server,
1099 (unsigned char *) sobj->key, strlen(sobj->key), sobj->expire,
1100 sobj->buffer, sobj->body_offset + sobj->body_length, sobj->pool);
1101 if (socache_mutex) {
1102 apr_status_t status = apr_global_mutex_unlock(socache_mutex);
1103 if (status != APR_SUCCESS) {
1104 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02385)
1105 "could not release lock, ignoring: %s", obj->key);
1106 apr_pool_destroy(sobj->pool);
1111 if (rv != APR_SUCCESS) {
1112 ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r, APLOGNO(02386)
1113 "could not write to cache, ignoring: %s", sobj->key);
1117 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02387)
1118 "commit_entity: Headers and body for URL %s cached for maximum of %d seconds.",
1119 sobj->name, (apr_uint32_t)apr_time_sec(sobj->expire - r->request_time));
1121 apr_pool_destroy(sobj->pool);
1127 /* For safety, remove any existing entry on failure, just in case it could not
1128 * be revalidated successfully.
1130 if (socache_mutex) {
1131 apr_status_t status = apr_global_mutex_lock(socache_mutex);
1132 if (status != APR_SUCCESS) {
1133 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02388)
1134 "could not acquire lock, ignoring: %s", obj->key);
1135 apr_pool_destroy(sobj->pool);
1140 conf->provider->socache_provider->remove(conf->provider->socache_instance,
1141 r->server, (unsigned char *) sobj->key, strlen(sobj->key), r->pool);
1142 if (socache_mutex) {
1143 apr_status_t status = apr_global_mutex_unlock(socache_mutex);
1144 if (status != APR_SUCCESS) {
1145 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02389)
1146 "could not release lock, ignoring: %s", obj->key);
1150 apr_pool_destroy(sobj->pool);
1155 static apr_status_t invalidate_entity(cache_handle_t *h, request_rec *r)
1157 /* mark the entity as invalidated */
1158 h->cache_obj->info.control.invalidated = 1;
1160 return commit_entity(h, r);
1163 static void *create_dir_config(apr_pool_t *p, char *dummy)
1165 cache_socache_dir_conf *dconf =
1166 apr_pcalloc(p, sizeof(cache_socache_dir_conf));
1168 dconf->max = DEFAULT_MAX_FILE_SIZE;
1169 dconf->maxtime = apr_time_from_sec(DEFAULT_MAXTIME);
1170 dconf->mintime = apr_time_from_sec(DEFAULT_MINTIME);
1171 dconf->readsize = DEFAULT_READSIZE;
1172 dconf->readtime = DEFAULT_READTIME;
1177 static void *merge_dir_config(apr_pool_t *p, void *basev, void *addv)
1179 cache_socache_dir_conf
1181 (cache_socache_dir_conf *) apr_pcalloc(p, sizeof(cache_socache_dir_conf));
1182 cache_socache_dir_conf *add = (cache_socache_dir_conf *) addv;
1183 cache_socache_dir_conf *base = (cache_socache_dir_conf *) basev;
1185 new->max = (add->max_set == 0) ? base->max : add->max;
1186 new->max_set = add->max_set || base->max_set;
1187 new->maxtime = (add->maxtime_set == 0) ? base->maxtime : add->maxtime;
1188 new->maxtime_set = add->maxtime_set || base->maxtime_set;
1189 new->mintime = (add->mintime_set == 0) ? base->mintime : add->mintime;
1190 new->mintime_set = add->mintime_set || base->mintime_set;
1191 new->readsize = (add->readsize_set == 0) ? base->readsize : add->readsize;
1192 new->readsize_set = add->readsize_set || base->readsize_set;
1193 new->readtime = (add->readtime_set == 0) ? base->readtime : add->readtime;
1194 new->readtime_set = add->readtime_set || base->readtime_set;
1199 static void *create_config(apr_pool_t *p, server_rec *s)
1201 cache_socache_conf *conf = apr_pcalloc(p, sizeof(cache_socache_conf));
1206 static void *merge_config(apr_pool_t *p, void *basev, void *overridesv)
1208 cache_socache_conf *ps;
1209 cache_socache_conf *base = (cache_socache_conf *) basev;
1210 cache_socache_conf *overrides = (cache_socache_conf *) overridesv;
1212 /* socache server config only has one field */
1213 ps = overrides ? overrides : base;
1219 * mod_cache_socache configuration directives handlers.
1221 static const char *set_cache_socache(cmd_parms *cmd, void *in_struct_ptr,
1224 cache_socache_conf *conf = ap_get_module_config(cmd->server->module_config,
1225 &cache_socache_module);
1226 cache_socache_provider_conf *provider = conf->provider
1227 = apr_pcalloc(cmd->pool, sizeof(cache_socache_provider_conf));
1229 const char *err = NULL, *sep, *name;
1231 /* Argument is of form 'name:args' or just 'name'. */
1232 sep = ap_strchr_c(arg, ':');
1234 name = apr_pstrmemdup(cmd->pool, arg, sep - arg);
1236 provider->args = sep;
1242 provider->socache_provider = ap_lookup_provider(AP_SOCACHE_PROVIDER_GROUP,
1243 name, AP_SOCACHE_PROVIDER_VERSION);
1244 if (provider->socache_provider == NULL) {
1245 err = apr_psprintf(cmd->pool,
1246 "Unknown socache provider '%s'. Maybe you need "
1247 "to load the appropriate socache module "
1248 "(mod_socache_%s?)", name, name);
1253 static const char *set_cache_max(cmd_parms *parms, void *in_struct_ptr,
1256 cache_socache_dir_conf *dconf = (cache_socache_dir_conf *) in_struct_ptr;
1258 if (apr_strtoff(&dconf->max, arg, NULL, 10) != APR_SUCCESS
1259 || dconf->max < 1024 || dconf->max > APR_UINT32_MAX) {
1260 return "CacheSocacheMaxSize argument must be a integer representing "
1261 "the max size of a cached entry (headers and body), at least 1024 "
1262 "and at most " APR_STRINGIFY(APR_UINT32_MAX);
1268 static const char *set_cache_maxtime(cmd_parms *parms, void *in_struct_ptr,
1271 cache_socache_dir_conf *dconf = (cache_socache_dir_conf *) in_struct_ptr;
1274 if (apr_strtoff(&seconds, arg, NULL, 10) != APR_SUCCESS || seconds < 0) {
1275 return "CacheSocacheMaxTime argument must be the maximum amount of time in seconds to cache an entry.";
1277 dconf->maxtime = apr_time_from_sec(seconds);
1278 dconf->maxtime_set = 1;
1282 static const char *set_cache_mintime(cmd_parms *parms, void *in_struct_ptr,
1285 cache_socache_dir_conf *dconf = (cache_socache_dir_conf *) in_struct_ptr;
1288 if (apr_strtoff(&seconds, arg, NULL, 10) != APR_SUCCESS || seconds < 0) {
1289 return "CacheSocacheMinTime argument must be the minimum amount of time in seconds to cache an entry.";
1291 dconf->mintime = apr_time_from_sec(seconds);
1292 dconf->mintime_set = 1;
1296 static const char *set_cache_readsize(cmd_parms *parms, void *in_struct_ptr,
1299 cache_socache_dir_conf *dconf = (cache_socache_dir_conf *) in_struct_ptr;
1301 if (apr_strtoff(&dconf->readsize, arg, NULL, 10) != APR_SUCCESS
1302 || dconf->readsize < 0) {
1303 return "CacheSocacheReadSize argument must be a non-negative integer representing the max amount of data to cache in go.";
1305 dconf->readsize_set = 1;
1309 static const char *set_cache_readtime(cmd_parms *parms, void *in_struct_ptr,
1312 cache_socache_dir_conf *dconf = (cache_socache_dir_conf *) in_struct_ptr;
1313 apr_off_t milliseconds;
1315 if (apr_strtoff(&milliseconds, arg, NULL, 10) != APR_SUCCESS
1316 || milliseconds < 0) {
1317 return "CacheSocacheReadTime argument must be a non-negative integer representing the max amount of time taken to cache in go.";
1319 dconf->readtime = apr_time_from_msec(milliseconds);
1320 dconf->readtime_set = 1;
1324 static apr_status_t remove_lock(void *data)
1326 if (socache_mutex) {
1327 apr_global_mutex_destroy(socache_mutex);
1328 socache_mutex = NULL;
1333 static apr_status_t destroy_cache(void *data)
1335 server_rec *s = data;
1336 cache_socache_conf *conf =
1337 ap_get_module_config(s->module_config, &cache_socache_module);
1338 if (conf->provider && conf->provider->socache_instance) {
1339 conf->provider->socache_provider->destroy(
1340 conf->provider->socache_instance, s);
1341 conf->provider->socache_instance = NULL;
1346 static int socache_status_hook(request_rec *r, int flags)
1348 apr_status_t status = APR_SUCCESS;
1349 cache_socache_conf *conf = ap_get_module_config(r->server->module_config,
1350 &cache_socache_module);
1351 if (!conf->provider || !conf->provider->socache_provider ||
1352 !conf->provider->socache_instance) {
1356 if (!(flags & AP_STATUS_SHORT)) {
1358 "<table cellspacing=0 cellpadding=0>\n"
1359 "<tr><td bgcolor=\"#000000\">\n"
1360 "<b><font color=\"#ffffff\" face=\"Arial,Helvetica\">"
1361 "mod_cache_socache Status:</font></b>\n"
1363 "<tr><td bgcolor=\"#ffffff\">\n", r);
1366 ap_rputs("ModCacheSocacheStatus\n", r);
1369 if (socache_mutex) {
1370 status = apr_global_mutex_lock(socache_mutex);
1371 if (status != APR_SUCCESS) {
1372 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02816)
1373 "could not acquire lock for cache status");
1377 if (status != APR_SUCCESS) {
1378 if (!(flags & AP_STATUS_SHORT)) {
1379 ap_rputs("No cache status data available\n", r);
1382 ap_rputs("NotAvailable\n", r);
1385 conf->provider->socache_provider->status(conf->provider->socache_instance,
1389 if (socache_mutex && status == APR_SUCCESS) {
1390 status = apr_global_mutex_unlock(socache_mutex);
1391 if (status != APR_SUCCESS) {
1392 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02817)
1393 "could not release lock for cache status");
1397 if (!(flags & AP_STATUS_SHORT)) {
1398 ap_rputs("</td></tr>\n</table>\n", r);
1403 static void socache_status_register(apr_pool_t *p)
1405 APR_OPTIONAL_HOOK(ap, status_hook, socache_status_hook, NULL, NULL, APR_HOOK_MIDDLE);
1408 static int socache_precfg(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptmp)
1410 apr_status_t rv = ap_mutex_register(pconf, cache_socache_id, NULL,
1411 APR_LOCK_DEFAULT, 0);
1412 if (rv != APR_SUCCESS) {
1413 ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, plog, APLOGNO(02390)
1414 "failed to register %s mutex", cache_socache_id);
1415 return 500; /* An HTTP status would be a misnomer! */
1418 /* Register to handle mod_status status page generation */
1419 socache_status_register(pconf);
1424 static int socache_post_config(apr_pool_t *pconf, apr_pool_t *plog,
1425 apr_pool_t *ptmp, server_rec *base_server)
1430 static struct ap_socache_hints socache_hints =
1431 { 64, 2048, 60000000 };
1433 for (s = base_server; s; s = s->next) {
1434 cache_socache_conf *conf =
1435 ap_get_module_config(s->module_config, &cache_socache_module);
1437 if (!conf->provider) {
1441 if (!socache_mutex && conf->provider->socache_provider->flags
1442 & AP_SOCACHE_FLAG_NOTMPSAFE) {
1444 rv = ap_global_mutex_create(&socache_mutex, NULL, cache_socache_id,
1446 if (rv != APR_SUCCESS) {
1447 ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, plog, APLOGNO(02391)
1448 "failed to create %s mutex", cache_socache_id);
1449 return 500; /* An HTTP status would be a misnomer! */
1451 apr_pool_cleanup_register(pconf, NULL, remove_lock,
1452 apr_pool_cleanup_null);
1455 errmsg = conf->provider->socache_provider->create(
1456 &conf->provider->socache_instance, conf->provider->args, ptmp,
1459 ap_log_perror(APLOG_MARK, APLOG_CRIT, 0, plog,
1460 APLOGNO(02392) "%s", errmsg);
1461 return 500; /* An HTTP status would be a misnomer! */
1464 rv = conf->provider->socache_provider->init(
1465 conf->provider->socache_instance, cache_socache_id,
1466 &socache_hints, s, pconf);
1467 if (rv != APR_SUCCESS) {
1468 ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, plog, APLOGNO(02393)
1469 "failed to initialise %s cache", cache_socache_id);
1470 return 500; /* An HTTP status would be a misnomer! */
1472 apr_pool_cleanup_register(pconf, (void *) s, destroy_cache,
1473 apr_pool_cleanup_null);
1480 static void socache_child_init(apr_pool_t *p, server_rec *s)
1484 if (!socache_mutex) {
1485 return; /* don't waste the overhead of creating mutex & cache */
1487 lock = apr_global_mutex_lockfile(socache_mutex);
1488 rv = apr_global_mutex_child_init(&socache_mutex, lock, p);
1489 if (rv != APR_SUCCESS) {
1490 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(02394)
1491 "failed to initialise mutex in child_init");
1495 static const command_rec cache_socache_cmds[] =
1497 AP_INIT_TAKE1("CacheSocache", set_cache_socache, NULL, RSRC_CONF,
1498 "The shared object cache to store cache files"),
1499 AP_INIT_TAKE1("CacheSocacheMaxTime", set_cache_maxtime, NULL, RSRC_CONF | ACCESS_CONF,
1500 "The maximum cache expiry age to cache a document in seconds"),
1501 AP_INIT_TAKE1("CacheSocacheMinTime", set_cache_mintime, NULL, RSRC_CONF | ACCESS_CONF,
1502 "The minimum cache expiry age to cache a document in seconds"),
1503 AP_INIT_TAKE1("CacheSocacheMaxSize", set_cache_max, NULL, RSRC_CONF | ACCESS_CONF,
1504 "The maximum cache entry size (headers and body) to cache a document"),
1505 AP_INIT_TAKE1("CacheSocacheReadSize", set_cache_readsize, NULL, RSRC_CONF | ACCESS_CONF,
1506 "The maximum quantity of data to attempt to read and cache in one go"),
1507 AP_INIT_TAKE1("CacheSocacheReadTime", set_cache_readtime, NULL, RSRC_CONF | ACCESS_CONF,
1508 "The maximum time taken to attempt to read and cache in go"),
1512 static const cache_provider cache_socache_provider =
1514 &remove_entity, &store_headers, &store_body, &recall_headers, &recall_body,
1515 &create_entity, &open_entity, &remove_url, &commit_entity,
1519 static void cache_socache_register_hook(apr_pool_t *p)
1521 /* cache initializer */
1522 ap_register_provider(p, CACHE_PROVIDER_GROUP, "socache", "0",
1523 &cache_socache_provider);
1524 ap_hook_pre_config(socache_precfg, NULL, NULL, APR_HOOK_MIDDLE);
1525 ap_hook_post_config(socache_post_config, NULL, NULL, APR_HOOK_MIDDLE);
1526 ap_hook_child_init(socache_child_init, NULL, NULL, APR_HOOK_MIDDLE);
1529 AP_DECLARE_MODULE(cache_socache) = { STANDARD20_MODULE_STUFF,
1530 create_dir_config, /* create per-directory config structure */
1531 merge_dir_config, /* merge per-directory config structures */
1532 create_config, /* create per-server config structure */
1533 merge_config, /* merge per-server config structures */
1534 cache_socache_cmds, /* command apr_table_t */
1535 cache_socache_register_hook /* register hooks */