~ubuntu-branches/ubuntu/feisty/apache2/feisty

« back to all changes in this revision

Viewing changes to server/util_filter.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Barth
  • Date: 2006-12-09 21:05:45 UTC
  • mfrom: (0.6.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20061209210545-h70s0xaqc2v8vqr2
Tags: 2.2.3-3.2
* Non-maintainer upload.
* 043_ajp_connection_reuse: Patch from upstream Bugzilla, fixing a critical
  issue with regard to connection reuse in mod_proxy_ajp.
  Closes: #396265

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
7
 *
 
8
 *     http://www.apache.org/licenses/LICENSE-2.0
 
9
 *
 
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.
 
15
 */
 
16
 
 
17
#define APR_WANT_STRFUNC
 
18
#include "apr_want.h"
 
19
#include "apr_lib.h"
 
20
#include "apr_hash.h"
 
21
#include "apr_strings.h"
 
22
 
 
23
#include "httpd.h"
 
24
#include "http_log.h"
 
25
#include "util_filter.h"
 
26
 
 
27
/* NOTE: Apache's current design doesn't allow a pool to be passed thru,
 
28
   so we depend on a global to hold the correct pool
 
29
*/
 
30
#define FILTER_POOL     apr_hook_global_pool
 
31
#include "apr_hooks.h"   /* for apr_hook_global_pool */
 
32
 
 
33
/*
 
34
** This macro returns true/false if a given filter should be inserted BEFORE
 
35
** another filter. This will happen when one of: 1) there isn't another
 
36
** filter; 2) that filter has a higher filter type (class); 3) that filter
 
37
** corresponds to a different request.
 
38
*/
 
39
#define INSERT_BEFORE(f, before_this) ((before_this) == NULL \
 
40
                           || (before_this)->frec->ftype > (f)->frec->ftype \
 
41
                           || (before_this)->r != (f)->r)
 
42
 
 
43
/* Trie structure to hold the mapping from registered
 
44
 * filter names to filters
 
45
 */
 
46
 
 
47
typedef struct filter_trie_node filter_trie_node;
 
48
 
 
49
typedef struct {
 
50
    int c;
 
51
    filter_trie_node *child;
 
52
} filter_trie_child_ptr;
 
53
 
 
54
/* Each trie node has an array of pointers to its children.
 
55
 * The array is kept in sorted order so that add_any_filter()
 
56
 * can do a binary search
 
57
 */
 
58
struct filter_trie_node {
 
59
    ap_filter_rec_t *frec;
 
60
    filter_trie_child_ptr *children;
 
61
    int nchildren;
 
62
    int size;
 
63
};
 
64
 
 
65
#define TRIE_INITIAL_SIZE 4
 
66
 
 
67
/* Link a trie node to its parent
 
68
 */
 
69
static void trie_node_link(apr_pool_t *p, filter_trie_node *parent,
 
70
                           filter_trie_node *child, int c)
 
71
{
 
72
    int i, j;
 
73
 
 
74
    if (parent->nchildren == parent->size) {
 
75
        filter_trie_child_ptr *new;
 
76
        parent->size *= 2;
 
77
        new = (filter_trie_child_ptr *)apr_palloc(p, parent->size *
 
78
                                             sizeof(filter_trie_child_ptr));
 
79
        memcpy(new, parent->children, parent->nchildren *
 
80
               sizeof(filter_trie_child_ptr));
 
81
        parent->children = new;
 
82
    }
 
83
 
 
84
    for (i = 0; i < parent->nchildren; i++) {
 
85
        if (c == parent->children[i].c) {
 
86
            return;
 
87
        }
 
88
        else if (c < parent->children[i].c) {
 
89
            break;
 
90
        }
 
91
    }
 
92
    for (j = parent->nchildren; j > i; j--) {
 
93
        parent->children[j].c = parent->children[j - 1].c;
 
94
        parent->children[j].child = parent->children[j - 1].child;
 
95
    }
 
96
    parent->children[i].c = c;
 
97
    parent->children[i].child = child;
 
98
 
 
99
    parent->nchildren++;
 
100
}
 
101
 
 
102
/* Allocate a new node for a trie.
 
103
 * If parent is non-NULL, link the new node under the parent node with
 
104
 * key 'c' (or, if an existing child node matches, return that one)
 
105
 */
 
