Index: modules/filters/mod_proxy_html.c
===================================================================
--- modules/filters/mod_proxy_html.c (Revision 1579365)
+++ modules/filters/mod_proxy_html.c (Arbeitskopie)
@@ -65,6 +65,8 @@
#define M_INTERPOLATE_TO 0x100
#define M_INTERPOLATE_FROM 0x200
+#define M_PROXY_CSS_MIN_BUFFER_SIZE 4096
+
typedef struct {
const char *val;
} tattr;
@@ -103,6 +105,7 @@
proxy_html_conf *cfg;
htmlParserCtxtPtr parser;
apr_bucket_brigade *bb;
+ apr_bucket_brigade *bbsave;
char *buf;
size_t offset;
size_t avail;
@@ -953,6 +956,137 @@
return APR_SUCCESS;
}
+static int proxy_css_filter(ap_filter_t *f, apr_bucket_brigade *bb)
+{
+ apr_status_t rv;
+ proxy_html_conf* cfg = ap_get_module_config(f->r->per_dir_config, &proxy_html_module);
+ saxctxt *fctx = f->ctx;
+ if (fctx == NULL) {
+ int skip_filter = 0;
+ // Skip this filter if this is no proxy request
+ if ( ! f->r->proxyreq ) {
+ skip_filter = 1;
+ // init filter only if content type is set
+ } else if ( ! f->r->content_type ) {
+ skip_filter = 1;
+ // init filter only for MIME type text/css
+ } else if ( strncmp(f->r->content_type, "text/css", 8) != 0 ) {
+ skip_filter = 1;
+ }
+
+ if (skip_filter) {
+ ap_filter_t* fnext = f->next;
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(fnext, bb);
+ }
+
+ // The CSS filter changes the output length
+ apr_table_unset(f->r->headers_out, "Content-Length") ;
+
+ // Initialize CSS output filter
+ fctx = f->ctx = apr_pcalloc(f->r->pool, sizeof(saxctxt));
+ fctx->f = f;
+ fctx->cfg = ap_get_module_config(f->r->per_dir_config, &proxy_html_module);
+ fctx->bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
+ fctx->bbsave = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
+
+ // Add rules for variable interpolation etc.
+ if (cfg->interp) fixup_rules(fctx);
+ else fctx->map = fctx->cfg->map;
+ }
+
+ int process_bb_out = 0;
+ apr_bucket* current_bucket;
+ apr_bucket* next_bucket = NULL;
+ for (current_bucket = APR_BRIGADE_FIRST(bb);
+ current_bucket != APR_BRIGADE_SENTINEL(bb);
+ current_bucket = (next_bucket ? next_bucket : APR_BUCKET_NEXT(current_bucket))) {
+ char* out_data = NULL;
+ apr_size_t out_data_length = 0;
+ next_bucket = NULL;
+ if (APR_BUCKET_IS_EOS(current_bucket)) {
+ process_bb_out = 1;
+ } else if (APR_BUCKET_IS_METADATA(current_bucket)) {
+ continue;
+ } else {
+ const char* in_data;
+ apr_size_t in_data_length;
+ rv = apr_bucket_read(current_bucket, &in_data, &in_data_length, APR_BLOCK_READ);
+ if (rv != APR_SUCCESS) return rv;
+
+ // skip empty data bucket
+ if (in_data_length == 0) continue;
+
+ // scan for newline characters
+ apr_size_t newline_offset = 0;
+ const char *le_r = memchr(in_data, '\r', in_data_length);
+ const char *le_n = memchr(in_data, '\n', in_data_length);
+ if (le_r) newline_offset = (le_r - in_data) + 1;
+ if (le_n > le_r) newline_offset = (le_n - in_data) + 1;
+
+ // Bucket without newline character: simply append to bbsave and process it later
+ if (newline_offset == 0) {
+ next_bucket = APR_BUCKET_NEXT(current_bucket);
+ APR_BUCKET_REMOVE(current_bucket);
+ apr_bucket_setaside(current_bucket, f->r->pool);
+ APR_BRIGADE_INSERT_TAIL(fctx->bbsave, current_bucket);
+ continue;
+ }
+
+ // If newline is in the middle of the bucket: split the bucket
+ if (newline_offset < in_data_length) {
+ apr_bucket_split(current_bucket, newline_offset);
+ }
+
+ // Append bucket (with data up to and including the newline character)
+ // to fctx->bbsave and trigger its processing
+ next_bucket = APR_BUCKET_NEXT(current_bucket);
+ APR_BUCKET_REMOVE(current_bucket);
+ APR_BRIGADE_INSERT_TAIL(fctx->bbsave, current_bucket);
+ process_bb_out = 1;
+ }
+
+ if (process_bb_out == 0) continue;
+
+ // Check buffer size requirements
+ apr_off_t needed_buffer_length;
+ apr_brigade_length(fctx->bbsave, 1, &needed_buffer_length);
+ if (needed_buffer_length > 0) {
+ // start with an empty buffer and ensure the needed buffer length is available
+ fctx->offset = 0;
+ preserve(fctx, needed_buffer_length + 1);
+
+ // Dump bucket brigade bbsave into the output buffer
+ apr_size_t current_length = fctx->avail;
+ rv = apr_brigade_flatten(fctx->bbsave, fctx->buf, ¤t_length);
+ if (rv != APR_SUCCESS) return rv;
+ fctx->offset = current_length;
+
+ // Terminate buffer with NULL byte, needed for regexp matching
+ const char zero_byte = 0;
+ pappend(fctx, &zero_byte, 1);
+
+ // cleanup the saved brigade and mark it as processed
+ apr_brigade_cleanup(fctx->bbsave);
+ process_bb_out = 0;
+
+ // DEBUG OUTPUT
+ const char* buf_log = apr_pstrndup(f->r->pool, fctx->buf, fctx->offset);
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, f->r, "Processing chunk: [%s]", buf_log);
+
+ // Replace matching regexps
+ dump_content(fctx);
+ }
+ if (APR_BUCKET_IS_EOS(current_bucket)) {
+ APR_BUCKET_REMOVE(current_bucket);
+ APR_BRIGADE_INSERT_TAIL(fctx->bb, current_bucket);
+ break;
+ }
+ }
+ apr_brigade_cleanup(bb);
+ return ap_pass_brigade(f->next, fctx->bb);
+}
+
static void *proxy_html_config(apr_pool_t *pool, char *x)
{
proxy_html_conf *ret = apr_pcalloc(pool, sizeof(proxy_html_conf));
@@ -1257,6 +1391,7 @@
if (xml2enc_filter)
xml2enc_filter(r, NULL, ENCIO_INPUT_CHECKS);
ap_add_output_filter("proxy-html", NULL, r, r->connection);
+ ap_add_output_filter("proxy-css", NULL, r, r->connection);
}
}
static void proxy_html_hooks(apr_pool_t *p)
@@ -1265,6 +1400,9 @@
ap_register_output_filter_protocol("proxy-html", proxy_html_filter,
NULL, AP_FTYPE_RESOURCE,
AP_FILTER_PROTO_CHANGE|AP_FILTER_PROTO_CHANGE_LENGTH);
+ ap_register_output_filter_protocol("proxy-css", proxy_css_filter,
+ NULL, AP_FTYPE_RESOURCE,
+ AP_FILTER_PROTO_CHANGE|AP_FILTER_PROTO_CHANGE_LENGTH);
/* move this to pre_config so old_expr is available to interpret
* old-style conditions on URL maps.
*/