xref: /libevent-2.1.12/bufferevent_filter.c (revision b92b0792)
1ea4b8724SNick Mathewson /*
2e49e2891SNick Mathewson  * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson
3eda27f95SNick Mathewson  * Copyright (c) 2002-2006 Niels Provos <[email protected]>
4ea4b8724SNick Mathewson  * All rights reserved.
5ea4b8724SNick Mathewson  *
6ea4b8724SNick Mathewson  * Redistribution and use in source and binary forms, with or without
7ea4b8724SNick Mathewson  * modification, are permitted provided that the following conditions
8ea4b8724SNick Mathewson  * are met:
9ea4b8724SNick Mathewson  * 1. Redistributions of source code must retain the above copyright
10ea4b8724SNick Mathewson  *    notice, this list of conditions and the following disclaimer.
11ea4b8724SNick Mathewson  * 2. Redistributions in binary form must reproduce the above copyright
12ea4b8724SNick Mathewson  *    notice, this list of conditions and the following disclaimer in the
13ea4b8724SNick Mathewson  *    documentation and/or other materials provided with the distribution.
14ea4b8724SNick Mathewson  * 3. The name of the author may not be used to endorse or promote products
15ea4b8724SNick Mathewson  *    derived from this software without specific prior written permission.
16ea4b8724SNick Mathewson  *
17ea4b8724SNick Mathewson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18ea4b8724SNick Mathewson  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19ea4b8724SNick Mathewson  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20ea4b8724SNick Mathewson  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21ea4b8724SNick Mathewson  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22ea4b8724SNick Mathewson  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23ea4b8724SNick Mathewson  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24ea4b8724SNick Mathewson  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25ea4b8724SNick Mathewson  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26ea4b8724SNick Mathewson  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27ea4b8724SNick Mathewson  */
28ea4b8724SNick Mathewson 
29ded0a090SKevin Bowling #include "evconfig-private.h"
30ded0a090SKevin Bowling 
31ea4b8724SNick Mathewson #include <sys/types.h>
32ea4b8724SNick Mathewson 
33ec347b92SNick Mathewson #include "event2/event-config.h"
34ea4b8724SNick Mathewson 
3568120d9bSNick Mathewson #ifdef EVENT__HAVE_SYS_TIME_H
36ea4b8724SNick Mathewson #include <sys/time.h>
37ea4b8724SNick Mathewson #endif
38ea4b8724SNick Mathewson 
39ea4b8724SNick Mathewson #include <errno.h>
40ea4b8724SNick Mathewson #include <stdio.h>
41ea4b8724SNick Mathewson #include <stdlib.h>
42ea4b8724SNick Mathewson #include <string.h>
4368120d9bSNick Mathewson #ifdef EVENT__HAVE_STDARG_H
44ea4b8724SNick Mathewson #include <stdarg.h>
45ea4b8724SNick Mathewson #endif
46ea4b8724SNick Mathewson 
479f560bfaSNick Mathewson #ifdef _WIN32
48ea4b8724SNick Mathewson #include <winsock2.h>
49ea4b8724SNick Mathewson #endif
50ea4b8724SNick Mathewson 
51ea4b8724SNick Mathewson #include "event2/util.h"
52ea4b8724SNick Mathewson #include "event2/bufferevent.h"
53ea4b8724SNick Mathewson #include "event2/buffer.h"
54ea4b8724SNick Mathewson #include "event2/bufferevent_struct.h"
55ea4b8724SNick Mathewson #include "event2/event.h"
56ea4b8724SNick Mathewson #include "log-internal.h"
57ea4b8724SNick Mathewson #include "mm-internal.h"
58ea4b8724SNick Mathewson #include "bufferevent-internal.h"
59ea4b8724SNick Mathewson #include "util-internal.h"
60ea4b8724SNick Mathewson 
61ea4b8724SNick Mathewson /* prototypes */
62ea4b8724SNick Mathewson static int be_filter_enable(struct bufferevent *, short);
63ea4b8724SNick Mathewson static int be_filter_disable(struct bufferevent *, short);
6402fbf687SNick Mathewson static void be_filter_unlink(struct bufferevent *);
65ea4b8724SNick Mathewson static void be_filter_destruct(struct bufferevent *);
66ea4b8724SNick Mathewson 
67ea4b8724SNick Mathewson static void be_filter_readcb(struct bufferevent *, void *);
68ea4b8724SNick Mathewson static void be_filter_writecb(struct bufferevent *, void *);
695232cfa3SNick Mathewson static void be_filter_eventcb(struct bufferevent *, short, void *);
70ea4b8724SNick Mathewson static int be_filter_flush(struct bufferevent *bufev,
71ea4b8724SNick Mathewson     short iotype, enum bufferevent_flush_mode mode);
7231d89f27SNick Mathewson static int be_filter_ctrl(struct bufferevent *, enum bufferevent_ctrl_op, union bufferevent_ctrl_data *);
73ea4b8724SNick Mathewson 
74b627ad88SEduardo Panisset static void bufferevent_filtered_inbuf_cb(struct evbuffer *buf,
75b627ad88SEduardo Panisset     const struct evbuffer_cb_info *cbinfo, void *arg);
76b627ad88SEduardo Panisset 
77ea4b8724SNick Mathewson static void bufferevent_filtered_outbuf_cb(struct evbuffer *buf,
78f1b1bad4SNick Mathewson     const struct evbuffer_cb_info *info, void *arg);
79ea4b8724SNick Mathewson 
80ea4b8724SNick Mathewson struct bufferevent_filtered {
811becc4c4SNick Mathewson 	struct bufferevent_private bev;
82ea4b8724SNick Mathewson 
83e3fd294aSNick Mathewson 	/** The bufferevent that we read/write filtered data from/to. */
84ea4b8724SNick Mathewson 	struct bufferevent *underlying;
85b627ad88SEduardo Panisset 	/** A callback on our inbuf to notice somebory removes data */
86b627ad88SEduardo Panisset 	struct evbuffer_cb_entry *inbuf_cb;
87ea4b8724SNick Mathewson 	/** A callback on our outbuf to notice when somebody adds data */
88ea4b8724SNick Mathewson 	struct evbuffer_cb_entry *outbuf_cb;
89ea4b8724SNick Mathewson 	/** True iff we have received an EOF callback from the underlying
90ea4b8724SNick Mathewson 	 * bufferevent. */
91ea4b8724SNick Mathewson 	unsigned got_eof;
92ea4b8724SNick Mathewson 
93ea4b8724SNick Mathewson 	/** Function to free context when we're done. */
94ea4b8724SNick Mathewson 	void (*free_context)(void *);
95ea4b8724SNick Mathewson 	/** Input filter */
96ea4b8724SNick Mathewson 	bufferevent_filter_cb process_in;
97ea4b8724SNick Mathewson 	/** Output filter */
98ea4b8724SNick Mathewson 	bufferevent_filter_cb process_out;
99ea4b8724SNick Mathewson 	/** User-supplied argument to the filters. */
100ea4b8724SNick Mathewson 	void *context;
101ea4b8724SNick Mathewson };
102ea4b8724SNick Mathewson 
10323085c92SNick Mathewson const struct bufferevent_ops bufferevent_ops_filter = {
104ea4b8724SNick Mathewson 	"filter",
105657d1b6dSNick Mathewson 	evutil_offsetof(struct bufferevent_filtered, bev.bev),
106ea4b8724SNick Mathewson 	be_filter_enable,
107ea4b8724SNick Mathewson 	be_filter_disable,
10802fbf687SNick Mathewson 	be_filter_unlink,
109ea4b8724SNick Mathewson 	be_filter_destruct,
110cb9da0bfSNick Mathewson 	bufferevent_generic_adj_timeouts_,
111ea4b8724SNick Mathewson 	be_filter_flush,
11231d89f27SNick Mathewson 	be_filter_ctrl,
113ea4b8724SNick Mathewson };
114ea4b8724SNick Mathewson 
115ea4b8724SNick Mathewson /* Given a bufferevent that's really the bev filter of a bufferevent_filtered,
116ea4b8724SNick Mathewson  * return that bufferevent_filtered. Returns NULL otherwise.*/
117ea4b8724SNick Mathewson static inline struct bufferevent_filtered *
upcast(struct bufferevent * bev)118ea4b8724SNick Mathewson upcast(struct bufferevent *bev)
119ea4b8724SNick Mathewson {
120ea4b8724SNick Mathewson 	struct bufferevent_filtered *bev_f;
12156faf02bSDominic Chen 	if (!BEV_IS_FILTER(bev))
122ea4b8724SNick Mathewson 		return NULL;
123ea4b8724SNick Mathewson 	bev_f = (void*)( ((char*)bev) -
1241becc4c4SNick Mathewson 			 evutil_offsetof(struct bufferevent_filtered, bev.bev));
12556faf02bSDominic Chen 	EVUTIL_ASSERT(BEV_IS_FILTER(&bev_f->bev.bev));
126ea4b8724SNick Mathewson 	return bev_f;
127ea4b8724SNick Mathewson }
128ea4b8724SNick Mathewson 
1291becc4c4SNick Mathewson #define downcast(bev_f) (&(bev_f)->bev.bev)
130ea4b8724SNick Mathewson 
131ea4b8724SNick Mathewson /** Return 1 iff bevf's underlying bufferevent's output buffer is at or
132ea4b8724SNick Mathewson  * over its high watermark such that we should not write to it in a given
133ea4b8724SNick Mathewson  * flush mode. */
134ea4b8724SNick Mathewson static int
be_underlying_writebuf_full(struct bufferevent_filtered * bevf,enum bufferevent_flush_mode state)135ea4b8724SNick Mathewson be_underlying_writebuf_full(struct bufferevent_filtered *bevf,
136ea4b8724SNick Mathewson     enum bufferevent_flush_mode state)
137ea4b8724SNick Mathewson {
138ea4b8724SNick Mathewson 	struct bufferevent *u = bevf->underlying;
139ea4b8724SNick Mathewson 	return state == BEV_NORMAL &&
140ea4b8724SNick Mathewson 	    u->wm_write.high &&
141a8f6d961SNick Mathewson 	    evbuffer_get_length(u->output) >= u->wm_write.high;
142ea4b8724SNick Mathewson }
143ea4b8724SNick Mathewson 
144ea4b8724SNick Mathewson /** Return 1 if our input buffer is at or over its high watermark such that we
145ea4b8724SNick Mathewson  * should not write to it in a given flush mode. */
146ea4b8724SNick Mathewson static int
be_readbuf_full(struct bufferevent_filtered * bevf,enum bufferevent_flush_mode state)147ea4b8724SNick Mathewson be_readbuf_full(struct bufferevent_filtered *bevf,
148ea4b8724SNick Mathewson     enum bufferevent_flush_mode state)
149ea4b8724SNick Mathewson {
150ea4b8724SNick Mathewson 	struct bufferevent *bufev = downcast(bevf);
151ea4b8724SNick Mathewson 	return state == BEV_NORMAL &&
152ea4b8724SNick Mathewson 	    bufev->wm_read.high &&
153a8f6d961SNick Mathewson 	    evbuffer_get_length(bufev->input) >= bufev->wm_read.high;
154ea4b8724SNick Mathewson }
155ea4b8724SNick Mathewson 
156ea4b8724SNick Mathewson 
157ea4b8724SNick Mathewson /* Filter to use when we're created with a NULL filter. */
158ea4b8724SNick Mathewson static enum bufferevent_filter_result
be_null_filter(struct evbuffer * src,struct evbuffer * dst,ev_ssize_t lim,enum bufferevent_flush_mode state,void * ctx)1590b22ca19SNick Mathewson be_null_filter(struct evbuffer *src, struct evbuffer *dst, ev_ssize_t lim,
160ea4b8724SNick Mathewson 	       enum bufferevent_flush_mode state, void *ctx)
161ea4b8724SNick Mathewson {
162ea4b8724SNick Mathewson 	(void)state;
163*b92b0792SGreg Hazel 	if (evbuffer_remove_buffer(src, dst, lim) >= 0)
164ea4b8724SNick Mathewson 		return BEV_OK;
165ea4b8724SNick Mathewson 	else
166ea4b8724SNick Mathewson 		return BEV_ERROR;
167ea4b8724SNick Mathewson }
168ea4b8724SNick Mathewson 
169ea4b8724SNick Mathewson struct bufferevent *
bufferevent_filter_new(struct bufferevent * underlying,bufferevent_filter_cb input_filter,bufferevent_filter_cb output_filter,int options,void (* free_context)(void *),void * ctx)170ea4b8724SNick Mathewson bufferevent_filter_new(struct bufferevent *underlying,
171ea4b8724SNick Mathewson 		       bufferevent_filter_cb input_filter,
172ea4b8724SNick Mathewson 		       bufferevent_filter_cb output_filter,
173b73ad7bcSNick Mathewson 		       int options,
174ea4b8724SNick Mathewson 		       void (*free_context)(void *),
175ea4b8724SNick Mathewson 		       void *ctx)
176ea4b8724SNick Mathewson {
177ea4b8724SNick Mathewson 	struct bufferevent_filtered *bufev_f;
178b73ad7bcSNick Mathewson 	int tmp_options = options & ~BEV_OPT_THREADSAFE;
179ea4b8724SNick Mathewson 
180ac27eb82SNick Mathewson 	if (!underlying)
181ac27eb82SNick Mathewson 		return NULL;
182ac27eb82SNick Mathewson 
183ea4b8724SNick Mathewson 	if (!input_filter)
184ea4b8724SNick Mathewson 		input_filter = be_null_filter;
185ea4b8724SNick Mathewson 	if (!output_filter)
186ea4b8724SNick Mathewson 		output_filter = be_null_filter;
187ea4b8724SNick Mathewson 
188ea4b8724SNick Mathewson 	bufev_f = mm_calloc(1, sizeof(struct bufferevent_filtered));
189ea4b8724SNick Mathewson 	if (!bufev_f)
190ea4b8724SNick Mathewson 		return NULL;
191ea4b8724SNick Mathewson 
1928ac3c4c2SNick Mathewson 	if (bufferevent_init_common_(&bufev_f->bev, underlying->ev_base,
193915193e7SNick Mathewson 				    &bufferevent_ops_filter, tmp_options) < 0) {
194ea4b8724SNick Mathewson 		mm_free(bufev_f);
195ea4b8724SNick Mathewson 		return NULL;
196ea4b8724SNick Mathewson 	}
197915193e7SNick Mathewson 	if (options & BEV_OPT_THREADSAFE) {
1988ac3c4c2SNick Mathewson 		bufferevent_enable_locking_(downcast(bufev_f), NULL);
199915193e7SNick Mathewson 	}
200ea4b8724SNick Mathewson 
201ea4b8724SNick Mathewson 	bufev_f->underlying = underlying;
202f1bc125eSNick Mathewson 
203ea4b8724SNick Mathewson 	bufev_f->process_in = input_filter;
204ea4b8724SNick Mathewson 	bufev_f->process_out = output_filter;
205ea4b8724SNick Mathewson 	bufev_f->free_context = free_context;
206ea4b8724SNick Mathewson 	bufev_f->context = ctx;
207ea4b8724SNick Mathewson 
208ea4b8724SNick Mathewson 	bufferevent_setcb(bufev_f->underlying,
2095232cfa3SNick Mathewson 	    be_filter_readcb, be_filter_writecb, be_filter_eventcb, bufev_f);
210ea4b8724SNick Mathewson 
211b627ad88SEduardo Panisset 	bufev_f->inbuf_cb = evbuffer_add_cb(downcast(bufev_f)->input,
212b627ad88SEduardo Panisset 		bufferevent_filtered_inbuf_cb, bufev_f);
213b627ad88SEduardo Panisset 	evbuffer_cb_clear_flags(downcast(bufev_f)->input, bufev_f->inbuf_cb,
214b627ad88SEduardo Panisset 		EVBUFFER_CB_ENABLED);
215b627ad88SEduardo Panisset 
2161becc4c4SNick Mathewson 	bufev_f->outbuf_cb = evbuffer_add_cb(downcast(bufev_f)->output,
217ea4b8724SNick Mathewson 	   bufferevent_filtered_outbuf_cb, bufev_f);
218ea4b8724SNick Mathewson 
219cb9da0bfSNick Mathewson 	bufferevent_init_generic_timeout_cbs_(downcast(bufev_f));
2208ac3c4c2SNick Mathewson 	bufferevent_incref_(underlying);
22134574db0SNick Mathewson 
222ac27eb82SNick Mathewson 	bufferevent_enable(underlying, EV_READ|EV_WRITE);
2238ac3c4c2SNick Mathewson 	bufferevent_suspend_read_(underlying, BEV_SUSPEND_FILT_READ);
224ac27eb82SNick Mathewson 
2251becc4c4SNick Mathewson 	return downcast(bufev_f);
226ea4b8724SNick Mathewson }
227ea4b8724SNick Mathewson 
228ea4b8724SNick Mathewson static void
be_filter_unlink(struct bufferevent * bev)22902fbf687SNick Mathewson be_filter_unlink(struct bufferevent *bev)
230ea4b8724SNick Mathewson {
231ea4b8724SNick Mathewson 	struct bufferevent_filtered *bevf = upcast(bev);
2322e36dbe1SNick Mathewson 	EVUTIL_ASSERT(bevf);
233ea4b8724SNick Mathewson 
234f1bc125eSNick Mathewson 	if (bevf->bev.options & BEV_OPT_CLOSE_ON_FREE) {
2358ac3c4c2SNick Mathewson 		/* Yes, there is also a decref in bufferevent_decref_.
236f1bc125eSNick Mathewson 		 * That decref corresponds to the incref when we set
237f1bc125eSNick Mathewson 		 * underlying for the first time.  This decref is an
238f1bc125eSNick Mathewson 		 * extra one to remove the last reference.
239f1bc125eSNick Mathewson 		 */
240f1bc125eSNick Mathewson 		if (BEV_UPCAST(bevf->underlying)->refcnt < 2) {
241f1bc125eSNick Mathewson 			event_warnx("BEV_OPT_CLOSE_ON_FREE set on an "
242f1bc125eSNick Mathewson 			    "bufferevent with too few references");
243f1bc125eSNick Mathewson 		} else {
244ea4b8724SNick Mathewson 			bufferevent_free(bevf->underlying);
245f1bc125eSNick Mathewson 		}
246ac27eb82SNick Mathewson 	} else {
247ac27eb82SNick Mathewson 		if (bevf->underlying) {
2481ac5b230SNick Mathewson 			if (bevf->underlying->errorcb == be_filter_eventcb)
249fc7b1b00SNick Mathewson 				bufferevent_setcb(bevf->underlying,
250fc7b1b00SNick Mathewson 				    NULL, NULL, NULL, NULL);
2518ac3c4c2SNick Mathewson 			bufferevent_unsuspend_read_(bevf->underlying,
252ac27eb82SNick Mathewson 			    BEV_SUSPEND_FILT_READ);
253ac27eb82SNick Mathewson 		}
254f1bc125eSNick Mathewson 	}
25502fbf687SNick Mathewson }
25634574db0SNick Mathewson 
25702fbf687SNick Mathewson static void
be_filter_destruct(struct bufferevent * bev)25802fbf687SNick Mathewson be_filter_destruct(struct bufferevent *bev)
25902fbf687SNick Mathewson {
26002fbf687SNick Mathewson 	struct bufferevent_filtered *bevf = upcast(bev);
26102fbf687SNick Mathewson 	EVUTIL_ASSERT(bevf);
26202fbf687SNick Mathewson 	if (bevf->free_context)
26302fbf687SNick Mathewson 		bevf->free_context(bevf->context);
264b627ad88SEduardo Panisset 
265b627ad88SEduardo Panisset 	if (bevf->inbuf_cb)
266b627ad88SEduardo Panisset 		evbuffer_remove_cb_entry(bev->input, bevf->inbuf_cb);
267b627ad88SEduardo Panisset 
268b627ad88SEduardo Panisset 	if (bevf->outbuf_cb)
269b627ad88SEduardo Panisset 		evbuffer_remove_cb_entry(bev->output, bevf->outbuf_cb);
270ea4b8724SNick Mathewson }
271ea4b8724SNick Mathewson 
272ea4b8724SNick Mathewson static int
be_filter_enable(struct bufferevent * bev,short event)273ea4b8724SNick Mathewson be_filter_enable(struct bufferevent *bev, short event)
274ea4b8724SNick Mathewson {
275ea4b8724SNick Mathewson 	struct bufferevent_filtered *bevf = upcast(bev);
276d3288293SNick Mathewson 	if (event & EV_WRITE)
277d3288293SNick Mathewson 		BEV_RESET_GENERIC_WRITE_TIMEOUT(bev);
278ac27eb82SNick Mathewson 
279ac27eb82SNick Mathewson 	if (event & EV_READ) {
280ac27eb82SNick Mathewson 		BEV_RESET_GENERIC_READ_TIMEOUT(bev);
2818ac3c4c2SNick Mathewson 		bufferevent_unsuspend_read_(bevf->underlying,
282ac27eb82SNick Mathewson 		    BEV_SUSPEND_FILT_READ);
283ac27eb82SNick Mathewson 	}
284ac27eb82SNick Mathewson 	return 0;
285ea4b8724SNick Mathewson }
286ea4b8724SNick Mathewson 
287ea4b8724SNick Mathewson static int
be_filter_disable(struct bufferevent * bev,short event)288ea4b8724SNick Mathewson be_filter_disable(struct bufferevent *bev, short event)
289ea4b8724SNick Mathewson {
290ea4b8724SNick Mathewson 	struct bufferevent_filtered *bevf = upcast(bev);
291d3288293SNick Mathewson 	if (event & EV_WRITE)
292d3288293SNick Mathewson 		BEV_DEL_GENERIC_WRITE_TIMEOUT(bev);
293ac27eb82SNick Mathewson 	if (event & EV_READ) {
294ac27eb82SNick Mathewson 		BEV_DEL_GENERIC_READ_TIMEOUT(bev);
2958ac3c4c2SNick Mathewson 		bufferevent_suspend_read_(bevf->underlying,
296ac27eb82SNick Mathewson 		    BEV_SUSPEND_FILT_READ);
297ac27eb82SNick Mathewson 	}
298ac27eb82SNick Mathewson 	return 0;
299ea4b8724SNick Mathewson }
300ea4b8724SNick Mathewson 
301ea4b8724SNick Mathewson static enum bufferevent_filter_result
be_filter_process_input(struct bufferevent_filtered * bevf,enum bufferevent_flush_mode state,int * processed_out)302ea4b8724SNick Mathewson be_filter_process_input(struct bufferevent_filtered *bevf,
303ea4b8724SNick Mathewson 			enum bufferevent_flush_mode state,
304ea4b8724SNick Mathewson 			int *processed_out)
305ea4b8724SNick Mathewson {
306ea4b8724SNick Mathewson 	enum bufferevent_filter_result res;
3071becc4c4SNick Mathewson 	struct bufferevent *bev = downcast(bevf);
308ea4b8724SNick Mathewson 
309ea4b8724SNick Mathewson 	if (state == BEV_NORMAL) {
310ea4b8724SNick Mathewson 		/* If we're in 'normal' mode, don't urge data on the filter
311ea4b8724SNick Mathewson 		 * unless we're reading data and under our high-water mark.*/
3121becc4c4SNick Mathewson 		if (!(bev->enabled & EV_READ) ||
313ea4b8724SNick Mathewson 		    be_readbuf_full(bevf, state))
314ea4b8724SNick Mathewson 			return BEV_OK;
315ea4b8724SNick Mathewson 	}
316ea4b8724SNick Mathewson 
317ea4b8724SNick Mathewson 	do {
3180b22ca19SNick Mathewson 		ev_ssize_t limit = -1;
3191becc4c4SNick Mathewson 		if (state == BEV_NORMAL && bev->wm_read.high)
3201becc4c4SNick Mathewson 			limit = bev->wm_read.high -
321a8f6d961SNick Mathewson 			    evbuffer_get_length(bev->input);
322ea4b8724SNick Mathewson 
323ea4b8724SNick Mathewson 		res = bevf->process_in(bevf->underlying->input,
3241becc4c4SNick Mathewson 		    bev->input, limit, state, bevf->context);
325ea4b8724SNick Mathewson 
326ea4b8724SNick Mathewson 		if (res == BEV_OK)
327ea4b8724SNick Mathewson 			*processed_out = 1;
328ea4b8724SNick Mathewson 	} while (res == BEV_OK &&
3291becc4c4SNick Mathewson 		 (bev->enabled & EV_READ) &&
330a8f6d961SNick Mathewson 		 evbuffer_get_length(bevf->underlying->input) &&
331ea4b8724SNick Mathewson 		 !be_readbuf_full(bevf, state));
332ea4b8724SNick Mathewson 
33334574db0SNick Mathewson 	if (*processed_out)
33434574db0SNick Mathewson 		BEV_RESET_GENERIC_READ_TIMEOUT(bev);
33534574db0SNick Mathewson 
336ea4b8724SNick Mathewson 	return res;
337ea4b8724SNick Mathewson }
338ea4b8724SNick Mathewson 
339ea4b8724SNick Mathewson 
340ea4b8724SNick Mathewson static enum bufferevent_filter_result
be_filter_process_output(struct bufferevent_filtered * bevf,enum bufferevent_flush_mode state,int * processed_out)341ea4b8724SNick Mathewson be_filter_process_output(struct bufferevent_filtered *bevf,
342ea4b8724SNick Mathewson 			 enum bufferevent_flush_mode state,
343ea4b8724SNick Mathewson 			 int *processed_out)
344ea4b8724SNick Mathewson {
345a62283a9SNick Mathewson 	/* Requires references and lock: might call writecb */
346ea4b8724SNick Mathewson 	enum bufferevent_filter_result res = BEV_OK;
347ea4b8724SNick Mathewson 	struct bufferevent *bufev = downcast(bevf);
348ea4b8724SNick Mathewson 	int again = 0;
349ea4b8724SNick Mathewson 
350ea4b8724SNick Mathewson 	if (state == BEV_NORMAL) {
351ea4b8724SNick Mathewson 		/* If we're in 'normal' mode, don't urge data on the
352ea4b8724SNick Mathewson 		 * filter unless we're writing data, and the underlying
353ea4b8724SNick Mathewson 		 * bufferevent is accepting data, and we have data to
354ea4b8724SNick Mathewson 		 * give the filter.  If we're in 'flush' or 'finish',
355ea4b8724SNick Mathewson 		 * call the filter no matter what. */
356ea4b8724SNick Mathewson 		if (!(bufev->enabled & EV_WRITE) ||
357ea4b8724SNick Mathewson 		    be_underlying_writebuf_full(bevf, state) ||
358a8f6d961SNick Mathewson 		    !evbuffer_get_length(bufev->output))
359ea4b8724SNick Mathewson 			return BEV_OK;
360ea4b8724SNick Mathewson 	}
361ea4b8724SNick Mathewson 
362ea4b8724SNick Mathewson 	/* disable the callback that calls this function
363ea4b8724SNick Mathewson 	   when the user adds to the output buffer. */
364c031215dSSimon Perreault 	evbuffer_cb_clear_flags(bufev->output, bevf->outbuf_cb,
365c031215dSSimon Perreault 	    EVBUFFER_CB_ENABLED);
366ea4b8724SNick Mathewson 
367ea4b8724SNick Mathewson 	do {
368ea4b8724SNick Mathewson 		int processed = 0;
369ea4b8724SNick Mathewson 		again = 0;
370ea4b8724SNick Mathewson 
371ea4b8724SNick Mathewson 		do {
3720b22ca19SNick Mathewson 			ev_ssize_t limit = -1;
373ea4b8724SNick Mathewson 			if (state == BEV_NORMAL &&
374ea4b8724SNick Mathewson 			    bevf->underlying->wm_write.high)
375ea4b8724SNick Mathewson 				limit = bevf->underlying->wm_write.high -
376a8f6d961SNick Mathewson 				    evbuffer_get_length(bevf->underlying->output);
377ea4b8724SNick Mathewson 
3781becc4c4SNick Mathewson 			res = bevf->process_out(downcast(bevf)->output,
379ea4b8724SNick Mathewson 			    bevf->underlying->output,
380ea4b8724SNick Mathewson 			    limit,
381ea4b8724SNick Mathewson 			    state,
382ea4b8724SNick Mathewson 			    bevf->context);
383ea4b8724SNick Mathewson 
384ea4b8724SNick Mathewson 			if (res == BEV_OK)
385ea4b8724SNick Mathewson 				processed = *processed_out = 1;
386ea4b8724SNick Mathewson 		} while (/* Stop if the filter wasn't successful...*/
387ea4b8724SNick Mathewson 			res == BEV_OK &&
388ea4b8724SNick Mathewson 			/* Or if we aren't writing any more. */
389ea4b8724SNick Mathewson 			(bufev->enabled & EV_WRITE) &&
390ea4b8724SNick Mathewson 			/* Of if we have nothing more to write and we are
391ea4b8724SNick Mathewson 			 * not flushing. */
392a8f6d961SNick Mathewson 			evbuffer_get_length(bufev->output) &&
393ea4b8724SNick Mathewson 			/* Or if we have filled the underlying output buffer. */
394ea4b8724SNick Mathewson 			!be_underlying_writebuf_full(bevf,state));
395ea4b8724SNick Mathewson 
39661ee18b8SOndřej Kuzník 		if (processed) {
397ea4b8724SNick Mathewson 			/* call the write callback.*/
39861ee18b8SOndřej Kuzník 			bufferevent_trigger_nolock_(bufev, EV_WRITE, 0);
399ea4b8724SNick Mathewson 
400ea4b8724SNick Mathewson 			if (res == BEV_OK &&
401ea4b8724SNick Mathewson 			    (bufev->enabled & EV_WRITE) &&
402a8f6d961SNick Mathewson 			    evbuffer_get_length(bufev->output) &&
403ea4b8724SNick Mathewson 			    !be_underlying_writebuf_full(bevf, state)) {
404ea4b8724SNick Mathewson 				again = 1;
405ea4b8724SNick Mathewson 			}
406ea4b8724SNick Mathewson 		}
407ea4b8724SNick Mathewson 	} while (again);
408ea4b8724SNick Mathewson 
409ea4b8724SNick Mathewson 	/* reenable the outbuf_cb */
410ea4b8724SNick Mathewson 	evbuffer_cb_set_flags(bufev->output,bevf->outbuf_cb,
411ea4b8724SNick Mathewson 	    EVBUFFER_CB_ENABLED);
412ea4b8724SNick Mathewson 
41334574db0SNick Mathewson 	if (*processed_out)
41434574db0SNick Mathewson 		BEV_RESET_GENERIC_WRITE_TIMEOUT(bufev);
41534574db0SNick Mathewson 
416ea4b8724SNick Mathewson 	return res;
417ea4b8724SNick Mathewson }
418ea4b8724SNick Mathewson 
419ea4b8724SNick Mathewson /* Called when the size of our outbuf changes. */
420ea4b8724SNick Mathewson static void
bufferevent_filtered_outbuf_cb(struct evbuffer * buf,const struct evbuffer_cb_info * cbinfo,void * arg)421ea4b8724SNick Mathewson bufferevent_filtered_outbuf_cb(struct evbuffer *buf,
422f1b1bad4SNick Mathewson     const struct evbuffer_cb_info *cbinfo, void *arg)
423ea4b8724SNick Mathewson {
424ea4b8724SNick Mathewson 	struct bufferevent_filtered *bevf = arg;
425a62283a9SNick Mathewson 	struct bufferevent *bev = downcast(bevf);
426ea4b8724SNick Mathewson 
427f1b1bad4SNick Mathewson 	if (cbinfo->n_added) {
428ea4b8724SNick Mathewson 		int processed_any = 0;
429ea4b8724SNick Mathewson 		/* Somebody added more data to the output buffer. Try to
430ea4b8724SNick Mathewson 		 * process it, if we should. */
431cb9da0bfSNick Mathewson 		bufferevent_incref_and_lock_(bev);
432ea4b8724SNick Mathewson 		be_filter_process_output(bevf, BEV_NORMAL, &processed_any);
433cb9da0bfSNick Mathewson 		bufferevent_decref_and_unlock_(bev);
434ea4b8724SNick Mathewson 	}
435ea4b8724SNick Mathewson }
436ea4b8724SNick Mathewson 
437ea4b8724SNick Mathewson static void
be_filter_read_nolock_(struct bufferevent * underlying,void * me_)438b627ad88SEduardo Panisset be_filter_read_nolock_(struct bufferevent *underlying, void *me_)
439ea4b8724SNick Mathewson {
440946b5841SNick Mathewson 	struct bufferevent_filtered *bevf = me_;
441ea4b8724SNick Mathewson 	enum bufferevent_filter_result res;
442ea4b8724SNick Mathewson 	enum bufferevent_flush_mode state;
443ea4b8724SNick Mathewson 	struct bufferevent *bufev = downcast(bevf);
4442c82aa0fSJohn Ohl 	struct bufferevent_private *bufev_private = BEV_UPCAST(bufev);
445ea4b8724SNick Mathewson 	int processed_any = 0;
446ea4b8724SNick Mathewson 
4472c82aa0fSJohn Ohl 	// It's possible our refcount is 0 at this point if another thread free'd our filterevent
4482c82aa0fSJohn Ohl 	EVUTIL_ASSERT(bufev_private->refcnt >= 0);
4492c82aa0fSJohn Ohl 
4502c82aa0fSJohn Ohl 	// If our refcount is > 0
4512c82aa0fSJohn Ohl 	if (bufev_private->refcnt > 0) {
452a62283a9SNick Mathewson 
453ea4b8724SNick Mathewson 		if (bevf->got_eof)
454ea4b8724SNick Mathewson 			state = BEV_FINISHED;
455ea4b8724SNick Mathewson 		else
456ea4b8724SNick Mathewson 			state = BEV_NORMAL;
457ea4b8724SNick Mathewson 
4587bcace2dSNick Mathewson 		/* XXXX use return value */
459ea4b8724SNick Mathewson 		res = be_filter_process_input(bevf, state, &processed_any);
46065707d7cSSebastian Hahn 		(void)res;
461ea4b8724SNick Mathewson 
462a62283a9SNick Mathewson 		/* XXX This should be in process_input, not here.  There are
463a62283a9SNick Mathewson 		 * other places that can call process-input, and they should
464a62283a9SNick Mathewson 		 * force readcb calls as needed. */
465b627ad88SEduardo Panisset 		if (processed_any) {
46661ee18b8SOndřej Kuzník 			bufferevent_trigger_nolock_(bufev, EV_READ, 0);
467b627ad88SEduardo Panisset 			if (evbuffer_get_length(underlying->input) > 0 &&
468b627ad88SEduardo Panisset 				be_readbuf_full(bevf, state)) {
469b627ad88SEduardo Panisset 				/* data left in underlying buffer and filter input buffer
470b627ad88SEduardo Panisset 				 * hit its read high watermark.
471b627ad88SEduardo Panisset 				 * Schedule callback to avoid data gets stuck in underlying
472b627ad88SEduardo Panisset 				 * input buffer.
473b627ad88SEduardo Panisset 				 */
474b627ad88SEduardo Panisset 				evbuffer_cb_set_flags(bufev->input, bevf->inbuf_cb,
475b627ad88SEduardo Panisset 					EVBUFFER_CB_ENABLED);
476b627ad88SEduardo Panisset 			}
477b627ad88SEduardo Panisset 		}
478b627ad88SEduardo Panisset 	}
4792c82aa0fSJohn Ohl }
480a62283a9SNick Mathewson 
481b627ad88SEduardo Panisset /* Called when the size of our inbuf changes. */
482b627ad88SEduardo Panisset static void
bufferevent_filtered_inbuf_cb(struct evbuffer * buf,const struct evbuffer_cb_info * cbinfo,void * arg)483b627ad88SEduardo Panisset bufferevent_filtered_inbuf_cb(struct evbuffer *buf,
484b627ad88SEduardo Panisset     const struct evbuffer_cb_info *cbinfo, void *arg)
485b627ad88SEduardo Panisset {
486b627ad88SEduardo Panisset 	struct bufferevent_filtered *bevf = arg;
487b627ad88SEduardo Panisset 	enum bufferevent_flush_mode state;
488b627ad88SEduardo Panisset 	struct bufferevent *bev = downcast(bevf);
489b627ad88SEduardo Panisset 
490b627ad88SEduardo Panisset 	BEV_LOCK(bev);
491b627ad88SEduardo Panisset 
492b627ad88SEduardo Panisset 	if (bevf->got_eof)
493b627ad88SEduardo Panisset 		state = BEV_FINISHED;
494b627ad88SEduardo Panisset 	else
495b627ad88SEduardo Panisset 		state = BEV_NORMAL;
496b627ad88SEduardo Panisset 
497b627ad88SEduardo Panisset 
498b627ad88SEduardo Panisset 	if (!be_readbuf_full(bevf, state)) {
499b627ad88SEduardo Panisset 		/* opportunity to read data which was left in underlying
500b627ad88SEduardo Panisset 		 * input buffer because filter input buffer hit read
501b627ad88SEduardo Panisset 		 * high watermark.
502b627ad88SEduardo Panisset 		 */
503b627ad88SEduardo Panisset 		evbuffer_cb_clear_flags(bev->input, bevf->inbuf_cb,
504b627ad88SEduardo Panisset 			EVBUFFER_CB_ENABLED);
505b627ad88SEduardo Panisset 		if (evbuffer_get_length(bevf->underlying->input) > 0)
506b627ad88SEduardo Panisset 			be_filter_read_nolock_(bevf->underlying, bevf);
507b627ad88SEduardo Panisset 	}
508b627ad88SEduardo Panisset 
509b627ad88SEduardo Panisset 	BEV_UNLOCK(bev);
510b627ad88SEduardo Panisset }
511b627ad88SEduardo Panisset 
512b627ad88SEduardo Panisset /* Called when the underlying socket has read. */
513b627ad88SEduardo Panisset static void
be_filter_readcb(struct bufferevent * underlying,void * me_)514b627ad88SEduardo Panisset be_filter_readcb(struct bufferevent *underlying, void *me_)
515b627ad88SEduardo Panisset {
516b627ad88SEduardo Panisset 	struct bufferevent_filtered *bevf = me_;
517b627ad88SEduardo Panisset 	struct bufferevent *bev = downcast(bevf);
518b627ad88SEduardo Panisset 
519b627ad88SEduardo Panisset 	BEV_LOCK(bev);
520b627ad88SEduardo Panisset 
521b627ad88SEduardo Panisset 	be_filter_read_nolock_(underlying, me_);
522b627ad88SEduardo Panisset 
523b627ad88SEduardo Panisset 	BEV_UNLOCK(bev);
524ea4b8724SNick Mathewson }
525ea4b8724SNick Mathewson 
526ea4b8724SNick Mathewson /* Called when the underlying socket has drained enough that we can write to
527ea4b8724SNick Mathewson    it. */
528ea4b8724SNick Mathewson static void
be_filter_writecb(struct bufferevent * underlying,void * me_)529946b5841SNick Mathewson be_filter_writecb(struct bufferevent *underlying, void *me_)
530ea4b8724SNick Mathewson {
531946b5841SNick Mathewson 	struct bufferevent_filtered *bevf = me_;
532a62283a9SNick Mathewson 	struct bufferevent *bev = downcast(bevf);
5332c82aa0fSJohn Ohl 	struct bufferevent_private *bufev_private = BEV_UPCAST(bev);
534ea4b8724SNick Mathewson 	int processed_any = 0;
535ea4b8724SNick Mathewson 
5362c82aa0fSJohn Ohl 	BEV_LOCK(bev);
5372c82aa0fSJohn Ohl 
5382c82aa0fSJohn Ohl 	// It's possible our refcount is 0 at this point if another thread free'd our filterevent
5392c82aa0fSJohn Ohl 	EVUTIL_ASSERT(bufev_private->refcnt >= 0);
5402c82aa0fSJohn Ohl 
5412c82aa0fSJohn Ohl 	// If our refcount is > 0
5422c82aa0fSJohn Ohl 	if (bufev_private->refcnt > 0) {
543ea4b8724SNick Mathewson 		be_filter_process_output(bevf, BEV_NORMAL, &processed_any);
5442c82aa0fSJohn Ohl 	}
5452c82aa0fSJohn Ohl 
5462c82aa0fSJohn Ohl 	BEV_UNLOCK(bev);
547ea4b8724SNick Mathewson }
548ea4b8724SNick Mathewson 
549ea4b8724SNick Mathewson /* Called when the underlying socket has given us an error */
550ea4b8724SNick Mathewson static void
be_filter_eventcb(struct bufferevent * underlying,short what,void * me_)551946b5841SNick Mathewson be_filter_eventcb(struct bufferevent *underlying, short what, void *me_)
552ea4b8724SNick Mathewson {
553946b5841SNick Mathewson 	struct bufferevent_filtered *bevf = me_;
5541becc4c4SNick Mathewson 	struct bufferevent *bev = downcast(bevf);
5552c82aa0fSJohn Ohl 	struct bufferevent_private *bufev_private = BEV_UPCAST(bev);
556ea4b8724SNick Mathewson 
5572c82aa0fSJohn Ohl 	BEV_LOCK(bev);
5582c82aa0fSJohn Ohl 
5592c82aa0fSJohn Ohl 	// It's possible our refcount is 0 at this point if another thread free'd our filterevent
5602c82aa0fSJohn Ohl 	EVUTIL_ASSERT(bufev_private->refcnt >= 0);
5612c82aa0fSJohn Ohl 
5622c82aa0fSJohn Ohl 	// If our refcount is > 0
5632c82aa0fSJohn Ohl 	if (bufev_private->refcnt > 0) {
5642c82aa0fSJohn Ohl 
5655232cfa3SNick Mathewson 		/* All we can really to is tell our own eventcb. */
566a7384c78SOndřej Kuzník 		bufferevent_run_eventcb_(bev, what, 0);
5672c82aa0fSJohn Ohl 	}
5682c82aa0fSJohn Ohl 
5692c82aa0fSJohn Ohl 	BEV_UNLOCK(bev);
570ea4b8724SNick Mathewson }
571ea4b8724SNick Mathewson 
572ea4b8724SNick Mathewson static int
be_filter_flush(struct bufferevent * bufev,short iotype,enum bufferevent_flush_mode mode)573ea4b8724SNick Mathewson be_filter_flush(struct bufferevent *bufev,
574ea4b8724SNick Mathewson     short iotype, enum bufferevent_flush_mode mode)
575ea4b8724SNick Mathewson {
576ea4b8724SNick Mathewson 	struct bufferevent_filtered *bevf = upcast(bufev);
577ea4b8724SNick Mathewson 	int processed_any = 0;
5782e36dbe1SNick Mathewson 	EVUTIL_ASSERT(bevf);
579ea4b8724SNick Mathewson 
580cb9da0bfSNick Mathewson 	bufferevent_incref_and_lock_(bufev);
581a62283a9SNick Mathewson 
582ea4b8724SNick Mathewson 	if (iotype & EV_READ) {
583ea4b8724SNick Mathewson 		be_filter_process_input(bevf, mode, &processed_any);
584ea4b8724SNick Mathewson 	}
585ea4b8724SNick Mathewson 	if (iotype & EV_WRITE) {
586ea4b8724SNick Mathewson 		be_filter_process_output(bevf, mode, &processed_any);
587ea4b8724SNick Mathewson 	}
588ea4b8724SNick Mathewson 	/* XXX check the return value? */
589ea4b8724SNick Mathewson 	/* XXX does this want to recursively call lower-level flushes? */
590ea4b8724SNick Mathewson 	bufferevent_flush(bevf->underlying, iotype, mode);
591ea4b8724SNick Mathewson 
592cb9da0bfSNick Mathewson 	bufferevent_decref_and_unlock_(bufev);
593a62283a9SNick Mathewson 
594ea4b8724SNick Mathewson 	return processed_any;
595ea4b8724SNick Mathewson }
59631d89f27SNick Mathewson 
59731d89f27SNick Mathewson static int
be_filter_ctrl(struct bufferevent * bev,enum bufferevent_ctrl_op op,union bufferevent_ctrl_data * data)59831d89f27SNick Mathewson be_filter_ctrl(struct bufferevent *bev, enum bufferevent_ctrl_op op,
59931d89f27SNick Mathewson     union bufferevent_ctrl_data *data)
60031d89f27SNick Mathewson {
60131d89f27SNick Mathewson 	struct bufferevent_filtered *bevf;
60231d89f27SNick Mathewson 	switch (op) {
60331d89f27SNick Mathewson 	case BEV_CTRL_GET_UNDERLYING:
60431d89f27SNick Mathewson 		bevf = upcast(bev);
60531d89f27SNick Mathewson 		data->ptr = bevf->underlying;
60631d89f27SNick Mathewson 		return 0;
60731d89f27SNick Mathewson 	case BEV_CTRL_SET_FD:
608ebfac517SGreg Hazel 	case BEV_CTRL_GET_FD:
609c2aa7dcbSMark Ellzey 		bevf = upcast(bev);
610c2aa7dcbSMark Ellzey 
611c2aa7dcbSMark Ellzey 		if (bevf->underlying &&
612c2aa7dcbSMark Ellzey 			bevf->underlying->be_ops &&
613c2aa7dcbSMark Ellzey 			bevf->underlying->be_ops->ctrl) {
614c2aa7dcbSMark Ellzey 		    return (bevf->underlying->be_ops->ctrl)(bevf->underlying, op, data);
615c2aa7dcbSMark Ellzey 		}
61640da44bdSAzat Khuzhin 		EVUTIL_FALLTHROUGH;
617c2aa7dcbSMark Ellzey 
618e6af35d7SNick Mathewson 	case BEV_CTRL_CANCEL_ALL:
61940da44bdSAzat Khuzhin 		EVUTIL_FALLTHROUGH;
62031d89f27SNick Mathewson 	default:
62131d89f27SNick Mathewson 		return -1;
62231d89f27SNick Mathewson 	}
623c2aa7dcbSMark Ellzey 
624c2aa7dcbSMark Ellzey 	return -1;
62531d89f27SNick Mathewson }
626