106
static filter_trie_node *trie_node_alloc(apr_pool_t *p,
 
107
                                         filter_trie_node *parent, char c)
 
108
{
 
109
    filter_trie_node *new_node;
 
110
    if (parent) {
 
111
        int i;
 
112
        for (i = 0; i < parent->nchildren; i++) {
 
113
            if (c == parent->children[i].c) {
 
114
                return parent->children[i].child;
 
115
            }
 
116
            else if (c < parent->children[i].c) {
 
117
                break;
 
118
            }
 
119
        }
 
120
        new_node =
 
121
            (filter_trie_node *)apr_palloc(p, sizeof(filter_trie_node));
 
122
        trie_node_link(p, parent, new_node, c);
 
123
    }
 
124
    else { /* No parent node */
 
125
        new_node = (filter_trie_node *)apr_palloc(p,
 
126
                                                  sizeof(filter_trie_node));
 
127
    }
 
128
 
 
129
    new_node->frec = NULL;
 
130
    new_node->nchildren = 0;
 
131
    new_node->size = TRIE_INITIAL_SIZE;
 
132
    new_node->children = (filter_trie_child_ptr *)apr_palloc(p,
 
133
                             new_node->size * sizeof(filter_trie_child_ptr));
 
134
    return new_node;
 
135
}
 
136
 
 
137
static filter_trie_node *registered_output_filters = NULL;
 
138
static filter_trie_node *registered_input_filters = NULL;
 
139
 
 
140
 
 
141
static apr_status_t filter_cleanup(void *ctx)
 
142
{
 
143
    registered_output_filters = NULL;
 
144
    registered_input_filters = NULL;
 
145
    return APR_SUCCESS;
 
146
}
 
147
 
 
148
static ap_filter_rec_t *get_filter_handle(const char *name,
 
149
                                          const filter_trie_node *filter_set)
 
150
{
 
151
    if (filter_set) {
 
152
        const char *n;
 
153
        const filter_trie_node *node;
 
154
 
 
155
        node = filter_set;
 
156
        for (n = name; *n; n++) {
 
157
            int start, end;
 
158
            start = 0;
 
159
            end = node->nchildren - 1;
 
160
            while (end >= start) {
 
161
                int middle = (end + start) / 2;
 
162
                char ch = node->children[middle].c;
 
163
                if (*n == ch) {
 
164
                    node = node->children[middle].child;
 
165
                    break;
 
166
                }
 
167
                else if (*n < ch) {
 
168
                    end = middle - 1;
 
169
                }
 
170
                else {
 
171
                    start = middle + 1;
 
172
                }
 
173
            }
 
174
            if (end < start) {
 
175
                node = NULL;
 
176
                break;
 
177
            }
 
178
        }
 
179
 
 
180
        if (node && node->frec) {
 
181
            return node->frec;
 
182
        }
 
183
    }
 
184
    return NULL;
 
185
}
 
186
 
 
187
AP_DECLARE(ap_filter_rec_t *)ap_get_output_filter_handle(const char *name)
 
188
{
 
189
    return get_filter_handle(name, registered_output_filters);
 
190
}
 
191
 
 
192
AP_DECLARE(ap_filter_rec_t *)ap_get_input_filter_handle(const char *name)
 
193
{
 
194
    return get_filter_handle(name, registered_input_filters);
 
195
}
 
196
 
 
197
static ap_filter_rec_t *register_filter(const char *name,
 
198
                            ap_filter_func filter_func,
 
199
                            ap_init_filter_func filter_init,
 
200
                            ap_filter_type ftype,
 
201
                            filter_trie_node **reg_filter_set)
 
