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 for (e
= APR_BRIGADE_FIRST(bb
);
85 e
!= APR_BRIGADE_SENTINEL(bb
);
86 e
= APR_BUCKET_NEXT(e
))
89 apr_size_t len
, i
, lastpos
;
91 if (APR_BUCKET_IS_EOS(e
)) {
93 APR_BRIGADE_INSERT_TAIL(ctx
->bb
, e
);
95 /* Okay, we've seen the EOS.
96 * Time to pass it along down the chain.
98 return ap_pass_brigade(f
->next
, ctx
->bb
);
101 if (APR_BUCKET_IS_FLUSH(e
)) {
103 * Ignore flush buckets for the moment..
104 * we decide what to stream
109 if (APR_BUCKET_IS_METADATA(e
)) {
110 /* metadata bucket */
112 apr_bucket_copy(e
, &cpy
);
113 APR_BRIGADE_INSERT_TAIL(ctx
->bb
, cpy
);
118 apr_bucket_read(e
, &data
, &len
, APR_BLOCK_READ
);
122 for (i
= 0; i
< len
; i
++) {
123 if (data
[i
] == c
->flushdelimiter
||
124 data
[i
] == c
->bucketdelimiter
||
125 data
[i
] == c
->passdelimiter
) {
127 if (i
- lastpos
> 0) {
128 p
= apr_bucket_pool_create(apr_pmemdup(f
->r
->pool
,
134 APR_BRIGADE_INSERT_TAIL(ctx
->bb
, p
);
137 if (data
[i
] == c
->flushdelimiter
) {
138 p
= apr_bucket_flush_create(f
->c
->bucket_alloc
);
139 APR_BRIGADE_INSERT_TAIL(ctx
->bb
, p
);
141 if (data
[i
] == c
->passdelimiter
) {
144 rv
= ap_pass_brigade(f
->next
, ctx
->bb
);
151 /* XXX: really should append this to the next 'real' bucket */
154 p
= apr_bucket_pool_create(apr_pmemdup(f
->r
->pool
,
161 APR_BRIGADE_INSERT_TAIL(ctx
->bb
, p
);
169 static void register_hooks(apr_pool_t
* p
)
171 ap_register_output_filter(bucketeerFilterName
, bucketeer_out_filter
,
172 NULL
, AP_FTYPE_RESOURCE
-1);
175 static const command_rec bucketeer_filter_cmds
[] = {
179 module AP_MODULE_DECLARE_DATA bucketeer_module
= {
180 STANDARD20_MODULE_STUFF
,
183 create_bucketeer_server_config
,
185 bucketeer_filter_cmds
,