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_ext_filter allows Unix-style filters to filter http content.
22 #include "http_config.h"
24 #include "http_protocol.h"
26 #include "http_core.h"
27 #include "apr_buckets.h"
28 #include "util_filter.h"
29 #include "util_script.h"
30 #include "util_time.h"
31 #include "apr_strings.h"
35 #define APR_WANT_STRFUNC
38 typedef struct ef_server_t {
43 typedef struct ef_filter_t {
45 enum {INPUT_FILTER=1, OUTPUT_FILTER} mode;
48 const char *enable_env;
49 const char *disable_env;
51 const char *intype; /* list of IMTs we process (well, just one for now) */
52 #define INTYPE_ALL (char *)1
53 const char *outtype; /* IMT of filtered output */
54 #define OUTTYPE_UNCHANGED (char *)1
55 int preserves_content_length;
58 typedef struct ef_dir_t {
63 typedef struct ef_ctx_t {
66 apr_procattr_t *procattr;
70 #if APR_FILES_AS_SOCKETS
71 apr_pollset_t *pollset;
75 module AP_MODULE_DECLARE_DATA ext_filter_module;
76 static const server_rec *main_server;
78 static apr_status_t ef_output_filter(ap_filter_t *, apr_bucket_brigade *);
79 static apr_status_t ef_input_filter(ap_filter_t *, apr_bucket_brigade *,
80 ap_input_mode_t, apr_read_type_e,
83 #define ERRFN_USERDATA_KEY "EXTFILTCHILDERRFN"
85 static void *create_ef_dir_conf(apr_pool_t *p, char *dummy)
87 ef_dir_t *dc = (ef_dir_t *)apr_pcalloc(p, sizeof(ef_dir_t));
95 static void *create_ef_server_conf(apr_pool_t *p, server_rec *s)
99 conf = (ef_server_t *)apr_pcalloc(p, sizeof(ef_server_t));
101 conf->h = apr_hash_make(conf->p);
105 static void *merge_ef_dir_conf(apr_pool_t *p, void *basev, void *overridesv)
107 ef_dir_t *a = (ef_dir_t *)apr_pcalloc (p, sizeof(ef_dir_t));
108 ef_dir_t *base = (ef_dir_t *)basev, *over = (ef_dir_t *)overridesv;
110 if (over->log_stderr != -1) { /* if admin coded something... */
111 a->log_stderr = over->log_stderr;
114 a->log_stderr = base->log_stderr;
117 if (over->onfail != -1) { /* if admin coded something... */
118 a->onfail = over->onfail;
121 a->onfail = base->onfail;
127 static const char *add_options(cmd_parms *cmd, void *in_dc,
130 ef_dir_t *dc = in_dc;
132 if (!strcasecmp(arg, "LogStderr")) {
135 else if (!strcasecmp(arg, "NoLogStderr")) {
138 else if (!strcasecmp(arg, "Onfail=remove")) {
141 else if (!strcasecmp(arg, "Onfail=abort")) {
145 return apr_pstrcat(cmd->temp_pool,
146 "Invalid ExtFilterOptions option: ",
154 static const char *parse_cmd(apr_pool_t *p, const char **args, ef_filter_t *filter)
157 const char *start = *args + 1;
162 ++*args; /* move past leading " */
163 /* find true end of args string (accounting for escaped quotes) */
164 while (**args && (**args != '"' || (**args == '"' && escaping))) {
168 else if (**args == '\\') {
174 return "Expected cmd= delimiter";
176 /* copy *just* the arg string for parsing, */
177 parms = apr_pstrndup(p, start, *args - start);
178 ++*args; /* move past trailing " */
180 /* parse and tokenize the args. */
181 rv = apr_tokenize_to_argv(parms, &(filter->args), p);
182 if (rv != APR_SUCCESS) {
183 return "cmd= parse error";
189 /* Allocate space for two argv pointers and parse the args. */
190 filter->args = (char **)apr_palloc(p, 2 * sizeof(char *));
191 filter->args[0] = ap_getword_white(p, args);
192 filter->args[1] = NULL; /* end of args */
194 if (!filter->args[0]) {
195 return "Invalid cmd= parameter";
197 filter->command = filter->args[0];
202 static const char *define_filter(cmd_parms *cmd, void *dummy, const char *args)
204 ef_server_t *conf = ap_get_module_config(cmd->server->module_config,
208 char *normalized_name;
211 name = ap_getword_white(cmd->pool, &args);
213 return "Filter name not found";
216 /* During request processing, we find information about the filter
217 * by looking up the filter name provided by core server in our
218 * hash table. But the core server has normalized the filter
219 * name by converting it to lower case. Thus, when adding the
220 * filter to our hash table we have to use lower case as well.
222 normalized_name = apr_pstrdup(cmd->pool, name);
223 ap_str_tolower(normalized_name);
225 if (apr_hash_get(conf->h, normalized_name, APR_HASH_KEY_STRING)) {
226 return apr_psprintf(cmd->pool, "ExtFilter %s is already defined",
230 filter = (ef_filter_t *)apr_pcalloc(conf->p, sizeof(ef_filter_t));
232 filter->mode = OUTPUT_FILTER;
233 filter->ftype = AP_FTYPE_RESOURCE;
234 apr_hash_set(conf->h, normalized_name, APR_HASH_KEY_STRING, filter);
237 while (apr_isspace(*args)) {
241 /* Nasty parsing... I wish I could simply use ap_getword_white()
242 * here and then look at the token, but ap_getword_white() doesn't
243 * do the right thing when we have cmd="word word word"
245 if (!strncasecmp(args, "preservescontentlength", 22)) {
246 token = ap_getword_white(cmd->pool, &args);
247 if (!strcasecmp(token, "preservescontentlength")) {
248 filter->preserves_content_length = 1;
251 return apr_psprintf(cmd->pool,
252 "mangled argument `%s'",
258 if (!strncasecmp(args, "mode=", 5)) {
260 token = ap_getword_white(cmd->pool, &args);
261 if (!strcasecmp(token, "output")) {
262 filter->mode = OUTPUT_FILTER;
264 else if (!strcasecmp(token, "input")) {
265 filter->mode = INPUT_FILTER;
268 return apr_psprintf(cmd->pool, "Invalid mode: `%s'",
274 if (!strncasecmp(args, "ftype=", 6)) {
276 token = ap_getword_white(cmd->pool, &args);
277 filter->ftype = atoi(token);
281 if (!strncasecmp(args, "enableenv=", 10)) {
283 token = ap_getword_white(cmd->pool, &args);
284 filter->enable_env = token;
288 if (!strncasecmp(args, "disableenv=", 11)) {
290 token = ap_getword_white(cmd->pool, &args);
291 filter->disable_env = token;
295 if (!strncasecmp(args, "intype=", 7)) {
297 filter->intype = ap_getword_white(cmd->pool, &args);
301 if (!strncasecmp(args, "outtype=", 8)) {
303 filter->outtype = ap_getword_white(cmd->pool, &args);
307 if (!strncasecmp(args, "cmd=", 4)) {
309 if ((token = parse_cmd(cmd->pool, &args, filter))) {
315 return apr_psprintf(cmd->pool, "Unexpected parameter: `%s'",
319 /* parsing is done... register the filter
321 if (filter->mode == OUTPUT_FILTER) {
322 /* XXX need a way to ensure uniqueness among all filters */
323 ap_register_output_filter(filter->name, ef_output_filter, NULL, filter->ftype);
325 else if (filter->mode == INPUT_FILTER) {
326 /* XXX need a way to ensure uniqueness among all filters */
327 ap_register_input_filter(filter->name, ef_input_filter, NULL, filter->ftype);
330 ap_assert(1 != 1); /* we set the field wrong somehow */
336 static const command_rec cmds[] =
338 AP_INIT_ITERATE("ExtFilterOptions",
341 ACCESS_CONF, /* same as SetInputFilter/SetOutputFilter */
342 "valid options: LogStderr, NoLogStderr"),
343 AP_INIT_RAW_ARGS("ExtFilterDefine",
347 "Define an external filter"),
351 static int ef_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *main_s)
353 main_server = main_s;
357 static void register_hooks(apr_pool_t *p)
359 ap_hook_post_config(ef_init, NULL, NULL, APR_HOOK_MIDDLE);
362 static apr_status_t set_resource_limits(request_rec *r,
363 apr_procattr_t *procattr)
365 #if defined(RLIMIT_CPU) || defined(RLIMIT_NPROC) || \
366 defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined (RLIMIT_AS)
367 core_dir_config *conf =
368 (core_dir_config *)ap_get_core_module_config(r->per_dir_config);
372 rv = apr_procattr_limit_set(procattr, APR_LIMIT_CPU, conf->limit_cpu);
373 ap_assert(rv == APR_SUCCESS); /* otherwise, we're out of sync with APR */
375 #if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS)
376 rv = apr_procattr_limit_set(procattr, APR_LIMIT_MEM, conf->limit_mem);
377 ap_assert(rv == APR_SUCCESS); /* otherwise, we're out of sync with APR */
380 rv = apr_procattr_limit_set(procattr, APR_LIMIT_NPROC, conf->limit_nproc);
381 ap_assert(rv == APR_SUCCESS); /* otherwise, we're out of sync with APR */
384 #endif /* if at least one limit defined */
389 static apr_status_t ef_close_file(void *vfile)
391 return apr_file_close(vfile);
394 static void child_errfn(apr_pool_t *pool, apr_status_t err, const char *description)
398 apr_file_t *stderr_log;
399 char time_str[APR_CTIME_LEN];
401 apr_pool_userdata_get(&vr, ERRFN_USERDATA_KEY, pool);
403 apr_file_open_stderr(&stderr_log, pool);
404 ap_recent_ctime(time_str, apr_time_now());
405 apr_file_printf(stderr_log,
406 "[%s] [client %s] mod_ext_filter (%d)%pm: %s\n",
414 /* init_ext_filter_process: get the external filter process going
415 * This is per-filter-instance (i.e., per-request) initialization.
417 static apr_status_t init_ext_filter_process(ap_filter_t *f)
419 ef_ctx_t *ctx = f->ctx;
421 ef_dir_t *dc = ctx->dc;
422 const char * const *env;
424 ctx->proc = apr_pcalloc(ctx->p, sizeof(*ctx->proc));
426 rc = apr_procattr_create(&ctx->procattr, ctx->p);
427 ap_assert(rc == APR_SUCCESS);
429 rc = apr_procattr_io_set(ctx->procattr,
433 ap_assert(rc == APR_SUCCESS);
435 rc = set_resource_limits(f->r, ctx->procattr);
436 ap_assert(rc == APR_SUCCESS);
438 if (dc->log_stderr > 0) {
439 rc = apr_procattr_child_err_set(ctx->procattr,
440 f->r->server->error_log, /* stderr in child */
442 ap_assert(rc == APR_SUCCESS);
445 rc = apr_procattr_child_errfn_set(ctx->procattr, child_errfn);
446 ap_assert(rc == APR_SUCCESS);
447 apr_pool_userdata_set(f->r, ERRFN_USERDATA_KEY, apr_pool_cleanup_null, ctx->p);
449 rc = apr_procattr_error_check_set(ctx->procattr, 1);
450 if (rc != APR_SUCCESS) {
454 /* add standard CGI variables as well as DOCUMENT_URI, DOCUMENT_PATH_INFO,
455 * and QUERY_STRING_UNESCAPED
457 ap_add_cgi_vars(f->r);
458 ap_add_common_vars(f->r);
459 apr_table_setn(f->r->subprocess_env, "DOCUMENT_URI", f->r->uri);
460 apr_table_setn(f->r->subprocess_env, "DOCUMENT_PATH_INFO", f->r->path_info);
462 /* QUERY_STRING is added by ap_add_cgi_vars */
463 char *arg_copy = apr_pstrdup(f->r->pool, f->r->args);
464 ap_unescape_url(arg_copy);
465 apr_table_setn(f->r->subprocess_env, "QUERY_STRING_UNESCAPED",
466 ap_escape_shell_cmd(f->r->pool, arg_copy));
469 env = (const char * const *) ap_create_environment(ctx->p,
470 f->r->subprocess_env);
472 rc = apr_proc_create(ctx->proc,
473 ctx->filter->command,
474 (const char * const *)ctx->filter->args,
475 env, /* environment */
478 if (rc != APR_SUCCESS) {
479 ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, f->r, APLOGNO(01458)
480 "couldn't create child process to run `%s'",
481 ctx->filter->command);
485 apr_pool_note_subprocess(ctx->p, ctx->proc, APR_KILL_AFTER_TIMEOUT);
487 /* We don't want the handle to the child's stdin inherited by any
488 * other processes created by httpd. Otherwise, when we close our
489 * handle, the child won't see EOF because another handle will still
493 apr_pool_cleanup_register(ctx->p, ctx->proc->in,
494 apr_pool_cleanup_null, /* other mechanism */
497 #if APR_FILES_AS_SOCKETS
499 apr_pollfd_t pfd = { 0 };
501 rc = apr_pollset_create(&ctx->pollset, 2, ctx->p, 0);
502 ap_assert(rc == APR_SUCCESS);
505 pfd.desc_type = APR_POLL_FILE;
506 pfd.reqevents = APR_POLLOUT;
507 pfd.desc.f = ctx->proc->in;
508 rc = apr_pollset_add(ctx->pollset, &pfd);
509 ap_assert(rc == APR_SUCCESS);
511 pfd.reqevents = APR_POLLIN;
512 pfd.desc.f = ctx->proc->out;
513 rc = apr_pollset_add(ctx->pollset, &pfd);
514 ap_assert(rc == APR_SUCCESS);
521 static const char *get_cfg_string(ef_dir_t *dc, ef_filter_t *filter, apr_pool_t *p)
523 const char *log_stderr_str = dc->log_stderr < 1 ?
524 "NoLogStderr" : "LogStderr";
525 const char *preserve_content_length_str = filter->preserves_content_length ?
526 "PreservesContentLength" : "!PreserveContentLength";
527 const char *intype_str = !filter->intype ?
528 "*/*" : filter->intype;
529 const char *outtype_str = !filter->outtype ?
530 "(unchanged)" : filter->outtype;
532 return apr_psprintf(p,
533 "ExtFilterOptions %s %s ExtFilterInType %s "
534 "ExtFilterOuttype %s",
535 log_stderr_str, preserve_content_length_str,
536 intype_str, outtype_str);
539 static ef_filter_t *find_filter_def(const server_rec *s, const char *fname)
544 sc = ap_get_module_config(s->module_config, &ext_filter_module);
545 f = apr_hash_get(sc->h, fname, APR_HASH_KEY_STRING);
546 if (!f && s != main_server) {
548 sc = ap_get_module_config(s->module_config, &ext_filter_module);
549 f = apr_hash_get(sc->h, fname, APR_HASH_KEY_STRING);
554 static apr_status_t init_filter_instance(ap_filter_t *f)
560 f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(ef_ctx_t));
561 dc = ap_get_module_config(f->r->per_dir_config,
564 /* look for the user-defined filter */
565 ctx->filter = find_filter_def(f->r->server, f->frec->name);
567 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, APLOGNO(01459)
568 "couldn't find definition of filter '%s'",
573 if (ctx->filter->intype &&
574 ctx->filter->intype != INTYPE_ALL) {
577 if (ctx->filter->mode == INPUT_FILTER) {
578 ctypes = apr_table_get(f->r->headers_in, "Content-Type");
581 ctypes = f->r->content_type;
585 const char *ctype = ap_getword(f->r->pool, &ctypes, ';');
587 if (strcasecmp(ctx->filter->intype, ctype)) {
588 /* wrong IMT for us; don't mess with the output */
596 if (ctx->filter->enable_env &&
597 !apr_table_get(f->r->subprocess_env, ctx->filter->enable_env)) {
598 /* an environment variable that enables the filter isn't set; bail */
601 if (ctx->filter->disable_env &&
602 apr_table_get(f->r->subprocess_env, ctx->filter->disable_env)) {
603 /* an environment variable that disables the filter is set; bail */
607 rv = init_ext_filter_process(f);
608 if (rv != APR_SUCCESS) {
611 if (ctx->filter->outtype &&
612 ctx->filter->outtype != OUTTYPE_UNCHANGED) {
613 ap_set_content_type(f->r, ctx->filter->outtype);
615 if (ctx->filter->preserves_content_length != 1) {
616 /* nasty, but needed to avoid confusing the browser
618 apr_table_unset(f->r->headers_out, "Content-Length");
622 if (APLOGrtrace1(f->r)) {
623 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, f->r,
624 "%sfiltering `%s' of type `%s' through `%s', cfg %s",
625 ctx->noop ? "NOT " : "",
626 f->r->uri ? f->r->uri : f->r->filename,
627 f->r->content_type ? f->r->content_type : "(unspecified)",
628 ctx->filter->command,
629 get_cfg_string(dc, ctx->filter, f->r->pool));
635 /* drain_available_output():
637 * if any data is available from the filter, read it and append it
638 * to the the bucket brigade
640 static apr_status_t drain_available_output(ap_filter_t *f,
641 apr_bucket_brigade *bb)
643 request_rec *r = f->r;
644 conn_rec *c = r->connection;
645 ef_ctx_t *ctx = f->ctx;
652 int lvl = APLOG_TRACE5;
654 rv = apr_file_read(ctx->proc->out, buf, &len);
655 if (rv && !APR_STATUS_IS_EAGAIN(rv))
657 ap_log_rerror(APLOG_MARK, lvl, rv, r, APLOGNO(01460)
658 "apr_file_read(child output), len %" APR_SIZE_T_FMT,
660 if (rv != APR_SUCCESS) {
663 b = apr_bucket_heap_create(buf, len, NULL, c->bucket_alloc);
664 APR_BRIGADE_INSERT_TAIL(bb, b);
667 /* we should never get here; if we do, a bogus error message would be
668 * the least of our problems
670 return APR_ANONYMOUS;
673 static apr_status_t pass_data_to_filter(ap_filter_t *f, const char *data,
674 apr_size_t len, apr_bucket_brigade *bb)
676 ef_ctx_t *ctx = f->ctx;
678 apr_size_t bytes_written = 0;
682 tmplen = len - bytes_written;
683 rv = apr_file_write_full(ctx->proc->in,
684 (const char *)data + bytes_written,
686 bytes_written += tmplen;
687 if (rv != APR_SUCCESS && !APR_STATUS_IS_EAGAIN(rv)) {
688 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r, APLOGNO(01461)
689 "apr_file_write(child input), len %" APR_SIZE_T_FMT,
693 if (APR_STATUS_IS_EAGAIN(rv)) {
694 /* XXX handle blocking conditions here... if we block, we need
695 * to read data from the child process and pass it down to the
698 rv = drain_available_output(f, bb);
699 if (APR_STATUS_IS_EAGAIN(rv)) {
700 #if APR_FILES_AS_SOCKETS
702 const apr_pollfd_t *pdesc;
704 rv = apr_pollset_poll(ctx->pollset, f->r->server->timeout,
705 &num_events, &pdesc);
706 if (rv != APR_SUCCESS && !APR_STATUS_IS_EINTR(rv)) {
707 ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, f->r, APLOGNO(01462)
708 "apr_pollset_poll()");
709 /* some error such as APR_TIMEUP */
712 ap_log_rerror(APLOG_MARK, APLOG_TRACE6, rv, f->r,
713 "apr_pollset_poll()");
714 #else /* APR_FILES_AS_SOCKETS */
715 /* Yuck... I'd really like to wait until I can read
716 * or write, but instead I have to sleep and try again
718 apr_sleep(apr_time_from_msec(100));
719 ap_log_rerror(APLOG_MARK, APLOG_TRACE6, 0, f->r, "apr_sleep()");
720 #endif /* APR_FILES_AS_SOCKETS */
722 else if (rv != APR_SUCCESS) {
726 } while (bytes_written < len);
730 /* ef_unified_filter:
732 * runs the bucket brigade bb through the filter and puts the result into
733 * bb, dropping the previous content of bb (the input)
736 static int ef_unified_filter(ap_filter_t *f, apr_bucket_brigade *bb)
738 request_rec *r = f->r;
739 conn_rec *c = r->connection;
740 ef_ctx_t *ctx = f->ctx;
746 apr_bucket *eos = NULL;
747 apr_bucket_brigade *bb_tmp;
749 bb_tmp = apr_brigade_create(r->pool, c->bucket_alloc);
751 for (b = APR_BRIGADE_FIRST(bb);
752 b != APR_BRIGADE_SENTINEL(bb);
753 b = APR_BUCKET_NEXT(b))
755 if (APR_BUCKET_IS_EOS(b)) {
760 rv = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
761 if (rv != APR_SUCCESS) {
762 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01463) "apr_bucket_read()");
766 /* Good cast, we just tested len isn't negative */
768 (rv = pass_data_to_filter(f, data, (apr_size_t)len, bb_tmp))
774 apr_brigade_cleanup(bb);
775 APR_BRIGADE_CONCAT(bb, bb_tmp);
776 apr_brigade_destroy(bb_tmp);
779 /* close the child's stdin to signal that no more data is coming;
780 * that will cause the child to finish generating output
782 if ((rv = apr_file_close(ctx->proc->in)) != APR_SUCCESS) {
783 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01464)
784 "apr_file_close(child input)");
787 /* since we've seen eos and closed the child's stdin, set the proper pipe
788 * timeout; we don't care if we don't return from apr_file_read() for a while...
790 rv = apr_file_pipe_timeout_set(ctx->proc->out,
793 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01465)
794 "apr_file_pipe_timeout_set(child output)");
800 int lvl = APLOG_TRACE6;
802 rv = apr_file_read(ctx->proc->out, buf, &len);
803 if (rv && !APR_STATUS_IS_EOF(rv) && !APR_STATUS_IS_EAGAIN(rv))
805 ap_log_rerror(APLOG_MARK, lvl, rv, r, APLOGNO(01466)
806 "apr_file_read(child output), len %" APR_SIZE_T_FMT,
808 if (APR_STATUS_IS_EAGAIN(rv)) {
810 /* should not occur, because we have an APR timeout in place */
811 AP_DEBUG_ASSERT(1 != 1);
816 if (rv == APR_SUCCESS) {
817 b = apr_bucket_heap_create(buf, len, NULL, c->bucket_alloc);
818 APR_BRIGADE_INSERT_TAIL(bb, b);
820 } while (rv == APR_SUCCESS);
822 if (!APR_STATUS_IS_EOF(rv)) {
827 b = apr_bucket_eos_create(c->bucket_alloc);
828 APR_BRIGADE_INSERT_TAIL(bb, b);
835 static apr_status_t ef_output_filter(ap_filter_t *f, apr_bucket_brigade *bb)
837 request_rec *r = f->r;
838 ef_ctx_t *ctx = f->ctx;
842 if ((rv = init_filter_instance(f)) != APR_SUCCESS) {
844 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01467)
845 "can't initialise output filter %s: %s",
847 (ctx->dc->onfail == 1) ? "removing" : "aborting");
848 ap_remove_output_filter(f);
849 if (ctx->dc->onfail == 1) {
850 return ap_pass_brigade(f->next, bb);
854 f->r->status_line = "500 Internal Server Error";
856 apr_brigade_cleanup(bb);
857 e = ap_bucket_error_create(HTTP_INTERNAL_SERVER_ERROR,
860 APR_BRIGADE_INSERT_TAIL(bb, e);
861 e = apr_bucket_eos_create(f->c->bucket_alloc);
862 APR_BRIGADE_INSERT_TAIL(bb, e);
863 ap_pass_brigade(f->next, bb);
864 return AP_FILTER_ERROR;
870 ap_remove_output_filter(f);
871 return ap_pass_brigade(f->next, bb);
874 rv = ef_unified_filter(f, bb);
875 if (rv != APR_SUCCESS) {
876 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01468)
877 "ef_unified_filter() failed");
880 if ((rv = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) {
881 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO(01469)
882 "ap_pass_brigade() failed");
887 static apr_status_t ef_input_filter(ap_filter_t *f, apr_bucket_brigade *bb,
888 ap_input_mode_t mode, apr_read_type_e block,
891 ef_ctx_t *ctx = f->ctx;
895 if ((rv = init_filter_instance(f)) != APR_SUCCESS) {
897 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r, APLOGNO(01470)
898 "can't initialise input filter %s: %s",
900 (ctx->dc->onfail == 1) ? "removing" : "aborting");
901 ap_remove_input_filter(f);
902 if (ctx->dc->onfail == 1) {
903 return ap_get_brigade(f->next, bb, mode, block, readbytes);
906 f->r->status = HTTP_INTERNAL_SERVER_ERROR;
907 return HTTP_INTERNAL_SERVER_ERROR;
914 /* Match behaviour of HTTP_IN if filter is re-invoked after
915 * hitting EOS: give back another EOS. */
916 apr_bucket *e = apr_bucket_eos_create(f->c->bucket_alloc);
917 APR_BRIGADE_INSERT_TAIL(bb, e);
922 ap_remove_input_filter(f);
923 return ap_get_brigade(f->next, bb, mode, block, readbytes);
926 rv = ap_get_brigade(f->next, bb, mode, block, readbytes);
927 if (rv != APR_SUCCESS) {
931 rv = ef_unified_filter(f, bb);
935 AP_DECLARE_MODULE(ext_filter) =
937 STANDARD20_MODULE_STUFF,
940 create_ef_server_conf,