202
{
 
203
    ap_filter_rec_t *frec;
 
204
    char *normalized_name;
 
205
    const char *n;
 
206
    filter_trie_node *node;
 
207
 
 
208
    if (!*reg_filter_set) {
 
209
        *reg_filter_set = trie_node_alloc(FILTER_POOL, NULL, 0);
 
210
    }
 
211
 
 
212
    normalized_name = apr_pstrdup(FILTER_POOL, name);
 
213
    ap_str_tolower(normalized_name);
 
214
 
 
215
    node = *reg_filter_set;
 
216
    for (n = normalized_name; *n; n++) {
 
217
        filter_trie_node *child = trie_node_alloc(FILTER_POOL, node, *n);
 
218
        if (apr_isalpha(*n)) {
 
219
            trie_node_link(FILTER_POOL, node, child, apr_toupper(*n));
 
220
        }
 
221
        node = child;
 
222
    }
 
223
    if (node->frec) {
 
224
        frec = node->frec;
 
225
    }
 
226
    else {
 
227
        frec = apr_pcalloc(FILTER_POOL, sizeof(*frec));
 
228
        node->frec = frec;
 
229
        frec->name = normalized_name;
 
230
    }
 
231
    frec->filter_func = filter_func;
 
232
    frec->filter_init_func = filter_init;
 
233
    frec->ftype = ftype;
 
234
 
 
235
    apr_pool_cleanup_register(FILTER_POOL, NULL, filter_cleanup,
 
236
                              apr_pool_cleanup_null);
 
237
    return frec;
 
238
}
 
239
 
 
240
AP_DECLARE(ap_filter_rec_t *) ap_register_input_filter(const char *name,
 
241
                                          ap_in_filter_func filter_func,
 
242
                                          ap_init_filter_func filter_init,
 
243
                                          ap_filter_type ftype)
 
244
{
 
245
    ap_filter_func f;
 
246
    f.in_func = filter_func;
 
247
    return register_filter(name, f, filter_init, ftype,
 
248
                           &registered_input_filters);
 
249
}
 
250
 
 
251
/* Prepare to make this a #define in 2.2 */
 
252
AP_DECLARE(ap_filter_rec_t *) ap_register_output_filter(const char *name,
 
253
                                           ap_out_filter_func filter_func,
 
254
                                           ap_init_filter_func filter_init,
 
255
                                           ap_filter_type ftype)
 
256
{
 
257
    return ap_register_output_filter_protocol(name, filter_func,
 
258
                                              filter_init, ftype, 0) ;
 
259
}
 
260
AP_DECLARE(ap_filter_rec_t *) ap_register_output_filter_protocol(
 
261
                                           const char *name,
 
262
                                           ap_out_filter_func filter_func,
 
263
                                           ap_init_filter_func filter_init,
 
264
                                           ap_filter_type ftype,
 
265
                                           unsigned int proto_flags)
 
266
{
 
267
    ap_filter_rec_t* ret ;
 
268
    ap_filter_func f;
 
269
    f.out_func = filter_func;
 
270
    ret = register_filter(name, f, filter_init, ftype,
 
271
                          &registered_output_filters);
 
272
    ret->proto_flags = proto_flags ;
 
273
    return ret ;
 
274
}
 
275
 
 
276
static ap_filter_t *add_any_filter_handle(ap_filter_rec_t *frec, void *ctx,
 
277
                                          request_rec *r, conn_rec *c,
 
278
                                          ap_filter_t **r_filters,
 
279
                                          ap_filter_t **p_filters,
 
280
                                          ap_filter_t **c_filters)
 
281
{
 
282
    apr_pool_t* p = r ? r->pool : c->pool;
 
283
    ap_filter_t *f = apr_palloc(p, sizeof(*f));
 
284
    ap_filter_t **outf;
 
285
 
 
286
    if (frec->ftype < AP_FTYPE_PROTOCOL) {
 
287
        if (r) {
 
288
            outf = r_filters;
 
289
        }
 
290
        else {
 
291
            ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
 
292
                      "a content filter was added without a request: %s", frec->name);
 
293
            return NULL;
 
294
        }
 
295
    }
 
296
    else if (frec->ftype < AP_FTYPE_CONNECTION) {
 
297
        if (r) {
 
298
            outf = p_filters;
 
299
        }
 
300
        else {
 
301
            ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
 
302
                         "a protocol filter was added without a request: %s", frec->name);
 
303
            return NULL;
 
304
        }
 
305
    }
 
306
    else {
 
307
        outf = c_filters;
 
308
    }
 
309
 
 
310
    f->frec = frec;
 
311
    f->ctx = ctx;
 
312
    f->r = r;
 
313
    f->c = c;
 
314
    f->next = NULL;
 
