Switch from GPL CRC code to public domain CRC code
[httpd-crcsyncproxy.git] / crccache / mod_crccache_client.c
blob797eabcff16e7f25a17f857b98e8231dd8badcb4
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 /* crcsync/crccache apache client module
19 * This module is designed to run as a cache server on the local end of a slow
20 * internet link. This module uses a crc running hash algorithm to reduce
21 * data transfer in cached but modified upstream files.
23 * CRC algorithm uses the crcsync library created by Rusty Russel
25 * Author: Toby Collett (2009)
26 * Contributor: Alex Wulms (2009)
32 #include <assert.h>
34 #include <apr_file_io.h>
35 #include <apr_file_info.h>
36 #include <apr_strings.h>
37 #include <apr_base64.h>
38 #include <apr_lib.h>
39 #include <apr_date.h>
40 #include <apr_tables.h>
41 #include "ap_provider.h"
42 #include <ap_regex.h>
43 #include "util_filter.h"
44 #include "util_script.h"
45 #include "util_charset.h"
46 #include <http_log.h>
47 #include <http_protocol.h>
49 #include "crccache.h"
50 #include "ap_log_helper.h"
51 #include <crcsync/crcsync.h>
52 #include <crc/crc.h>
53 #include <zlib.h>
55 #include "mod_crccache_client.h"
56 #include "mod_crccache_client_find_similar.h"
58 static ap_filter_rec_t *crccache_decode_filter_handle;
59 static ap_filter_rec_t *cache_save_filter_handle;
60 static ap_filter_rec_t *cache_save_subreq_filter_handle;
62 module AP_MODULE_DECLARE_DATA crccache_client_module;
65 APR_DECLARE_OPTIONAL_FN(apr_status_t,
66 ap_cache_generate_key,
67 (request_rec *r, apr_pool_t*p, char**key ));
68 APR_OPTIONAL_FN_TYPE(ap_cache_generate_key) *cache_generate_key;
70 // const char* cache_create_key( request_rec*r );
73 // extern APR_OPTIONAL_FN_TYPE(ap_cache_generate_key) *cache_generate_key;
76 * Private strucures and constants only used in mod_crccache_client
79 // hashes per file
80 #define FULL_BLOCK_COUNT 40
82 typedef enum decoding_state {
83 DECODING_NEW_SECTION,
84 DECODING_COMPRESSED,
85 DECODING_LITERAL_BODY,
86 DECODING_LITERAL_SIZE,
87 DECODING_HASH,
88 DECODING_BLOCK_HEADER,
89 DECODING_BLOCK
90 } decoding_state;
92 typedef enum {
93 DECOMPRESSION_INITIALIZED,
94 DECOMPRESSION_ENDED
95 } decompression_state_t;
97 typedef struct crccache_client_ctx_t {
98 apr_bucket_brigade *bb;
99 size_t block_size;
100 size_t tail_block_size;
101 apr_bucket * cached_bucket;// original data so we can fill in the matched blocks
103 decoding_state state;
104 decompression_state_t decompression_state;
105 z_stream *decompression_stream;
106 int headers_checked;
107 struct apr_sha1_ctx_t sha1_ctx;
108 unsigned char sha1_value_rx[APR_SHA1_DIGESTSIZE];
109 unsigned rx_count;
110 unsigned literal_size;
111 unsigned char * partial_literal;// original data so we can fill in the matched blocks
112 } crccache_client_ctx;
114 static int crccache_client_post_config_per_virtual_host(apr_pool_t *p, apr_pool_t *plog,
115 apr_pool_t *ptemp, server_rec *s)
118 * The cache can be configured (differently) per virtual host, hence make the correct settings
119 * at the virtual host level
121 crccache_client_conf *conf = ap_get_module_config(s->module_config,
122 &crccache_client_module);
123 ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, s,
124 "mod_crccache_client.post_config_per_vhost (%s): Number of cacheable URLs: %d",
125 format_hostinfo(ptemp, s), conf->cacheenable->nelts);
127 if (conf->cacheenable->nelts != 0) {
128 // Cache client is enabled in this (virtual) server. Initialize it
129 if (!conf->cache_root) {
130 ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
131 "mod_crccache_client.post_config_per_vhost (%s): Please set parameter CacheRootClient in (virtual) server config %s",
132 format_hostinfo(ptemp, s), s->defn_name);
133 return APR_EGENERAL;
135 return crccache_client_fsp_post_config_per_virtual_host(p, plog, ptemp, s, conf->similar_page_cache, conf->cache_root);
138 return OK;
141 static int crccache_client_post_config(apr_pool_t *p, apr_pool_t *plog,
142 apr_pool_t *ptemp, server_rec *s)
144 int result;
147 * TODO: AW: Why is the cache_generate_key function looked-up as an optional function?
148 * As far as I can see, this function is:
149 * 1) Very platform agnostic (so the need to ever write a platform specific module
150 * in order to export a platform specific implementation hardly exists...)
151 * 2) Very closely coupled to the way it is used in the cache client (an externally
152 * exported function could break the client by implementing other semantics!)
153 * 3) There are no existing modules registering/exporting an ap_cache_generate_key function
154 * (which confirmes the two above points, that there is no need for it)
155 * Nevertheless, I assume the original author of mod_cache knows what he is doing so I
156 * leave this indirect look-up here for the time being
158 cache_generate_key = APR_RETRIEVE_OPTIONAL_FN(ap_cache_generate_key);
159 if (!cache_generate_key) {
160 cache_generate_key = cache_generate_key_default;
163 // Perform the post_config logic for the 'find similar page' feature
164 server_rec *current_server = s;
165 while (current_server != NULL)
167 result = crccache_client_post_config_per_virtual_host(p, plog, ptemp, current_server);
168 if (result != OK)
169 return result;
171 current_server = current_server->next;
173 return OK;
177 static void crccache_client_child_init_per_virtual_host(apr_pool_t *p, server_rec *s)
179 crccache_client_conf *conf = ap_get_module_config(s->module_config,
180 &crccache_client_module);
181 crccache_client_fsp_child_init_per_virtual_host(p, s, conf->similar_page_cache);
184 static void crccache_client_child_init(apr_pool_t *p, server_rec *s)
186 server_rec *current_server = s;
187 while (current_server != NULL)
189 crccache_client_child_init_per_virtual_host(p, current_server);
190 current_server = current_server->next;
196 * Clean-up memory used by helper libraries, that don't know about apr_palloc
197 * and that (probably) use classical malloc/free
199 apr_status_t deflate_ctx_cleanup(void *data)
201 crccache_client_ctx *ctx = (crccache_client_ctx *)data;
203 if (ctx != NULL)
205 if (ctx->decompression_state != DECOMPRESSION_ENDED)
207 inflateEnd(ctx->decompression_stream);
208 ctx->decompression_state = DECOMPRESSION_ENDED;
211 return APR_SUCCESS;
214 // ______ continue with refactoring
217 * Request CRCSYNC/delta-http encoding for a page: open the previous data file from the cache, preferably
218 * for the exact URL but if not present then for a similar URL. Then calculate the CRC blocks for the
219 * opened page and generate the header
220 * Returns APR_SUCCESS if the delta-http could be prepared and APR_EGENERAL or APR_NOTFOUND in case of
221 * error conditions (APR_NOTFOUND if no body could be found, APR_EGENERAL for all other errors)
223 static apr_status_t request_crcsync_encoding(cache_handle_t *h, request_rec *r, crccache_client_conf *client_conf)
225 const char *data;
226 apr_size_t len;
227 apr_bucket *e;
228 unsigned i;
229 int z_RC;
231 ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server, "Preparing CRCSYNC/delta-http for %s", r->unparsed_uri);
232 disk_cache_object_t *dobj = (disk_cache_object_t *) h->cache_obj->vobj;
233 if (!dobj->fd)
235 ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,
236 "No page found in cache for requested URL. Trying to find page for similar URLs");
237 dobj = apr_palloc(r->pool, sizeof(disk_cache_object_t));
238 if (find_similar_page(dobj, r, client_conf->similar_page_cache) != APR_SUCCESS)
240 ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,
241 "Failed to prepare CRCSYNC/delta-http. No (similar) page found in cache");
242 return APR_NOTFOUND;
246 e = apr_bucket_file_create(dobj->fd, 0, (apr_size_t) dobj->file_size, r->pool,
247 r->connection->bucket_alloc);
249 /* Read file into bucket. Hopefully the entire file fits in the bucket */
250 apr_bucket_read(e, &data, &len, APR_BLOCK_READ);
251 if (len != dobj->file_size)
253 ap_log_error(APLOG_MARK, APLOG_WARNING, APR_SUCCESS, r->server,
254 "crccache_client: Only read %" APR_SIZE_T_FMT " bytes out of %" APR_SIZE_T_FMT " bytes from the "
255 "original response data into the bucket cache",
256 len, dobj->file_size);
257 return APR_EGENERAL;
259 // this will be rounded down, but thats okay
260 size_t blocksize = len/FULL_BLOCK_COUNT;
261 size_t tail_block_size = blocksize + len % FULL_BLOCK_COUNT;
262 size_t block_count_including_final_block = FULL_BLOCK_COUNT;
263 ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,
264 "Read file into bucket, len: %" APR_SIZE_T_FMT ", blocksize: %" APR_SIZE_T_FMT ", tail_block_size: %" APR_SIZE_T_FMT,
265 len, blocksize, tail_block_size);
267 // sanity check for very small files
268 if (blocksize <= 4)
270 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "Skipped CRCSYNC/delta-http, due to too small blocksize");
271 return APR_SUCCESS;
274 crccache_client_ctx * ctx;
275 ctx = apr_pcalloc(r->pool, sizeof(*ctx));
276 ctx->bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
277 ctx->block_size = blocksize;
278 ctx->tail_block_size = tail_block_size;
279 ctx->state = DECODING_NEW_SECTION;
280 ctx->cached_bucket = e;
282 // Setup inflate for decompressing non-matched literal data
283 ctx->decompression_stream = apr_palloc(r->pool, sizeof(*(ctx->decompression_stream)));
284 ctx->decompression_stream->zalloc = Z_NULL;
285 ctx->decompression_stream->zfree = Z_NULL;
286 ctx->decompression_stream->opaque = Z_NULL;
287 ctx->decompression_stream->avail_in = 0;
288 ctx->decompression_stream->next_in = Z_NULL;
289 z_RC = inflateInit(ctx->decompression_stream);
290 if (z_RC != Z_OK)
292 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
293 "Can not initialize decompression engine, return code: %d", z_RC);
294 return APR_EGENERAL;
296 ctx->decompression_state = DECOMPRESSION_INITIALIZED;
298 // Register a cleanup function to cleanup internal libz resources
299 apr_pool_cleanup_register(r->pool, ctx, deflate_ctx_cleanup,
300 apr_pool_cleanup_null);
302 // All OK to go for the crcsync decoding: add the headers
303 // and set-up the decoding filter
305 // add one for base 64 overflow and null terminator
306 char hash_set[HASH_HEADER_SIZE+1];
308 uint64_t crcs[block_count_including_final_block];
309 crc_of_blocks(data, len, blocksize, tail_block_size, HASH_SIZE, crcs);
311 // swap to network byte order
312 for (i = 0; i < block_count_including_final_block;++i)
314 htobe64(crcs[i]);
317 apr_base64_encode (hash_set, (char *)crcs, block_count_including_final_block*sizeof(crcs[0]));
318 hash_set[HASH_HEADER_SIZE] = '\0';
319 //apr_bucket_delete(e);
321 // TODO; bit of a safety margin here, could calculate exact size
322 const int block_header_max_size = HASH_HEADER_SIZE+40;
323 char block_header_txt[block_header_max_size];
324 apr_snprintf(block_header_txt, block_header_max_size,"v=1; fs=%" APR_SIZE_T_FMT "; h=%s",len,hash_set);
325 apr_table_set(r->headers_in, BLOCK_HEADER, block_header_txt);
326 // TODO: do we want to cache the hashes here?
328 // initialise the context for our sha1 digest of the unencoded response
329 apr_sha1_init(&ctx->sha1_ctx);
331 // we want to add a filter here so that we can decode the response.
332 // we need access to the original cached data when we get the response as
333 // we need that to fill in the matched blocks.
334 // TODO: does the original cached data file remain open between this request
335 // and the subsequent response or do we run the risk that a concurrent
336 // request modifies it?
337 ap_add_output_filter_handle(crccache_decode_filter_handle, ctx, r, r->connection);
338 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "Successfully prepared CRCSYNC/delta-http");
340 return APR_SUCCESS;
345 * Reads headers from a buffer and returns an array of headers.
346 * Returns NULL on file error
347 * This routine tries to deal with too long lines and continuation lines.
348 * @@@: XXX: FIXME: currently the headers are passed thru un-merged.
349 * Is that okay, or should they be collapsed where possible?
351 apr_status_t recall_headers(cache_handle_t *h, request_rec *r) {
352 disk_cache_object_t *dobj = (disk_cache_object_t *) h->cache_obj->vobj;
354 /* This case should not happen... */
355 if (!dobj->hfd) {
356 /* XXX log message */
357 return APR_NOTFOUND;
360 h->req_hdrs = apr_table_make(r->pool, 20);
361 h->resp_hdrs = apr_table_make(r->pool, 20);
363 /* Call routine to read the header lines/status line */
364 read_table(/*h, r, */r->server, h->resp_hdrs, dobj->hfd);
365 read_table(/*h, r, */r->server, h->req_hdrs, dobj->hfd);
366 apr_file_close(dobj->hfd);
368 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
369 "crccache_client: Recalled headers for URL %s", dobj->name);
370 return APR_SUCCESS;
374 * CACHE_DECODE filter
375 * ----------------
376 * Deliver crc decoded content (headers and body) up the stack.
378 static int crccache_decode_filter(ap_filter_t *f, apr_bucket_brigade *bb) {
379 apr_bucket *e;
380 request_rec *r = f->r;
381 crccache_client_ctx *ctx = f->ctx;
383 // if this is the first pass in decoding we should check the headers etc
384 // and fix up those headers that we modified as part of the encoding
385 if (ctx->headers_checked == 0)
387 ctx->headers_checked = 1;
389 ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,
390 "CRCSYNC returned status code (%d)", r->status);
392 // TODO: make this work if we have multiple encodings
393 const char * content_encoding;
394 content_encoding = apr_table_get(r->headers_out, ENCODING_HEADER);
395 if (content_encoding == NULL || strcmp(CRCCACHE_ENCODING, content_encoding)
396 != 0) {
397 ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,
398 "CRCSYNC not decoding, content encoding bad (%s)", content_encoding?content_encoding:"NULL");
399 ap_remove_output_filter(f);
400 return ap_pass_brigade(f->next, bb);
403 // remove the encoding header
404 apr_table_unset(r->headers_out, ENCODING_HEADER);
406 // remove If-Block from the Vary header
407 char * vary = apr_pstrdup(r->pool, apr_table_get(r->headers_out, "Vary"));
408 if (vary)
410 ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server, "Incoming Vary header: %s", vary);
411 apr_table_unset(r->headers_out, "Vary");
412 char * tok;
413 char * last = NULL;
414 for (tok = apr_strtok(vary,", ",&last);tok != NULL;tok = apr_strtok(NULL,", ",&last))
416 if (strcmp(BLOCK_HEADER,tok)!=0)
418 apr_table_mergen(r->headers_out,"Vary",tok);
423 // fix up etag
424 char * etag = apr_pstrdup(r->pool, apr_table_get(r->headers_out, ETAG_HEADER));
425 if (etag)
427 // TODO: get original encoding from etag header so that it can be re-applied
428 ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server, "Incoming ETag header: %s", etag);
429 int etaglen = strlen(etag);
430 if (etaglen>strlen(CRCCACHE_ENCODING) + 1)
432 if (strcmp("-"CRCCACHE_ENCODING,&etag[etaglen-(strlen(CRCCACHE_ENCODING) + 1)])==0)
434 etag[etaglen-(strlen(CRCCACHE_ENCODING) + 1)] = '\0';
435 apr_table_setn(r->headers_out,"etag",etag);
443 /* Do nothing if asked to filter nothing. */
444 if (APR_BRIGADE_EMPTY(bb)) {
445 return ap_pass_brigade(f->next, bb);
448 /* We require that we have a context already, otherwise we dont have our cached file
449 * to fill in the gaps with.
451 if (!ctx) {
452 ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,
453 "No context available %s", r->uri);
454 ap_remove_output_filter(f);
455 return ap_pass_brigade(f->next, bb);
458 while (!APR_BRIGADE_EMPTY(bb))
460 const char *data;
461 apr_size_t len;
463 e = APR_BRIGADE_FIRST(bb);
465 if (APR_BUCKET_IS_EOS(e)) {
467 /* Remove EOS from the old list, and insert into the new. */
468 APR_BUCKET_REMOVE(e);
469 APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
471 /* This filter is done once it has served up its content */
472 ap_remove_output_filter(f);
474 // check strong hash here
475 unsigned char sha1_value[APR_SHA1_DIGESTSIZE];
476 apr_sha1_final(sha1_value, &ctx->sha1_ctx);
477 if (memcmp(sha1_value, ctx->sha1_value_rx, APR_SHA1_DIGESTSIZE) != 0)
479 ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,"CRCSYNC-DECODE HASH CHECK FAILED for uri %s", r->unparsed_uri);
480 apr_brigade_cleanup(bb);
481 return APR_EGENERAL;
483 else
485 ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,"CRCSYNC-DECODE HASH CHECK PASSED for uri %s", r->unparsed_uri);
488 /* Okay, we've seen the EOS.
489 * Time to pass it along down the chain.
491 return ap_pass_brigade(f->next, ctx->bb);
494 if (APR_BUCKET_IS_FLUSH(e)) {
495 apr_status_t rv;
497 /* Remove flush bucket from old brigade anf insert into the new. */
498 APR_BUCKET_REMOVE(e);
499 APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
500 rv = ap_pass_brigade(f->next, ctx->bb);
501 if (rv != APR_SUCCESS) {
502 return rv;
504 continue;
507 if (APR_BUCKET_IS_METADATA(e)) {
509 * Remove meta data bucket from old brigade and insert into the
510 * new.
512 APR_BUCKET_REMOVE(e);
513 APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
514 continue;
517 /* read */
518 apr_bucket_read(e, &data, &len, APR_BLOCK_READ);
519 //ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,"CRCSYNC-DECODE read %" APR_SIZE_T_FMT " bytes",len);
521 apr_size_t consumed_bytes = 0;
522 while (consumed_bytes < len)
524 //ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,"CRCSYNC-DECODE remaining %" APR_SIZE_T_FMT " bytes",len - consumed_bytes);
525 // no guaruntee that our buckets line up with our encoding sections
526 // so we need a processing state machine stored in our context
527 switch (ctx->state)
529 case DECODING_NEW_SECTION:
531 // check if we have a compressed section or a block section
532 if (data[consumed_bytes] == ENCODING_COMPRESSED)
533 ctx->state = DECODING_COMPRESSED;
534 else if (data[consumed_bytes] == ENCODING_BLOCK)
535 ctx->state = DECODING_BLOCK_HEADER;
536 else if (data[consumed_bytes] == ENCODING_LITERAL)
538 ctx->state = DECODING_LITERAL_SIZE;
539 ctx->partial_literal = NULL;
540 ctx->rx_count = 0;
542 else if (data[consumed_bytes] == ENCODING_HASH)
544 ctx->state = DECODING_HASH;
545 ctx->rx_count = 0;
547 else
549 ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, r->server,
550 "CRCSYNC-DECODE, unknown section %d(%c)",data[consumed_bytes],data[consumed_bytes]);
551 apr_brigade_cleanup(bb);
552 return APR_EGENERAL;
554 //ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,"CRCSYNC-DECODE found a new section %d",ctx->state);
555 consumed_bytes++;
556 break;
558 case DECODING_BLOCK_HEADER:
560 unsigned char block_number = data[consumed_bytes];
561 consumed_bytes++;
562 ctx->state = DECODING_NEW_SECTION;
564 // TODO: Output the indicated block here
565 size_t current_block_size = block_number < FULL_BLOCK_COUNT-1 ? ctx->block_size : ctx->tail_block_size;
566 ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,
567 "CRCSYNC-DECODE block section, block %d, size %" APR_SIZE_T_FMT, block_number, current_block_size);
569 char * buf = apr_palloc(r->pool, current_block_size);
570 const char * source_data;
571 size_t source_len;
572 apr_bucket_read(ctx->cached_bucket, &source_data, &source_len, APR_BLOCK_READ);
573 assert(block_number < (FULL_BLOCK_COUNT /*+ (ctx->tail_block_size != 0)*/));
574 memcpy(buf,&source_data[block_number*ctx->block_size],current_block_size);
575 // update our sha1 hash
576 apr_sha1_update_binary(&ctx->sha1_ctx, (const unsigned char *)buf, current_block_size);
577 apr_bucket * b = apr_bucket_pool_create(buf, current_block_size, r->pool, f->c->bucket_alloc);
578 APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
579 break;
581 case DECODING_LITERAL_SIZE:
583 unsigned avail_in = len - consumed_bytes;
584 // if we havent got the full int then store the data for later
585 if (avail_in < 4 || ctx->rx_count != 0)
587 if (ctx->partial_literal == NULL)
589 ctx->partial_literal = apr_palloc(r->pool, 4);
591 unsigned len_to_copy = MIN(4-ctx->rx_count, avail_in);
592 memcpy(&ctx->partial_literal[ctx->rx_count], &data[consumed_bytes],len_to_copy);
593 ctx->rx_count += len_to_copy;
594 consumed_bytes += len_to_copy;
596 if (ctx->rx_count == 4)
598 ctx->literal_size = ntohl(*(unsigned*)ctx->partial_literal);
599 ctx->rx_count = 0;
601 else
603 break;
606 else
608 ctx->literal_size = ntohl(*(unsigned*)&data[consumed_bytes]);
609 consumed_bytes += 4;
611 ctx->partial_literal = apr_palloc(r->pool, ctx->literal_size);
612 ctx->state = DECODING_LITERAL_BODY;
613 break;
615 case DECODING_LITERAL_BODY:
617 unsigned avail_in = len - consumed_bytes;
618 unsigned len_to_copy = MIN(ctx->literal_size-ctx->rx_count, avail_in);
619 memcpy(&ctx->partial_literal[ctx->rx_count], &data[consumed_bytes],len_to_copy);
620 ctx->rx_count += len_to_copy;
621 consumed_bytes += len_to_copy;
623 if (ctx->rx_count == ctx->literal_size)
625 apr_sha1_update_binary(&ctx->sha1_ctx, ctx->partial_literal, ctx->literal_size);
626 apr_bucket * b = apr_bucket_pool_create((char*)ctx->partial_literal, ctx->literal_size, r->pool, f->c->bucket_alloc);
627 APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
628 ctx->state = DECODING_NEW_SECTION;
631 break;
633 case DECODING_HASH:
635 unsigned avail_in = len - consumed_bytes;
636 // APR_SHA1_DIGESTSIZE bytes for a SHA1 hash
637 unsigned needed = MIN(APR_SHA1_DIGESTSIZE-ctx->rx_count, avail_in);
638 memcpy(&ctx->sha1_value_rx[ctx->rx_count], &data[consumed_bytes], needed);
639 ctx->rx_count+=needed;
640 consumed_bytes += needed;
641 if (ctx->rx_count == 20)
643 ctx->state = DECODING_NEW_SECTION;
645 break;
647 case DECODING_COMPRESSED:
649 unsigned char decompressed_data_buf[30000];
650 int z_RC;
651 z_stream *strm = ctx->decompression_stream;
652 strm->avail_in = len - consumed_bytes;
653 strm->next_in = (Bytef *)(data + consumed_bytes);
654 // ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server, "CRCSYNC-DECODE inflating %d bytes", strm.avail_in);
655 // ap_log_hex(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server, strm.next_in, strm.avail_in);
656 do {
657 strm->avail_out = sizeof(decompressed_data_buf);
658 strm->next_out = decompressed_data_buf;
659 uInt avail_in_pre_inflate = strm->avail_in;
660 z_RC = inflate(strm, Z_NO_FLUSH);
661 if (z_RC == Z_NEED_DICT || z_RC == Z_DATA_ERROR || z_RC == Z_MEM_ERROR)
663 ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, r->server, "CRCSYNC-DECODE inflate error: %d", z_RC);
664 apr_brigade_cleanup(bb);
665 return APR_EGENERAL;
667 int have = sizeof(decompressed_data_buf) - strm->avail_out;
668 ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,
669 "CRCSYNC-DECODE inflate rslt %d, consumed %d, produced %d",
670 z_RC, avail_in_pre_inflate - strm->avail_in, have);
671 if (have)
673 // write output data
674 char * buf = apr_palloc(r->pool, have);
675 memcpy(buf,decompressed_data_buf,have);
676 apr_sha1_update_binary(&ctx->sha1_ctx, (const unsigned char *)buf, have);
677 apr_bucket * b = apr_bucket_pool_create(buf, have, r->pool, f->c->bucket_alloc);
678 APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
680 } while (strm->avail_out == 0);
681 consumed_bytes = len - strm->avail_in;
682 if (z_RC == Z_STREAM_END)
684 ctx->state = DECODING_NEW_SECTION;
685 inflateReset(strm);
687 break;
689 default:
691 ap_log_error(APLOG_MARK, APLOG_ERR, APR_SUCCESS, r->server,
692 "CRCSYNC-DECODE, unknown state %d, terminating transaction",ctx->state);
693 apr_brigade_cleanup(bb);
694 return APR_EGENERAL; // TODO: figure out how to pass the error on to the client
697 APR_BUCKET_REMOVE(e);
701 apr_brigade_cleanup(bb);
702 return APR_SUCCESS;
705 static void *crccache_client_create_config(apr_pool_t *p, server_rec *s) {
706 ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, s,
707 "mod_crccache_client: entering crccache_client_create_config (%s)",
708 format_hostinfo(p, s));
711 crccache_client_conf *conf = apr_pcalloc(p, sizeof(crccache_client_conf));
712 /* array of URL prefixes for which caching is enabled */
713 conf->cacheenable = apr_array_make(p, 10, sizeof(struct cache_enable));
714 /* array of URL prefixes for which caching is disabled */
715 conf->cachedisable = apr_array_make(p, 10, sizeof(struct cache_disable));
717 /* XXX: Set default values */
718 conf->dirlevels = DEFAULT_DIRLEVELS;
719 conf->dirlength = DEFAULT_DIRLENGTH;
720 conf->maxfs = DEFAULT_MAX_FILE_SIZE;
721 conf->minfs = DEFAULT_MIN_FILE_SIZE;
723 conf->cache_root = NULL;
724 // apr_pcalloc has already initialized everyting to 0
725 // conf->cache_root_len = 0;
727 // TODO: ____ rename once it works
728 conf->similar_page_cache = create_similar_page_cache(p);
730 return conf;
734 * mod_disk_cache configuration directives handlers.
736 static const char *set_cache_root(cmd_parms *parms, void *in_struct_ptr,
737 const char *arg) {
738 crccache_client_conf *conf = ap_get_module_config(parms->server->module_config,
739 &crccache_client_module);
740 conf->cache_root = arg;
741 conf->cache_root_len = strlen(arg);
742 /* TODO: canonicalize cache_root and strip off any trailing slashes */
744 return NULL;
748 * Consider eliminating the next two directives in favor of
749 * Ian's prime number hash...
750 * key = hash_fn( r->uri)
751 * filename = "/key % prime1 /key %prime2/key %prime3"
753 static const char *set_cache_dirlevels(cmd_parms *parms, void *in_struct_ptr,
754 const char *arg) {
755 crccache_client_conf *conf = ap_get_module_config(parms->server->module_config,
756 &crccache_client_module);
757 int val = atoi(arg);
758 if (val < 1)
759 return "CacheDirLevelsClient value must be an integer greater than 0";
760 if (val * conf->dirlength > CACHEFILE_LEN)
761 return "CacheDirLevelsClient*CacheDirLengthClient value must not be higher than 20";
762 conf->dirlevels = val;
763 return NULL;
765 static const char *set_cache_dirlength(cmd_parms *parms, void *in_struct_ptr,
766 const char *arg) {
767 crccache_client_conf *conf = ap_get_module_config(parms->server->module_config,
768 &crccache_client_module);
769 int val = atoi(arg);
770 if (val < 1)
771 return "CacheDirLengthClient value must be an integer greater than 0";
772 if (val * conf->dirlevels > CACHEFILE_LEN)
773 return "CacheDirLevelsClient*CacheDirLengthClient value must not be higher than 20";
775 conf->dirlength = val;
776 return NULL;
779 static const char *set_cache_minfs(cmd_parms *parms, void *in_struct_ptr,
780 const char *arg) {
781 crccache_client_conf *conf = ap_get_module_config(parms->server->module_config,
782 &crccache_client_module);
784 if (apr_strtoff(&conf->minfs, arg, NULL, 0) != APR_SUCCESS || conf->minfs
785 < 0) {
786 return "CacheMinFileSizeClient argument must be a non-negative integer representing the min size of a file to cache in bytes.";
788 return NULL;
791 static const char *set_cache_maxfs(cmd_parms *parms, void *in_struct_ptr,
792 const char *arg) {
793 crccache_client_conf *conf = ap_get_module_config(parms->server->module_config,
794 &crccache_client_module);
795 if (apr_strtoff(&conf->maxfs, arg, NULL, 0) != APR_SUCCESS || conf->maxfs
796 < 0) {
797 return "CacheMaxFileSizeClient argument must be a non-negative integer representing the max size of a file to cache in bytes.";
799 return NULL;
802 static const char *add_crc_client_enable(cmd_parms *parms, void *dummy,
803 const char *url)
805 crccache_client_conf *conf;
806 struct cache_enable *new;
808 conf =
809 (crccache_client_conf *)ap_get_module_config(parms->server->module_config,
810 &crccache_client_module);
811 new = apr_array_push(conf->cacheenable);
812 if (apr_uri_parse(parms->pool, url, &(new->url))) {
813 return NULL;
815 if (new->url.path) {
816 new->pathlen = strlen(new->url.path);
817 } else {
818 new->pathlen = 1;
819 new->url.path = "/";
821 return NULL;
824 static const char *set_cache_bytes(cmd_parms *parms, void *in_struct_ptr,
825 const char *arg) {
826 crccache_client_conf *conf = ap_get_module_config(parms->server->module_config,
827 &crccache_client_module);
828 return crccache_client_fsp_set_cache_bytes(parms, in_struct_ptr, arg, conf->similar_page_cache);
831 static const command_rec crccache_client_cmds[] =
833 AP_INIT_TAKE1("CRCClientEnable", add_crc_client_enable, NULL, RSRC_CONF, "A partial URL prefix below which caching is enabled"),
834 AP_INIT_TAKE1("CacheRootClient", set_cache_root, NULL, RSRC_CONF,"The directory to store cache files"),
835 AP_INIT_TAKE1("CacheDirLevelsClient", set_cache_dirlevels, NULL, RSRC_CONF, "The number of levels of subdirectories in the cache"),
836 AP_INIT_TAKE1("CacheDirLengthClient", set_cache_dirlength, NULL, RSRC_CONF, "The number of characters in subdirectory names"),
837 AP_INIT_TAKE1("CacheMinFileSizeClient", set_cache_minfs, NULL, RSRC_CONF, "The minimum file size to cache a document"),
838 AP_INIT_TAKE1("CacheMaxFileSizeClient", set_cache_maxfs, NULL, RSRC_CONF, "The maximum file size to cache a document"),
839 AP_INIT_TAKE1("CRCClientSharedCacheSize", set_cache_bytes, NULL, RSRC_CONF, "Set the size of the shared memory cache (in bytes). Use 0 "
840 "to disable the shared memory cache. (default: 10MB). "
841 "Disabling the shared memory cache prevents the cache client from using a similar page as base for the delta-request "
842 "when an exact match (on the URL) can not be found in the cache"),
843 { NULL }
846 int ap_run_insert_filter(request_rec *r);
848 int crccache_client_url_handler(request_rec *r, int lookup)
850 const char *auth;
851 cache_request_rec *cache;
852 crccache_client_conf *conf;
854 /* Delay initialization until we know we are handling a GET */
855 if (r->method_number != M_GET) {
856 return DECLINED;
859 conf = (crccache_client_conf *) ap_get_module_config(r->server->module_config,
860 &crccache_client_module);
862 if (conf->cacheenable->nelts == 0)
863 return DECLINED;
865 /* make space for the per request config */
866 cache = (cache_request_rec *) ap_get_module_config(r->request_config,
867 &crccache_client_module);
868 if (!cache) {
869 cache = apr_pcalloc(r->pool, sizeof(cache_request_rec));
870 ap_set_module_config(r->request_config, &crccache_client_module, cache);
874 * Are we allowed to serve cached info at all?
877 /* find certain cache controlling headers */
878 auth = apr_table_get(r->headers_in, "Authorization");
880 /* First things first - does the request allow us to return
881 * cached information at all? If not, just decline the request.
883 if (auth) {
884 return DECLINED;
888 * Add cache_save filter to cache this request. Choose
889 * the correct filter by checking if we are a subrequest
890 * or not.
892 if (r->main) {
893 ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
894 r->server,
895 "Adding CACHE_SAVE_SUBREQ filter for %s",
896 r->uri);
897 ap_add_output_filter_handle(cache_save_subreq_filter_handle,
898 NULL, r, r->connection);
900 else {
901 ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
902 r->server, "Adding CACHE_SAVE filter for %s",
903 r->uri);
904 ap_add_output_filter_handle(cache_save_filter_handle,
905 NULL, r, r->connection);
908 cache_handle_t *h;
909 char *key;
911 if (cache_generate_key(r, r->pool, &key) != APR_SUCCESS) {
912 ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
913 r->server, "Failed to generate key");
914 return DECLINED;
916 h = apr_palloc(r->pool, sizeof(cache_handle_t));
917 if (open_entity(h, r, key) == OK)
919 if (recall_headers(h, r) == APR_SUCCESS) {
920 cache->handle = h;
922 else {
923 ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
924 r->server, "Failed to recall headers");
928 else
930 ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
931 r->server, "Failed to open entity");
933 if (request_crcsync_encoding(h, r, conf) != APR_SUCCESS) {
934 ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
935 r->server, "Failed to request CRCSYNC/delta-http encoding");
937 return DECLINED;
940 void post_store_body_callback(disk_cache_object_t *dobj, request_rec *r)
942 // ____
943 crccache_client_conf *conf = (crccache_client_conf *) ap_get_module_config(r->server->module_config,
944 &crccache_client_module);
945 update_or_add_similar_page(dobj, r, conf->similar_page_cache);
950 * CACHE_SAVE filter
951 * ---------------
953 * Decide whether or not this content should be cached.
954 * If we decide no it should not:
955 * remove the filter from the chain
956 * If we decide yes it should:
957 * Have we already started saving the response?
958 * If we have started, pass the data to the storage manager via store_body
959 * Otherwise:
960 * Check to see if we *can* save this particular response.
961 * If we can, call cache_create_entity() and save the headers and body
962 * Finally, pass the data to the next filter (the network or whatever)
964 int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in)
966 int rv = !OK;
967 request_rec *r = f->r;
968 cache_request_rec *cache;
969 crccache_client_conf *conf;
970 //const char *cc_out, *cl;
971 const char *cl;
972 const char *exps, /* *lastmods,*/ *dates;//, *etag;
973 apr_time_t exp, date,/* lastmod,*/ now;
974 apr_off_t size;
975 cache_info_t *info = NULL;
976 char *reason;
977 apr_pool_t *p;
979 conf = (crccache_client_conf *) ap_get_module_config(r->server->module_config,
980 &crccache_client_module);
982 /* Setup cache_request_rec */
983 cache = (cache_request_rec *) ap_get_module_config(r->request_config,
984 &crccache_client_module);
985 if (!cache) {
986 /* user likely configured CACHE_SAVE manually; they should really use
987 * mod_cache configuration to do that
988 * TODO: Don't support this situation. In stead, write a WARNING to the log and abort the filter processing
990 cache = apr_pcalloc(r->pool, sizeof(cache_request_rec));
991 ap_set_module_config(r->request_config, &crccache_client_module, cache);
994 reason = NULL;
995 p = r->pool;
997 * Pass Data to Cache
998 * ------------------
999 * This section passes the brigades into the cache modules, but only
1000 * if the setup section (see below) is complete.
1002 if (cache->block_response) {
1003 /* We've already sent down the response and EOS. So, ignore
1004 * whatever comes now.
1006 return APR_SUCCESS;
1009 /* have we already run the cachability check and set up the
1010 * cached file handle?
1012 if (cache->in_checked) {
1013 /* pass the brigades into the cache, then pass them
1014 * up the filter stack
1016 rv = store_body(cache->handle, r, in, &post_store_body_callback);
1017 if (rv != APR_SUCCESS) {
1018 ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server,
1019 "cache: Cache provider's store_body failed!");
1020 ap_remove_output_filter(f);
1022 return ap_pass_brigade(f->next, in);
1026 * Setup Data in Cache
1027 * -------------------
1028 * This section opens the cache entity and sets various caching
1029 * parameters, and decides whether this URL should be cached at
1030 * all. This section is* run before the above section.
1033 /* read expiry date; if a bad date, then leave it so the client can
1034 * read it
1036 exps = apr_table_get(r->err_headers_out, "Expires");
1037 if (exps == NULL) {
1038 exps = apr_table_get(r->headers_out, "Expires");
1040 if (exps != NULL) {
1041 if (APR_DATE_BAD == (exp = apr_date_parse_http(exps))) {
1042 exps = NULL;
1045 else {
1046 exp = APR_DATE_BAD;
1050 * what responses should we not cache?
1052 * At this point we decide based on the response headers whether it
1053 * is appropriate _NOT_ to cache the data from the server. There are
1054 * a whole lot of conditions that prevent us from caching this data.
1055 * They are tested here one by one to be clear and unambiguous.
1057 if (r->status != HTTP_OK && r->status != HTTP_NON_AUTHORITATIVE
1058 && r->status != HTTP_MULTIPLE_CHOICES
1059 && r->status != HTTP_MOVED_PERMANENTLY
1060 && r->status != HTTP_NOT_MODIFIED) {
1061 /* RFC2616 13.4 we are allowed to cache 200, 203, 206, 300, 301 or 410
1062 * We don't cache 206, because we don't (yet) cache partial responses.
1063 * We include 304 Not Modified here too as this is the origin server
1064 * telling us to serve the cached copy.
1068 if (reason) {
1069 /* noop */
1072 else if (r->status == HTTP_NOT_MODIFIED &&
1073 !cache->handle && !cache->stale_handle) {
1074 /* if the server said 304 Not Modified but we have no cache
1075 * file - pass this untouched to the user agent, it's not for us.
1077 reason = "HTTP Status 304 Not Modified";
1080 else if (r->header_only && !cache->stale_handle) {
1081 /* Forbid HEAD requests unless we have it cached already */
1082 reason = "HTTP HEAD request";
1084 if (reason) {
1085 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1086 "cache: %s not cached. Reason: %s", r->unparsed_uri,
1087 reason);
1089 /* remove this filter from the chain */
1090 ap_remove_output_filter(f);
1092 /* ship the data up the stack */
1093 return ap_pass_brigade(f->next, in);
1096 /* Make it so that we don't execute this path again. */
1097 cache->in_checked = 1;
1099 /* Set the content length if known.
1101 cl = apr_table_get(r->err_headers_out, "Content-Length");
1102 if (cl == NULL) {
1103 cl = apr_table_get(r->headers_out, "Content-Length");
1105 if (cl) {
1106 char *errp;
1107 if (apr_strtoff(&size, cl, &errp, 10) || *errp || size < 0) {
1108 cl = NULL; /* parse error, see next 'if' block */
1112 if (!cl) {
1113 /* if we don't get the content-length, see if we have all the
1114 * buckets and use their length to calculate the size
1116 apr_bucket *e;
1117 int all_buckets_here=0;
1118 int unresolved_length = 0;
1119 size=0;
1120 for (e = APR_BRIGADE_FIRST(in);
1121 e != APR_BRIGADE_SENTINEL(in);
1122 e = APR_BUCKET_NEXT(e))
1124 if (APR_BUCKET_IS_EOS(e)) {
1125 all_buckets_here=1;
1126 break;
1128 if (APR_BUCKET_IS_FLUSH(e)) {
1129 unresolved_length = 1;
1130 continue;
1132 if (e->length == (apr_size_t)-1) {
1133 break;
1135 size += e->length;
1137 if (!all_buckets_here) {
1138 size = -1;
1142 /* It's safe to cache the response.
1144 * There are two possiblities at this point:
1145 * - cache->handle == NULL. In this case there is no previously
1146 * cached entity anywhere on the system. We must create a brand
1147 * new entity and store the response in it.
1148 * - cache->stale_handle != NULL. In this case there is a stale
1149 * entity in the system which needs to be replaced by new
1150 * content (unless the result was 304 Not Modified, which means
1151 * the cached entity is actually fresh, and we should update
1152 * the headers).
1155 /* Did we have a stale cache entry that really is stale?
1157 * Note that for HEAD requests, we won't get the body, so for a stale
1158 * HEAD request, we don't remove the entity - instead we let the
1159 * CACHE_REMOVE_URL filter remove the stale item from the cache.
1161 if (cache->stale_handle) {
1162 if (r->status == HTTP_NOT_MODIFIED) {
1163 /* Oh, hey. It isn't that stale! Yay! */
1164 cache->handle = cache->stale_handle;
1165 info = &cache->handle->cache_obj->info;
1166 rv = OK;
1168 else if (!r->header_only) {
1169 /* Oh, well. Toss it. */
1170 remove_entity(cache->stale_handle);
1171 /* Treat the request as if it wasn't conditional. */
1172 cache->stale_handle = NULL;
1174 * Restore the original request headers as they may be needed
1175 * by further output filters like the byterange filter to make
1176 * the correct decisions.
1178 r->headers_in = cache->stale_headers;
1182 /* no cache handle, create a new entity only for non-HEAD requests */
1183 if (!cache->handle && !r->header_only) {
1184 char *key;
1185 cache_handle_t *h = apr_pcalloc(r->pool, sizeof(cache_handle_t));
1186 rv = cache_generate_key(r, r->pool, &key);
1187 if (rv != APR_SUCCESS) {
1188 return rv;
1190 rv = create_entity(h, r, key, size);
1191 if (rv != APR_SUCCESS) {
1192 return rv;
1194 cache->handle = h;
1195 info = apr_pcalloc(r->pool, sizeof(cache_info_t));
1196 /* We only set info->status upon the initial creation. */
1197 info->status = r->status;
1200 if (rv != OK) {
1201 /* Caching layer declined the opportunity to cache the response */
1202 ap_remove_output_filter(f);
1203 return ap_pass_brigade(f->next, in);
1206 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1207 "cache: Caching url: %s", r->unparsed_uri);
1209 /* We are actually caching this response. So it does not
1210 * make sense to remove this entity any more.
1212 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1213 "cache: Removing CACHE_REMOVE_URL filter.");
1214 // ap_remove_output_filter(cache->remove_url_filter);
1217 * We now want to update the cache file header information with
1218 * the new date, last modified, expire and content length and write
1219 * it away to our cache file. First, we determine these values from
1220 * the response, using heuristics if appropriate.
1222 * In addition, we make HTTP/1.1 age calculations and write them away
1223 * too.
1226 /* Read the date. Generate one if one is not supplied */
1227 dates = apr_table_get(r->err_headers_out, "Date");
1228 if (dates == NULL) {
1229 dates = apr_table_get(r->headers_out, "Date");
1231 if (dates != NULL) {
1232 info->date = apr_date_parse_http(dates);
1234 else {
1235 info->date = APR_DATE_BAD;
1238 now = apr_time_now();
1239 if (info->date == APR_DATE_BAD) { /* No, or bad date */
1240 /* no date header (or bad header)! */
1241 info->date = now;
1243 date = info->date;
1245 /* set response_time for HTTP/1.1 age calculations */
1246 info->response_time = now;
1248 /* get the request time */
1249 info->request_time = r->request_time;
1251 info->expire = exp;
1253 /* We found a stale entry which wasn't really stale. */
1254 if (cache->stale_handle) {
1255 /* Load in the saved status and clear the status line. */
1256 r->status = info->status;
1257 r->status_line = NULL;
1259 /* RFC 2616 10.3.5 states that entity headers are not supposed
1260 * to be in the 304 response. Therefore, we need to combine the
1261 * response headers with the cached headers *before* we update
1262 * the cached headers.
1264 * However, before doing that, we need to first merge in
1265 * err_headers_out and we also need to strip any hop-by-hop
1266 * headers that might have snuck in.
1268 r->headers_out = ap_cache_cacheable_headers_out(r);
1270 /* Merge in our cached headers. However, keep any updated values. */
1271 ap_cache_accept_headers(cache->handle, r, 1);
1274 /* Write away header information to cache. It is possible that we are
1275 * trying to update headers for an entity which has already been cached.
1277 * This may fail, due to an unwritable cache area. E.g. filesystem full,
1278 * permissions problems or a read-only (re)mount. This must be handled
1279 * later.
1281 rv = store_headers(cache->handle, r, info);
1283 if(rv != APR_SUCCESS) {
1284 ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server,
1285 "cache: store_headers failed");
1286 ap_remove_output_filter(f);
1288 return ap_pass_brigade(f->next, in);
1291 rv = store_body(cache->handle, r, in, &post_store_body_callback);
1292 if (rv != APR_SUCCESS) {
1293 ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server,
1294 "cache: store_body failed");
1295 ap_remove_output_filter(f);
1298 return ap_pass_brigade(f->next, in);
1301 static void crccache_client_register_hook(apr_pool_t *p) {
1302 ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, NULL,
1303 "Registering crccache client module, (C) 2009, Toby Collett");
1305 /* cache initializer */
1306 ap_hook_post_config(crccache_client_post_config, NULL, NULL, APR_HOOK_REALLY_FIRST);
1307 /* cache handler */
1308 ap_hook_quick_handler(crccache_client_url_handler, NULL, NULL, APR_HOOK_FIRST);
1309 /* child initializer */
1310 ap_hook_child_init(crccache_client_child_init, NULL, NULL, APR_HOOK_REALLY_FIRST);
1313 * CRCCACHE_DECODE must go into the filter chain before the cache save filter,
1314 * so that the cache saves the decoded response
1316 crccache_decode_filter_handle = ap_register_output_filter(
1317 "CRCCACHE_DECODE", crccache_decode_filter, NULL,
1318 AP_FTYPE_CONTENT_SET);
1320 /* cache filters
1321 * XXX The cache filters need to run right after the handlers and before
1322 * any other filters. Consider creating AP_FTYPE_CACHE for this purpose.
1324 * Depending on the type of request (subrequest / main request) they
1325 * need to be run before AP_FTYPE_CONTENT_SET / after AP_FTYPE_CONTENT_SET
1326 * filters. Thus create two filter handles for each type:
1327 * cache_save_filter_handle / cache_out_filter_handle to be used by
1328 * main requests and
1329 * cache_save_subreq_filter_handle / cache_out_subreq_filter_handle
1330 * to be run by subrequest
1333 * CACHE_SAVE must go into the filter chain after a possible DEFLATE
1334 * filter to ensure that the compressed content is stored.
1335 * Incrementing filter type by 1 ensures his happens.
1336 * TODO: Revise this logic. In order for the crccache to work properly,
1337 * the plain text content must be cached and not the deflated content
1338 * Even more so, when receiving compressed content from the upstream
1339 * server, the cache_save_filter handler should uncompress it before
1340 * storing in the cache (but provide the compressed data to the client)
1342 cache_save_filter_handle =
1343 ap_register_output_filter("CACHE_SAVE",
1344 cache_save_filter,
1345 NULL,
1346 AP_FTYPE_CONTENT_SET+1);
1348 * CACHE_SAVE_SUBREQ must go into the filter chain before SUBREQ_CORE to
1349 * handle subrequsts. Decrementing filter type by 1 ensures this
1350 * happens.
1352 cache_save_subreq_filter_handle =
1353 ap_register_output_filter("CACHE_SAVE_SUBREQ",
1354 cache_save_filter,
1355 NULL,
1356 AP_FTYPE_CONTENT_SET-1);
1359 module AP_MODULE_DECLARE_DATA crccache_client_module = {
1360 STANDARD20_MODULE_STUFF,
1361 NULL, /* create per-directory config structure */
1362 NULL, /* merge per-directory config structures */
1363 crccache_client_create_config, /* create per-server config structure */
1364 NULL, /* merge per-server config structures */
1365 crccache_client_cmds, /* command apr_table_t */
1366 crccache_client_register_hook /* register hooks */