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