315
 
 
316
    if (INSERT_BEFORE(f, *outf)) {
 
317
        f->next = *outf;
 
318
 
 
319
        if (*outf) {
 
320
            ap_filter_t *first = NULL;
 
321
 
 
322
            if (r) {
 
323
                /* If we are adding our first non-connection filter,
 
324
                 * Then don't try to find the right location, it is
 
325
                 * automatically first.
 
326
                 */
 
327
                if (*r_filters != *c_filters) {
 
328
                    first = *r_filters;
 
329
                    while (first && (first->next != (*outf))) {
 
330
                        first = first->next;
 
331
                    }
 
332
                }
 
333
            }
 
334
            if (first && first != (*outf)) {
 
335
                first->next = f;
 
336
            }
 
337
        }
 
338
        *outf = f;
 
339
    }
 
340
    else {
 
341
        ap_filter_t *fscan = *outf;
 
342
        while (!INSERT_BEFORE(f, fscan->next))
 
343
            fscan = fscan->next;
 
344
 
 
345
        f->next = fscan->next;
 
346
        fscan->next = f;
 
347
    }
 
348
 
 
349
    if (frec->ftype < AP_FTYPE_CONNECTION && (*r_filters == *c_filters)) {
 
350
        *r_filters = *p_filters;
 
351
    }
 
352
    return f;
 
353
}
 
354
 
 
355
static ap_filter_t *add_any_filter(const char *name, void *ctx,
 
356
                                   request_rec *r, conn_rec *c,
 
357
                                   const filter_trie_node *reg_filter_set,
 
358
                                   ap_filter_t **r_filters,
 
359
                                   ap_filter_t **p_filters,
 
360
                                   ap_filter_t **c_filters)
 
361
{
 
362
    if (reg_filter_set) {
 
363
        const char *n;
 
364
        const filter_trie_node *node;
 
365
 
 
366
        node = reg_filter_set;
 
367
        for (n = name; *n; n++) {
 
368
            int start, end;
 
369
            start = 0;
 
370
            end = node->nchildren - 1;
 
371
            while (end >= start) {
 
372
                int middle = (end + start) / 2;
 
373
                char ch = node->children[middle].c;
 
374
                if (*n == ch) {
 
375
                    node = node->children[middle].child;
 
376
                    break;
 
377
                }
 
378
                else if (*n < ch) {
 
379
                    end = middle - 1;
 
380
                }
 
381
                else {
 
382
                    start = middle + 1;
 
383
                }
 
384
            }
 
385
            if (end < start) {
 
386
                node = NULL;
 
387
                break;
 
388
            }
 
389
        }
 
390
 
 
391
        if (node && node->frec) {
 
392
            return add_any_filter_handle(node->frec, ctx, r, c, r_filters,
 
393
                                         p_filters, c_filters);
 
394
        }
 
395
    }
 
396
 
 
397
    ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
 
398
                 "an unknown filter was not added: %s", name);
 
399
    return NULL;
 
400
}
 
401
 
 
402
AP_DECLARE(ap_filter_t *) ap_add_input_filter(const char *name, void *ctx,
 
403
                                              request_rec *r, conn_rec *c)
 
404
{
 
405
    return add_any_filter(name, ctx, r, c, registered_input_filters,
 
406
                          r ? &r->input_filters : NULL,
 
407
                          r ? &r->proto_input_filters : NULL, &c->input_filters);
 
408
}
 
409
 
 
410
AP_DECLARE(ap_filter_t *) ap_add_input_filter_handle(ap_filter_rec_t *f,
 
411
                                                     void *ctx,
 
412
                                                     request_rec *r,
 
413
                                                     conn_rec *c)
 
414
{
 
415
    return add_any_filter_handle(f, ctx, r, c, r ? &r->input_filters : NULL,
 
416
                                 r ? &r->proto_input_filters : NULL,
 
417
                                 &c->input_filters);
 
418
}
 
419
 
 
420
AP_DECLARE(ap_filter_t *) ap_add_output_filter(const char *name, void *ctx,
 
421
                                               request_rec *r, conn_rec *c)
 
422
{
 
423
    return add_any_filter(name, ctx, r, c, registered_output_filters,
 
424
                          r ? &r->output_filters : NULL,
 
425
                          r ? &r->proto_output_filters : NULL, &c->output_filters);
 
426
}
 
