RubyInputFilter/RubyOutputFilter patch
why the lucky stiff <[email protected]> Tue, 27 Dec 2005 08:58:16 -0700
| Newsgroups | gmane.comp.apache.mod-ruby |
|---|---|
| Message-ID | <[email protected]> |
Hi, Shugo and friends.
I'm becoming quite jealous of mod_perl and mod_python folks and their
Apache2 filter hooks. So I looked through the mod_python source and
scratched together some filter code for mod_ruby. See the patch clipped
on to this message.
In httpd.conf:
RubyRequire filter_test
RubyOutputFilter FilterTest.instance TEST
SetOutputFilter TEST
In lib/filter_test.rb
require 'singleton'
class FilterTest
include Singleton
def output_filter(filter)
s = filter.read
while s
filter.write s.gsub(/mod_python/i, 'mod_ruby')
s = filter.read
end
filter.close if filter.eos?
end
end
Obviously, this is just a start. It's probably buggy. I've ripped off
ruby_handler pretty disgustingly. The API could be more Ruby-like.
Overall, what do you think?
_why
mod_ruby.filters.patch
(text/x-patch, 31.8 KB)
Index: Makefile.in
===================================================================
--- Makefile.in (revision 102)
+++ Makefile.in (working copy)
@@ -99,7 +99,8 @@
bucket.@OBJEXT@ \
apache_request.@OBJEXT@ \
apache_multipart_buffer.@OBJEXT@ \
- apache_cookie.@OBJEXT@
+ apache_cookie.@OBJEXT@ \
+ apache_filter.@OBJEXT@
@COMPILE_RULES@
@@ -186,3 +187,4 @@
apache_request.@OBJEXT@: apache_request.c mod_ruby.h apache_request.h
apache_multipart_buffer.@OBJEXT@: apache_multipart_buffer.c mod_ruby.h apache_request.h apache_multipart_buffer.h
apache_cookie.@OBJEXT@: apache_cookie.c apache_cookie.h apache_request.h
+apache_filter.@OBJEXT@: apache_filter.c apache_filter.h mod_ruby.h
Index: apachelib.c
===================================================================
--- apachelib.c (revision 102)
+++ apachelib.c (working copy)
@@ -424,6 +424,7 @@
rb_init_apache_cookie();
#ifdef APACHE2
rb_init_apache_bucket();
+ rb_init_apache_filter();
#endif
}
Index: mod_ruby.c
===================================================================
--- mod_ruby.c (revision 102)
+++ mod_ruby.c (working copy)
@@ -202,6 +202,12 @@
{"RubyChildInitHandler", ruby_cmd_child_init_handler,
NULL, RSRC_CONF, TAKE1,
"set child init handler object"},
+ {"RubyInputFilter", ruby_cmd_input_filter,
+ NULL, RSRC_CONF|ACCESS_CONF, TAKE12,
+ "set input filter."},
+ {"RubyOutputFilter", ruby_cmd_output_filter,
+ NULL, RSRC_CONF|ACCESS_CONF, TAKE12,
+ "set output filter."},
{NULL}
};
@@ -841,6 +847,7 @@
}
static int ruby_handler(request_rec *, array_header *, ID, int, int);
+/* static int ruby_filterhdlr(ApacheFilter *, array_header *, ID, int, int); */
#ifdef APACHE2
static void ruby_child_init(pool *p, server_rec *s)
@@ -1421,7 +1428,244 @@
rb_intern("post_read_request"), 1, 0);
}
+typedef struct filterhdlr_0_arg {
+ ApacheFilter *f;
+ char *filterhdlr;
+ ID mid;
+} filterhdlr_0_arg_t;
+
+static VALUE ruby_filterhdlr_0(void *arg)
+{
+ filterhdlr_0_arg_t *ha = (filterhdlr_0_arg_t *) arg;
+ ApacheFilter *f = ha->f;
+ char *filterhdlr = ha->filterhdlr;
+ ID mid = ha->mid;
+ VALUE ret;
+ int state;
+
+ ret = rb_protect_funcall(rb_eval_string(filterhdlr), mid, &state,
+ 1, f->filter_object);
+ if (state) {
+ if (state == TAG_RAISE &&
+ rb_obj_is_kind_of(ruby_errinfo, rb_eSystemExit)) {
+ ret = rb_iv_get(ruby_errinfo, "status");
+ }
+ else {
+ handle_error(f->req, state);
+ return INT2NUM(HTTP_INTERNAL_SERVER_ERROR);
+ }
+ }
+ return ret;
+}
+
+typedef struct filterhdlr_internal_arg {
+ ApacheFilter *f;
+ array_header *filterhdlrs_arr;
+ ID mid;
+ int run_all;
+ int flush;
+ VALUE retval;
+} filterhdlr_internal_arg_t;
+
+static void *ruby_filterhdlr_internal(filterhdlr_internal_arg_t *iarg)
+{
+ ApacheFilter *f = iarg->f;
+ array_header *filterhdlrs_arr = iarg->filterhdlrs_arr;
+ ID mid = iarg->mid;
+ int run_all = iarg->run_all;
+ int flush = iarg->flush;
+ ruby_server_config *sconf;
+ ruby_dir_config *dconf;
+ int safe_level;
+ int state;
+ VALUE ret;
+ filterhdlr_0_arg_t arg;
+ int i, filterhdlrs_len;
+ char **filterhdlrs;
+
+ sconf = get_server_config(f->req->server);
+ dconf = get_dir_config(f->req);
+ safe_level = dconf ? dconf->safe_level : MR_DEFAULT_SAFE_LEVEL;
+ filterhdlrs = (char **) filterhdlrs_arr->elts;
+ filterhdlrs_len = filterhdlrs_arr->nelts;
+ iarg->retval = DECLINED;
+
+ per_request_init(f->req);
+ for (i = 0; i < filterhdlrs_len; i++) {
+ arg.f = f;
+ arg.filterhdlr = filterhdlrs[i];
+ arg.mid = mid;
+ ap_soft_timeout("call ruby filterhdlr", r);
+ if ((state = run_safely(safe_level, sconf->timeout,
+ ruby_filterhdlr_0, &arg, &ret)) == 0) {
+ iarg->retval = OK;
+ }
+ else {
+ handle_error(f->req, state);
+ iarg->retval = HTTP_INTERNAL_SERVER_ERROR;
+ }
+ ap_kill_timeout(f->req);
+ if (iarg->retval != DECLINED && (!run_all || iarg->retval != OK))
+ break;
+ }
+ per_request_cleanup(f->req, flush && iarg->retval == OK);
+ return NULL;
+}
+
/*
+static int ruby_filterhdlr(ApacheFilter *f,
+ array_header *filterhdlrs_arr, ID mid,
+ int run_all, int flush)
+{
+ filterhdlr_internal_arg_t *arg;
+
+ if (filterhdlrs_arr == NULL)
+ return DECLINED;
+
+ arg = ap_palloc(f->req->pool, sizeof(filterhdlr_internal_arg_t));
+ arg->f = f;
+ arg->filterhdlrs_arr = filterhdlrs_arr;
+ arg->mid = mid;
+ arg->run_all = run_all;
+ arg->flush = flush;
+ arg->retval = 0;
+#if APR_HAS_THREADS
+ if (ruby_is_threaded_mpm) {
+ apr_status_t status;
+ char buf[256];
+
+ status =
+ ruby_call_interpreter(f->req->pool,
+ (ruby_interp_func_t) ruby_filterhdlr_internal,
+ arg, NULL, 0);
+ if (status != APR_SUCCESS) {
+ apr_strerror(status, buf, sizeof(buf));
+ ruby_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, f->req->server,
+ "ruby_call_interpreter() failed: %s", buf);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ }
+ else {
+#endif
+ ruby_filterhdlr_internal(arg);
+#if APR_HAS_THREADS
+ }
+#endif
+ return arg->retval;
+}
+*/
+
+static apr_status_t ruby_filter(int is_input, ap_filter_t *f,
+ apr_bucket_brigade *bb,
+ ap_input_mode_t mode,
+ apr_read_type_e block,
+ apr_size_t readbytes) {
+ filterhdlr_internal_arg_t *arg;
+
+ ruby_dir_config * conf;
+ request_rec *r;
+ ApacheFilter *filter;
+ VALUE filter_obj;
+ ruby_filter_ctx *ctx;
+ ruby_filter_handler *fh;
+ array_header *hdlr = NULL;
+ ID mid;
+
+ /* we only allow request level filters so far */
+ r = f->r;
+
+ /* create ctx if not there yet */
+ if (!f->ctx) {
+ ctx = (ruby_filter_ctx *) apr_pcalloc(r->pool, sizeof(ruby_filter_ctx));
+ f->ctx = (void *)ctx;
+ }
+ else {
+ ctx = (ruby_filter_ctx *) f->ctx;
+ }
+
+ /* are we in transparent mode? transparent mode is on after an error,
+ so a filter can spit out an error without causing infinite loop */
+ if (ctx->transparent) {
+ if (is_input)
+ return ap_get_brigade(f->next, bb, mode, block, readbytes);
+ else
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ /* get configuration */
+ conf = get_dir_config(r);
+
+ /* the name of ruby function to call */
+ if (is_input)
+ {
+ fh = apr_hash_get(conf->in_filters, f->frec->name, APR_HASH_KEY_STRING);
+ mid = rb_intern("input_filter");
+ }
+ else
+ {
+ fh = apr_hash_get(conf->out_filters, f->frec->name, APR_HASH_KEY_STRING);
+ mid = rb_intern("output_filter");
+ }
+
+ /* create filter */
+ filter_obj = apache_filter_new(f, bb, is_input, mode, readbytes,
+ fh->handler, fh->dir);
+ filter = get_filter_data(filter_obj);
+ filter->req = r;
+
+ hdlr = ap_make_array(r->pool, 1, sizeof(char*));
+ *(char **) ap_push_array(hdlr) = fh->handler;
+
+ arg = ap_palloc(r->pool, sizeof(filterhdlr_internal_arg_t));
+ arg->f = filter;
+ arg->filterhdlrs_arr = hdlr;
+ arg->mid = mid;
+ arg->run_all = 0;
+ arg->flush = 0;
+ arg->retval = 0;
+#if APR_HAS_THREADS
+ if (ruby_is_threaded_mpm) {
+ apr_status_t status;
+ char buf[256];
+
+ status =
+ ruby_call_interpreter(r->pool,
+ (ruby_interp_func_t) ruby_filterhdlr_internal,
+ arg, NULL, 0);
+ if (status != APR_SUCCESS) {
+ apr_strerror(status, buf, sizeof(buf));
+ ruby_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r->server,
+ "ruby_call_interpreter() failed: %s", buf);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ }
+ else {
+#endif
+ ruby_filterhdlr_internal(arg);
+#if APR_HAS_THREADS
+ }
+#endif
+
+ rb_funcall(filter_obj, rb_intern("flush"), 0);
+ return filter->rc;
+}
+
+apr_status_t ruby_input_filter(ap_filter_t *f,
+ apr_bucket_brigade *bb,
+ ap_input_mode_t mode,
+ apr_read_type_e block,
+ apr_off_t readbytes)
+{
+ return ruby_filter(1, f, bb, mode, block, readbytes);
+}
+
+apr_status_t ruby_output_filter(ap_filter_t *f,
+ apr_bucket_brigade *bb)
+{
+ return ruby_filter(0, f, bb, 0, 0, 0);
+}
+
+/*
* Local variables:
* mode: C
* tab-width: 8
Index: mod_ruby.h
===================================================================
--- mod_ruby.h (revision 102)
+++ mod_ruby.h (working copy)
@@ -134,6 +134,7 @@
#include "apache_request.h"
#include "apache_multipart_buffer.h"
#include "apache_cookie.h"
+#include "apache_filter.h"
#define MOD_RUBY_STRING_VERSION "mod_ruby/1.2.4"
#define RUBY_GATEWAY_INTERFACE "CGI-Ruby/1.1"
@@ -153,6 +154,7 @@
int output_mode;
array_header *load_path;
table *options;
+ char *config_dir;
array_header *ruby_handler;
array_header *ruby_trans_handler;
array_header *ruby_authen_handler;
@@ -165,6 +167,8 @@
array_header *ruby_post_read_request_handler;
array_header *ruby_init_handler;
array_header *ruby_cleanup_handler;
+ apr_hash_t *in_filters;
+ apr_hash_t *out_filters;
} ruby_dir_config;
typedef struct {
@@ -178,6 +182,20 @@
VALUE request_object;
} ruby_request_config;
+/* filter context */
+typedef struct
+{
+ int transparent;
+} ruby_filter_ctx;
+
+/* a structure to hold a handler,
+ * used in configuration for filters */
+typedef struct
+{
+ char *handler;
+ char *dir;
+} ruby_filter_handler;
+
#define MR_DEFAULT_TIMEOUT 270
#define MR_DEFAULT_SAFE_LEVEL 1
#define MR_DEFAULT_RESTRICT_DIRECTIVES 0
@@ -230,6 +248,13 @@
apr_status_t ruby_call_interpreter(pool *p, ruby_interp_func_t func,
void *arg, void **result, int *state);
+apr_status_t ruby_input_filter(ap_filter_t *f,
+ apr_bucket_brigade *bb,
+ ap_input_mode_t mode,
+ apr_read_type_e block,
+ apr_off_t readbytes);
+apr_status_t ruby_output_filter(ap_filter_t *f,
+ apr_bucket_brigade *bb);
#endif
#endif /* !MOD_RUBY_H */
Index: ruby_config.c
===================================================================
--- ruby_config.c (revision 102)
+++ ruby_config.c (working copy)
@@ -89,7 +89,7 @@
return (void *) new;
}
-void *ruby_create_dir_config(pool *p, char *dirname)
+void *ruby_create_dir_config(pool *p, char *dir)
{
ruby_dir_config *conf =
(ruby_dir_config *) ap_palloc(p, sizeof (ruby_dir_config));
@@ -112,6 +112,15 @@
conf->ruby_post_read_request_handler = NULL;
conf->ruby_init_handler = NULL;
conf->ruby_cleanup_handler = NULL;
+ conf->in_filters = apr_hash_make(p);
+ conf->out_filters = apr_hash_make(p);
+
+ /* make sure directory ends with a slash */
+ if (dir && (dir[strlen(dir) - 1] != '/'))
+ conf->config_dir = apr_pstrcat(p, dir, "/", NULL);
+ else
+ conf->config_dir = apr_pstrdup(p, dir);
+
return conf;
}
@@ -122,6 +131,11 @@
ruby_dir_config *base = (ruby_dir_config *) basev;
ruby_dir_config *add = (ruby_dir_config *) addv;
+ apr_hash_index_t *hi;
+ char *key;
+ apr_ssize_t klen;
+ void *hle;
+
new->kcode = add->kcode ? add->kcode : base->kcode;
new->env = ap_overlay_tables(p, add->env, base->env);
if (add->safe_level >= base->safe_level) {
@@ -143,8 +157,36 @@
new->load_path = ap_append_arrays(p, base->load_path, add->load_path);
}
+ /* copy base */
new->options = ap_overlay_tables(p, add->options, base->options);
+ new->in_filters = apr_hash_make(p);
+ new->out_filters = apr_hash_make(p);
+ new->config_dir = apr_pstrdup(p, base->config_dir);
+ for (hi = apr_hash_first(p, base->in_filters); hi; hi=apr_hash_next(hi)) {
+ apr_hash_this(hi, (const void **)&key, &klen, &hle);
+ apr_hash_set(new->in_filters, key, klen, hle);
+ }
+
+ for (hi = apr_hash_first(p, base->out_filters); hi; hi=apr_hash_next(hi)) {
+ apr_hash_this(hi, (const void **)&key, &klen, &hle);
+ apr_hash_set(new->out_filters, key, klen, hle);
+ }
+
+ /* copy new */
+ if (add->config_dir)
+ new->config_dir = apr_pstrdup(p, add->config_dir);
+
+ for (hi = apr_hash_first(p, add->in_filters); hi; hi=apr_hash_next(hi)) {
+ apr_hash_this(hi, (const void**)&key, &klen, &hle);
+ apr_hash_set(new->in_filters, key, klen, hle);
+ }
+
+ for (hi = apr_hash_first(p, add->out_filters); hi; hi=apr_hash_next(hi)) {
+ apr_hash_this(hi, (const void**)&key, &klen, &hle);
+ apr_hash_set(new->out_filters, key, klen, hle);
+ }
+
new->ruby_handler =
merge_handlers(p, base->ruby_handler, add->ruby_handler);
new->ruby_trans_handler =
@@ -503,6 +545,57 @@
return NULL;
}
+
+const char *ruby_cmd_input_filter(cmd_parms *cmd, ruby_dir_config *conf,
+ const char *handler, const char *name)
+{
+ ruby_filter_handler *fh;
+ ap_filter_rec_t *frec;
+
+ check_restrict_directives(cmd, conf)
+
+ if (!name)
+ name = apr_pstrdup(cmd->pool, handler);
+
+ /* register the filter NOTE - this only works so long as the
+ directive is only allowed in the main config. For .htaccess we
+ would have to make sure not to duplicate this */
+ frec = ap_register_input_filter(name, ruby_input_filter, NULL, AP_FTYPE_RESOURCE);
+
+ fh = (ruby_filter_handler *) apr_pcalloc(cmd->pool, sizeof(ruby_filter_handler));
+ fh->handler = (char *)handler;
+ fh->dir = conf->config_dir;
+
+ apr_hash_set(conf->in_filters, frec->name, APR_HASH_KEY_STRING, fh);
+
+ return NULL;
+}
+
+const char *ruby_cmd_output_filter(cmd_parms *cmd, ruby_dir_config *conf,
+ const char *handler, const char *name)
+{
+ ruby_filter_handler *fh;
+ ap_filter_rec_t *frec;
+
+ check_restrict_directives(cmd, conf)
+
+ if (!name)
+ name = apr_pstrdup(cmd->pool, handler);
+
+ /* register the filter NOTE - this only works so long as the
+ directive is only allowed in the main config. For .htaccess we
+ would have to make sure not to duplicate this */
+ frec = ap_register_output_filter(name, ruby_output_filter, NULL, AP_FTYPE_RESOURCE);
+
+ fh = (ruby_filter_handler *) apr_pcalloc(cmd->pool, sizeof(ruby_filter_handler));
+ fh->handler = (char *)handler;
+ fh->dir = conf->config_dir;
+
+ apr_hash_set(conf->out_filters, frec->name, APR_HASH_KEY_STRING, fh);
+
+ return NULL;
+}
+
/*
* Local variables:
* mode: C
Index: ruby_config.h
===================================================================
--- ruby_config.h (revision 102)
+++ ruby_config.h (working copy)
@@ -57,6 +57,10 @@
const char *ruby_cmd_init_handler(cmd_parms*, ruby_dir_config*, char*);
const char *ruby_cmd_cleanup_handler(cmd_parms*, ruby_dir_config*, char*);
const char *ruby_cmd_child_init_handler(cmd_parms*, void*, char*);
+const char *ruby_cmd_input_filter(cmd_parms*, ruby_dir_config*,
+ const char*, const char *);
+const char *ruby_cmd_output_filter(cmd_parms*, ruby_dir_config*,
+ const char*, const char*);
#endif /* !RUBY_CONFIG_H */
Index: apache_filter.c
===================================================================
--- apache_filter.c (revision 0)
+++ apache_filter.c (revision 0)
@@ -0,0 +1,456 @@
+/*
+ * $Id: filter.c 93 2005-08-03 01:32:41Z why $
+ * Copyright (C) 2001 Shugo Maeda <[email protected]>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "mod_ruby.h"
+#include "apachelib.h"
+
+VALUE rb_cApacheFilter;
+
+static APR_CLEANUP_RETURN_TYPE cleanup_filter_object(void *data)
+{
+ VALUE obj;
+ ApacheFilter *f = (ApacheFilter *) data;
+
+ if (f->filter_object == Qnil)
+ APR_CLEANUP_RETURN_SUCCESS();
+
+ obj = f->filter_object;
+ if (TYPE(obj) == T_DATA) {
+ free(RDATA(obj)->data);
+ RDATA(obj)->data = NULL;
+ }
+#if APR_HAS_THREADS
+ if (ruby_is_threaded_mpm) {
+ ruby_call_interpreter(f->req->pool,
+ (ruby_interp_func_t) rb_apache_unregister_object,
+ (void *) obj, NULL, 0);
+ }
+ else {
+#endif
+ rb_apache_unregister_object(obj);
+#if APR_HAS_THREADS
+ }
+#endif
+ APR_CLEANUP_RETURN_SUCCESS();
+}
+
+VALUE apache_filter_new(ap_filter_t *f, apr_bucket_brigade *bb, int is_input,
+ ap_input_mode_t mode, apr_size_t readbytes,
+ char * handler, char *dir)
+{
+ ApacheFilter *filter;
+ VALUE obj = Data_Make_Struct(rb_cApacheFilter, ApacheFilter, NULL, free, filter);
+ filter->f = f;
+ filter->is_input = is_input;
+
+ filter->rc = APR_SUCCESS;
+
+ if (is_input) {
+ filter->bb_in = NULL;
+ filter->bb_out = bb;
+ filter->mode = mode;
+ filter->readbytes = readbytes;
+ }
+ else {
+ filter->bb_in = bb;
+ filter->bb_out = NULL;
+ filter->mode = 0;
+ filter->readbytes = 0;
+ }
+
+ filter->eos = 0;
+ filter->closed = 0;
+ filter->softspace = 0;
+
+ filter->handler = handler;
+ filter->dir = dir;
+
+ filter->req = NULL;
+
+ rb_apache_register_object(obj);
+ filter->filter_object = obj;
+
+ apr_pool_cleanup_register(f->r->pool, (void *)filter, cleanup_filter_object,
+ apr_pool_cleanup_null);
+
+ return obj;
+}
+
+ApacheFilter *get_filter_data(VALUE obj)
+{
+ ApacheFilter *filter;
+
+ Check_Type(obj, T_DATA);
+ filter = (ApacheFilter *) RDATA(obj)->data;
+ if (filter == NULL)
+ rb_raise(rb_eArgError, "destroyed request");
+ return filter;
+}
+
+static VALUE rb_filter_pass_on(VALUE self)
+{
+ ApacheFilter *filter;
+
+ filter = get_filter_data(self);
+ if (filter->is_input)
+ filter->rc = ap_get_brigade(filter->f->next, filter->bb_out,
+ filter->mode, APR_BLOCK_READ,
+ filter->readbytes);
+ else
+ filter->rc = ap_pass_brigade(filter->f->next, filter->bb_in);
+
+ return Qnil;
+}
+
+static VALUE _rb_filter_read(int argc, VALUE *argv, VALUE self, int readline)
+{
+ apr_bucket *b;
+ long bytes_read;
+ VALUE result;
+ int newline = 0;
+ long len = -1;
+ VALUE lenv;
+ ApacheFilter *filter = get_filter_data(self);
+ conn_rec *c = filter->req->connection;
+
+ if (rb_scan_args(argc, argv, "01", &lenv) == 1)
+ {
+ len = NUM2INT(lenv);
+ }
+
+ if (filter->closed) {
+ rb_raise(rb_eTypeError, "I/O operation on closed filter");
+ return Qnil;
+ }
+
+ if (filter->is_input) {
+
+ /* does the output brigade exist? */
+ if (!filter->bb_in) {
+ filter->bb_in = apr_brigade_create(filter->f->r->pool,
+ c->bucket_alloc);
+ }
+
+ filter->rc = ap_get_brigade(filter->f->next, filter->bb_in, filter->mode,
+ APR_BLOCK_READ, filter->readbytes);
+
+ if (!APR_STATUS_IS_EAGAIN(filter->rc) && !APR_STATUS_IS_SUCCESS(filter->rc)) {
+ rb_raise(rb_eIOError, "Input filter read error");
+ return Qnil;
+ }
+ }
+
+ /*
+ * loop through the brigade reading buckets into the string
+ */
+
+ b = APR_BRIGADE_FIRST(filter->bb_in);
+
+ if (b == APR_BRIGADE_SENTINEL(filter->bb_in))
+ return Qnil;
+ // return rb_str_new2("");
+
+ /* reached eos ? */
+ if (APR_BUCKET_IS_EOS(b)) {
+ apr_bucket_delete(b);
+ filter->eos = 1;
+ return Qnil;
+ }
+
+ result = rb_str_new2("");
+
+ bytes_read = 0;
+
+ while ((bytes_read < len || len == -1) &&
+ !(APR_BUCKET_IS_EOS(b) || APR_BUCKET_IS_FLUSH(b) ||
+ b == APR_BRIGADE_SENTINEL(filter->bb_in))) {
+
+ const char *data;
+ apr_size_t size;
+ apr_bucket *old;
+ int i;
+
+ if (apr_bucket_read(b, &data, &size, APR_BLOCK_READ) != APR_SUCCESS) {
+ rb_raise(rb_eIOError, "Filter read error");
+ return Qnil;
+ }
+
+ if (readline) {
+
+ /* scan for newline */
+ for (i=0; i<size; i++) {
+ if (data[i] == '\n') {
+ if (i+1 != size) { /* (no need to split if we're at end of bucket) */
+
+ /* split after newline */
+ apr_bucket_split(b, i+1);
+ size = i + 1;
+ }
+ newline = 1;
+ break;
+ }
+ }
+ }
+
+ rb_str_cat(result, data, (long)size);
+ bytes_read += size;
+
+ if (readline && newline) {
+ apr_bucket_delete(b);
+ break;
+ }
+
+ old = b;
+ b = APR_BUCKET_NEXT(b);
+ apr_bucket_delete(old);
+
+/* if (filter->is_input) { */
+
+/* if (b == APR_BRIGADE_SENTINEL(filter->bb_in)) { */
+/* /\* brigade ended, but no EOS - get another */
+/* brigade *\/ */
+
+/* Py_BEGIN_ALLOW_THREADS; */
+/* filter->rc = ap_get_brigade(filter->f->next, filter->bb_in, filter->mode, */
+/* APR_BLOCK_READ, filter->readbytes); */
+/* Py_END_ALLOW_THREADS; */
+
+/* if (! APR_STATUS_IS_SUCCESS(filter->rc)) { */
+/* PyErr_SetObject(PyExc_IOError, */
+/* PyString_FromString("Input filter read error")); */
+/* return NULL; */
+/* } */
+/* b = APR_BRIGADE_FIRST(filter->bb_in); */
+/* } */
+/* } */
+ }
+
+ return result;
+}
+
+static VALUE rb_filter_read(int argc, VALUE *argv, VALUE self)
+{
+ return _rb_filter_read(argc, argv, self, 0);
+}
+
+static VALUE rb_filter_readline(int argc, VALUE *argv, VALUE self)
+{
+ return _rb_filter_read(argc, argv, self, 1);
+}
+
+static VALUE rb_filter_write(VALUE self, VALUE s)
+{
+ char *buff;
+ int len;
+ apr_bucket *b;
+ ApacheFilter *filter = get_filter_data(self);
+ conn_rec *c = filter->req->connection;
+ VALUE tmp, port;
+ int taint;
+
+ if (NIL_P(tmp = rb_check_string_type(s))) {
+ rb_raise(rb_eTypeError, "Argument to write() must be a string");
+ return Qnil;
+ }
+ taint = OBJ_TAINTED(port); /* original taintedness */
+ port = tmp;
+
+ if (filter->closed) {
+ rb_raise(rb_eTypeError, "I/O operation on closed filter");
+ return Qnil;
+ }
+
+ len = RSTRING(s)->len;
+
+ if (len) {
+
+ /* does the output brigade exist? */
+ if (!filter->bb_out) {
+ filter->bb_out = apr_brigade_create(filter->f->r->pool,
+ c->bucket_alloc);
+ }
+
+ buff = apr_bucket_alloc(len, c->bucket_alloc);
+ memcpy(buff, RSTRING(s)->ptr, len);
+
+ b = apr_bucket_heap_create(buff, len, apr_bucket_free,
+ c->bucket_alloc);
+
+ APR_BRIGADE_INSERT_TAIL(filter->bb_out, b);
+ }
+
+ return Qnil;
+}
+
+static VALUE rb_filter_flush(VALUE self)
+{
+ ApacheFilter *filter = get_filter_data(self);
+ conn_rec *c = filter->req->connection;
+
+ /* does the output brigade exist? */
+ if (!filter->bb_out) {
+ filter->bb_out = apr_brigade_create(filter->f->r->pool,
+ c->bucket_alloc);
+ }
+
+ APR_BRIGADE_INSERT_TAIL(filter->bb_out,
+ apr_bucket_flush_create(c->bucket_alloc));
+
+ if (!filter->is_input) {
+
+ filter->rc = ap_pass_brigade(filter->f->next, filter->bb_out);
+ apr_brigade_destroy(filter->bb_out);
+
+ if(filter->rc != APR_SUCCESS) {
+ rb_raise(rb_eIOError, "Flush failed.");
+ return Qnil;
+ }
+ }
+
+ return Qnil;
+}
+
+static VALUE rb_filter_close(VALUE self)
+{
+ ApacheFilter *filter = get_filter_data(self);
+ conn_rec *c = filter->req->connection;
+
+ if (! filter->closed) {
+
+ /* does the output brigade exist? */
+ if (!filter->bb_out) {
+ filter->bb_out = apr_brigade_create(filter->f->r->pool,
+ c->bucket_alloc);
+ }
+
+ APR_BRIGADE_INSERT_TAIL(filter->bb_out,
+ apr_bucket_eos_create(c->bucket_alloc));
+
+ if (! filter->is_input) {
+ filter->rc = ap_pass_brigade(filter->f->next, filter->bb_out);
+ apr_brigade_destroy(filter->bb_out);
+ filter->bb_out = NULL;
+ }
+
+ filter->closed = 1;
+ }
+
+ return Qnil;
+}
+
+static VALUE rb_filter_disable(VALUE self)
+{
+ ApacheFilter *filter = get_filter_data(self);
+ ruby_filter_ctx *ctx;
+
+ ctx = (ruby_filter_ctx *) filter->f->ctx;
+ ctx->transparent = 1;
+
+ return Qnil;
+
+}
+
+static VALUE rb_filter_attr_name(VALUE self)
+{
+ ApacheFilter *filter = get_filter_data(self);
+ if (! filter->f->frec->name) {
+ return Qnil;
+ } else {
+ return rb_str_new2(filter->f->frec->name);
+ }
+}
+
+static VALUE rb_filter_attr_req(VALUE self)
+{
+ ApacheFilter *filter = get_filter_data(self);
+ return rb_get_request_object(filter->req);
+}
+
+static VALUE rb_filter_attr_softspace(VALUE self)
+{
+ ApacheFilter *filter = get_filter_data(self);
+ return INT2FIX(filter->softspace);
+}
+
+static VALUE rb_filter_attr_closed(VALUE self)
+{
+ ApacheFilter *filter = get_filter_data(self);
+ return filter->closed >= 1 ? Qtrue : Qfalse;
+}
+
+static VALUE rb_filter_attr_eos(VALUE self)
+{
+ ApacheFilter *filter = get_filter_data(self);
+ return filter->eos >= 1 ? Qtrue : Qfalse;
+}
+
+static VALUE rb_filter_attr_is_input(VALUE self)
+{
+ ApacheFilter *filter = get_filter_data(self);
+ return filter->is_input >= 1 ? Qtrue : Qfalse;
+}
+
+static VALUE rb_filter_attr_handler(VALUE self)
+{
+ ApacheFilter *filter = get_filter_data(self);
+ return rb_str_new2(filter->handler);
+}
+
+static VALUE rb_filter_attr_dir(VALUE self)
+{
+ ApacheFilter *filter = get_filter_data(self);
+ return rb_str_new2(filter->dir);
+}
+
+/* Module initializer */
+void rb_init_apache_filter()
+{
+ /* Kluge to make Rdoc see the associations in this file */
+#if FOR_RDOC_PARSER
+ rb_mApache = rb_define_module( "Apache" );
+#endif
+
+ rb_cApacheFilter = rb_define_class_under( rb_mApache, "Filter", rb_cObject );
+ rb_undef_method(CLASS_OF(rb_cApacheFilter), "new");
+ rb_define_method(rb_cApacheFilter, "pass_on", rb_filter_pass_on, 0);
+ rb_define_method(rb_cApacheFilter, "read", rb_filter_read, -1);
+ rb_define_method(rb_cApacheFilter, "readline", rb_filter_readline, -1);
+ rb_define_method(rb_cApacheFilter, "write", rb_filter_write, 1);
+ rb_define_method(rb_cApacheFilter, "flush", rb_filter_flush, 0);
+ rb_define_method(rb_cApacheFilter, "close", rb_filter_close, 0);
+ rb_define_method(rb_cApacheFilter, "disable", rb_filter_disable, 0);
+ rb_define_method(rb_cApacheFilter, "softspace", rb_filter_attr_softspace, 0);
+ rb_define_method(rb_cApacheFilter, "closed?", rb_filter_attr_closed, 0);
+ rb_define_method(rb_cApacheFilter, "eos?", rb_filter_attr_eos, 0);
+ rb_define_method(rb_cApacheFilter, "name", rb_filter_attr_name, 0);
+ rb_define_method(rb_cApacheFilter, "req", rb_filter_attr_req, 0);
+ rb_define_method(rb_cApacheFilter, "is_input", rb_filter_attr_is_input, 0);
+ rb_define_method(rb_cApacheFilter, "handler", rb_filter_attr_handler, 0);
+ rb_define_method(rb_cApacheFilter, "dir", rb_filter_attr_dir, 0);
+}
+
+/* vim: set filetype=c ts=8 sw=4 : */
Index: request.c
===================================================================
--- request.c (revision 102)
+++ request.c (working copy)
@@ -45,29 +45,6 @@
static ID id_post_max, id_disable_uploads,
id_temp_dir, id_hook_data, id_upload_hook;
-typedef struct request_data {
- request_rec *request;
- VALUE outbuf;
- VALUE connection;
- VALUE server;
- VALUE headers_in;
- VALUE headers_out;
- VALUE err_headers_out;
- VALUE subprocess_env;
- VALUE notes;
- VALUE finfo;
- VALUE attributes;
- VALUE error_message;
- VALUE exception;
- ApacheRequest *apreq;
- VALUE upload_hook;
- VALUE upload_hook_arg;
- VALUE upload_table;
- VALUE cookies;
- VALUE param_table;
- VALUE options;
-} request_data;
-
#define REQ_SYNC_HEADER FL_USER1
#define REQ_SYNC_OUTPUT FL_USER2
#define REQ_HEADER_PENDING FL_USER3
Index: apache_filter.h
===================================================================
--- apache_filter.h (revision 0)
+++ apache_filter.h (revision 0)
@@ -0,0 +1,83 @@
+/* Copyright 1999-2004 The Apache Software Foundation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+/* Modified by Shugo Maeda for mod_ruby. */
+
+
+#ifndef _APACHE_FILTER_H
+#define _APACHE_FILTER_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct request_data {
+ request_rec *request;
+ VALUE outbuf;
+ VALUE connection;
+ VALUE server;
+ VALUE headers_in;
+ VALUE headers_out;
+ VALUE err_headers_out;
+ VALUE subprocess_env;
+ VALUE notes;
+ VALUE finfo;
+ VALUE attributes;
+ VALUE error_message;
+ VALUE exception;
+ ApacheRequest *apreq;
+ VALUE upload_hook;
+ VALUE upload_hook_arg;
+ VALUE upload_table;
+ VALUE cookies;
+ VALUE param_table;
+ VALUE options;
+} request_data;
+
+typedef struct {
+ ap_filter_t *f;
+
+ /* in out refers to the dircetion of data with respect to
+ filter, not the filter type */
+ apr_bucket_brigade *bb_in;
+ apr_bucket_brigade *bb_out;
+
+ apr_status_t rc;
+
+ int is_input;
+ ap_input_mode_t mode;
+ apr_size_t readbytes;
+
+ int eos;
+ int closed;
+ int softspace;
+ int bytes_written;
+
+ char *handler;
+ char *dir;
+
+ request_rec *req;
+ VALUE filter_object;
+
+} ApacheFilter;
+
+VALUE apache_filter_new(ap_filter_t *f, apr_bucket_brigade *bb, int is_input,
+ ap_input_mode_t mode, apr_size_t readbytes,
+ char * handler, char *dir);
+ApacheFilter *get_filter_data(VALUE obj);
+void rb_init_apache_filter();
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* !_APACHE_FILTER_H */