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.
18 * mod_bucketeer.c: split buckets whenever we find a control-char
20 * Written by Ian Holsman
25 #include "http_config.h"
27 #include "apr_strings.h"
28 #include "apr_general.h"
29 #include "util_filter.h"
30 #include "apr_buckets.h"
31 #include "http_request.h"
32 #include "http_protocol.h"
34 static const char bucketeerFilterName[] = "BUCKETEER";
35 module AP_MODULE_DECLARE_DATA bucketeer_module;
37 typedef struct bucketeer_filter_config_t
42 } bucketeer_filter_config_t;
45 static void *create_bucketeer_server_config(apr_pool_t *p, server_rec *s)
47 bucketeer_filter_config_t *c = apr_pcalloc(p, sizeof *c);
49 c->bucketdelimiter = 0x02; /* ^B */
50 c->passdelimiter = 0x10; /* ^P */
51 c->flushdelimiter = 0x06; /* ^F */
56 typedef struct bucketeer_ctx_t
58 apr_bucket_brigade *bb;
61 static apr_status_t bucketeer_out_filter(ap_filter_t *f,
62 apr_bucket_brigade *bb)
65 request_rec *r = f->r;
66 bucketeer_ctx_t *ctx = f->ctx;
67 bucketeer_filter_config_t *c;
69 c = ap_get_module_config(r->server->module_config, &bucketeer_module);
71 /* If have a context, it means we've done this before successfully. */
73 if (!r->content_type || strncmp(r->content_type, "text/", 5)) {
74 ap_remove_output_filter(f);
75 return ap_pass_brigade(f->next, bb);
78 /* We're cool with filtering this. */
79 ctx = f->ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
80 ctx->bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
81 apr_table_unset(f->r->headers_out, "Content-Length");
84 APR_BRIGADE_FOREACH(e, bb) {
86 apr_size_t len, i, lastpos;
88 if (APR_BUCKET_IS_EOS(e)) {
90 APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
92 /* Okay, we've seen the EOS.
93 * Time to pass it along down the chain.
95 return ap_pass_brigade(f->next, ctx->bb);
98 if (APR_BUCKET_IS_FLUSH(e)) {
100 * Ignore flush buckets for the moment..
101 * we decide what to stream
106 if (APR_BUCKET_IS_METADATA(e)) {
107 /* metadata bucket */
109 apr_bucket_copy(e, &cpy);
110 APR_BRIGADE_INSERT_TAIL(ctx->bb, cpy);
115 apr_bucket_read(e, &data, &len, APR_BLOCK_READ);
119 for (i = 0; i < len; i++) {
120 if (data[i] == c->flushdelimiter ||
121 data[i] == c->bucketdelimiter ||
122 data[i] == c->passdelimiter) {
124 if (i - lastpos > 0) {
125 p = apr_bucket_pool_create(apr_pmemdup(f->r->pool,
131 APR_BRIGADE_INSERT_TAIL(ctx->bb, p);
134 if (data[i] == c->flushdelimiter) {
135 p = apr_bucket_flush_create(f->c->bucket_alloc);
136 APR_BRIGADE_INSERT_TAIL(ctx->bb, p);
138 if (data[i] == c->flushdelimiter ||
139 data[i] == c->passdelimiter) {
140 ap_pass_brigade(f->next, ctx->bb);
141 /* apr_brigade_cleanup(ctx->bb);*/
145 /* XXX: really should append this to the next 'real' bucket */
148 p = apr_bucket_pool_create(apr_pmemdup(f->r->pool,
155 APR_BRIGADE_INSERT_TAIL(ctx->bb, p);
163 static void register_hooks(apr_pool_t * p)
165 ap_register_output_filter(bucketeerFilterName, bucketeer_out_filter,
166 NULL, AP_FTYPE_RESOURCE-1);
169 static const command_rec bucketeer_filter_cmds[] = {
173 module AP_MODULE_DECLARE_DATA bucketeer_module = {
174 STANDARD20_MODULE_STUFF,
177 create_bucketeer_server_config,
179 bucketeer_filter_cmds,