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 
22 #include "apr_version.h"
23 #if !APR_VERSION_AT_LEAST(2,0,0)
24 #include "apu_version.h"
25 #endif
26 
27 #include "httpd.h"
28 #include "http_config.h"
29 #include "http_log.h"
30 #include "http_core.h"
31 #include "http_protocol.h"
32 #include "ap_provider.h"
33 #include "ap_socache.h"
34 #include "util_filter.h"
35 #include "util_script.h"
36 #include "util_charset.h"
37 #include "util_mutex.h"
38 
39 #include "mod_cache.h"
40 #include "mod_status.h"
41 
42 #include "cache_socache_common.h"
43 
44 /*
45  * mod_cache_socache: Shared Object Cache Based HTTP 1.1 Cache.
46  *
47  * Flow to Find the entry:
48  *   Incoming client requests URI /foo/bar/baz
49  *   Fetch URI key (may contain Format #1 or Format #2)
50  *   If format #1 (Contains a list of Vary Headers):
51  *      Use each header name (from .header) with our request values (headers_in) to
52  *      regenerate key using HeaderName+HeaderValue+.../foo/bar/baz
53  *      re-read in key (must be format #2)
54  *
55  * Format #1:
56  *   apr_uint32_t format;
57  *   apr_time_t expire;
58  *   apr_array_t vary_headers (delimited by CRLF)
59  *
60  * Format #2:
61  *   cache_socache_info_t (first sizeof(apr_uint32_t) bytes is the format)
62  *   entity name (sobj->name) [length is in cache_socache_info_t->name_len]
63  *   r->headers_out (delimited by CRLF)
64  *   CRLF
65  *   r->headers_in (delimited by CRLF)
66  *   CRLF
67  */
68 
69 module AP_MODULE_DECLARE_DATA cache_socache_module;
70 
71 /*
72  * cache_socache_object_t
73  * Pointed to by cache_object_t::vobj
74  */
75 typedef struct cache_socache_object_t
76 {
77     apr_pool_t *pool; /* pool */
78     unsigned char *buffer; /* the cache buffer */
79     apr_size_t buffer_len; /* size of the buffer */
80     apr_bucket_brigade *body; /* brigade containing the body, if any */
81     apr_table_t *headers_in; /* Input headers to save */
82     apr_table_t *headers_out; /* Output headers to save */
83     cache_socache_info_t socache_info; /* Header information. */
84     apr_size_t body_offset; /* offset to the start of the body */
85     apr_off_t body_length; /* length of the cached entity body */
86     apr_time_t expire; /* when to expire the entry */
87 
88     const char *name; /* Requested URI without vary bits - suitable for mortals. */
89     const char *key; /* On-disk prefix; URI with Vary bits (if present) */
90     apr_off_t offset; /* Max size to set aside */
91     apr_time_t timeout; /* Max time to set aside */
92     unsigned int newbody :1; /* whether a new body is present */
93     unsigned int done :1; /* Is the attempt to cache complete? */
94 } cache_socache_object_t;
95 
96 /*
97  * mod_cache_socache configuration
98  */
99 #define DEFAULT_MAX_FILE_SIZE 100*1024
100 #define DEFAULT_MAXTIME 86400
101 #define DEFAULT_MINTIME 600
102 #define DEFAULT_READSIZE 0
103 #define DEFAULT_READTIME 0
104 
105 typedef struct cache_socache_provider_conf
106 {
107     const char *args;
108     ap_socache_provider_t *socache_provider;
109     ap_socache_instance_t *socache_instance;
110 } cache_socache_provider_conf;
111 
112 typedef struct cache_socache_conf
113 {
114     cache_socache_provider_conf *provider;
115 } cache_socache_conf;
116 
117 typedef struct cache_socache_dir_conf
118 {
119     apr_off_t max; /* maximum file size for cached files */
120     apr_time_t maxtime; /* maximum expiry time */
121     apr_time_t mintime; /* minimum expiry time */
122     apr_off_t readsize; /* maximum data to attempt to cache in one go */
123     apr_time_t readtime; /* maximum time taken to cache in one go */
124     unsigned int max_set :1;
125     unsigned int maxtime_set :1;
126     unsigned int mintime_set :1;
127     unsigned int readsize_set :1;
128     unsigned int readtime_set :1;
129 } cache_socache_dir_conf;
130 
131 /* Shared object cache and mutex */
132 static const char * const cache_socache_id = "cache-socache";
133 static apr_global_mutex_t *socache_mutex = NULL;
134 
135 /*
136  * Local static functions
137  */
138 
read_array(request_rec * r,apr_array_header_t * arr,unsigned char * buffer,apr_size_t buffer_len,apr_size_t * slider)139 static apr_status_t read_array(request_rec *r, apr_array_header_t *arr,
140         unsigned char *buffer, apr_size_t buffer_len, apr_size_t *slider)
141 {
142     apr_size_t val = *slider;
143 
144     while (*slider < buffer_len) {
145         if (buffer[*slider] == '\r') {
146             if (val == *slider) {
147                 (*slider)++;
148                 return APR_SUCCESS;
149             }
150             *((const char **) apr_array_push(arr)) = apr_pstrndup(r->pool,
151                     (const char *) buffer + val, *slider - val);
152             (*slider)++;
153             if (buffer[*slider] == '\n') {
154                 (*slider)++;
155             }
156             val = *slider;
157         }
158         else if (buffer[*slider] == '\0') {
159             (*slider)++;
160             return APR_SUCCESS;
161         }
162         else {
163             (*slider)++;
164         }
165     }
166 
167     return APR_EOF;
168 }
169 
store_array(apr_array_header_t * arr,unsigned char * buffer,apr_size_t buffer_len,apr_size_t * slider)170 static apr_status_t store_array(apr_array_header_t *arr, unsigned char *buffer,
171         apr_size_t buffer_len, apr_size_t *slider)
172 {
173     int i, len;
174     const char **elts;
175 
176     elts = (const char **) arr->elts;
177 
178     for (i = 0; i < arr->nelts; i++) {
179         apr_size_t e_len = strlen(elts[i]);
180         if (e_len + 3 >= buffer_len - *slider) {
181             return APR_EOF;
182         }
183         len = apr_snprintf(buffer ? (char *) buffer + *slider : NULL,
184                 buffer ? buffer_len - *slider : 0, "%s" CRLF, elts[i]);
185         *slider += len;
186     }
187     if (buffer) {
188         memcpy(buffer + *slider, CRLF, sizeof(CRLF) - 1);
189     }
190     *slider += sizeof(CRLF) - 1;
191 
192     return APR_SUCCESS;
193 }
194 
read_table(cache_handle_t * handle,request_rec * r,apr_table_t * table,unsigned char * buffer,apr_size_t buffer_len,apr_size_t * slider)195 static apr_status_t read_table(cache_handle_t *handle, request_rec *r,
196         apr_table_t *table, unsigned char *buffer, apr_size_t buffer_len,
197         apr_size_t *slider)
198 {
199     apr_size_t key = *slider, colon = 0, len = 0;
200 
201     while (*slider < buffer_len) {
202         if (buffer[*slider] == ':') {
203             if (!colon) {
204                 colon = *slider;
205             }
206             (*slider)++;
207         }
208         else if (buffer[*slider] == '\r') {
209             len = colon;
210             if (key == *slider) {
211                 (*slider)++;
212                 if (buffer[*slider] == '\n') {
213                     (*slider)++;
214                 }
215                 return APR_SUCCESS;
216             }
217             if (!colon || buffer[colon++] != ':') {
218                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02344)
219                         "Premature end of cache headers.");
220                 return APR_EGENERAL;
221             }
222             /* Do not go past the \r from above as apr_isspace('\r') is true */
223             while (apr_isspace(buffer[colon]) && (colon < *slider)) {
224                 colon++;
225             }
226             apr_table_addn(table, apr_pstrmemdup(r->pool, (const char *) buffer
227                     + key, len - key), apr_pstrmemdup(r->pool,
228                     (const char *) buffer + colon, *slider - colon));
229             (*slider)++;
230             if (buffer[*slider] == '\n') {
231                 (*slider)++;
232             }
233             key = *slider;
234             colon = 0;
235         }
236         else if (buffer[*slider] == '\0') {
237             (*slider)++;
238             return APR_SUCCESS;
239         }
240         else {
241             (*slider)++;
242         }
243     }
244 
245     return APR_EOF;
246 }
247 
store_table(apr_table_t * table,unsigned char * buffer,apr_size_t buffer_len,apr_size_t * slider)248 static apr_status_t store_table(apr_table_t *table, unsigned char *buffer,
249         apr_size_t buffer_len, apr_size_t *slider)
250 {
251     int i, len;
252     apr_table_entry_t *elts;
253 
254     elts = (apr_table_entry_t *) apr_table_elts(table)->elts;
255     for (i = 0; i < apr_table_elts(table)->nelts; ++i) {
256         if (elts[i].key != NULL) {
257             apr_size_t key_len = strlen(elts[i].key);
258             apr_size_t val_len = strlen(elts[i].val);
259             if (key_len + val_len + 5 >= buffer_len - *slider) {
260                 return APR_EOF;
261             }
262             len = apr_snprintf(buffer ? (char *) buffer + *slider : NULL,
263                     buffer ? buffer_len - *slider : 0, "%s: %s" CRLF,
264                     elts[i].key, elts[i].val);
265             *slider += len;
266         }
267     }
268     if (3 >= buffer_len - *slider) {
269         return APR_EOF;
270     }
271     if (buffer) {
272         memcpy(buffer + *slider, CRLF, sizeof(CRLF) - 1);
273     }
274     *slider += sizeof(CRLF) - 1;
275 
276     return APR_SUCCESS;
277 }
278 
regen_key(apr_pool_t * p,apr_table_t * headers,apr_array_header_t * varray,const char * oldkey,apr_size_t * newkeylen)279 static const char* regen_key(apr_pool_t *p, apr_table_t *headers,
280                              apr_array_header_t *varray, const char *oldkey,
281                              apr_size_t *newkeylen)
282 {
283     struct iovec *iov;
284     int i, k;
285     int nvec;
286     const char *header;
287     const char **elts;
288 
289     nvec = (varray->nelts * 2) + 1;
290     iov = apr_palloc(p, sizeof(struct iovec) * nvec);
291     elts = (const char **) varray->elts;
292 
293     /* TODO:
294      *    - Handle multiple-value headers better. (sort them?)
295      *    - Handle Case in-sensitive Values better.
296      *        This isn't the end of the world, since it just lowers the cache
297      *        hit rate, but it would be nice to fix.
298      *
299      * The majority are case insenstive if they are values (encoding etc).
300      * Most of rfc2616 is case insensitive on header contents.
301      *
302      * So the better solution may be to identify headers which should be
303      * treated case-sensitive?
304      *  HTTP URI's (3.2.3) [host and scheme are insensitive]
305      *  HTTP method (5.1.1)
306      *  HTTP-date values (3.3.1)
307      *  3.7 Media Types [excerpt]
308      *     The type, subtype, and parameter attribute names are case-
309      *     insensitive. Parameter values might or might not be case-sensitive,
310      *     depending on the semantics of the parameter name.
311      *  4.20 Except [excerpt]
312      *     Comparison of expectation values is case-insensitive for unquoted
313      *     tokens (including the 100-continue token), and is case-sensitive for
314      *     quoted-string expectation-extensions.
315      */
316 
317     for (i = 0, k = 0; i < varray->nelts; i++) {
318         header = apr_table_get(headers, elts[i]);
319         if (!header) {
320             header = "";
321         }
322         iov[k].iov_base = (char*) elts[i];
323         iov[k].iov_len = strlen(elts[i]);
324         k++;
325         iov[k].iov_base = (char*) header;
326         iov[k].iov_len = strlen(header);
327         k++;
328     }
329     iov[k].iov_base = (char*) oldkey;
330     iov[k].iov_len = strlen(oldkey);
331     k++;
332 
333     return apr_pstrcatv(p, iov, k, newkeylen);
334 }
335 
array_alphasort(const void * fn1,const void * fn2)336 static int array_alphasort(const void *fn1, const void *fn2)
337 {
338     return strcmp(*(char**) fn1, *(char**) fn2);
339 }
340 
tokens_to_array(apr_pool_t * p,const char * data,apr_array_header_t * arr)341 static void tokens_to_array(apr_pool_t *p, const char *data,
342         apr_array_header_t *arr)
343 {
344     char *token;
345 
346     while ((token = ap_get_list_item(p, &data)) != NULL) {
347         *((const char **) apr_array_push(arr)) = token;
348     }
349 
350     /* Sort it so that "Vary: A, B" and "Vary: B, A" are stored the same. */
351     qsort((void *) arr->elts, arr->nelts, sizeof(char *), array_alphasort);
352 }
353 
354 /*
355  * Hook and mod_cache callback functions
356  */
create_entity(cache_handle_t * h,request_rec * r,const char * key,apr_off_t len,apr_bucket_brigade * bb)357 static int create_entity(cache_handle_t *h, request_rec *r, const char *key,
358         apr_off_t len, apr_bucket_brigade *bb)
359 {
360     cache_socache_dir_conf *dconf =
361             ap_get_module_config(r->per_dir_config, &cache_socache_module);
362     cache_socache_conf *conf = ap_get_module_config(r->server->module_config,
363             &cache_socache_module);
364     cache_object_t *obj;
365     cache_socache_object_t *sobj;
366     apr_size_t total;
367 
368     if (conf->provider == NULL) {
369         return DECLINED;
370     }
371 
372     /* we don't support caching of range requests (yet) */
373     /* TODO: but we could */
374     if (r->status == HTTP_PARTIAL_CONTENT) {
375         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02345)
376                 "URL %s partial content response not cached",
377                 key);
378         return DECLINED;
379     }
380 
381     /*
382      * We have a chicken and egg problem. We don't know until we
383      * attempt to store_headers just how big the response will be
384      * and whether it will fit in the cache limits set. But we
385      * need to make a decision now as to whether we plan to try.
386      * If we make the wrong decision, we could prevent another
387      * cache implementation, such as cache_disk, from getting the
388      * opportunity to cache, and that would be unfortunate.
389      *
390      * In a series of tests, from cheapest to most expensive,
391      * decide whether or not to ignore this attempt to cache,
392      * with a small margin just to be sure.
393      */
394     if (len < 0) {
395         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02346)
396                 "URL '%s' had no explicit size, ignoring", key);
397         return DECLINED;
398     }
399     if (len > dconf->max) {
400         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02347)
401                 "URL '%s' body larger than limit, ignoring "
402                 "(%" APR_OFF_T_FMT " > %" APR_OFF_T_FMT ")",
403                 key, len, dconf->max);
404         return DECLINED;
405     }
406 
407     /* estimate the total cached size, given current headers */
408     total = len + sizeof(cache_socache_info_t) + strlen(key);
409     if (APR_SUCCESS != store_table(r->headers_out, NULL, dconf->max, &total)
410             || APR_SUCCESS != store_table(r->headers_in, NULL, dconf->max,
411                     &total)) {
412         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02348)
413                 "URL '%s' estimated headers size larger than limit, ignoring "
414                 "(%" APR_SIZE_T_FMT " > %" APR_OFF_T_FMT ")",
415                 key, total, dconf->max);
416         return DECLINED;
417     }
418 
419     if (total >= dconf->max) {
420         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02349)
421                 "URL '%s' body and headers larger than limit, ignoring "
422                 "(%" APR_OFF_T_FMT " > %" APR_OFF_T_FMT ")",
423                 key, len, dconf->max);
424         return DECLINED;
425     }
426 
427     /* Allocate and initialize cache_object_t and cache_socache_object_t */
428     h->cache_obj = obj = apr_pcalloc(r->pool, sizeof(*obj));
429     obj->vobj = sobj = apr_pcalloc(r->pool, sizeof(*sobj));
430 
431     obj->key = apr_pstrdup(r->pool, key);
432     sobj->key = obj->key;
433     sobj->name = obj->key;
434 
435     return OK;
436 }
437 
sobj_body_pre_cleanup(void * baton)438 static apr_status_t sobj_body_pre_cleanup(void *baton)
439 {
440     cache_socache_object_t *sobj = baton;
441     apr_brigade_cleanup(sobj->body);
442     sobj->body = NULL;
443     return APR_SUCCESS;
444 }
445 
open_entity(cache_handle_t * h,request_rec * r,const char * key)446 static int open_entity(cache_handle_t *h, request_rec *r, const char *key)
447 {
448     cache_socache_dir_conf *dconf =
449             ap_get_module_config(r->per_dir_config, &cache_socache_module);
450     cache_socache_conf *conf = ap_get_module_config(r->server->module_config,
451             &cache_socache_module);
452     apr_uint32_t format;
453     apr_size_t slider;
454     unsigned int buffer_len;
455     const char *nkey;
456     apr_status_t rc;
457     cache_object_t *obj;
458     cache_info *info;
459     cache_socache_object_t *sobj;
460     apr_size_t len;
461 
462     nkey = NULL;
463     h->cache_obj = NULL;
464 
465     if (!conf->provider || !conf->provider->socache_instance) {
466         return DECLINED;
467     }
468 
469     /* Create and init the cache object */
470     obj = apr_pcalloc(r->pool, sizeof(cache_object_t));
471     sobj = apr_pcalloc(r->pool, sizeof(cache_socache_object_t));
472 
473     info = &(obj->info);
474 
475     /* Create a temporary pool for the buffer, and destroy it if something
476      * goes wrong so we don't have large buffers of unused memory hanging
477      * about for the lifetime of the response.
478      */
479     apr_pool_create(&sobj->pool, r->pool);
480     apr_pool_tag(sobj->pool, "mod_cache_socache (open_entity)");
481 
482     sobj->buffer = apr_palloc(sobj->pool, dconf->max);
483     sobj->buffer_len = dconf->max;
484 
485     /* attempt to retrieve the cached entry */
486     if (socache_mutex) {
487         apr_status_t status = apr_global_mutex_lock(socache_mutex);
488         if (status != APR_SUCCESS) {
489             ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02350)
490                     "could not acquire lock, ignoring: %s", obj->key);
491             apr_pool_destroy(sobj->pool);
492             sobj->pool = NULL;
493             return DECLINED;
494         }
495     }
496     buffer_len = sobj->buffer_len;
497     rc = conf->provider->socache_provider->retrieve(
498             conf->provider->socache_instance, r->server, (unsigned char *) key,
499             strlen(key), sobj->buffer, &buffer_len, r->pool);
500     if (socache_mutex) {
501         apr_status_t status = apr_global_mutex_unlock(socache_mutex);
502         if (status != APR_SUCCESS) {
503             ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02351)
504                     "could not release lock, ignoring: %s", obj->key);
505             apr_pool_destroy(sobj->pool);
506             sobj->pool = NULL;
507             return DECLINED;
508         }
509     }
510     if (rc != APR_SUCCESS) {
511         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rc, r, APLOGNO(02352)
512                 "Key not found in cache: %s", key);
513         apr_pool_destroy(sobj->pool);
514         sobj->pool = NULL;
515         return DECLINED;
516     }
517     if (buffer_len >= sobj->buffer_len) {
518         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rc, r, APLOGNO(02353)
519                 "Key found in cache but too big, ignoring: %s", key);
520         apr_pool_destroy(sobj->pool);
521         sobj->pool = NULL;
522         return DECLINED;
523     }
524 
525     /* read the format from the cache file */
526     memcpy(&format, sobj->buffer, sizeof(format));
527     slider = sizeof(format);
528 
529     if (format == CACHE_SOCACHE_VARY_FORMAT_VERSION) {
530         apr_array_header_t* varray;
531         apr_time_t expire;
532 
533         memcpy(&expire, sobj->buffer + slider, sizeof(expire));
534         slider += sizeof(expire);
535 
536         varray = apr_array_make(r->pool, 5, sizeof(char*));
537         rc = read_array(r, varray, sobj->buffer, buffer_len, &slider);
538         if (rc != APR_SUCCESS) {
539             ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, APLOGNO(02354)
540                     "Cannot parse vary entry for key: %s", key);
541             apr_pool_destroy(sobj->pool);
542             sobj->pool = NULL;
543             return DECLINED;
544         }
545 
546         nkey = regen_key(r->pool, r->headers_in, varray, key, &len);
547 
548         /* attempt to retrieve the cached entry */
549         if (socache_mutex) {
550             apr_status_t status = apr_global_mutex_lock(socache_mutex);
551             if (status != APR_SUCCESS) {
552                 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02355)
553                         "could not acquire lock, ignoring: %s", obj->key);
554                 apr_pool_destroy(sobj->pool);
555                 sobj->pool = NULL;
556                 return DECLINED;
557             }
558         }
559         buffer_len = sobj->buffer_len;
560         rc = conf->provider->socache_provider->retrieve(
561                 conf->provider->socache_instance, r->server,
562                 (unsigned char *) nkey, len, sobj->buffer,
563                 &buffer_len, r->pool);
564         if (socache_mutex) {
565             apr_status_t status = apr_global_mutex_unlock(socache_mutex);
566             if (status != APR_SUCCESS) {
567                 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02356)
568                         "could not release lock, ignoring: %s", obj->key);
569                 apr_pool_destroy(sobj->pool);
570                 sobj->pool = NULL;
571                 return DECLINED;
572             }
573         }
574         if (rc != APR_SUCCESS) {
575             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rc, r, APLOGNO(02357)
576                     "Key not found in cache: %s", key);
577             apr_pool_destroy(sobj->pool);
578             sobj->pool = NULL;
579             return DECLINED;
580         }
581         if (buffer_len >= sobj->buffer_len) {
582             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rc, r, APLOGNO(02358)
583                     "Key found in cache but too big, ignoring: %s", key);
584             goto fail;
585         }
586 
587     }
588     else if (format != CACHE_SOCACHE_DISK_FORMAT_VERSION) {
589         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02359)
590                 "Key '%s' found in cache has version %d, expected %d, ignoring",
591                 key, format, CACHE_SOCACHE_DISK_FORMAT_VERSION);
592         goto fail;
593     }
594     else {
595         nkey = key;
596     }
597 
598     obj->key = nkey;
599     sobj->key = nkey;
600     sobj->name = key;
601 
602     if (buffer_len >= sizeof(cache_socache_info_t)) {
603         memcpy(&sobj->socache_info, sobj->buffer, sizeof(cache_socache_info_t));
604     }
605     else {
606         ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, APLOGNO(02360)
607                 "Cache entry for key '%s' too short, removing", nkey);
608         goto fail;
609     }
610     slider = sizeof(cache_socache_info_t);
611 
612     /* Store it away so we can get it later. */
613     info->status = sobj->socache_info.status;
614     info->date = sobj->socache_info.date;
615     info->expire = sobj->socache_info.expire;
616     info->request_time = sobj->socache_info.request_time;
617     info->response_time = sobj->socache_info.response_time;
618 
619     memcpy(&info->control, &sobj->socache_info.control, sizeof(cache_control_t));
620 
621     if (sobj->socache_info.name_len <= buffer_len - slider) {
622         if (strncmp((const char *) sobj->buffer + slider, sobj->name,
623                 sobj->socache_info.name_len)) {
624             ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, APLOGNO(02361)
625                     "Cache entry for key '%s' URL mismatch, ignoring", nkey);
626             apr_pool_destroy(sobj->pool);
627             sobj->pool = NULL;
628             return DECLINED;
629         }
630         slider += sobj->socache_info.name_len;
631     }
632     else {
633         ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, APLOGNO(02362)
634                 "Cache entry for key '%s' too short, removing", nkey);
635         goto fail;
636     }
637 
638     /* Is this a cached HEAD request? */
639     if (sobj->socache_info.header_only && !r->header_only) {
640         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(02363)
641                 "HEAD request cached, non-HEAD requested, ignoring: %s",
642                 sobj->key);
643         apr_pool_destroy(sobj->pool);
644         sobj->pool = NULL;
645         return DECLINED;
646     }
647 
648     h->req_hdrs = apr_table_make(r->pool, 20);
649     h->resp_hdrs = apr_table_make(r->pool, 20);
650 
651     /* Call routine to read the header lines/status line */
652     if (APR_SUCCESS != read_table(h, r, h->resp_hdrs, sobj->buffer, buffer_len,
653             &slider)) {
654         ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, APLOGNO(02364)
655                 "Cache entry for key '%s' response headers unreadable, removing", nkey);
656         goto fail;
657     }
658     if (APR_SUCCESS != read_table(h, r, h->req_hdrs, sobj->buffer, buffer_len,
659             &slider)) {
660         ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, APLOGNO(02365)
661                 "Cache entry for key '%s' request headers unreadable, removing", nkey);
662         goto fail;
663     }
664 
665     /* Retrieve the body if we have one */
666     len = buffer_len - slider;
667     if (len > 0) {
668         apr_bucket *e;
669         /* Create the body brigade later concatenated to the output filters'
670          * brigade by recall_body(). Since sobj->buffer (the data) point to
671          * sobj->pool (a subpool of r->pool), be safe by using a pool bucket
672          * which can morph to heap if sobj->pool is destroyed while the bucket
673          * is still alive. But if sobj->pool gets destroyed while the bucket is
674          * still in sobj->body (i.e. recall_body() was never called), we don't
675          * need to morph to something just about to be freed, so a pre_cleanup
676          * will take care of cleaning up sobj->body before this happens (and is
677          * a noop otherwise).
678          */
679         sobj->body = apr_brigade_create(sobj->pool, r->connection->bucket_alloc);
680         apr_pool_pre_cleanup_register(sobj->pool, sobj, sobj_body_pre_cleanup);
681         e = apr_bucket_pool_create((const char *) sobj->buffer + slider, len,
682                                    sobj->pool, r->connection->bucket_alloc);
683         APR_BRIGADE_INSERT_TAIL(sobj->body, e);
684     }
685 
686     /* make the configuration stick */
687     h->cache_obj = obj;
688     obj->vobj = sobj;
689 
690     return OK;
691 
692 fail:
693     if (socache_mutex) {
694         apr_status_t status = apr_global_mutex_lock(socache_mutex);
695         if (status != APR_SUCCESS) {
696             ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02366)
697                     "could not acquire lock, ignoring: %s", obj->key);
698             apr_pool_destroy(sobj->pool);
699             sobj->pool = NULL;
700             return DECLINED;
701         }
702     }
703     conf->provider->socache_provider->remove(
704             conf->provider->socache_instance, r->server,
705             (unsigned char *) nkey, strlen(nkey), r->pool);
706     if (socache_mutex) {
707         apr_status_t status = apr_global_mutex_unlock(socache_mutex);
708         if (status != APR_SUCCESS) {
709             ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02367)
710                     "could not release lock, ignoring: %s", obj->key);
711         }
712     }
713     apr_pool_destroy(sobj->pool);
714     sobj->pool = NULL;
715     return DECLINED;
716 }
717 
remove_entity(cache_handle_t * h)718 static int remove_entity(cache_handle_t *h)
719 {
720     /* Null out the cache object pointer so next time we start from scratch  */
721     h->cache_obj = NULL;
722     return OK;
723 }
724 
remove_url(cache_handle_t * h,request_rec * r)725 static int remove_url(cache_handle_t *h, request_rec *r)
726 {
727     cache_socache_conf *conf = ap_get_module_config(r->server->module_config,
728             &cache_socache_module);
729     cache_socache_object_t *sobj;
730 
731     sobj = (cache_socache_object_t *) h->cache_obj->vobj;
732     if (!sobj) {
733         return DECLINED;
734     }
735 
736     /* Remove the key from the cache */
737     if (socache_mutex) {
738         apr_status_t status = apr_global_mutex_lock(socache_mutex);
739         if (status != APR_SUCCESS) {
740             ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02368)
741                     "could not acquire lock, ignoring: %s", sobj->key);
742             apr_pool_destroy(sobj->pool);
743             sobj->pool = NULL;
744             return DECLINED;
745         }
746     }
747     conf->provider->socache_provider->remove(conf->provider->socache_instance,
748             r->server, (unsigned char *) sobj->key, strlen(sobj->key), r->pool);
749     if (socache_mutex) {
750         apr_status_t status = apr_global_mutex_unlock(socache_mutex);
751         if (status != APR_SUCCESS) {
752             ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02369)
753                     "could not release lock, ignoring: %s", sobj->key);
754             apr_pool_destroy(sobj->pool);
755             sobj->pool = NULL;
756             return DECLINED;
757         }
758     }
759 
760     return OK;
761 }
762 
recall_headers(cache_handle_t * h,request_rec * r)763 static apr_status_t recall_headers(cache_handle_t *h, request_rec *r)
764 {
765     /* we recalled the headers during open_entity, so do nothing */
766     return APR_SUCCESS;
767 }
768 
recall_body(cache_handle_t * h,apr_pool_t * p,apr_bucket_brigade * bb)769 static apr_status_t recall_body(cache_handle_t *h, apr_pool_t *p,
770         apr_bucket_brigade *bb)
771 {
772     cache_socache_object_t *sobj = (cache_socache_object_t*) h->cache_obj->vobj;
773 
774     if (sobj->body) {
775         APR_BRIGADE_CONCAT(bb, sobj->body);
776     }
777 
778     return APR_SUCCESS;
779 }
780 
store_headers(cache_handle_t * h,request_rec * r,cache_info * info)781 static apr_status_t store_headers(cache_handle_t *h, request_rec *r,
782         cache_info *info)
783 {
784     cache_socache_dir_conf *dconf =
785             ap_get_module_config(r->per_dir_config, &cache_socache_module);
786     cache_socache_conf *conf = ap_get_module_config(r->server->module_config,
787             &cache_socache_module);
788     apr_size_t slider;
789     apr_status_t rv;
790     cache_object_t *obj = h->cache_obj;
791     cache_socache_object_t *sobj = (cache_socache_object_t*) obj->vobj;
792     cache_socache_info_t *socache_info;
793 
794     memcpy(&h->cache_obj->info, info, sizeof(cache_info));
795 
796     if (r->headers_out) {
797         sobj->headers_out = ap_cache_cacheable_headers_out(r);
798     }
799 
800     if (r->headers_in) {
801         sobj->headers_in = ap_cache_cacheable_headers_in(r);
802     }
803 
804     sobj->expire
805             = obj->info.expire > r->request_time + dconf->maxtime ? r->request_time
806                     + dconf->maxtime
807                     : obj->info.expire + dconf->mintime;
808 
809     apr_pool_create(&sobj->pool, r->pool);
810     apr_pool_tag(sobj->pool, "mod_cache_socache (store_headers)");
811 
812     sobj->buffer = apr_palloc(sobj->pool, dconf->max);
813     sobj->buffer_len = dconf->max;
814     socache_info = (cache_socache_info_t *) sobj->buffer;
815 
816     if (sobj->headers_out) {
817         const char *vary;
818 
819         vary = apr_table_get(sobj->headers_out, "Vary");
820 
821         if (vary) {
822             apr_array_header_t* varray;
823             apr_uint32_t format = CACHE_SOCACHE_VARY_FORMAT_VERSION;
824 
825             memcpy(sobj->buffer, &format, sizeof(format));
826             slider = sizeof(format);
827 
828             memcpy(sobj->buffer + slider, &obj->info.expire,
829                     sizeof(obj->info.expire));
830             slider += sizeof(obj->info.expire);
831 
832             varray = apr_array_make(r->pool, 6, sizeof(char*));
833             tokens_to_array(r->pool, vary, varray);
834 
835             if (APR_SUCCESS != (rv = store_array(varray, sobj->buffer,
836                     sobj->buffer_len, &slider))) {
837                 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(02370)
838                         "buffer too small for Vary array, caching aborted: %s",
839                         obj->key);
840                 apr_pool_destroy(sobj->pool);
841                 sobj->pool = NULL;
842                 return rv;
843             }
844             if (socache_mutex) {
845                 apr_status_t status = apr_global_mutex_lock(socache_mutex);
846                 if (status != APR_SUCCESS) {
847                     ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02371)
848                             "could not acquire lock, ignoring: %s", obj->key);
849                     apr_pool_destroy(sobj->pool);
850                     sobj->pool = NULL;
851                     return status;
852                 }
853             }
854             rv = conf->provider->socache_provider->store(
855                     conf->provider->socache_instance, r->server,
856                     (unsigned char *) obj->key, strlen(obj->key), sobj->expire,
857                     (unsigned char *) sobj->buffer, (unsigned int) slider,
858                     sobj->pool);
859             if (socache_mutex) {
860                 apr_status_t status = apr_global_mutex_unlock(socache_mutex);
861                 if (status != APR_SUCCESS) {
862                     ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02372)
863                             "could not release lock, ignoring: %s", obj->key);
864                 }
865             }
866             if (rv != APR_SUCCESS) {
867                 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO(02373)
868                         "Vary not written to cache, ignoring: %s", obj->key);
869                 apr_pool_destroy(sobj->pool);
870                 sobj->pool = NULL;
871                 return rv;
872             }
873 
874             obj->key = sobj->key = regen_key(r->pool, sobj->headers_in, varray,
875                                              sobj->name, NULL);
876         }
877     }
878 
879     socache_info->format = CACHE_SOCACHE_DISK_FORMAT_VERSION;
880     socache_info->date = obj->info.date;
881     socache_info->expire = obj->info.expire;
882     socache_info->entity_version = sobj->socache_info.entity_version++;
883     socache_info->request_time = obj->info.request_time;
884     socache_info->response_time = obj->info.response_time;
885     socache_info->status = obj->info.status;
886 
887     if (r->header_only && r->status != HTTP_NOT_MODIFIED) {
888         socache_info->header_only = 1;
889     }
890     else {
891         socache_info->header_only = sobj->socache_info.header_only;
892     }
893 
894     socache_info->name_len = strlen(sobj->name);
895 
896     memcpy(&socache_info->control, &obj->info.control, sizeof(cache_control_t));
897     slider = sizeof(cache_socache_info_t);
898 
899     if (slider + socache_info->name_len >= sobj->buffer_len) {
900         ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(02374)
901                 "cache buffer too small for name: %s",
902                 sobj->name);
903         apr_pool_destroy(sobj->pool);
904         sobj->pool = NULL;
905         return APR_EGENERAL;
906     }
907     memcpy(sobj->buffer + slider, sobj->name, socache_info->name_len);
908     slider += socache_info->name_len;
909 
910     if (sobj->headers_out) {
911         if (APR_SUCCESS != store_table(sobj->headers_out, sobj->buffer,
912                 sobj->buffer_len, &slider)) {
913             ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(02375)
914                     "out-headers didn't fit in buffer: %s", sobj->name);
915             apr_pool_destroy(sobj->pool);
916             sobj->pool = NULL;
917             return APR_EGENERAL;
918         }
919     }
920 
921     /* Parse the vary header and dump those fields from the headers_in. */
922     /* TODO: Make call to the same thing cache_select calls to crack Vary. */
923     if (sobj->headers_in) {
924         if (APR_SUCCESS != store_table(sobj->headers_in, sobj->buffer,
925                 sobj->buffer_len, &slider)) {
926             ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(02376)
927                     "in-headers didn't fit in buffer %s",
928                     sobj->key);
929             apr_pool_destroy(sobj->pool);
930             sobj->pool = NULL;
931             return APR_EGENERAL;
932         }
933     }
934 
935     sobj->body_offset = slider;
936 
937     return APR_SUCCESS;
938 }
939 
store_body(cache_handle_t * h,request_rec * r,apr_bucket_brigade * in,apr_bucket_brigade * out)940 static apr_status_t store_body(cache_handle_t *h, request_rec *r,
941         apr_bucket_brigade *in, apr_bucket_brigade *out)
942 {
943     apr_bucket *e;
944     apr_status_t rv = APR_SUCCESS;
945     cache_socache_object_t *sobj =
946             (cache_socache_object_t *) h->cache_obj->vobj;
947     cache_socache_dir_conf *dconf =
948             ap_get_module_config(r->per_dir_config, &cache_socache_module);
949     int seen_eos = 0;
950 
951     if (!sobj->offset) {
952         sobj->offset = dconf->readsize;
953     }
954     if (!sobj->timeout && dconf->readtime) {
955         sobj->timeout = apr_time_now() + dconf->readtime;
956     }
957 
958     if (!sobj->newbody) {
959         sobj->body_length = 0;
960         sobj->newbody = 1;
961     }
962     if (sobj->offset) {
963         apr_brigade_partition(in, sobj->offset, &e);
964     }
965 
966     while (APR_SUCCESS == rv && !APR_BRIGADE_EMPTY(in)) {
967         const char *str;
968         apr_size_t length;
969 
970         e = APR_BRIGADE_FIRST(in);
971 
972         /* are we done completely? if so, pass any trailing buckets right through */
973         if (sobj->done || !sobj->pool) {
974             APR_BUCKET_REMOVE(e);
975             APR_BRIGADE_INSERT_TAIL(out, e);
976             continue;
977         }
978 
979         /* have we seen eos yet? */
980         if (APR_BUCKET_IS_EOS(e)) {
981             seen_eos = 1;
982             sobj->done = 1;
983             APR_BUCKET_REMOVE(e);
984             APR_BRIGADE_INSERT_TAIL(out, e);
985             break;
986         }
987 
988         /* honour flush buckets, we'll get called again */
989         if (APR_BUCKET_IS_FLUSH(e)) {
990             APR_BUCKET_REMOVE(e);
991             APR_BRIGADE_INSERT_TAIL(out, e);
992             break;
993         }
994 
995         /* metadata buckets are preserved as is */
996         if (APR_BUCKET_IS_METADATA(e)) {
997             APR_BUCKET_REMOVE(e);
998             APR_BRIGADE_INSERT_TAIL(out, e);
999             continue;
1000         }
1001 
1002         /* read the bucket, write to the cache */
1003         rv = apr_bucket_read(e, &str, &length, APR_BLOCK_READ);
1004         APR_BUCKET_REMOVE(e);
1005         APR_BRIGADE_INSERT_TAIL(out, e);
1006         if (rv != APR_SUCCESS) {
1007             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02377)
1008                     "Error when reading bucket for URL %s",
1009                     h->cache_obj->key);
1010             /* Remove the intermediate cache file and return non-APR_SUCCESS */
1011             apr_pool_destroy(sobj->pool);
1012             sobj->pool = NULL;
1013             return rv;
1014         }
1015 
1016         /* don't write empty buckets to the cache */
1017         if (!length) {
1018             continue;
1019         }
1020 
1021         sobj->body_length += length;
1022         if (sobj->body_length >= sobj->buffer_len - sobj->body_offset) {
1023             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02378)
1024                     "URL %s failed the buffer size check "
1025                     "(%" APR_OFF_T_FMT ">=%" APR_SIZE_T_FMT ")",
1026                     h->cache_obj->key, sobj->body_length,
1027                     sobj->buffer_len - sobj->body_offset);
1028             apr_pool_destroy(sobj->pool);
1029             sobj->pool = NULL;
1030             return APR_EGENERAL;
1031         }
1032         memcpy(sobj->buffer + sobj->body_offset + sobj->body_length - length,
1033                str, length);
1034 
1035         /* have we reached the limit of how much we're prepared to write in one
1036          * go? If so, leave, we'll get called again. This prevents us from trying
1037          * to swallow too much data at once, or taking so long to write the data
1038          * the client times out.
1039          */
1040         sobj->offset -= length;
1041         if (sobj->offset <= 0) {
1042             sobj->offset = 0;
1043             break;
1044         }
1045         if ((dconf->readtime && apr_time_now() > sobj->timeout)) {
1046             sobj->timeout = 0;
1047             break;
1048         }
1049 
1050     }
1051 
1052     /* Was this the final bucket? If yes, perform sanity checks.
1053      */
1054     if (seen_eos) {
1055         const char *cl_header;
1056         apr_off_t cl;
1057 
1058         if (r->connection->aborted || r->no_cache) {
1059             ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02380)
1060                     "Discarding body for URL %s "
1061                     "because connection has been aborted.",
1062                     h->cache_obj->key);
1063             apr_pool_destroy(sobj->pool);
1064             sobj->pool = NULL;
1065             return APR_EGENERAL;
1066         }
1067 
1068         cl_header = apr_table_get(r->headers_out, "Content-Length");
1069         if (cl_header && (!ap_parse_strict_length(&cl, cl_header)
1070                           || cl != sobj->body_length)) {
1071             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02381)
1072                     "URL %s didn't receive complete response, not caching",
1073                     h->cache_obj->key);
1074             apr_pool_destroy(sobj->pool);
1075             sobj->pool = NULL;
1076             return APR_EGENERAL;
1077         }
1078 
1079         /* All checks were fine, we're good to go when the commit comes */
1080 
1081     }
1082 
1083     return APR_SUCCESS;
1084 }
1085 
commit_entity(cache_handle_t * h,request_rec * r)1086 static apr_status_t commit_entity(cache_handle_t *h, request_rec *r)
1087 {
1088     cache_socache_conf *conf = ap_get_module_config(r->server->module_config,
1089             &cache_socache_module);
1090     cache_object_t *obj = h->cache_obj;
1091     cache_socache_object_t *sobj = (cache_socache_object_t *) obj->vobj;
1092     apr_status_t rv;
1093 
1094     if (socache_mutex) {
1095         apr_status_t status = apr_global_mutex_lock(socache_mutex);
1096         if (status != APR_SUCCESS) {
1097             ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02384)
1098                     "could not acquire lock, ignoring: %s", obj->key);
1099             apr_pool_destroy(sobj->pool);
1100             sobj->pool = NULL;
1101             return status;
1102         }
1103     }
1104     rv = conf->provider->socache_provider->store(
1105             conf->provider->socache_instance, r->server,
1106             (unsigned char *) sobj->key, strlen(sobj->key), sobj->expire,
1107             sobj->buffer, sobj->body_offset + sobj->body_length, sobj->pool);
1108     if (socache_mutex) {
1109         apr_status_t status = apr_global_mutex_unlock(socache_mutex);
1110         if (status != APR_SUCCESS) {
1111             ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02385)
1112                     "could not release lock, ignoring: %s", obj->key);
1113             apr_pool_destroy(sobj->pool);
1114             sobj->pool = NULL;
1115             return status;
1116         }
1117     }
1118     if (rv != APR_SUCCESS) {
1119         ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r, APLOGNO(02386)
1120                 "could not write to cache, ignoring: %s", sobj->key);
1121         goto fail;
1122     }
1123 
1124     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02387)
1125             "commit_entity: Headers and body for URL %s cached for maximum of %d seconds.",
1126             sobj->name, (apr_uint32_t)apr_time_sec(sobj->expire - r->request_time));
1127 
1128     apr_pool_destroy(sobj->pool);
1129     sobj->pool = NULL;
1130 
1131     return APR_SUCCESS;
1132 
1133 fail:
1134     /* For safety, remove any existing entry on failure, just in case it could not
1135      * be revalidated successfully.
1136      */
1137     if (socache_mutex) {
1138         apr_status_t status = apr_global_mutex_lock(socache_mutex);
1139         if (status != APR_SUCCESS) {
1140             ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02388)
1141                     "could not acquire lock, ignoring: %s", obj->key);
1142             apr_pool_destroy(sobj->pool);
1143             sobj->pool = NULL;
1144             return rv;
1145         }
1146     }
1147     conf->provider->socache_provider->remove(conf->provider->socache_instance,
1148             r->server, (unsigned char *) sobj->key, strlen(sobj->key), r->pool);
1149     if (socache_mutex) {
1150         apr_status_t status = apr_global_mutex_unlock(socache_mutex);
1151         if (status != APR_SUCCESS) {
1152             ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02389)
1153                     "could not release lock, ignoring: %s", obj->key);
1154         }
1155     }
1156 
1157     apr_pool_destroy(sobj->pool);
1158     sobj->pool = NULL;
1159     return rv;
1160 }
1161 
invalidate_entity(cache_handle_t * h,request_rec * r)1162 static apr_status_t invalidate_entity(cache_handle_t *h, request_rec *r)
1163 {
1164     /* mark the entity as invalidated */
1165     h->cache_obj->info.control.invalidated = 1;
1166 
1167     return commit_entity(h, r);
1168 }
1169 
create_dir_config(apr_pool_t * p,char * dummy)1170 static void *create_dir_config(apr_pool_t *p, char *dummy)
1171 {
1172     cache_socache_dir_conf *dconf =
1173             apr_pcalloc(p, sizeof(cache_socache_dir_conf));
1174 
1175     dconf->max = DEFAULT_MAX_FILE_SIZE;
1176     dconf->maxtime = apr_time_from_sec(DEFAULT_MAXTIME);
1177     dconf->mintime = apr_time_from_sec(DEFAULT_MINTIME);
1178     dconf->readsize = DEFAULT_READSIZE;
1179     dconf->readtime = DEFAULT_READTIME;
1180 
1181     return dconf;
1182 }
1183 
merge_dir_config(apr_pool_t * p,void * basev,void * addv)1184 static void *merge_dir_config(apr_pool_t *p, void *basev, void *addv)
1185 {
1186     cache_socache_dir_conf
1187             *new =
1188                     (cache_socache_dir_conf *) apr_pcalloc(p, sizeof(cache_socache_dir_conf));
1189     cache_socache_dir_conf *add = (cache_socache_dir_conf *) addv;
1190     cache_socache_dir_conf *base = (cache_socache_dir_conf *) basev;
1191 
1192     new->max = (add->max_set == 0) ? base->max : add->max;
1193     new->max_set = add->max_set || base->max_set;
1194     new->maxtime = (add->maxtime_set == 0) ? base->maxtime : add->maxtime;
1195     new->maxtime_set = add->maxtime_set || base->maxtime_set;
1196     new->mintime = (add->mintime_set == 0) ? base->mintime : add->mintime;
1197     new->mintime_set = add->mintime_set || base->mintime_set;
1198     new->readsize = (add->readsize_set == 0) ? base->readsize : add->readsize;
1199     new->readsize_set = add->readsize_set || base->readsize_set;
1200     new->readtime = (add->readtime_set == 0) ? base->readtime : add->readtime;
1201     new->readtime_set = add->readtime_set || base->readtime_set;
1202 
1203     return new;
1204 }
1205 
create_config(apr_pool_t * p,server_rec * s)1206 static void *create_config(apr_pool_t *p, server_rec *s)
1207 {
1208     cache_socache_conf *conf = apr_pcalloc(p, sizeof(cache_socache_conf));
1209 
1210     return conf;
1211 }
1212 
merge_config(apr_pool_t * p,void * basev,void * overridesv)1213 static void *merge_config(apr_pool_t *p, void *basev, void *overridesv)
1214 {
1215     cache_socache_conf *ps;
1216     cache_socache_conf *base = (cache_socache_conf *) basev;
1217     cache_socache_conf *overrides = (cache_socache_conf *) overridesv;
1218 
1219     /* socache server config only has one field */
1220     ps = overrides ? overrides : base;
1221 
1222     return ps;
1223 }
1224 
1225 /*
1226  * mod_cache_socache configuration directives handlers.
1227  */
set_cache_socache(cmd_parms * cmd,void * in_struct_ptr,const char * arg)1228 static const char *set_cache_socache(cmd_parms *cmd, void *in_struct_ptr,
1229         const char *arg)
1230 {
1231     cache_socache_conf *conf = ap_get_module_config(cmd->server->module_config,
1232             &cache_socache_module);
1233     cache_socache_provider_conf *provider = conf->provider
1234             = apr_pcalloc(cmd->pool, sizeof(cache_socache_provider_conf));
1235 
1236     const char *err = NULL, *sep, *name;
1237 
1238     /* Argument is of form 'name:args' or just 'name'. */
1239     sep = ap_strchr_c(arg, ':');
1240     if (sep) {
1241         name = apr_pstrmemdup(cmd->pool, arg, sep - arg);
1242         sep++;
1243         provider->args = sep;
1244     }
1245     else {
1246         name = arg;
1247     }
1248 
1249     provider->socache_provider = ap_lookup_provider(AP_SOCACHE_PROVIDER_GROUP,
1250             name, AP_SOCACHE_PROVIDER_VERSION);
1251     if (provider->socache_provider == NULL) {
1252         err = apr_psprintf(cmd->pool,
1253                     "Unknown socache provider '%s'. Maybe you need "
1254                     "to load the appropriate socache module "
1255                     "(mod_socache_%s?)", name, name);
1256     }
1257     return err;
1258 }
1259 
set_cache_max(cmd_parms * parms,void * in_struct_ptr,const char * arg)1260 static const char *set_cache_max(cmd_parms *parms, void *in_struct_ptr,
1261         const char *arg)
1262 {
1263     cache_socache_dir_conf *dconf = (cache_socache_dir_conf *) in_struct_ptr;
1264 
1265     if (apr_strtoff(&dconf->max, arg, NULL, 10) != APR_SUCCESS
1266             || dconf->max < 1024 || dconf->max > APR_UINT32_MAX) {
1267         return "CacheSocacheMaxSize argument must be a integer representing "
1268                "the max size of a cached entry (headers and body), at least 1024 "
1269                "and at most " APR_STRINGIFY(APR_UINT32_MAX);
1270     }
1271     dconf->max_set = 1;
1272     return NULL;
1273 }
1274 
set_cache_maxtime(cmd_parms * parms,void * in_struct_ptr,const char * arg)1275 static const char *set_cache_maxtime(cmd_parms *parms, void *in_struct_ptr,
1276         const char *arg)
1277 {
1278     cache_socache_dir_conf *dconf = (cache_socache_dir_conf *) in_struct_ptr;
1279     apr_off_t seconds;
1280 
1281     if (apr_strtoff(&seconds, arg, NULL, 10) != APR_SUCCESS || seconds < 0) {
1282         return "CacheSocacheMaxTime argument must be the maximum amount of time in seconds to cache an entry.";
1283     }
1284     dconf->maxtime = apr_time_from_sec(seconds);
1285     dconf->maxtime_set = 1;
1286     return NULL;
1287 }
1288 
set_cache_mintime(cmd_parms * parms,void * in_struct_ptr,const char * arg)1289 static const char *set_cache_mintime(cmd_parms *parms, void *in_struct_ptr,
1290         const char *arg)
1291 {
1292     cache_socache_dir_conf *dconf = (cache_socache_dir_conf *) in_struct_ptr;
1293     apr_off_t seconds;
1294 
1295     if (apr_strtoff(&seconds, arg, NULL, 10) != APR_SUCCESS || seconds < 0) {
1296         return "CacheSocacheMinTime argument must be the minimum amount of time in seconds to cache an entry.";
1297     }
1298     dconf->mintime = apr_time_from_sec(seconds);
1299     dconf->mintime_set = 1;
1300     return NULL;
1301 }
1302 
set_cache_readsize(cmd_parms * parms,void * in_struct_ptr,const char * arg)1303 static const char *set_cache_readsize(cmd_parms *parms, void *in_struct_ptr,
1304         const char *arg)
1305 {
1306     cache_socache_dir_conf *dconf = (cache_socache_dir_conf *) in_struct_ptr;
1307 
1308     if (apr_strtoff(&dconf->readsize, arg, NULL, 10) != APR_SUCCESS
1309             || dconf->readsize < 0) {
1310         return "CacheSocacheReadSize argument must be a non-negative integer representing the max amount of data to cache in go.";
1311     }
1312     dconf->readsize_set = 1;
1313     return NULL;
1314 }
1315 
set_cache_readtime(cmd_parms * parms,void * in_struct_ptr,const char * arg)1316 static const char *set_cache_readtime(cmd_parms *parms, void *in_struct_ptr,
1317         const char *arg)
1318 {
1319     cache_socache_dir_conf *dconf = (cache_socache_dir_conf *) in_struct_ptr;
1320     apr_off_t milliseconds;
1321 
1322     if (apr_strtoff(&milliseconds, arg, NULL, 10) != APR_SUCCESS
1323             || milliseconds < 0) {
1324         return "CacheSocacheReadTime argument must be a non-negative integer representing the max amount of time taken to cache in go.";
1325     }
1326     dconf->readtime = apr_time_from_msec(milliseconds);
1327     dconf->readtime_set = 1;
1328     return NULL;
1329 }
1330 
remove_lock(void * data)1331 static apr_status_t remove_lock(void *data)
1332 {
1333     if (socache_mutex) {
1334         apr_global_mutex_destroy(socache_mutex);
1335         socache_mutex = NULL;
1336     }
1337     return APR_SUCCESS;
1338 }
1339 
destroy_cache(void * data)1340 static apr_status_t destroy_cache(void *data)
1341 {
1342     server_rec *s = data;
1343     cache_socache_conf *conf =
1344             ap_get_module_config(s->module_config, &cache_socache_module);
1345     if (conf->provider && conf->provider->socache_instance) {
1346         conf->provider->socache_provider->destroy(
1347                 conf->provider->socache_instance, s);
1348         conf->provider->socache_instance = NULL;
1349     }
1350     return APR_SUCCESS;
1351 }
1352 
socache_status_hook(request_rec * r,int flags)1353 static int socache_status_hook(request_rec *r, int flags)
1354 {
1355     apr_status_t status = APR_SUCCESS;
1356     cache_socache_conf *conf = ap_get_module_config(r->server->module_config,
1357                                                     &cache_socache_module);
1358     if (!conf->provider || !conf->provider->socache_provider ||
1359         !conf->provider->socache_instance) {
1360         return DECLINED;
1361     }
1362 
1363     if (!(flags & AP_STATUS_SHORT)) {
1364         ap_rputs("<hr>\n"
1365                  "<table cellspacing=0 cellpadding=0>\n"
1366                  "<tr><td bgcolor=\"#000000\">\n"
1367                  "<b><font color=\"#ffffff\" face=\"Arial,Helvetica\">"
1368                  "mod_cache_socache Status:</font></b>\n"
1369                  "</td></tr>\n"
1370                  "<tr><td bgcolor=\"#ffffff\">\n", r);
1371     }
1372     else {
1373         ap_rputs("ModCacheSocacheStatus\n", r);
1374     }
1375 
1376     if (socache_mutex) {
1377         status = apr_global_mutex_lock(socache_mutex);
1378         if (status != APR_SUCCESS) {
1379             ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02816)
1380                     "could not acquire lock for cache status");
1381         }
1382     }
1383 
1384     if (status != APR_SUCCESS) {
1385         if (!(flags & AP_STATUS_SHORT)) {
1386             ap_rputs("No cache status data available\n", r);
1387         }
1388         else {
1389             ap_rputs("NotAvailable\n", r);
1390         }
1391     } else {
1392         conf->provider->socache_provider->status(conf->provider->socache_instance,
1393                                                  r, flags);
1394     }
1395 
1396     if (socache_mutex && status == APR_SUCCESS) {
1397         status = apr_global_mutex_unlock(socache_mutex);
1398         if (status != APR_SUCCESS) {
1399             ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(02817)
1400                     "could not release lock for cache status");
1401         }
1402     }
1403 
1404     if (!(flags & AP_STATUS_SHORT)) {
1405         ap_rputs("</td></tr>\n</table>\n", r);
1406     }
1407     return OK;
1408 }
1409 
socache_status_register(apr_pool_t * p)1410 static void socache_status_register(apr_pool_t *p)
1411 {
1412     APR_OPTIONAL_HOOK(ap, status_hook, socache_status_hook, NULL, NULL, APR_HOOK_MIDDLE);
1413 }
1414 
socache_precfg(apr_pool_t * pconf,apr_pool_t * plog,apr_pool_t * ptmp)1415 static int socache_precfg(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptmp)
1416 {
1417     apr_status_t rv = ap_mutex_register(pconf, cache_socache_id, NULL,
1418             APR_LOCK_DEFAULT, 0);
1419     if (rv != APR_SUCCESS) {
1420         ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, plog, APLOGNO(02390)
1421                 "failed to register %s mutex", cache_socache_id);
1422         return 500; /* An HTTP status would be a misnomer! */
1423     }
1424 
1425     /* Register to handle mod_status status page generation */
1426     socache_status_register(pconf);
1427 
1428     return OK;
1429 }
1430 
socache_post_config(apr_pool_t * pconf,apr_pool_t * plog,apr_pool_t * ptmp,server_rec * base_server)1431 static int socache_post_config(apr_pool_t *pconf, apr_pool_t *plog,
1432         apr_pool_t *ptmp, server_rec *base_server)
1433 {
1434     server_rec *s;
1435     apr_status_t rv;
1436     const char *errmsg;
1437     static struct ap_socache_hints socache_hints =
1438     { 64, 2048, 60000000 };
1439 
1440     for (s = base_server; s; s = s->next) {
1441         cache_socache_conf *conf =
1442                 ap_get_module_config(s->module_config, &cache_socache_module);
1443 
1444         if (!conf->provider) {
1445             continue;
1446         }
1447 
1448         if (!socache_mutex && conf->provider->socache_provider->flags
1449                 & AP_SOCACHE_FLAG_NOTMPSAFE) {
1450 
1451             rv = ap_global_mutex_create(&socache_mutex, NULL, cache_socache_id,
1452                     NULL, s, pconf, 0);
1453             if (rv != APR_SUCCESS) {
1454                 ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, plog, APLOGNO(02391)
1455                         "failed to create %s mutex", cache_socache_id);
1456                 return 500; /* An HTTP status would be a misnomer! */
1457             }
1458             apr_pool_cleanup_register(pconf, NULL, remove_lock,
1459                     apr_pool_cleanup_null);
1460         }
1461 
1462         errmsg = conf->provider->socache_provider->create(
1463                 &conf->provider->socache_instance, conf->provider->args, ptmp,
1464                 pconf);
1465         if (errmsg) {
1466             ap_log_perror(APLOG_MARK, APLOG_CRIT, 0, plog,
1467                     APLOGNO(02392) "%s", errmsg);
1468             return 500; /* An HTTP status would be a misnomer! */
1469         }
1470 
1471         rv = conf->provider->socache_provider->init(
1472                 conf->provider->socache_instance, cache_socache_id,
1473                 &socache_hints, s, pconf);
1474         if (rv != APR_SUCCESS) {
1475             ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, plog, APLOGNO(02393)
1476                     "failed to initialise %s cache", cache_socache_id);
1477             return 500; /* An HTTP status would be a misnomer! */
1478         }
1479         apr_pool_cleanup_register(pconf, (void *) s, destroy_cache,
1480                 apr_pool_cleanup_null);
1481 
1482     }
1483 
1484     return OK;
1485 }
1486 
socache_child_init(apr_pool_t * p,server_rec * s)1487 static void socache_child_init(apr_pool_t *p, server_rec *s)
1488 {
1489     const char *lock;
1490     apr_status_t rv;
1491     if (!socache_mutex) {
1492         return; /* don't waste the overhead of creating mutex & cache */
1493     }
1494     lock = apr_global_mutex_lockfile(socache_mutex);
1495     rv = apr_global_mutex_child_init(&socache_mutex, lock, p);
1496     if (rv != APR_SUCCESS) {
1497         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(02394)
1498                 "failed to initialise mutex in child_init");
1499     }
1500 }
1501 
1502 static const command_rec cache_socache_cmds[] =
1503 {
1504     AP_INIT_TAKE1("CacheSocache", set_cache_socache, NULL, RSRC_CONF,
1505             "The shared object cache to store cache files"),
1506     AP_INIT_TAKE1("CacheSocacheMaxTime", set_cache_maxtime, NULL, RSRC_CONF | ACCESS_CONF,
1507             "The maximum cache expiry age to cache a document in seconds"),
1508     AP_INIT_TAKE1("CacheSocacheMinTime", set_cache_mintime, NULL, RSRC_CONF | ACCESS_CONF,
1509             "The minimum cache expiry age to cache a document in seconds"),
1510     AP_INIT_TAKE1("CacheSocacheMaxSize", set_cache_max, NULL, RSRC_CONF | ACCESS_CONF,
1511             "The maximum cache entry size (headers and body) to cache a document"),
1512     AP_INIT_TAKE1("CacheSocacheReadSize", set_cache_readsize, NULL, RSRC_CONF | ACCESS_CONF,
1513             "The maximum quantity of data to attempt to read and cache in one go"),
1514     AP_INIT_TAKE1("CacheSocacheReadTime", set_cache_readtime, NULL, RSRC_CONF | ACCESS_CONF,
1515             "The maximum time taken to attempt to read and cache in go"),
1516     { NULL }
1517 };
1518 
1519 static const cache_provider cache_socache_provider =
1520 {
1521     &remove_entity, &store_headers, &store_body, &recall_headers, &recall_body,
1522     &create_entity, &open_entity, &remove_url, &commit_entity,
1523     &invalidate_entity
1524 };
1525 
cache_socache_register_hook(apr_pool_t * p)1526 static void cache_socache_register_hook(apr_pool_t *p)
1527 {
1528     /* cache initializer */
1529     ap_register_provider(p, CACHE_PROVIDER_GROUP, "socache", "0",
1530             &cache_socache_provider);
1531     ap_hook_pre_config(socache_precfg, NULL, NULL, APR_HOOK_MIDDLE);
1532     ap_hook_post_config(socache_post_config, NULL, NULL, APR_HOOK_MIDDLE);
1533     ap_hook_child_init(socache_child_init, NULL, NULL, APR_HOOK_MIDDLE);
1534 }
1535 
1536 AP_DECLARE_MODULE(cache_socache) = { STANDARD20_MODULE_STUFF,
1537     create_dir_config,  /* create per-directory config structure */
1538     merge_dir_config, /* merge per-directory config structures */
1539     create_config, /* create per-server config structure */
1540     merge_config, /* merge per-server config structures */
1541     cache_socache_cmds, /* command apr_table_t */
1542     cache_socache_register_hook /* register hooks */
1543 };
1544