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