427
 
 
428
AP_DECLARE(ap_filter_t *) ap_add_output_filter_handle(ap_filter_rec_t *f,
 
429
                                                      void *ctx,
 
430
                                                      request_rec *r,
 
431
                                                      conn_rec *c)
 
432
{
 
433
    return add_any_filter_handle(f, ctx, r, c, r ? &r->output_filters : NULL,
 
434
                                 r ? &r->proto_output_filters : NULL,
 
435
                                 &c->output_filters);
 
436
}
 
437
 
 
438
static void remove_any_filter(ap_filter_t *f, ap_filter_t **r_filt, ap_filter_t **p_filt,
 
439
                              ap_filter_t **c_filt)
 
440
{
 
441
    ap_filter_t **curr = r_filt ? r_filt : c_filt;
 
442
    ap_filter_t *fscan = *curr;
 
443
 
 
444
    if (p_filt && *p_filt == f)
 
445
        *p_filt = (*p_filt)->next;
 
446
 
 
447
    if (*curr == f) {
 
448
        *curr = (*curr)->next;
 
449
        return;
 
450
    }
 
451
 
 
452
    while (fscan->next != f) {
 
453
        if (!(fscan = fscan->next)) {
 
454
            return;
 
455
        }
 
456
    }
 
457
 
 
458
    fscan->next = f->next;
 
459
}
 
460
 
 
461
AP_DECLARE(void) ap_remove_input_filter(ap_filter_t *f)
 
462
{
 
463
    remove_any_filter(f, f->r ? &f->r->input_filters : NULL,
 
464
                      f->r ? &f->r->proto_input_filters : NULL,
 
465
                      &f->c->input_filters);
 
466
}
 
467
 
 
468
AP_DECLARE(void) ap_remove_output_filter(ap_filter_t *f)
 
469
{
 
470
    remove_any_filter(f, f->r ? &f->r->output_filters : NULL,
 
471
                      f->r ? &f->r->proto_output_filters : NULL,
 
472
                      &f->c->output_filters);
 
473
}
 
474
 
 
475
/*
 
476
 * Read data from the next filter in the filter stack.  Data should be
 
477
 * modified in the bucket brigade that is passed in.  The core allocates the
 
478
 * bucket brigade, modules that wish to replace large chunks of data or to
 
479
 * save data off to the side should probably create their own temporary
 
480
 * brigade especially for that use.
 
481
 */
 
482
AP_DECLARE(apr_status_t) ap_get_brigade(ap_filter_t *next,
 
483
                                        apr_bucket_brigade *bb,
 
484
                                        ap_input_mode_t mode,
 
485
                                        apr_read_type_e block,
 
486
                                        apr_off_t readbytes)
 
487
{
 
488
    if (next) {
 
489
        return next->frec->filter_func.in_func(next, bb, mode, block,
 
490
                                               readbytes);
 
491
    }
 
492
    return AP_NOBODY_READ;
 
493
}
 
494
 
 
495
/* Pass the buckets to the next filter in the filter stack.  If the
 
496
 * current filter is a handler, we should get NULL passed in instead of
 
497
 * the current filter.  At that point, we can just call the first filter in
 
498
 * the stack, or r->output_filters.
 
499
 */
 
500
AP_DECLARE(apr_status_t) ap_pass_brigade(ap_filter_t *next,
 
501
                                         apr_bucket_brigade *bb)
 
502
{
 
503
    if (next) {
 
504
        apr_bucket *e;
 
505
        if ((e = APR_BRIGADE_LAST(bb)) && APR_BUCKET_IS_EOS(e) && next->r) {
 
506
            /* This is only safe because HTTP_HEADER filter is always in
 
507
             * the filter stack.   This ensures that there is ALWAYS a
 
508
             * request-based filter that we can attach this to.  If the
 
509
             * HTTP_FILTER is removed, and another filter is not put in its
 
510
             * place, then handlers like mod_cgi, which attach their own
 
511
             * EOS bucket to the brigade will be broken, because we will
 
512
             * get two EOS buckets on the same request.
 
513
             */
 
514
            next->r->eos_sent = 1;
 
515
 
 
516
            /* remember the eos for internal redirects, too */
 
517
            if (next->r->prev) {
 
518
                request_rec *prev = next->r->prev;
 
519
 
 
520
                while (prev) {
 
521
                    prev->eos_sent = 1;
 
522
                    prev = prev->prev;
 
523
                }
 
524
            }
 
525
        }
 
526
        return next->frec->filter_func.out_func(next, bb);
 
527
    }
 
528
    return AP_NOBODY_WROTE;
 
529
}
 
