]> granicus.if.org Git - apache/blob - modules/cache/mod_cache_socache.c
Fix a corner case where automatic APLOGNO number generation generates invalid code...
[apache] / modules / cache / mod_cache_socache.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 #include "apr_lib.h"
18 #include "apr_file_io.h"
19 #include "apr_strings.h"
20 #include "apr_buckets.h"
21 #include "httpd.h"
22 #include "http_config.h"
23 #include "http_log.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"
32
33 #include "mod_cache.h"
34 #include "mod_status.h"
35
36 #include "cache_socache_common.h"
37
38 /*
39  * mod_cache_socache: Shared Object Cache Based HTTP 1.1 Cache.
40  *
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)
48  *
49  * Format #1:
50  *   apr_uint32_t format;
51  *   apr_time_t expire;
52  *   apr_array_t vary_headers (delimited by CRLF)
53  *
54  * Format #2:
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)
58  *   CRLF
59  *   r->headers_in (delimited by CRLF)
60  *   CRLF
61  */
62
63 module AP_MODULE_DECLARE_DATA cache_socache_module;
64
65 /*
66  * cache_socache_object_t
67  * Pointed to by cache_object_t::vobj
68  */
69 typedef struct cache_socache_object_t
70 {
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 */
81
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;
89
90 /*
91  * mod_cache_socache configuration
92  */
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
98
99 typedef struct cache_socache_provider_conf
100 {
101     const char *args;
102     ap_socache_provider_t *socache_provider;
103     ap_socache_instance_t *socache_instance;
104 } cache_socache_provider_conf;
105
106 typedef struct cache_socache_conf
107 {
108     cache_socache_provider_conf *provider;
109 } cache_socache_conf;
110
111 typedef struct cache_socache_dir_conf
112 {
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;
124
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;
128
129 /*
130  * Local static functions
131  */
132
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)
135 {
136     apr_size_t val = *slider;
137
138     while (*slider < buffer_len) {
139         if (buffer[*slider] == '\r') {
140             if (val == *slider) {
141                 (*slider)++;
142                 return APR_SUCCESS;
143             }
144             *((const char **) apr_array_push(arr)) = apr_pstrndup(r->pool,
145                     (const char *) buffer + val, *slider - val);
146             (*slider)++;
147             if (buffer[*slider] == '\n') {
148                 (*slider)++;
149             }
150             val = *slider;
151         }
152         else if (buffer[*slider] == '\0') {
153             (*slider)++;
154             return APR_SUCCESS;
155         }
156         else {
157             (*slider)++;
158         }
159     }
160
161     return APR_EOF;
162 }
163
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)
166 {
167     int i, len;
168     const char **elts;
169
170     elts = (const char **) arr->elts;
171
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) {
175             return APR_EOF;
176         }
177         len = apr_snprintf(buffer ? (char *) buffer + *slider : NULL,
178                 buffer ? buffer_len - *slider : 0, "%s" CRLF, elts[i]);
179         *slider += len;
180     }
181     if (buffer) {
182         memcpy(buffer + *slider, CRLF, sizeof(CRLF) - 1);
183     }
184     *slider += sizeof(CRLF) - 1;
185
186     return APR_SUCCESS;
187 }
188
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,
191         apr_size_t *slider)
192 {
193     apr_size_t key = *slider, colon = 0, len = 0;
194
195     while (*slider < buffer_len) {
196         if (buffer[*slider] == ':') {
197             if (!colon) {
198                 colon = *slider;
199             }
200             (*slider)++;
201         }
202         else if (buffer[*slider] == '\r') {
203             len = colon;
204             if (key == *slider) {
205                 (*slider)++;
206                 if (buffer[*slider] == '\n') {
207                     (*slider)++;
208                 }
209                 return APR_SUCCESS;
210             }
211             if (!colon || buffer[colon++] != ':') {
212                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02344)
213                         "Premature end of cache headers.");
214                 return APR_EGENERAL;
215             }
216             /* Do not go past the \r from above as apr_isspace('\r') is true */
217             while (apr_isspace(buffer[colon]) && (colon < *slider)) {
218                 colon++;
219             }
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));
223             (*slider)++;
224             if (buffer[*slider] == '\n') {
225                 (*slider)++;
226             }
227             key = *slider;
228             colon = 0;
229         }
230         else if (buffer[*slider] == '\0') {
231             (*slider)++;
232             return APR_SUCCESS;
233         }
234         else {
235             (*slider)++;
236         }
237     }
238
239     return APR_EOF;
240 }
241
242 static apr_status_t store_table(apr_table_t *table, unsigned char *buffer,
243         apr_size_t buffer_len, apr_size_t *slider)
244 {
245     int i, len;
246     apr_table_entry_t *elts;
247
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) {
254                 return APR_EOF;
255             }
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);
259             *slider += len;
260         }
261     }
262     if (3 >= buffer_len - *slider) {
263         return APR_EOF;
264     }
265     if (buffer) {
266         memcpy(buffer + *slider, CRLF, sizeof(CRLF) - 1);
267     }
268     *slider += sizeof(CRLF) - 1;
269
270     return APR_SUCCESS;
271 }
272
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)
276 {
277     struct iovec *iov;
278     int i, k;
279     int nvec;
280     const char *header;
281     const char **elts;
282
283     nvec = (varray->nelts * 2) + 1;
284     iov = apr_palloc(p, sizeof(struct iovec) * nvec);
285     elts = (const char **) varray->elts;
286
287     /* TODO:
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.
292      *
293      * The majority are case insenstive if they are values (encoding etc).
294      * Most of rfc2616 is case insensitive on header contents.
295      *
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.
309      */
310
311     for (i = 0, k = 0; i < varray->nelts; i++) {
312         header = apr_table_get(headers, elts[i]);
313         if (!header) {
314             header = "";
315         }
316         iov[k].iov_base = (char*) elts[i];
317         iov[k].iov_len = strlen(elts[i]);
318         k++;
319         iov[k].iov_base = (char*) header;
320         iov[k].iov_len = strlen(header);
321         k++;
322     }
323     iov[k].iov_base = (char*) oldkey;
324     iov[k].iov_len = strlen(oldkey);
325     k++;
326
327     return apr_pstrcatv(p, iov, k, newkeylen);
328 }
329
330 static int array_alphasort(const void *fn1, const void *fn2)
331 {
332     return strcmp(*(char**) fn1, *(char**) fn2);
333 }
334
335 static void tokens_to_array(apr_pool_t *p, const char *data,
336         apr_array_header_t *arr)
337 {
338     char *token;
339
340     while ((token = ap_get_list_item(p, &data)) != NULL) {
341         *((const char **) apr_array_push(arr)) = token;
342     }
343
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);
346 }
347
348 /*
349  * Hook and mod_cache callback functions
350  */
351 static int create_entity(cache_handle_t *h, request_rec *r, const char *key,
352         apr_off_t len, apr_bucket_brigade *bb)
353 {
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);
358     cache_object_t *obj;
359     cache_socache_object_t *sobj;
360     apr_size_t total;
361
362     if (conf->provider == NULL) {
363         return DECLINED;
364     }
365
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",
371                 key);
372         return DECLINED;
373     }
374
375     /*
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.
383      *
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.
387      */
388     if (len < 0) {
389         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02346)
390                 "URL '%s' had no explicit size, ignoring", key);
391         return DECLINED;
392     }
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);
398         return DECLINED;
399     }
400
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,
405                     &total)) {
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);
410         return DECLINED;
411     }
412
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);
418         return DECLINED;
419     }
420
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));
424
425     obj->key = apr_pstrdup(r->pool, key);
426     sobj->key = obj->key;
427     sobj->name = obj->key;
428
429     return OK;
430 }
431
432 static apr_status_t sobj_body_pre_cleanup(void *baton)
433 {
434     cache_socache_object_t *sobj = baton;
435     apr_brigade_cleanup(sobj->body);
436     sobj->body = NULL;
437     return APR_SUCCESS;
438 }
439
440 static int open_entity(cache_handle_t *h, request_rec *r, const char *key)
441 {
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);
446     apr_uint32_t format;
447     apr_size_t slider;
448     unsigned int buffer_len;
449     const char *nkey;
450     apr_status_t rc;
451     cache_object_t *obj;
452     cache_info *info;
453     cache_socache_object_t *sobj;
454     apr_size_t len;
455
456     nkey = NULL;
457     h->cache_obj = NULL;
458
459     if (!conf->provider || !conf->provider->socache_instance) {
460         return DECLINED;
461     }
462
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));
466
467     info = &(obj->info);
468
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.
472      */
473     apr_pool_create(&sobj->pool, r->pool);
474
475     sobj->buffer = apr_palloc(sobj->pool, dconf->max);
476     sobj->buffer_len = dconf->max;
477
478     /* attempt to retrieve the cached entry */
479     if (socache_mutex) {
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);
485             sobj->pool = NULL;
486             return DECLINED;
487         }
488     }
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);
493     if (socache_mutex) {
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);
499             sobj->pool = NULL;
500             return DECLINED;
501         }
502     }
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);
507         sobj->pool = NULL;
508         return DECLINED;
509     }
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);
514         sobj->pool = NULL;
515         return DECLINED;
516     }
517
518     /* read the format from the cache file */
519     memcpy(&format, sobj->buffer, sizeof(format));
520     slider = sizeof(format);
521
522     if (format == CACHE_SOCACHE_VARY_FORMAT_VERSION) {
523         apr_array_header_t* varray;
524         apr_time_t expire;
525
526         memcpy(&expire, sobj->buffer + slider, sizeof(expire));
527         slider += sizeof(expire);
528
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);
535             sobj->pool = NULL;
536             return DECLINED;
537         }
538
539         nkey = regen_key(r->pool, r->headers_in, varray, key, &len);
540
541         /* attempt to retrieve the cached entry */
542         if (socache_mutex) {
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);
548                 sobj->pool = NULL;
549                 return DECLINED;
550             }
551         }
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);
557         if (socache_mutex) {
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);
563                 sobj->pool = NULL;
564                 return DECLINED;
565             }
566         }
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);
571             sobj->pool = NULL;
572             return DECLINED;
573         }
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);
577             goto fail;
578         }
579
580     }
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);
585         goto fail;
586     }
587     else {
588         nkey = key;
589     }
590
591     obj->key = nkey;
592     sobj->key = nkey;
593     sobj->name = key;
594
595     if (buffer_len >= sizeof(cache_socache_info_t)) {
596         memcpy(&sobj->socache_info, sobj->buffer, sizeof(cache_socache_info_t));
597     }
598     else {
599         ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, APLOGNO(02360)
600                 "Cache entry for key '%s' too short, removing", nkey);
601         goto fail;
602     }
603     slider = sizeof(cache_socache_info_t);
604
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;
611
612     memcpy(&info->control, &sobj->socache_info.control, sizeof(cache_control_t));
613
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);
620             sobj->pool = NULL;
621             return DECLINED;
622         }
623         slider += sobj->socache_info.name_len;
624     }
625     else {
626         ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, APLOGNO(02362)
627                 "Cache entry for key '%s' too short, removing", nkey);
628         goto fail;
629     }
630
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",
635                 sobj->key);
636         apr_pool_destroy(sobj->pool);
637         sobj->pool = NULL;
638         return DECLINED;
639     }
640
641     h->req_hdrs = apr_table_make(r->pool, 20);
642     h->resp_hdrs = apr_table_make(r->pool, 20);
643
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,
646             &slider)) {
647         ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, APLOGNO(02364)
648                 "Cache entry for key '%s' response headers unreadable, removing", nkey);
649         goto fail;
650     }
651     if (APR_SUCCESS != read_table(h, r, h->req_hdrs, sobj->buffer, buffer_len,
652             &slider)) {
653         ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, APLOGNO(02365)
654                 "Cache entry for key '%s' request headers unreadable, removing", nkey);
655         goto fail;
656     }
657
658     /* Retrieve the body if we have one */
659     len = buffer_len - slider;
660     if (len > 0) {
661         apr_bucket *e;
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
670          * a noop otherwise).
671          */
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);
677     }
678
679     /* make the configuration stick */
680     h->cache_obj = obj;
681     obj->vobj = sobj;
682
683     return OK;
684
685 fail:
686     if (socache_mutex) {
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);
692             sobj->pool = NULL;
693             return DECLINED;
694         }
695     }
696     conf->provider->socache_provider->remove(
697             conf->provider->socache_instance, r->server,
698             (unsigned char *) nkey, strlen(nkey), r->pool);
699     if (socache_mutex) {
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);
704         }
705     }
706     apr_pool_destroy(sobj->pool);
707     sobj->pool = NULL;
708     return DECLINED;
709 }
710
711 static int remove_entity(cache_handle_t *h)
712 {
713     /* Null out the cache object pointer so next time we start from scratch  */
714     h->cache_obj = NULL;
715     return OK;
716 }
717
718 static int remove_url(cache_handle_t *h, request_rec *r)
719 {
720     cache_socache_conf *conf = ap_get_module_config(r->server->module_config,
721             &cache_socache_module);
722     cache_socache_object_t *sobj;
723
724     sobj = (cache_socache_object_t *) h->cache_obj->vobj;
725     if (!sobj) {
726         return DECLINED;
727     }
728
729     /* Remove the key from the cache */
730     if (socache_mutex) {
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);
736             sobj->pool = NULL;
737             return DECLINED;
738         }
739     }
740     conf->provider->socache_provider->remove(conf->provider->socache_instance,
741             r->server, (unsigned char *) sobj->key, strlen(sobj->key), r->pool);
742     if (socache_mutex) {
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);
748             sobj->pool = NULL;
749             return DECLINED;
750         }
751     }
752
753     return OK;
754 }
755
756 static apr_status_t recall_headers(cache_handle_t *h, request_rec *r)
757 {
758     /* we recalled the headers during open_entity, so do nothing */
759     return APR_SUCCESS;
760 }
761
762 static apr_status_t recall_body(cache_handle_t *h, apr_pool_t *p,
763         apr_bucket_brigade *bb)
764 {
765     cache_socache_object_t *sobj = (cache_socache_object_t*) h->cache_obj->vobj;
766
767     if (sobj->body) {
768         APR_BRIGADE_CONCAT(bb, sobj->body);
769     }
770
771     return APR_SUCCESS;
772 }
773
774 static apr_status_t store_headers(cache_handle_t *h, request_rec *r,
775         cache_info *info)
776 {
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);
781     apr_size_t slider;
782     apr_status_t rv;
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;
786
787     memcpy(&h->cache_obj->info, info, sizeof(cache_info));
788
789     if (r->headers_out) {
790         sobj->headers_out = ap_cache_cacheable_headers_out(r);
791     }
792
793     if (r->headers_in) {
794         sobj->headers_in = ap_cache_cacheable_headers_in(r);
795     }
796
797     sobj->expire
798             = obj->info.expire > r->request_time + dconf->maxtime ? r->request_time
799                     + dconf->maxtime
800                     : obj->info.expire + dconf->mintime;
801
802     apr_pool_create(&sobj->pool, r->pool);
803
804     sobj->buffer = apr_palloc(sobj->pool, dconf->max);
805     sobj->buffer_len = dconf->max;
806     socache_info = (cache_socache_info_t *) sobj->buffer;
807
808     if (sobj->headers_out) {
809         const char *vary;
810
811         vary = apr_table_get(sobj->headers_out, "Vary");
812
813         if (vary) {
814             apr_array_header_t* varray;
815             apr_uint32_t format = CACHE_SOCACHE_VARY_FORMAT_VERSION;
816
817             memcpy(sobj->buffer, &format, sizeof(format));
818             slider = sizeof(format);
819
820             memcpy(sobj->buffer + slider, &obj->info.expire,
821                     sizeof(obj->info.expire));
822             slider += sizeof(obj->info.expire);
823
824             varray = apr_array_make(r->pool, 6, sizeof(char*));
825             tokens_to_array(r->pool, vary, varray);
826
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",
831                         obj->key);
832                 apr_pool_destroy(sobj->pool);
833                 sobj->pool = NULL;
834                 return rv;
835             }
836             if (socache_mutex) {
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);
842                     sobj->pool = NULL;
843                     return status;
844                 }
845             }
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,
850                     sobj->pool);
851             if (socache_mutex) {
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);
856                 }
857             }
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);
862                 sobj->pool = NULL;
863                 return rv;
864             }
865
866             obj->key = sobj->key = regen_key(r->pool, sobj->headers_in, varray,
867                                              sobj->name, NULL);
868         }
869     }
870
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;
878
879     if (r->header_only && r->status != HTTP_NOT_MODIFIED) {
880         socache_info->header_only = 1;
881     }
882     else {
883         socache_info->header_only = sobj->socache_info.header_only;
884     }
885
886     socache_info->name_len = strlen(sobj->name);
887
888     memcpy(&socache_info->control, &obj->info.control, sizeof(cache_control_t));
889     slider = sizeof(cache_socache_info_t);
890
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",
894                 sobj->name);
895         apr_pool_destroy(sobj->pool);
896         sobj->pool = NULL;
897         return APR_EGENERAL;
898     }
899     memcpy(sobj->buffer + slider, sobj->name, socache_info->name_len);
900     slider += socache_info->name_len;
901
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);
908             sobj->pool = NULL;
909             return APR_EGENERAL;
910         }
911     }
912
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",
920                     sobj->key);
921             apr_pool_destroy(sobj->pool);
922             sobj->pool = NULL;
923             return APR_EGENERAL;
924         }
925     }
926
927     sobj->body_offset = slider;
928
929     return APR_SUCCESS;
930 }
931
932 static apr_status_t store_body(cache_handle_t *h, request_rec *r,
933         apr_bucket_brigade *in, apr_bucket_brigade *out)
934 {
935     apr_bucket *e;
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);
941     int seen_eos = 0;
942
943     if (!sobj->offset) {
944         sobj->offset = dconf->readsize;
945     }
946     if (!sobj->timeout && dconf->readtime) {
947         sobj->timeout = apr_time_now() + dconf->readtime;
948     }
949
950     if (!sobj->newbody) {
951         sobj->body_length = 0;
952         sobj->newbody = 1;
953     }
954     if (sobj->offset) {
955         apr_brigade_partition(in, sobj->offset, &e);
956     }
957
958     while (APR_SUCCESS == rv && !APR_BRIGADE_EMPTY(in)) {
959         const char *str;
960         apr_size_t length;
961
962         e = APR_BRIGADE_FIRST(in);
963
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);
968             continue;
969         }
970
971         /* have we seen eos yet? */
972         if (APR_BUCKET_IS_EOS(e)) {
973             seen_eos = 1;
974             sobj->done = 1;
975             APR_BUCKET_REMOVE(e);
976             APR_BRIGADE_INSERT_TAIL(out, e);
977             break;
978         }
979
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);
984             break;
985         }
986
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);
991             continue;
992         }
993
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",
1001                     h->cache_obj->key);
1002             /* Remove the intermediate cache file and return non-APR_SUCCESS */
1003             apr_pool_destroy(sobj->pool);
1004             sobj->pool = NULL;
1005             return rv;
1006         }
1007
1008         /* don't write empty buckets to the cache */
1009         if (!length) {
1010             continue;
1011         }
1012
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);
1021             sobj->pool = NULL;
1022             return APR_EGENERAL;
1023         }
1024         memcpy(sobj->buffer + sobj->body_offset + sobj->body_length - length,
1025                str, length);
1026
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.
1031          */
1032         sobj->offset -= length;
1033         if (sobj->offset <= 0) {
1034             sobj->offset = 0;
1035             break;
1036         }
1037         if ((dconf->readtime && apr_time_now() > sobj->timeout)) {
1038             sobj->timeout = 0;
1039             break;
1040         }
1041
1042     }
1043
1044     /* Was this the final bucket? If yes, perform sanity checks.
1045      */
1046     if (seen_eos) {
1047         const char *cl_header = apr_table_get(r->headers_out, "Content-Length");
1048
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.",
1053                     h->cache_obj->key);
1054             apr_pool_destroy(sobj->pool);
1055             sobj->pool = NULL;
1056             return APR_EGENERAL;
1057         }
1058         if (cl_header) {
1059             apr_off_t cl;
1060             char *cl_endp;
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",
1065                         h->cache_obj->key);
1066                 apr_pool_destroy(sobj->pool);
1067                 sobj->pool = NULL;
1068                 return APR_EGENERAL;
1069             }
1070         }
1071
1072         /* All checks were fine, we're good to go when the commit comes */
1073
1074     }
1075
1076     return APR_SUCCESS;
1077 }
1078
1079 static apr_status_t commit_entity(cache_handle_t *h, request_rec *r)
1080 {
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;
1085     apr_status_t rv;
1086
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);
1093             sobj->pool = NULL;
1094             return status;
1095         }
1096     }
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);
1107             sobj->pool = NULL;
1108             return status;
1109         }
1110     }
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);
1114         goto fail;
1115     }
1116
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));
1120
1121     apr_pool_destroy(sobj->pool);
1122     sobj->pool = NULL;
1123
1124     return APR_SUCCESS;
1125
1126 fail:
1127     /* For safety, remove any existing entry on failure, just in case it could not
1128      * be revalidated successfully.
1129      */
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);
1136             sobj->pool = NULL;
1137             return rv;
1138         }
1139     }
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);
1147         }
1148     }
1149
1150     apr_pool_destroy(sobj->pool);
1151     sobj->pool = NULL;
1152     return rv;
1153 }
1154
1155 static apr_status_t invalidate_entity(cache_handle_t *h, request_rec *r)
1156 {
1157     /* mark the entity as invalidated */
1158     h->cache_obj->info.control.invalidated = 1;
1159
1160     return commit_entity(h, r);
1161 }
1162
1163 static void *create_dir_config(apr_pool_t *p, char *dummy)
1164 {
1165     cache_socache_dir_conf *dconf =
1166             apr_pcalloc(p, sizeof(cache_socache_dir_conf));
1167
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;
1173
1174     return dconf;
1175 }
1176
1177 static void *merge_dir_config(apr_pool_t *p, void *basev, void *addv)
1178 {
1179     cache_socache_dir_conf
1180             *new =
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;
1184
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;
1195
1196     return new;
1197 }
1198
1199 static void *create_config(apr_pool_t *p, server_rec *s)
1200 {
1201     cache_socache_conf *conf = apr_pcalloc(p, sizeof(cache_socache_conf));
1202
1203     return conf;
1204 }
1205
1206 static void *merge_config(apr_pool_t *p, void *basev, void *overridesv)
1207 {
1208     cache_socache_conf *ps;
1209     cache_socache_conf *base = (cache_socache_conf *) basev;
1210     cache_socache_conf *overrides = (cache_socache_conf *) overridesv;
1211
1212     /* socache server config only has one field */
1213     ps = overrides ? overrides : base;
1214
1215     return ps;
1216 }
1217
1218 /*
1219  * mod_cache_socache configuration directives handlers.
1220  */
1221 static const char *set_cache_socache(cmd_parms *cmd, void *in_struct_ptr,
1222         const char *arg)
1223 {
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));
1228
1229     const char *err = NULL, *sep, *name;
1230
1231     /* Argument is of form 'name:args' or just 'name'. */
1232     sep = ap_strchr_c(arg, ':');
1233     if (sep) {
1234         name = apr_pstrmemdup(cmd->pool, arg, sep - arg);
1235         sep++;
1236         provider->args = sep;
1237     }
1238     else {
1239         name = arg;
1240     }
1241
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);
1249     }
1250     return err;
1251 }
1252
1253 static const char *set_cache_max(cmd_parms *parms, void *in_struct_ptr,
1254         const char *arg)
1255 {
1256     cache_socache_dir_conf *dconf = (cache_socache_dir_conf *) in_struct_ptr;
1257
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);
1263     }
1264     dconf->max_set = 1;
1265     return NULL;
1266 }
1267
1268 static const char *set_cache_maxtime(cmd_parms *parms, void *in_struct_ptr,
1269         const char *arg)
1270 {
1271     cache_socache_dir_conf *dconf = (cache_socache_dir_conf *) in_struct_ptr;
1272     apr_off_t seconds;
1273
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.";
1276     }
1277     dconf->maxtime = apr_time_from_sec(seconds);
1278     dconf->maxtime_set = 1;
1279     return NULL;
1280 }
1281
1282 static const char *set_cache_mintime(cmd_parms *parms, void *in_struct_ptr,
1283         const char *arg)
1284 {
1285     cache_socache_dir_conf *dconf = (cache_socache_dir_conf *) in_struct_ptr;
1286     apr_off_t seconds;
1287
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.";
1290     }
1291     dconf->mintime = apr_time_from_sec(seconds);
1292     dconf->mintime_set = 1;
1293     return NULL;
1294 }
1295
1296 static const char *set_cache_readsize(cmd_parms *parms, void *in_struct_ptr,
1297         const char *arg)
1298 {
1299     cache_socache_dir_conf *dconf = (cache_socache_dir_conf *) in_struct_ptr;
1300
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.";
1304     }
1305     dconf->readsize_set = 1;
1306     return NULL;
1307 }
1308
1309 static const char *set_cache_readtime(cmd_parms *parms, void *in_struct_ptr,
1310         const char *arg)
1311 {
1312     cache_socache_dir_conf *dconf = (cache_socache_dir_conf *) in_struct_ptr;
1313     apr_off_t milliseconds;
1314
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.";
1318     }
1319     dconf->readtime = apr_time_from_msec(milliseconds);
1320     dconf->readtime_set = 1;
1321     return NULL;
1322 }
1323
1324 static apr_status_t remove_lock(void *data)
1325 {
1326     if (socache_mutex) {
1327         apr_global_mutex_destroy(socache_mutex);
1328         socache_mutex = NULL;
1329     }
1330     return APR_SUCCESS;
1331 }
1332
1333 static apr_status_t destroy_cache(void *data)
1334 {
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;
1342     }
1343     return APR_SUCCESS;
1344 }
1345
1346 static int socache_status_hook(request_rec *r, int flags)
1347 {
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) {
1353         return DECLINED;
1354     }
1355
1356     if (!(flags & AP_STATUS_SHORT)) {
1357         ap_rputs("<hr>\n"
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"
1362                  "</td></tr>\n"
1363                  "<tr><td bgcolor=\"#ffffff\">\n", r);
1364     }
1365     else {
1366         ap_rputs("ModCacheSocacheStatus\n", r);
1367     }
1368
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");
1374         }
1375     }
1376
1377     if (status != APR_SUCCESS) {
1378         if (!(flags & AP_STATUS_SHORT)) {
1379             ap_rputs("No cache status data available\n", r);
1380         }
1381         else {
1382             ap_rputs("NotAvailable\n", r);
1383         }
1384     } else {
1385         conf->provider->socache_provider->status(conf->provider->socache_instance,
1386                                                  r, flags);
1387     }
1388
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");
1394         }
1395     }
1396
1397     if (!(flags & AP_STATUS_SHORT)) {
1398         ap_rputs("</td></tr>\n</table>\n", r);
1399     }
1400     return OK;
1401 }
1402
1403 static void socache_status_register(apr_pool_t *p)
1404 {
1405     APR_OPTIONAL_HOOK(ap, status_hook, socache_status_hook, NULL, NULL, APR_HOOK_MIDDLE);
1406 }
1407
1408 static int socache_precfg(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptmp)
1409 {
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! */
1416     }
1417
1418     /* Register to handle mod_status status page generation */
1419     socache_status_register(pconf);
1420
1421     return OK;
1422 }
1423
1424 static int socache_post_config(apr_pool_t *pconf, apr_pool_t *plog,
1425         apr_pool_t *ptmp, server_rec *base_server)
1426 {
1427     server_rec *s;
1428     apr_status_t rv;
1429     const char *errmsg;
1430     static struct ap_socache_hints socache_hints =
1431     { 64, 2048, 60000000 };
1432
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);
1436
1437         if (!conf->provider) {
1438             continue;
1439         }
1440
1441         if (!socache_mutex && conf->provider->socache_provider->flags
1442                 & AP_SOCACHE_FLAG_NOTMPSAFE) {
1443
1444             rv = ap_global_mutex_create(&socache_mutex, NULL, cache_socache_id,
1445                     NULL, s, pconf, 0);
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! */
1450             }
1451             apr_pool_cleanup_register(pconf, NULL, remove_lock,
1452                     apr_pool_cleanup_null);
1453         }
1454
1455         errmsg = conf->provider->socache_provider->create(
1456                 &conf->provider->socache_instance, conf->provider->args, ptmp,
1457                 pconf);
1458         if (errmsg) {
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! */
1462         }
1463
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! */
1471         }
1472         apr_pool_cleanup_register(pconf, (void *) s, destroy_cache,
1473                 apr_pool_cleanup_null);
1474
1475     }
1476
1477     return OK;
1478 }
1479
1480 static void socache_child_init(apr_pool_t *p, server_rec *s)
1481 {
1482     const char *lock;
1483     apr_status_t rv;
1484     if (!socache_mutex) {
1485         return; /* don't waste the overhead of creating mutex & cache */
1486     }
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");
1492     }
1493 }
1494
1495 static const command_rec cache_socache_cmds[] =
1496 {
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"),
1509     { NULL }
1510 };
1511
1512 static const cache_provider cache_socache_provider =
1513 {
1514     &remove_entity, &store_headers, &store_body, &recall_headers, &recall_body,
1515     &create_entity, &open_entity, &remove_url, &commit_entity,
1516     &invalidate_entity
1517 };
1518
1519 static void cache_socache_register_hook(apr_pool_t *p)
1520 {
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);
1527 }
1528
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 */
1536 };