text: 
  #include <lighttpd/base.h>
  #include <lighttpd/filter.h>
  
  static void li_filter_stop(liFilter *filter);
  
  void li_vrequest_filters_init(liVRequest *vr) {
  	vr->filters_in_last = vr->filters_out_last = NULL;
  	vr->filters_in_first = vr->filters_out_first = NULL;
  	vr->filters = g_ptr_array_new();
  }
  
  void li_vrequest_filters_clear(liVRequest *vr) {
  	li_vrequest_filters_reset(vr);
  	g_ptr_array_free(vr->filters, TRUE);
  	vr->filters = NULL;
  }
  
  void li_vrequest_filters_reset(liVRequest *vr) {
  	while (vr->filters->len > 0) {
  		li_filter_stop(g_ptr_array_index(vr->filters, vr->filters->len - 1));
  	}
  	vr->filters_in_last = vr->filters_out_last = NULL;
  	vr->filters_in_first = vr->filters_out_first = NULL;
  }
  
  static void filter_handle_data(liFilter *filter) {
  	if (NULL != filter->handle_data) {
  		goffset curoutlen = filter->stream.out->length;
  		gboolean curout_closed = filter->stream.out->is_closed;
  
  		LI_FORCE_ASSERT(NULL != filter->out);
  
  		switch (filter->handle_data(filter->vr, filter)) {
  		case LI_HANDLER_GO_ON:
  			if (NULL != filter->stream.source && (0 != filter->stream.source->out->length)) {
  				/* auto comeback */
  				li_stream_again(&filter->stream);
  			}
  			break;
  		case LI_HANDLER_COMEBACK:
  			li_stream_again(&filter->stream);
  			break;
  		case LI_HANDLER_WAIT_FOR_EVENT:
  			break;
  		case LI_HANDLER_ERROR:
  			filter->in = NULL;
  			if (NULL != filter->vr) li_vrequest_error(filter->vr);
  			li_stream_reset(&filter->stream);
  			break;
  		}
  
  		if (NULL != filter->stream.source && (0 == filter->stream.source->out->length) && filter->stream.source->out->is_closed) {
  			li_stream_disconnect(&filter->stream);
  		}
  
  		if (curoutlen != filter->stream.out->length || curout_closed != filter->stream.out->is_closed) {
  			li_stream_notify(&filter->stream);
  		}
  	}
  }
  
  static void filter_stream_cb(liStream *stream, liStreamEvent event) {
  	liFilter *filter = LI_CONTAINER_OF(stream, liFilter, stream);
  
  	switch (event) {
  	case LI_STREAM_NEW_DATA:
  		if (NULL != filter->handle_event) {
  			filter->handle_event(filter->vr, filter, event);
  		}
  		filter_handle_data(filter);
  		break;
  	case LI_STREAM_NEW_CQLIMIT:
  		if (NULL != filter->handle_event) {
  			filter->handle_event(filter->vr, filter, event);
  		}
  		break;
  	case LI_STREAM_CONNECTED_SOURCE:
  		filter->in = (NULL != filter->stream.source) ? filter->stream.source->out : NULL;
  		if (NULL != filter->handle_event) {
  			filter->handle_event(filter->vr, filter, event);
  		} else {
  			li_stream_again(stream);
  		}
  		break;
  	case LI_STREAM_CONNECTED_DEST:
  		if (NULL != filter->handle_event) {
  			filter->handle_event(filter->vr, filter, event);
  		}
  		break;
  	case LI_STREAM_DISCONNECTED_SOURCE:
  		filter->in = NULL;
  		if (NULL != filter->handle_event) {
  			filter->handle_event(filter->vr, filter, event);
  		} else {
  			if (!stream->out->is_closed) li_stream_again(stream);
  		}
  		break;
  	case LI_STREAM_DISCONNECTED_DEST:
  		if (NULL != filter->handle_event) {
  			filter->handle_event(filter->vr, filter, event);
  		} else {
  			stream->out->is_closed = TRUE;
  			li_chunkqueue_skip_all(stream->out);
  		}
  		break;
  	case LI_STREAM_DESTROY:
  		filter->out = NULL;
  		if (NULL != filter->handle_event) {
  			filter->handle_event(filter->vr, filter, event);
  		}
  		if (NULL != filter->handle_free) {
  			filter->handle_free(filter->vr, filter);
  		}
  		g_slice_free(liFilter, filter);
  		break;
  	}
  }
  
  liFilter* li_filter_new(liVRequest* vr, liFilterHandlerCB handle_data, liFilterFreeCB handle_free, liFilterEventCB handle_event, gpointer param) {
  	liFilter *f;
  
  	f = g_slice_new0(liFilter);
  	li_stream_init(&f->stream, &vr->wrk->loop, filter_stream_cb);
  	f->out = f->stream.out;
  
  	f->param = param;
  	f->handle_data = handle_data;
  	f->handle_free = handle_free;
  	f->handle_event = handle_event;
  
  	f->vr = vr;
  	f->filter_ndx = vr->filters->len;
  	g_ptr_array_add(vr->filters, f);
  
  	return f;
  }
  
  static void li_filter_stop(liFilter *filter) {
  	liVRequest *vr = filter->vr;
  
  	/* remove from vr filters list */
  	LI_FORCE_ASSERT(vr->filters->len > 0);
  	LI_FORCE_ASSERT(g_ptr_array_index(vr->filters, filter->filter_ndx) == filter);
  	if (vr->filters->len - 1 != filter->filter_ndx) {
  		/* not the last filter, swap: */
  		liFilter *last = g_ptr_array_index(vr->filters, vr->filters->len - 1);
  		last->filter_ndx = filter->filter_ndx;
  		g_ptr_array_index(vr->filters, filter->filter_ndx) = last;
  	}
  	g_ptr_array_set_size(vr->filters, vr->filters->len - 1);
  
  	filter->vr = NULL;
  	li_stream_release(&filter->stream);
  }
  
  
  liFilter* li_vrequest_add_filter_in(liVRequest *vr, liFilterHandlerCB handle_data, liFilterFreeCB handle_free, liFilterEventCB handle_event, gpointer param) {
  	liFilter *f;
  
  	/* as soon as we have a backend -> too late for in filters */
  	if (LI_VRS_READ_CONTENT <= vr->state) return NULL;
  
  	f = li_filter_new(vr, handle_data, handle_free, handle_event, param);
  
  	if (NULL == vr->filters_in_first) {
  		LI_FORCE_ASSERT(NULL == vr->filters_in_last);
  
  		vr->filters_in_first = &f->stream;
  		vr->filters_in_last = &f->stream;
  	} else {
  		LI_FORCE_ASSERT(NULL != vr->filters_in_last);
  
  		li_stream_connect(vr->filters_in_last, &f->stream);
  		vr->filters_in_last = &f->stream;
  	}
  
  	return f;
  }
  
  liFilter* li_vrequest_add_filter_out(liVRequest *vr, liFilterHandlerCB handle_data, liFilterFreeCB handle_free, liFilterEventCB handle_event, gpointer param) {
  	liFilter *f;
  
  	/* as soon as we write the response it is too late for out filters */
  	if (LI_VRS_WRITE_CONTENT <= vr->state) return NULL;
  
  	f = li_filter_new(vr, handle_data, handle_free, handle_event, param);
  
  	if (NULL == vr->filters_out_first) {
  		LI_FORCE_ASSERT(NULL == vr->filters_out_last);
  
  		vr->filters_out_first = &f->stream;
  		vr->filters_out_last = &f->stream;
  	} else {
  		LI_FORCE_ASSERT(NULL != vr->filters_out_last);
  
  		li_stream_connect(vr->filters_out_last, &f->stream);
  		vr->filters_out_last = &f->stream;
  	}
  
  	return f;
  }
  