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)
34 #include <apr_file_io.h>
35 #include <apr_file_info.h>
36 #include <apr_strings.h>
37 #include <apr_base64.h>
40 #include <apr_tables.h>
41 #include "ap_provider.h"
43 #include "util_filter.h"
44 #include "util_script.h"
45 #include "util_charset.h"
47 #include <http_protocol.h>
50 #include "ap_log_helper.h"
51 #include <crcsync/crcsync.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
80 #define FULL_BLOCK_COUNT 40
82 typedef enum decoding_state
{
85 DECODING_LITERAL_BODY
,
86 DECODING_LITERAL_SIZE
,
88 DECODING_BLOCK_HEADER
,
93 DECOMPRESSION_INITIALIZED
,
95 } decompression_state_t
;
97 typedef struct crccache_client_ctx_t
{
98 apr_bucket_brigade
*bb
;
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
;
107 struct apr_sha1_ctx_t sha1_ctx
;
108 unsigned char sha1_value_rx
[APR_SHA1_DIGESTSIZE
];
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
);
135 return crccache_client_fsp_post_config_per_virtual_host(p
, plog
, ptemp
, s
, conf
->similar_page_cache
, conf
->cache_root
);
141 static int crccache_client_post_config(apr_pool_t
*p
, apr_pool_t
*plog
,
142 apr_pool_t
*ptemp
, server_rec
*s
)
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
);
171 current_server
= current_server
->next
;
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
;
205 if (ctx
->decompression_state
!= DECOMPRESSION_ENDED
)
207 inflateEnd(ctx
->decompression_stream
);
208 ctx
->decompression_state
= DECOMPRESSION_ENDED
;
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
)
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
;
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");
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
);
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
270 ap_log_error(APLOG_MARK
, APLOG_DEBUG
, 0, r
->server
, "Skipped CRCSYNC/delta-http, due to too small blocksize");
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
);
292 ap_log_error(APLOG_MARK
, APLOG_WARNING
, 0, r
->server
,
293 "Can not initialize decompression engine, return code: %d", z_RC
);
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
)
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");
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... */
356 /* XXX log message */
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
);
374 * CACHE_DECODE filter
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
) {
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
)
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"));
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");
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
);
424 char * etag
= apr_pstrdup(r
->pool
, apr_table_get(r
->headers_out
, ETAG_HEADER
));
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.
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
))
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
);
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
)) {
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
) {
507 if (APR_BUCKET_IS_METADATA(e
)) {
509 * Remove meta data bucket from old brigade and insert into the
512 APR_BUCKET_REMOVE(e
);
513 APR_BRIGADE_INSERT_TAIL(ctx
->bb
, e
);
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
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
;
542 else if (data
[consumed_bytes
] == ENCODING_HASH
)
544 ctx
->state
= DECODING_HASH
;
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
);
554 //ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,"CRCSYNC-DECODE found a new section %d",ctx->state);
558 case DECODING_BLOCK_HEADER
:
560 unsigned char block_number
= data
[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
;
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
);
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
);
608 ctx
->literal_size
= ntohl(*(unsigned*)&data
[consumed_bytes
]);
611 ctx
->partial_literal
= apr_palloc(r
->pool
, ctx
->literal_size
);
612 ctx
->state
= DECODING_LITERAL_BODY
;
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
;
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
;
647 case DECODING_COMPRESSED
:
649 unsigned char decompressed_data_buf
[30000];
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);
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
);
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
);
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
;
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
);
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
);
734 * mod_disk_cache configuration directives handlers.
736 static const char *set_cache_root(cmd_parms
*parms
, void *in_struct_ptr
,
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 */
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
,
755 crccache_client_conf
*conf
= ap_get_module_config(parms
->server
->module_config
,
756 &crccache_client_module
);
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
;
765 static const char *set_cache_dirlength(cmd_parms
*parms
, void *in_struct_ptr
,
767 crccache_client_conf
*conf
= ap_get_module_config(parms
->server
->module_config
,
768 &crccache_client_module
);
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
;
779 static const char *set_cache_minfs(cmd_parms
*parms
, void *in_struct_ptr
,
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
786 return "CacheMinFileSizeClient argument must be a non-negative integer representing the min size of a file to cache in bytes.";
791 static const char *set_cache_maxfs(cmd_parms
*parms
, void *in_struct_ptr
,
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
797 return "CacheMaxFileSizeClient argument must be a non-negative integer representing the max size of a file to cache in bytes.";
802 static const char *add_crc_client_enable(cmd_parms
*parms
, void *dummy
,
805 crccache_client_conf
*conf
;
806 struct cache_enable
*new;
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
))) {
816 new->pathlen
= strlen(new->url
.path
);
824 static const char *set_cache_bytes(cmd_parms
*parms
, void *in_struct_ptr
,
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"),
846 int ap_run_insert_filter(request_rec
*r
);
848 int crccache_client_url_handler(request_rec
*r
, int lookup
)
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
) {
859 conf
= (crccache_client_conf
*) ap_get_module_config(r
->server
->module_config
,
860 &crccache_client_module
);
862 if (conf
->cacheenable
->nelts
== 0)
865 /* make space for the per request config */
866 cache
= (cache_request_rec
*) ap_get_module_config(r
->request_config
,
867 &crccache_client_module
);
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.
888 * Add cache_save filter to cache this request. Choose
889 * the correct filter by checking if we are a subrequest
893 ap_log_error(APLOG_MARK
, APLOG_DEBUG
, APR_SUCCESS
,
895 "Adding CACHE_SAVE_SUBREQ filter for %s",
897 ap_add_output_filter_handle(cache_save_subreq_filter_handle
,
898 NULL
, r
, r
->connection
);
901 ap_log_error(APLOG_MARK
, APLOG_DEBUG
, APR_SUCCESS
,
902 r
->server
, "Adding CACHE_SAVE filter for %s",
904 ap_add_output_filter_handle(cache_save_filter_handle
,
905 NULL
, r
, r
->connection
);
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");
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
) {
923 ap_log_error(APLOG_MARK
, APLOG_DEBUG
, APR_SUCCESS
,
924 r
->server
, "Failed to recall headers");
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");
940 void post_store_body_callback(disk_cache_object_t
*dobj
, request_rec
*r
)
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
);
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
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
)
967 request_rec
*r
= f
->r
;
968 cache_request_rec
*cache
;
969 crccache_client_conf
*conf
;
970 //const char *cc_out, *cl;
972 const char *exps
, /* *lastmods,*/ *dates
;//, *etag;
973 apr_time_t exp
, date
,/* lastmod,*/ now
;
975 cache_info_t
*info
= NULL
;
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
);
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
);
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.
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
1036 exps
= apr_table_get(r
->err_headers_out
, "Expires");
1038 exps
= apr_table_get(r
->headers_out
, "Expires");
1041 if (APR_DATE_BAD
== (exp
= apr_date_parse_http(exps
))) {
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.
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";
1085 ap_log_error(APLOG_MARK
, APLOG_DEBUG
, 0, r
->server
,
1086 "cache: %s not cached. Reason: %s", r
->unparsed_uri
,
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");
1103 cl
= apr_table_get(r
->headers_out
, "Content-Length");
1107 if (apr_strtoff(&size
, cl
, &errp
, 10) || *errp
|| size
< 0) {
1108 cl
= NULL
; /* parse error, see next 'if' block */
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
1117 int all_buckets_here
=0;
1118 int unresolved_length
= 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
)) {
1128 if (APR_BUCKET_IS_FLUSH(e
)) {
1129 unresolved_length
= 1;
1132 if (e
->length
== (apr_size_t
)-1) {
1137 if (!all_buckets_here
) {
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
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
;
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
) {
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
) {
1190 rv
= create_entity(h
, r
, key
, size
);
1191 if (rv
!= APR_SUCCESS
) {
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
;
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
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
);
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)! */
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
;
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
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
);
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
);
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
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",
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
1352 cache_save_subreq_filter_handle
=
1353 ap_register_output_filter("CACHE_SAVE_SUBREQ",
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 */