530
 
 
531
AP_DECLARE(apr_status_t) ap_save_brigade(ap_filter_t *f,
 
532
                                         apr_bucket_brigade **saveto,
 
533
                                         apr_bucket_brigade **b, apr_pool_t *p)
 
534
{
 
535
    apr_bucket *e;
 
536
    apr_status_t rv, srv = APR_SUCCESS;
 
537
 
 
538
    /* If have never stored any data in the filter, then we had better
 
539
     * create an empty bucket brigade so that we can concat.
 
540
     */
 
541
    if (!(*saveto)) {
 
542
        *saveto = apr_brigade_create(p, f->c->bucket_alloc);
 
543
    }
 
544
 
 
545
    for (e = APR_BRIGADE_FIRST(*b);
 
546
         e != APR_BRIGADE_SENTINEL(*b);
 
547
         e = APR_BUCKET_NEXT(e))
 
548
    {
 
549
        rv = apr_bucket_setaside(e, p);
 
550
 
 
551
        /* If the bucket type does not implement setaside, then
 
552
         * (hopefully) morph it into a bucket type which does, and set
 
553
         * *that* aside... */
 
554
        if (rv == APR_ENOTIMPL) {
 
555
            const char *s;
 
556
            apr_size_t n;
 
557
 
 
558
            rv = apr_bucket_read(e, &s, &n, APR_BLOCK_READ);
 
559
            if (rv == APR_SUCCESS) {
 
560
                rv = apr_bucket_setaside(e, p);
 
561
            }
 
562
        }
 
563
 
 
564
        if (rv != APR_SUCCESS) {
 
565
            srv = rv;
 
566
            /* Return an error but still save the brigade if
 
567
             * ->setaside() is really not implemented. */
 
568
            if (rv != APR_ENOTIMPL) {
 
569
                return rv;
 
570
            }
 
571
        }
 
572
    }
 
573
    APR_BRIGADE_CONCAT(*saveto, *b);
 
574
    return srv;
 
575
}
 
576
 
 
577
AP_DECLARE_NONSTD(apr_status_t) ap_filter_flush(apr_bucket_brigade *bb,
 
578
                                                void *ctx)
 
579
{
 
580
    ap_filter_t *f = ctx;
 
581
 
 
582
    return ap_pass_brigade(f, bb);
 
583
}
 
584
 
 
585
AP_DECLARE(apr_status_t) ap_fflush(ap_filter_t *f, apr_bucket_brigade *bb)
 
586
{
 
587
    apr_bucket *b;
 
588
 
 
589
    b = apr_bucket_flush_create(f->c->bucket_alloc);
 
590
    APR_BRIGADE_INSERT_TAIL(bb, b);
 
591
    return ap_pass_brigade(f, bb);
 
592
}
 
593
 
 
594
AP_DECLARE_NONSTD(apr_status_t) ap_fputstrs(ap_filter_t *f,
 
595
                                            apr_bucket_brigade *bb, ...)
 
596
{
 
597
    va_list args;
 
598
    apr_status_t rv;
 
599
 
 
600
    va_start(args, bb);
 
601
    rv = apr_brigade_vputstrs(bb, ap_filter_flush, f, args);
 
602
    va_end(args);
 
603
    return rv;
 
604
}
 
605
 
 
606
AP_DECLARE_NONSTD(apr_status_t) ap_fprintf(ap_filter_t *f,
 
607
                                           apr_bucket_brigade *bb,
 
608
                                           const char *fmt,
 
609
                                           ...)
 
610
{
 
611
    va_list args;
 
612
    apr_status_t rv;
 
613
 
 
614
    va_start(args, fmt);
 
615
    rv = apr_brigade_vprintf(bb, ap_filter_flush, f, fmt, args);
 
616
    va_end(args);
 
617
    return rv;
 
618
}
 
619
AP_DECLARE(void) ap_filter_protocol(ap_filter_t *f, unsigned int flags)
 
620
{
 
621
    f->frec->proto_flags = flags ;
 
622
}