1275970Scy/*
2275970Scy * Copyright (c) 2003-2007 Niels Provos <provos@citi.umich.edu>
3275970Scy * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson
4275970Scy *
5275970Scy * Redistribution and use in source and binary forms, with or without
6275970Scy * modification, are permitted provided that the following conditions
7275970Scy * are met:
8275970Scy * 1. Redistributions of source code must retain the above copyright
9275970Scy *    notice, this list of conditions and the following disclaimer.
10275970Scy * 2. Redistributions in binary form must reproduce the above copyright
11275970Scy *    notice, this list of conditions and the following disclaimer in the
12275970Scy *    documentation and/or other materials provided with the distribution.
13275970Scy * 3. The name of the author may not be used to endorse or promote products
14275970Scy *    derived from this software without specific prior written permission.
15275970Scy *
16275970Scy * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17275970Scy * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18275970Scy * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19275970Scy * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20275970Scy * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21275970Scy * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22275970Scy * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23275970Scy * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24275970Scy * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25275970Scy * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26275970Scy */
27275970Scy#include "util-internal.h"
28275970Scy
29275970Scy/* The old tests here need assertions to work. */
30275970Scy#undef NDEBUG
31275970Scy
32275970Scy#ifdef _WIN32
33275970Scy#include <winsock2.h>
34275970Scy#include <windows.h>
35275970Scy#endif
36275970Scy
37275970Scy#include "event2/event-config.h"
38275970Scy
39275970Scy#include <sys/types.h>
40275970Scy#include <sys/stat.h>
41275970Scy#ifdef EVENT__HAVE_SYS_TIME_H
42275970Scy#include <sys/time.h>
43275970Scy#endif
44275970Scy#include <sys/queue.h>
45275970Scy#ifndef _WIN32
46275970Scy#include <sys/socket.h>
47275970Scy#include <sys/wait.h>
48275970Scy#include <signal.h>
49275970Scy#include <unistd.h>
50275970Scy#include <netdb.h>
51275970Scy#include <netinet/in.h>
52275970Scy#endif
53275970Scy#include <fcntl.h>
54275970Scy#include <signal.h>
55275970Scy#include <stdlib.h>
56275970Scy#include <stdio.h>
57275970Scy#include <string.h>
58275970Scy#include <errno.h>
59275970Scy#include <assert.h>
60275970Scy
61275970Scy#ifdef EVENT__HAVE_ARPA_INET_H
62275970Scy#include <arpa/inet.h>
63275970Scy#endif
64275970Scy
65275970Scy#include "event2/event-config.h"
66275970Scy#include "event2/event.h"
67275970Scy#include "event2/event_struct.h"
68275970Scy#include "event2/event_compat.h"
69275970Scy#include "event2/tag.h"
70275970Scy#include "event2/buffer.h"
71275970Scy#include "event2/bufferevent.h"
72275970Scy#include "event2/bufferevent_compat.h"
73275970Scy#include "event2/bufferevent_struct.h"
74275970Scy#include "event2/listener.h"
75275970Scy#include "event2/util.h"
76275970Scy
77275970Scy#include "bufferevent-internal.h"
78282408Scy#include "evthread-internal.h"
79275970Scy#include "util-internal.h"
80275970Scy#ifdef _WIN32
81275970Scy#include "iocp-internal.h"
82275970Scy#endif
83275970Scy
84275970Scy#include "regress.h"
85275970Scy#include "regress_testutils.h"
86275970Scy
87275970Scy/*
88275970Scy * simple bufferevent test
89275970Scy */
90275970Scy
91275970Scystatic void
92275970Scyreadcb(struct bufferevent *bev, void *arg)
93275970Scy{
94275970Scy	if (evbuffer_get_length(bev->input) == 8333) {
95275970Scy		struct evbuffer *evbuf = evbuffer_new();
96275970Scy		assert(evbuf != NULL);
97275970Scy
98275970Scy		/* gratuitous test of bufferevent_read_buffer */
99275970Scy		bufferevent_read_buffer(bev, evbuf);
100275970Scy
101275970Scy		bufferevent_disable(bev, EV_READ);
102275970Scy
103275970Scy		if (evbuffer_get_length(evbuf) == 8333) {
104275970Scy			test_ok++;
105275970Scy		}
106275970Scy
107275970Scy		evbuffer_free(evbuf);
108275970Scy	}
109275970Scy}
110275970Scy
111275970Scystatic void
112275970Scywritecb(struct bufferevent *bev, void *arg)
113275970Scy{
114275970Scy	if (evbuffer_get_length(bev->output) == 0) {
115275970Scy		test_ok++;
116275970Scy	}
117275970Scy}
118275970Scy
119275970Scystatic void
120275970Scyerrorcb(struct bufferevent *bev, short what, void *arg)
121275970Scy{
122275970Scy	test_ok = -2;
123275970Scy}
124275970Scy
125275970Scystatic void
126275970Scytest_bufferevent_impl(int use_pair)
127275970Scy{
128275970Scy	struct bufferevent *bev1 = NULL, *bev2 = NULL;
129275970Scy	char buffer[8333];
130275970Scy	int i;
131275970Scy
132275970Scy	if (use_pair) {
133275970Scy		struct bufferevent *pair[2];
134275970Scy		tt_assert(0 == bufferevent_pair_new(NULL, 0, pair));
135275970Scy		bev1 = pair[0];
136275970Scy		bev2 = pair[1];
137275970Scy		bufferevent_setcb(bev1, readcb, writecb, errorcb, bev1);
138275970Scy		bufferevent_setcb(bev2, readcb, writecb, errorcb, NULL);
139275970Scy		tt_int_op(bufferevent_getfd(bev1), ==, -1);
140275970Scy		tt_ptr_op(bufferevent_get_underlying(bev1), ==, NULL);
141275970Scy		tt_ptr_op(bufferevent_pair_get_partner(bev1), ==, bev2);
142275970Scy		tt_ptr_op(bufferevent_pair_get_partner(bev2), ==, bev1);
143275970Scy	} else {
144275970Scy		bev1 = bufferevent_new(pair[0], readcb, writecb, errorcb, NULL);
145275970Scy		bev2 = bufferevent_new(pair[1], readcb, writecb, errorcb, NULL);
146275970Scy		tt_int_op(bufferevent_getfd(bev1), ==, pair[0]);
147275970Scy		tt_ptr_op(bufferevent_get_underlying(bev1), ==, NULL);
148275970Scy		tt_ptr_op(bufferevent_pair_get_partner(bev1), ==, NULL);
149275970Scy		tt_ptr_op(bufferevent_pair_get_partner(bev2), ==, NULL);
150275970Scy	}
151275970Scy
152275970Scy	{
153275970Scy		/* Test getcb. */
154275970Scy		bufferevent_data_cb r, w;
155275970Scy		bufferevent_event_cb e;
156275970Scy		void *a;
157275970Scy		bufferevent_getcb(bev1, &r, &w, &e, &a);
158275970Scy		tt_ptr_op(r, ==, readcb);
159275970Scy		tt_ptr_op(w, ==, writecb);
160275970Scy		tt_ptr_op(e, ==, errorcb);
161275970Scy		tt_ptr_op(a, ==, use_pair ? bev1 : NULL);
162275970Scy	}
163275970Scy
164275970Scy	bufferevent_disable(bev1, EV_READ);
165275970Scy	bufferevent_enable(bev2, EV_READ);
166275970Scy
167275970Scy	tt_int_op(bufferevent_get_enabled(bev1), ==, EV_WRITE);
168275970Scy	tt_int_op(bufferevent_get_enabled(bev2), ==, EV_WRITE|EV_READ);
169275970Scy
170275970Scy	for (i = 0; i < (int)sizeof(buffer); i++)
171275970Scy		buffer[i] = i;
172275970Scy
173275970Scy	bufferevent_write(bev1, buffer, sizeof(buffer));
174275970Scy
175275970Scy	event_dispatch();
176275970Scy
177282408Scy	bufferevent_free(bev2);
178282408Scy	tt_ptr_op(bufferevent_pair_get_partner(bev1), ==, NULL);
179275970Scy	bufferevent_free(bev1);
180275970Scy
181275970Scy	if (test_ok != 2)
182275970Scy		test_ok = 0;
183275970Scyend:
184275970Scy	;
185275970Scy}
186275970Scy
187275970Scystatic void
188275970Scytest_bufferevent(void)
189275970Scy{
190275970Scy	test_bufferevent_impl(0);
191275970Scy}
192275970Scy
193275970Scystatic void
194275970Scytest_bufferevent_pair(void)
195275970Scy{
196275970Scy	test_bufferevent_impl(1);
197275970Scy}
198275970Scy
199282408Scy#if defined(EVTHREAD_USE_PTHREADS_IMPLEMENTED)
200282408Scy/**
201282408Scy * Trace lock/unlock/alloc/free for locks.
202282408Scy * (More heavier then evthread_debug*)
203282408Scy */
204282408Scytypedef struct
205282408Scy{
206282408Scy	void *lock;
207282408Scy	enum {
208282408Scy		ALLOC, FREE,
209282408Scy	} status;
210282408Scy	size_t locked /** allow recursive locking */;
211282408Scy} lock_wrapper;
212282408Scystruct lock_unlock_base
213282408Scy{
214282408Scy	/* Original callbacks */
215282408Scy	struct evthread_lock_callbacks cbs;
216282408Scy	/* Map of locks */
217282408Scy	lock_wrapper *locks;
218282408Scy	size_t nr_locks;
219282408Scy} lu_base = {
220282408Scy	.locks = NULL,
221282408Scy};
222282408Scy
223282408Scystatic lock_wrapper *lu_find(void *lock_)
224282408Scy{
225282408Scy	size_t i;
226282408Scy	for (i = 0; i < lu_base.nr_locks; ++i) {
227282408Scy		lock_wrapper *lock = &lu_base.locks[i];
228282408Scy		if (lock->lock == lock_)
229282408Scy			return lock;
230282408Scy	}
231282408Scy	return NULL;
232282408Scy}
233282408Scy
234282408Scystatic void *trace_lock_alloc(unsigned locktype)
235282408Scy{
236282408Scy	++lu_base.nr_locks;
237282408Scy	lu_base.locks = realloc(lu_base.locks,
238282408Scy		sizeof(lock_wrapper) * lu_base.nr_locks);
239282408Scy	void *lock = lu_base.cbs.alloc(locktype);
240282408Scy	lu_base.locks[lu_base.nr_locks - 1] = (lock_wrapper){ lock, ALLOC, 0 };
241282408Scy	return lock;
242282408Scy}
243282408Scystatic void trace_lock_free(void *lock_, unsigned locktype)
244282408Scy{
245282408Scy	lock_wrapper *lock = lu_find(lock_);
246282408Scy	if (!lock || lock->status == FREE || lock->locked) {
247282408Scy		__asm__("int3");
248282408Scy		TT_FAIL(("lock: free error"));
249282408Scy	} else {
250282408Scy		lock->status = FREE;
251282408Scy		lu_base.cbs.free(lock_, locktype);
252282408Scy	}
253282408Scy}
254282408Scystatic int trace_lock_lock(unsigned mode, void *lock_)
255282408Scy{
256282408Scy	lock_wrapper *lock = lu_find(lock_);
257282408Scy	if (!lock || lock->status == FREE) {
258282408Scy		TT_FAIL(("lock: lock error"));
259282408Scy		return -1;
260282408Scy	} else {
261282408Scy		++lock->locked;
262282408Scy		return lu_base.cbs.lock(mode, lock_);
263282408Scy	}
264282408Scy}
265282408Scystatic int trace_lock_unlock(unsigned mode, void *lock_)
266282408Scy{
267282408Scy	lock_wrapper *lock = lu_find(lock_);
268282408Scy	if (!lock || lock->status == FREE || !lock->locked) {
269282408Scy		TT_FAIL(("lock: unlock error"));
270282408Scy		return -1;
271282408Scy	} else {
272282408Scy		--lock->locked;
273282408Scy		return lu_base.cbs.unlock(mode, lock_);
274282408Scy	}
275282408Scy}
276282408Scystatic void lock_unlock_free_thread_cbs()
277282408Scy{
278282408Scy	event_base_free(NULL);
279282408Scy
280282408Scy	/** drop immutable flag */
281282408Scy	evthread_set_lock_callbacks(NULL);
282282408Scy	/** avoid calling of event_global_setup_locks_() for new cbs */
283282408Scy	libevent_global_shutdown();
284282408Scy	/** drop immutable flag for non-debug ops (since called after shutdown) */
285282408Scy	evthread_set_lock_callbacks(NULL);
286282408Scy}
287282408Scy
288282408Scystatic int use_lock_unlock_profiler(void)
289282408Scy{
290282408Scy	struct evthread_lock_callbacks cbs = {
291282408Scy		EVTHREAD_LOCK_API_VERSION,
292282408Scy		EVTHREAD_LOCKTYPE_RECURSIVE,
293282408Scy		trace_lock_alloc,
294282408Scy		trace_lock_free,
295282408Scy		trace_lock_lock,
296282408Scy		trace_lock_unlock,
297282408Scy	};
298282408Scy	memcpy(&lu_base.cbs, evthread_get_lock_callbacks(),
299282408Scy		sizeof(lu_base.cbs));
300282408Scy	{
301282408Scy		lock_unlock_free_thread_cbs();
302282408Scy
303282408Scy		evthread_set_lock_callbacks(&cbs);
304282408Scy		/** re-create debug locks correctly */
305282408Scy		evthread_enable_lock_debugging();
306282408Scy
307282408Scy		event_init();
308282408Scy	}
309282408Scy	return 0;
310282408Scy}
311282408Scystatic void free_lock_unlock_profiler(struct basic_test_data *data)
312282408Scy{
313282408Scy	lock_unlock_free_thread_cbs();
314282408Scy	free(lu_base.locks);
315282408Scy	data->base = NULL;
316282408Scy}
317282408Scy
318282408Scystatic void test_bufferevent_pair_release_lock(void *arg)
319282408Scy{
320282408Scy	struct basic_test_data *data = arg;
321282408Scy	use_lock_unlock_profiler();
322282408Scy	{
323282408Scy		struct bufferevent *pair[2];
324282408Scy		if (!bufferevent_pair_new(NULL, BEV_OPT_THREADSAFE, pair)) {
325282408Scy			bufferevent_free(pair[0]);
326282408Scy			bufferevent_free(pair[1]);
327282408Scy		} else
328282408Scy			tt_abort_perror("bufferevent_pair_new");
329282408Scy	}
330282408Scy	free_lock_unlock_profiler(data);
331282408Scyend:
332282408Scy	;
333282408Scy}
334282408Scy#endif
335282408Scy
336275970Scy/*
337275970Scy * test watermarks and bufferevent
338275970Scy */
339275970Scy
340275970Scystatic void
341275970Scywm_readcb(struct bufferevent *bev, void *arg)
342275970Scy{
343275970Scy	struct evbuffer *evbuf = evbuffer_new();
344275970Scy	int len = (int)evbuffer_get_length(bev->input);
345275970Scy	static int nread;
346275970Scy
347275970Scy	assert(len >= 10 && len <= 20);
348275970Scy
349275970Scy	assert(evbuf != NULL);
350275970Scy
351275970Scy	/* gratuitous test of bufferevent_read_buffer */
352275970Scy	bufferevent_read_buffer(bev, evbuf);
353275970Scy
354275970Scy	nread += len;
355275970Scy	if (nread == 65000) {
356275970Scy		bufferevent_disable(bev, EV_READ);
357275970Scy		test_ok++;
358275970Scy	}
359275970Scy
360275970Scy	evbuffer_free(evbuf);
361275970Scy}
362275970Scy
363275970Scystatic void
364275970Scywm_writecb(struct bufferevent *bev, void *arg)
365275970Scy{
366275970Scy	assert(evbuffer_get_length(bev->output) <= 100);
367275970Scy	if (evbuffer_get_length(bev->output) == 0) {
368275970Scy		evbuffer_drain(bev->output, evbuffer_get_length(bev->output));
369275970Scy		test_ok++;
370275970Scy	}
371275970Scy}
372275970Scy
373275970Scystatic void
374275970Scywm_errorcb(struct bufferevent *bev, short what, void *arg)
375275970Scy{
376275970Scy	test_ok = -2;
377275970Scy}
378275970Scy
379275970Scystatic void
380275970Scytest_bufferevent_watermarks_impl(int use_pair)
381275970Scy{
382275970Scy	struct bufferevent *bev1 = NULL, *bev2 = NULL;
383275970Scy	char buffer[65000];
384275970Scy	size_t low, high;
385275970Scy	int i;
386275970Scy	test_ok = 0;
387275970Scy
388275970Scy	if (use_pair) {
389275970Scy		struct bufferevent *pair[2];
390275970Scy		tt_assert(0 == bufferevent_pair_new(NULL, 0, pair));
391275970Scy		bev1 = pair[0];
392275970Scy		bev2 = pair[1];
393275970Scy		bufferevent_setcb(bev1, NULL, wm_writecb, errorcb, NULL);
394275970Scy		bufferevent_setcb(bev2, wm_readcb, NULL, errorcb, NULL);
395275970Scy	} else {
396275970Scy		bev1 = bufferevent_new(pair[0], NULL, wm_writecb, wm_errorcb, NULL);
397275970Scy		bev2 = bufferevent_new(pair[1], wm_readcb, NULL, wm_errorcb, NULL);
398275970Scy	}
399275970Scy	tt_assert(bev1);
400275970Scy	tt_assert(bev2);
401275970Scy	bufferevent_disable(bev1, EV_READ);
402275970Scy	bufferevent_enable(bev2, EV_READ);
403275970Scy
404275970Scy	/* By default, low watermarks are set to 0 */
405275970Scy	bufferevent_getwatermark(bev1, EV_READ, &low, NULL);
406275970Scy	tt_int_op(low, ==, 0);
407275970Scy	bufferevent_getwatermark(bev2, EV_WRITE, &low, NULL);
408275970Scy	tt_int_op(low, ==, 0);
409275970Scy
410275970Scy	for (i = 0; i < (int)sizeof(buffer); i++)
411275970Scy		buffer[i] = (char)i;
412275970Scy
413275970Scy	/* limit the reading on the receiving bufferevent */
414275970Scy	bufferevent_setwatermark(bev2, EV_READ, 10, 20);
415275970Scy
416275970Scy	bufferevent_getwatermark(bev2, EV_READ, &low, &high);
417275970Scy	tt_int_op(low, ==, 10);
418275970Scy	tt_int_op(high, ==, 20);
419275970Scy
420275970Scy	/* Tell the sending bufferevent not to notify us till it's down to
421275970Scy	   100 bytes. */
422275970Scy	bufferevent_setwatermark(bev1, EV_WRITE, 100, 2000);
423275970Scy
424275970Scy	bufferevent_getwatermark(bev1, EV_WRITE, &low, &high);
425275970Scy	tt_int_op(low, ==, 100);
426275970Scy	tt_int_op(high, ==, 2000);
427275970Scy
428282408Scy	{
429282408Scy	int r = bufferevent_getwatermark(bev1, EV_WRITE | EV_READ, &low, &high);
430282408Scy	tt_int_op(r, !=, 0);
431282408Scy	}
432282408Scy
433275970Scy	bufferevent_write(bev1, buffer, sizeof(buffer));
434275970Scy
435275970Scy	event_dispatch();
436275970Scy
437275970Scy	tt_int_op(test_ok, ==, 2);
438275970Scy
439275970Scy	/* The write callback drained all the data from outbuf, so we
440275970Scy	 * should have removed the write event... */
441275970Scy	tt_assert(!event_pending(&bev2->ev_write, EV_WRITE, NULL));
442275970Scy
443275970Scyend:
444275970Scy	if (bev1)
445275970Scy		bufferevent_free(bev1);
446275970Scy	if (bev2)
447275970Scy		bufferevent_free(bev2);
448275970Scy}
449275970Scy
450275970Scystatic void
451275970Scytest_bufferevent_watermarks(void)
452275970Scy{
453275970Scy	test_bufferevent_watermarks_impl(0);
454275970Scy}
455275970Scy
456275970Scystatic void
457275970Scytest_bufferevent_pair_watermarks(void)
458275970Scy{
459275970Scy	test_bufferevent_watermarks_impl(1);
460275970Scy}
461275970Scy
462275970Scy/*
463275970Scy * Test bufferevent filters
464275970Scy */
465275970Scy
466275970Scy/* strip an 'x' from each byte */
467275970Scy
468275970Scystatic enum bufferevent_filter_result
469275970Scybufferevent_input_filter(struct evbuffer *src, struct evbuffer *dst,
470275970Scy    ev_ssize_t lim, enum bufferevent_flush_mode state, void *ctx)
471275970Scy{
472275970Scy	const unsigned char *buffer;
473275970Scy	unsigned i;
474275970Scy
475275970Scy	buffer = evbuffer_pullup(src, evbuffer_get_length(src));
476275970Scy	for (i = 0; i < evbuffer_get_length(src); i += 2) {
477275970Scy		assert(buffer[i] == 'x');
478275970Scy		evbuffer_add(dst, buffer + i + 1, 1);
479275970Scy
480275970Scy		if (i + 2 > evbuffer_get_length(src))
481275970Scy			break;
482275970Scy	}
483275970Scy
484275970Scy	evbuffer_drain(src, i);
485275970Scy	return (BEV_OK);
486275970Scy}
487275970Scy
488275970Scy/* add an 'x' before each byte */
489275970Scy
490275970Scystatic enum bufferevent_filter_result
491275970Scybufferevent_output_filter(struct evbuffer *src, struct evbuffer *dst,
492275970Scy    ev_ssize_t lim, enum bufferevent_flush_mode state, void *ctx)
493275970Scy{
494275970Scy	const unsigned char *buffer;
495275970Scy	unsigned i;
496275970Scy
497275970Scy	buffer = evbuffer_pullup(src, evbuffer_get_length(src));
498275970Scy	for (i = 0; i < evbuffer_get_length(src); ++i) {
499275970Scy		evbuffer_add(dst, "x", 1);
500275970Scy		evbuffer_add(dst, buffer + i, 1);
501275970Scy	}
502275970Scy
503275970Scy	evbuffer_drain(src, evbuffer_get_length(src));
504275970Scy	return (BEV_OK);
505275970Scy}
506275970Scy
507275970Scystatic void
508275970Scytest_bufferevent_filters_impl(int use_pair)
509275970Scy{
510275970Scy	struct bufferevent *bev1 = NULL, *bev2 = NULL;
511275970Scy	struct bufferevent *bev1_base = NULL, *bev2_base = NULL;
512275970Scy	char buffer[8333];
513275970Scy	int i;
514275970Scy
515275970Scy	test_ok = 0;
516275970Scy
517275970Scy	if (use_pair) {
518275970Scy		struct bufferevent *pair[2];
519275970Scy		tt_assert(0 == bufferevent_pair_new(NULL, 0, pair));
520275970Scy		bev1 = pair[0];
521275970Scy		bev2 = pair[1];
522275970Scy	} else {
523275970Scy		bev1 = bufferevent_socket_new(NULL, pair[0], 0);
524275970Scy		bev2 = bufferevent_socket_new(NULL, pair[1], 0);
525275970Scy	}
526275970Scy	bev1_base = bev1;
527275970Scy	bev2_base = bev2;
528275970Scy
529275970Scy	for (i = 0; i < (int)sizeof(buffer); i++)
530275970Scy		buffer[i] = i;
531275970Scy
532275970Scy	bev1 = bufferevent_filter_new(bev1, NULL, bufferevent_output_filter,
533275970Scy				      BEV_OPT_CLOSE_ON_FREE, NULL, NULL);
534275970Scy
535275970Scy	bev2 = bufferevent_filter_new(bev2, bufferevent_input_filter,
536275970Scy				      NULL, BEV_OPT_CLOSE_ON_FREE, NULL, NULL);
537275970Scy	bufferevent_setcb(bev1, NULL, writecb, errorcb, NULL);
538275970Scy	bufferevent_setcb(bev2, readcb, NULL, errorcb, NULL);
539275970Scy
540275970Scy	tt_ptr_op(bufferevent_get_underlying(bev1), ==, bev1_base);
541275970Scy	tt_ptr_op(bufferevent_get_underlying(bev2), ==, bev2_base);
542275970Scy	tt_int_op(bufferevent_getfd(bev1), ==, -1);
543275970Scy	tt_int_op(bufferevent_getfd(bev2), ==, -1);
544275970Scy
545275970Scy	bufferevent_disable(bev1, EV_READ);
546275970Scy	bufferevent_enable(bev2, EV_READ);
547275970Scy	/* insert some filters */
548275970Scy	bufferevent_write(bev1, buffer, sizeof(buffer));
549275970Scy
550275970Scy	event_dispatch();
551275970Scy
552275970Scy	if (test_ok != 2)
553275970Scy		test_ok = 0;
554275970Scy
555275970Scyend:
556275970Scy	if (bev1)
557275970Scy		bufferevent_free(bev1);
558275970Scy	if (bev2)
559275970Scy		bufferevent_free(bev2);
560275970Scy
561275970Scy}
562275970Scy
563275970Scystatic void
564275970Scytest_bufferevent_filters(void)
565275970Scy{
566275970Scy	test_bufferevent_filters_impl(0);
567275970Scy}
568275970Scy
569275970Scystatic void
570275970Scytest_bufferevent_pair_filters(void)
571275970Scy{
572275970Scy	test_bufferevent_filters_impl(1);
573275970Scy}
574275970Scy
575275970Scy
576275970Scystatic void
577275970Scysender_writecb(struct bufferevent *bev, void *ctx)
578275970Scy{
579275970Scy	if (evbuffer_get_length(bufferevent_get_output(bev)) == 0) {
580275970Scy		bufferevent_disable(bev,EV_READ|EV_WRITE);
581275970Scy		TT_BLATHER(("Flushed %d: freeing it.", (int)bufferevent_getfd(bev)));
582275970Scy		bufferevent_free(bev);
583275970Scy	}
584275970Scy}
585275970Scy
586275970Scystatic void
587275970Scysender_errorcb(struct bufferevent *bev, short what, void *ctx)
588275970Scy{
589275970Scy	TT_FAIL(("Got sender error %d",(int)what));
590275970Scy}
591275970Scy
592275970Scystatic int bufferevent_connect_test_flags = 0;
593275970Scystatic int bufferevent_trigger_test_flags = 0;
594275970Scystatic int n_strings_read = 0;
595275970Scystatic int n_reads_invoked = 0;
596275970Scy
597275970Scy#define TEST_STR "Now is the time for all good events to signal for " \
598275970Scy	"the good of their protocol"
599275970Scystatic void
600275970Scylisten_cb(struct evconnlistener *listener, evutil_socket_t fd,
601275970Scy    struct sockaddr *sa, int socklen, void *arg)
602275970Scy{
603275970Scy	struct event_base *base = arg;
604275970Scy	struct bufferevent *bev;
605275970Scy	const char s[] = TEST_STR;
606275970Scy	TT_BLATHER(("Got a request on socket %d", (int)fd ));
607275970Scy	bev = bufferevent_socket_new(base, fd, bufferevent_connect_test_flags);
608275970Scy	tt_assert(bev);
609275970Scy	bufferevent_setcb(bev, NULL, sender_writecb, sender_errorcb, NULL);
610275970Scy	bufferevent_write(bev, s, sizeof(s));
611275970Scyend:
612275970Scy	;
613275970Scy}
614275970Scy
615275970Scystatic void
616275970Scyreader_eventcb(struct bufferevent *bev, short what, void *ctx)
617275970Scy{
618275970Scy	struct event_base *base = ctx;
619275970Scy	if (what & BEV_EVENT_ERROR) {
620275970Scy		perror("foobar");
621275970Scy		TT_FAIL(("got connector error %d", (int)what));
622275970Scy		return;
623275970Scy	}
624275970Scy	if (what & BEV_EVENT_CONNECTED) {
625275970Scy		TT_BLATHER(("connected on %d", (int)bufferevent_getfd(bev)));
626275970Scy		bufferevent_enable(bev, EV_READ);
627275970Scy	}
628275970Scy	if (what & BEV_EVENT_EOF) {
629275970Scy		char buf[512];
630275970Scy		size_t n;
631275970Scy		n = bufferevent_read(bev, buf, sizeof(buf)-1);
632275970Scy		tt_int_op(n, >=, 0);
633275970Scy		buf[n] = '\0';
634275970Scy		tt_str_op(buf, ==, TEST_STR);
635275970Scy		if (++n_strings_read == 2)
636275970Scy			event_base_loopexit(base, NULL);
637275970Scy		TT_BLATHER(("EOF on %d: %d strings read.",
638275970Scy			(int)bufferevent_getfd(bev), n_strings_read));
639275970Scy	}
640275970Scyend:
641275970Scy	;
642275970Scy}
643275970Scy
644275970Scystatic void
645275970Scyreader_readcb(struct bufferevent *bev, void *ctx)
646275970Scy{
647275970Scy	TT_BLATHER(("Read invoked on %d.", (int)bufferevent_getfd(bev)));
648275970Scy	n_reads_invoked++;
649275970Scy}
650275970Scy
651275970Scystatic void
652275970Scytest_bufferevent_connect(void *arg)
653275970Scy{
654275970Scy	struct basic_test_data *data = arg;
655275970Scy	struct evconnlistener *lev=NULL;
656275970Scy	struct bufferevent *bev1=NULL, *bev2=NULL;
657275970Scy	struct sockaddr_in localhost;
658275970Scy	struct sockaddr_storage ss;
659275970Scy	struct sockaddr *sa;
660275970Scy	ev_socklen_t slen;
661275970Scy
662275970Scy	int be_flags=BEV_OPT_CLOSE_ON_FREE;
663275970Scy
664275970Scy	if (strstr((char*)data->setup_data, "defer")) {
665275970Scy		be_flags |= BEV_OPT_DEFER_CALLBACKS;
666275970Scy	}
667275970Scy	if (strstr((char*)data->setup_data, "unlocked")) {
668275970Scy		be_flags |= BEV_OPT_UNLOCK_CALLBACKS;
669275970Scy	}
670275970Scy	if (strstr((char*)data->setup_data, "lock")) {
671275970Scy		be_flags |= BEV_OPT_THREADSAFE;
672275970Scy	}
673275970Scy	bufferevent_connect_test_flags = be_flags;
674275970Scy#ifdef _WIN32
675275970Scy	if (!strcmp((char*)data->setup_data, "unset_connectex")) {
676275970Scy		struct win32_extension_fns *ext =
677275970Scy		    (struct win32_extension_fns *)
678275970Scy		    event_get_win32_extension_fns_();
679275970Scy		ext->ConnectEx = NULL;
680275970Scy	}
681275970Scy#endif
682275970Scy
683275970Scy	memset(&localhost, 0, sizeof(localhost));
684275970Scy
685275970Scy	localhost.sin_port = 0; /* pick-a-port */
686275970Scy	localhost.sin_addr.s_addr = htonl(0x7f000001L);
687275970Scy	localhost.sin_family = AF_INET;
688275970Scy	sa = (struct sockaddr *)&localhost;
689275970Scy	lev = evconnlistener_new_bind(data->base, listen_cb, data->base,
690275970Scy	    LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE,
691275970Scy	    16, sa, sizeof(localhost));
692275970Scy	tt_assert(lev);
693275970Scy
694275970Scy	sa = (struct sockaddr *)&ss;
695275970Scy	slen = sizeof(ss);
696275970Scy	if (regress_get_listener_addr(lev, sa, &slen) < 0) {
697275970Scy		tt_abort_perror("getsockname");
698275970Scy	}
699275970Scy
700275970Scy	tt_assert(!evconnlistener_enable(lev));
701275970Scy	bev1 = bufferevent_socket_new(data->base, -1, be_flags);
702275970Scy	bev2 = bufferevent_socket_new(data->base, -1, be_flags);
703275970Scy	tt_assert(bev1);
704275970Scy	tt_assert(bev2);
705275970Scy	bufferevent_setcb(bev1, reader_readcb,NULL, reader_eventcb, data->base);
706275970Scy	bufferevent_setcb(bev2, reader_readcb,NULL, reader_eventcb, data->base);
707275970Scy
708275970Scy	bufferevent_enable(bev1, EV_READ);
709275970Scy	bufferevent_enable(bev2, EV_READ);
710275970Scy
711275970Scy	tt_want(!bufferevent_socket_connect(bev1, sa, sizeof(localhost)));
712275970Scy	tt_want(!bufferevent_socket_connect(bev2, sa, sizeof(localhost)));
713275970Scy
714275970Scy	event_base_dispatch(data->base);
715275970Scy
716275970Scy	tt_int_op(n_strings_read, ==, 2);
717275970Scy	tt_int_op(n_reads_invoked, >=, 2);
718275970Scyend:
719275970Scy	if (lev)
720275970Scy		evconnlistener_free(lev);
721275970Scy
722275970Scy	if (bev1)
723275970Scy		bufferevent_free(bev1);
724275970Scy
725275970Scy	if (bev2)
726275970Scy		bufferevent_free(bev2);
727275970Scy}
728275970Scy
729275970Scystatic void
730275970Scywant_fail_eventcb(struct bufferevent *bev, short what, void *ctx)
731275970Scy{
732275970Scy	struct event_base *base = ctx;
733275970Scy	const char *err;
734275970Scy	evutil_socket_t s;
735275970Scy
736275970Scy	if (what & BEV_EVENT_ERROR) {
737275970Scy		s = bufferevent_getfd(bev);
738275970Scy		err = evutil_socket_error_to_string(evutil_socket_geterror(s));
739275970Scy		TT_BLATHER(("connection failure on "EV_SOCK_FMT": %s",
740275970Scy			EV_SOCK_ARG(s), err));
741275970Scy		test_ok = 1;
742275970Scy	} else {
743275970Scy		TT_FAIL(("didn't fail? what %hd", what));
744275970Scy	}
745275970Scy
746275970Scy	event_base_loopexit(base, NULL);
747275970Scy}
748275970Scy
749275970Scystatic void
750275970Scyclose_socket_cb(evutil_socket_t fd, short what, void *arg)
751275970Scy{
752275970Scy	evutil_socket_t *fdp = arg;
753275970Scy	if (*fdp >= 0) {
754275970Scy		evutil_closesocket(*fdp);
755275970Scy		*fdp = -1;
756275970Scy	}
757275970Scy}
758275970Scy
759275970Scystatic void
760275970Scytest_bufferevent_connect_fail(void *arg)
761275970Scy{
762275970Scy	struct basic_test_data *data = (struct basic_test_data *)arg;
763275970Scy	struct bufferevent *bev=NULL;
764275970Scy	struct sockaddr_in localhost;
765275970Scy	struct sockaddr *sa = (struct sockaddr*)&localhost;
766275970Scy	evutil_socket_t fake_listener = -1;
767275970Scy	ev_socklen_t slen = sizeof(localhost);
768275970Scy	struct event close_listener_event;
769275970Scy	int close_listener_event_added = 0;
770275970Scy	struct timeval one_second = { 1, 0 };
771275970Scy	int r;
772275970Scy
773275970Scy	test_ok = 0;
774275970Scy
775275970Scy	memset(&localhost, 0, sizeof(localhost));
776275970Scy	localhost.sin_port = 0; /* have the kernel pick a port */
777275970Scy	localhost.sin_addr.s_addr = htonl(0x7f000001L);
778275970Scy	localhost.sin_family = AF_INET;
779275970Scy
780275970Scy	/* bind, but don't listen or accept. should trigger
781275970Scy	   "Connection refused" reliably on most platforms. */
782275970Scy	fake_listener = socket(localhost.sin_family, SOCK_STREAM, 0);
783275970Scy	tt_assert(fake_listener >= 0);
784275970Scy	tt_assert(bind(fake_listener, sa, slen) == 0);
785275970Scy	tt_assert(getsockname(fake_listener, sa, &slen) == 0);
786275970Scy	bev = bufferevent_socket_new(data->base, -1,
787275970Scy		BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
788275970Scy	tt_assert(bev);
789275970Scy	bufferevent_setcb(bev, NULL, NULL, want_fail_eventcb, data->base);
790275970Scy
791275970Scy	r = bufferevent_socket_connect(bev, sa, slen);
792275970Scy	/* XXXX we'd like to test the '0' case everywhere, but FreeBSD tells
793275970Scy	 * detects the error immediately, which is not really wrong of it. */
794275970Scy	tt_want(r == 0 || r == -1);
795275970Scy
796275970Scy	/* Close the listener socket after a second. This should trigger
797275970Scy	   "connection refused" on some other platforms, including OSX. */
798275970Scy	evtimer_assign(&close_listener_event, data->base, close_socket_cb,
799275970Scy	    &fake_listener);
800275970Scy	event_add(&close_listener_event, &one_second);
801275970Scy	close_listener_event_added = 1;
802275970Scy
803275970Scy	event_base_dispatch(data->base);
804275970Scy
805275970Scy	tt_int_op(test_ok, ==, 1);
806275970Scy
807275970Scyend:
808275970Scy	if (fake_listener >= 0)
809275970Scy		evutil_closesocket(fake_listener);
810275970Scy
811275970Scy	if (bev)
812275970Scy		bufferevent_free(bev);
813275970Scy
814275970Scy	if (close_listener_event_added)
815275970Scy		event_del(&close_listener_event);
816275970Scy}
817275970Scy
818275970Scystruct timeout_cb_result {
819275970Scy	struct timeval read_timeout_at;
820275970Scy	struct timeval write_timeout_at;
821275970Scy	struct timeval last_wrote_at;
822275970Scy	int n_read_timeouts;
823275970Scy	int n_write_timeouts;
824275970Scy	int total_calls;
825275970Scy};
826275970Scy
827275970Scystatic void
828275970Scybev_timeout_write_cb(struct bufferevent *bev, void *arg)
829275970Scy{
830275970Scy	struct timeout_cb_result *res = arg;
831275970Scy	evutil_gettimeofday(&res->last_wrote_at, NULL);
832275970Scy}
833275970Scy
834275970Scystatic void
835275970Scybev_timeout_event_cb(struct bufferevent *bev, short what, void *arg)
836275970Scy{
837275970Scy	struct timeout_cb_result *res = arg;
838275970Scy	++res->total_calls;
839275970Scy
840275970Scy	if ((what & (BEV_EVENT_READING|BEV_EVENT_TIMEOUT))
841275970Scy	    == (BEV_EVENT_READING|BEV_EVENT_TIMEOUT)) {
842275970Scy		evutil_gettimeofday(&res->read_timeout_at, NULL);
843275970Scy		++res->n_read_timeouts;
844275970Scy	}
845275970Scy	if ((what & (BEV_EVENT_WRITING|BEV_EVENT_TIMEOUT))
846275970Scy	    == (BEV_EVENT_WRITING|BEV_EVENT_TIMEOUT)) {
847275970Scy		evutil_gettimeofday(&res->write_timeout_at, NULL);
848275970Scy		++res->n_write_timeouts;
849275970Scy	}
850275970Scy}
851275970Scy
852275970Scystatic void
853275970Scytest_bufferevent_timeouts(void *arg)
854275970Scy{
855275970Scy	/* "arg" is a string containing "pair" and/or "filter". */
856275970Scy	struct bufferevent *bev1 = NULL, *bev2 = NULL;
857275970Scy	struct basic_test_data *data = arg;
858275970Scy	int use_pair = 0, use_filter = 0;
859275970Scy	struct timeval tv_w, tv_r, started_at;
860275970Scy	struct timeout_cb_result res1, res2;
861275970Scy	char buf[1024];
862275970Scy
863275970Scy	memset(&res1, 0, sizeof(res1));
864275970Scy	memset(&res2, 0, sizeof(res2));
865275970Scy
866275970Scy	if (strstr((char*)data->setup_data, "pair"))
867275970Scy		use_pair = 1;
868275970Scy	if (strstr((char*)data->setup_data, "filter"))
869275970Scy		use_filter = 1;
870275970Scy
871275970Scy	if (use_pair) {
872275970Scy		struct bufferevent *p[2];
873275970Scy		tt_int_op(0, ==, bufferevent_pair_new(data->base, 0, p));
874275970Scy		bev1 = p[0];
875275970Scy		bev2 = p[1];
876275970Scy	} else {
877275970Scy		bev1 = bufferevent_socket_new(data->base, data->pair[0], 0);
878275970Scy		bev2 = bufferevent_socket_new(data->base, data->pair[1], 0);
879275970Scy	}
880275970Scy
881275970Scy	tt_assert(bev1);
882275970Scy	tt_assert(bev2);
883275970Scy
884275970Scy	if (use_filter) {
885275970Scy		struct bufferevent *bevf1, *bevf2;
886275970Scy		bevf1 = bufferevent_filter_new(bev1, NULL, NULL,
887275970Scy		    BEV_OPT_CLOSE_ON_FREE, NULL, NULL);
888275970Scy		bevf2 = bufferevent_filter_new(bev2, NULL, NULL,
889275970Scy		    BEV_OPT_CLOSE_ON_FREE, NULL, NULL);
890275970Scy		tt_assert(bevf1);
891275970Scy		tt_assert(bevf2);
892275970Scy		bev1 = bevf1;
893275970Scy		bev2 = bevf2;
894275970Scy	}
895275970Scy
896275970Scy	/* Do this nice and early. */
897275970Scy	bufferevent_disable(bev2, EV_READ);
898275970Scy
899275970Scy	/* bev1 will try to write and read.  Both will time out. */
900275970Scy	evutil_gettimeofday(&started_at, NULL);
901275970Scy	tv_w.tv_sec = tv_r.tv_sec = 0;
902275970Scy	tv_w.tv_usec = 100*1000;
903275970Scy	tv_r.tv_usec = 150*1000;
904275970Scy	bufferevent_setcb(bev1, NULL, bev_timeout_write_cb,
905275970Scy	    bev_timeout_event_cb, &res1);
906275970Scy	bufferevent_setwatermark(bev1, EV_WRITE, 1024*1024+10, 0);
907275970Scy	bufferevent_set_timeouts(bev1, &tv_r, &tv_w);
908275970Scy	if (use_pair) {
909275970Scy		/* For a pair, the fact that the other side isn't reading
910275970Scy		 * makes the writer stall */
911275970Scy		bufferevent_write(bev1, "ABCDEFG", 7);
912275970Scy	} else {
913275970Scy		/* For a real socket, the kernel's TCP buffers can eat a
914275970Scy		 * fair number of bytes; make sure that at some point we
915275970Scy		 * have some bytes that will stall. */
916275970Scy		struct evbuffer *output = bufferevent_get_output(bev1);
917275970Scy		int i;
918275970Scy		memset(buf, 0xbb, sizeof(buf));
919275970Scy		for (i=0;i<1024;++i) {
920275970Scy			evbuffer_add_reference(output, buf, sizeof(buf),
921275970Scy			    NULL, NULL);
922275970Scy		}
923275970Scy	}
924275970Scy	bufferevent_enable(bev1, EV_READ|EV_WRITE);
925275970Scy
926275970Scy	/* bev2 has nothing to say, and isn't listening. */
927275970Scy	bufferevent_setcb(bev2, NULL,  bev_timeout_write_cb,
928275970Scy	    bev_timeout_event_cb, &res2);
929275970Scy	tv_w.tv_sec = tv_r.tv_sec = 0;
930275970Scy	tv_w.tv_usec = 200*1000;
931275970Scy	tv_r.tv_usec = 100*1000;
932275970Scy	bufferevent_set_timeouts(bev2, &tv_r, &tv_w);
933275970Scy	bufferevent_enable(bev2, EV_WRITE);
934275970Scy
935275970Scy	tv_r.tv_sec = 0;
936275970Scy	tv_r.tv_usec = 350000;
937275970Scy
938275970Scy	event_base_loopexit(data->base, &tv_r);
939275970Scy	event_base_dispatch(data->base);
940275970Scy
941275970Scy	/* XXXX Test that actually reading or writing a little resets the
942275970Scy	 * timeouts. */
943275970Scy
944275970Scy	/* Each buf1 timeout happens, and happens only once. */
945275970Scy	tt_want(res1.n_read_timeouts);
946275970Scy	tt_want(res1.n_write_timeouts);
947275970Scy	tt_want(res1.n_read_timeouts == 1);
948275970Scy	tt_want(res1.n_write_timeouts == 1);
949275970Scy
950275970Scy	test_timeval_diff_eq(&started_at, &res1.read_timeout_at, 150);
951275970Scy	test_timeval_diff_eq(&started_at, &res1.write_timeout_at, 100);
952275970Scy
953275970Scyend:
954275970Scy	if (bev1)
955275970Scy		bufferevent_free(bev1);
956275970Scy	if (bev2)
957275970Scy		bufferevent_free(bev2);
958275970Scy}
959275970Scy
960275970Scystatic void
961275970Scytrigger_failure_cb(evutil_socket_t fd, short what, void *ctx)
962275970Scy{
963275970Scy	TT_FAIL(("The triggered callback did not fire or the machine is really slow (try increasing timeout)."));
964275970Scy}
965275970Scy
966275970Scystatic void
967275970Scytrigger_eventcb(struct bufferevent *bev, short what, void *ctx)
968275970Scy{
969275970Scy	struct event_base *base = ctx;
970275970Scy	if (what == ~0) {
971275970Scy		TT_BLATHER(("Event successfully triggered."));
972275970Scy		event_base_loopexit(base, NULL);
973275970Scy		return;
974275970Scy	}
975275970Scy	reader_eventcb(bev, what, ctx);
976275970Scy}
977275970Scy
978275970Scystatic void
979275970Scytrigger_readcb_triggered(struct bufferevent *bev, void *ctx)
980275970Scy{
981275970Scy	TT_BLATHER(("Read successfully triggered."));
982275970Scy	n_reads_invoked++;
983275970Scy	bufferevent_trigger_event(bev, ~0, bufferevent_trigger_test_flags);
984275970Scy}
985275970Scy
986275970Scystatic void
987275970Scytrigger_readcb(struct bufferevent *bev, void *ctx)
988275970Scy{
989275970Scy	struct timeval timeout = { 30, 0 };
990275970Scy	struct event_base *base = ctx;
991275970Scy	size_t low, high, len;
992275970Scy	int expected_reads;
993275970Scy
994275970Scy	TT_BLATHER(("Read invoked on %d.", (int)bufferevent_getfd(bev)));
995275970Scy	expected_reads = ++n_reads_invoked;
996275970Scy
997275970Scy	bufferevent_setcb(bev, trigger_readcb_triggered, NULL, trigger_eventcb, ctx);
998275970Scy
999275970Scy	bufferevent_getwatermark(bev, EV_READ, &low, &high);
1000275970Scy	len = evbuffer_get_length(bufferevent_get_input(bev));
1001275970Scy
1002275970Scy	bufferevent_setwatermark(bev, EV_READ, len + 1, 0);
1003275970Scy	bufferevent_trigger(bev, EV_READ, bufferevent_trigger_test_flags);
1004275970Scy	/* no callback expected */
1005275970Scy	tt_int_op(n_reads_invoked, ==, expected_reads);
1006275970Scy
1007275970Scy	if ((bufferevent_trigger_test_flags & BEV_TRIG_DEFER_CALLBACKS) ||
1008275970Scy	    (bufferevent_connect_test_flags & BEV_OPT_DEFER_CALLBACKS)) {
1009275970Scy		/* will be deferred */
1010275970Scy	} else {
1011275970Scy		expected_reads++;
1012275970Scy	}
1013275970Scy
1014275970Scy	event_base_once(base, -1, EV_TIMEOUT, trigger_failure_cb, NULL, &timeout);
1015275970Scy
1016275970Scy	bufferevent_trigger(bev, EV_READ,
1017275970Scy	    bufferevent_trigger_test_flags | BEV_TRIG_IGNORE_WATERMARKS);
1018275970Scy	tt_int_op(n_reads_invoked, ==, expected_reads);
1019275970Scy
1020275970Scy	bufferevent_setwatermark(bev, EV_READ, low, high);
1021275970Scyend:
1022275970Scy	;
1023275970Scy}
1024275970Scy
1025275970Scystatic void
1026275970Scytest_bufferevent_trigger(void *arg)
1027275970Scy{
1028275970Scy	struct basic_test_data *data = arg;
1029275970Scy	struct evconnlistener *lev=NULL;
1030275970Scy	struct bufferevent *bev=NULL;
1031275970Scy	struct sockaddr_in localhost;
1032275970Scy	struct sockaddr_storage ss;
1033275970Scy	struct sockaddr *sa;
1034275970Scy	ev_socklen_t slen;
1035275970Scy
1036275970Scy	int be_flags=BEV_OPT_CLOSE_ON_FREE;
1037275970Scy	int trig_flags=0;
1038275970Scy
1039275970Scy	if (strstr((char*)data->setup_data, "defer")) {
1040275970Scy		be_flags |= BEV_OPT_DEFER_CALLBACKS;
1041275970Scy	}
1042275970Scy	bufferevent_connect_test_flags = be_flags;
1043275970Scy
1044275970Scy	if (strstr((char*)data->setup_data, "postpone")) {
1045275970Scy		trig_flags |= BEV_TRIG_DEFER_CALLBACKS;
1046275970Scy	}
1047275970Scy	bufferevent_trigger_test_flags = trig_flags;
1048275970Scy
1049275970Scy	memset(&localhost, 0, sizeof(localhost));
1050275970Scy
1051275970Scy	localhost.sin_port = 0; /* pick-a-port */
1052275970Scy	localhost.sin_addr.s_addr = htonl(0x7f000001L);
1053275970Scy	localhost.sin_family = AF_INET;
1054275970Scy	sa = (struct sockaddr *)&localhost;
1055275970Scy	lev = evconnlistener_new_bind(data->base, listen_cb, data->base,
1056275970Scy	    LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE,
1057275970Scy	    16, sa, sizeof(localhost));
1058275970Scy	tt_assert(lev);
1059275970Scy
1060275970Scy	sa = (struct sockaddr *)&ss;
1061275970Scy	slen = sizeof(ss);
1062275970Scy	if (regress_get_listener_addr(lev, sa, &slen) < 0) {
1063275970Scy		tt_abort_perror("getsockname");
1064275970Scy	}
1065275970Scy
1066275970Scy	tt_assert(!evconnlistener_enable(lev));
1067275970Scy	bev = bufferevent_socket_new(data->base, -1, be_flags);
1068275970Scy	tt_assert(bev);
1069275970Scy	bufferevent_setcb(bev, trigger_readcb, NULL, trigger_eventcb, data->base);
1070275970Scy
1071275970Scy	bufferevent_enable(bev, EV_READ);
1072275970Scy
1073275970Scy	tt_want(!bufferevent_socket_connect(bev, sa, sizeof(localhost)));
1074275970Scy
1075275970Scy	event_base_dispatch(data->base);
1076275970Scy
1077275970Scy	tt_int_op(n_reads_invoked, ==, 2);
1078275970Scyend:
1079275970Scy	if (lev)
1080275970Scy		evconnlistener_free(lev);
1081275970Scy
1082275970Scy	if (bev)
1083275970Scy		bufferevent_free(bev);
1084275970Scy}
1085275970Scy
1086275970Scystruct testcase_t bufferevent_testcases[] = {
1087275970Scy
1088275970Scy	LEGACY(bufferevent, TT_ISOLATED),
1089275970Scy	LEGACY(bufferevent_pair, TT_ISOLATED),
1090282408Scy#if defined(EVTHREAD_USE_PTHREADS_IMPLEMENTED)
1091282408Scy	{ "bufferevent_pair_release_lock", test_bufferevent_pair_release_lock,
1092282408Scy	  TT_FORK|TT_ISOLATED|TT_NEED_THREADS|TT_NEED_BASE|TT_LEGACY,
1093282408Scy	  &basic_setup, NULL },
1094282408Scy#endif
1095275970Scy	LEGACY(bufferevent_watermarks, TT_ISOLATED),
1096275970Scy	LEGACY(bufferevent_pair_watermarks, TT_ISOLATED),
1097275970Scy	LEGACY(bufferevent_filters, TT_ISOLATED),
1098275970Scy	LEGACY(bufferevent_pair_filters, TT_ISOLATED),
1099275970Scy	{ "bufferevent_connect", test_bufferevent_connect, TT_FORK|TT_NEED_BASE,
1100275970Scy	  &basic_setup, (void*)"" },
1101275970Scy	{ "bufferevent_connect_defer", test_bufferevent_connect,
1102275970Scy	  TT_FORK|TT_NEED_BASE, &basic_setup, (void*)"defer" },
1103275970Scy	{ "bufferevent_connect_lock", test_bufferevent_connect,
1104275970Scy	  TT_FORK|TT_NEED_BASE|TT_NEED_THREADS, &basic_setup, (void*)"lock" },
1105275970Scy	{ "bufferevent_connect_lock_defer", test_bufferevent_connect,
1106275970Scy	  TT_FORK|TT_NEED_BASE|TT_NEED_THREADS, &basic_setup,
1107275970Scy	  (void*)"defer lock" },
1108275970Scy	{ "bufferevent_connect_unlocked_cbs", test_bufferevent_connect,
1109275970Scy	  TT_FORK|TT_NEED_BASE|TT_NEED_THREADS, &basic_setup,
1110275970Scy	  (void*)"lock defer unlocked" },
1111275970Scy	{ "bufferevent_connect_fail", test_bufferevent_connect_fail,
1112275970Scy	  TT_FORK|TT_NEED_BASE, &basic_setup, NULL },
1113275970Scy	{ "bufferevent_timeout", test_bufferevent_timeouts,
1114275970Scy	  TT_FORK|TT_NEED_BASE|TT_NEED_SOCKETPAIR, &basic_setup, (void*)"" },
1115275970Scy	{ "bufferevent_timeout_pair", test_bufferevent_timeouts,
1116275970Scy	  TT_FORK|TT_NEED_BASE, &basic_setup, (void*)"pair" },
1117275970Scy	{ "bufferevent_timeout_filter", test_bufferevent_timeouts,
1118275970Scy	  TT_FORK|TT_NEED_BASE, &basic_setup, (void*)"filter" },
1119275970Scy	{ "bufferevent_timeout_filter_pair", test_bufferevent_timeouts,
1120275970Scy	  TT_FORK|TT_NEED_BASE, &basic_setup, (void*)"filter pair" },
1121275970Scy	{ "bufferevent_trigger", test_bufferevent_trigger, TT_FORK|TT_NEED_BASE,
1122275970Scy	  &basic_setup, (void*)"" },
1123275970Scy	{ "bufferevent_trigger_defer", test_bufferevent_trigger,
1124275970Scy	  TT_FORK|TT_NEED_BASE, &basic_setup, (void*)"defer" },
1125275970Scy	{ "bufferevent_trigger_postpone", test_bufferevent_trigger,
1126275970Scy	  TT_FORK|TT_NEED_BASE|TT_NEED_THREADS, &basic_setup,
1127275970Scy	  (void*)"postpone" },
1128275970Scy	{ "bufferevent_trigger_defer_postpone", test_bufferevent_trigger,
1129275970Scy	  TT_FORK|TT_NEED_BASE|TT_NEED_THREADS, &basic_setup,
1130275970Scy	  (void*)"defer postpone" },
1131275970Scy#ifdef EVENT__HAVE_LIBZ
1132275970Scy	LEGACY(bufferevent_zlib, TT_ISOLATED),
1133275970Scy#else
1134275970Scy	{ "bufferevent_zlib", NULL, TT_SKIP, NULL, NULL },
1135275970Scy#endif
1136275970Scy
1137275970Scy	END_OF_TESTCASES,
1138275970Scy};
1139275970Scy
1140275970Scystruct testcase_t bufferevent_iocp_testcases[] = {
1141275970Scy
1142275970Scy	LEGACY(bufferevent, TT_ISOLATED|TT_ENABLE_IOCP),
1143275970Scy	LEGACY(bufferevent_watermarks, TT_ISOLATED|TT_ENABLE_IOCP),
1144275970Scy	LEGACY(bufferevent_filters, TT_ISOLATED|TT_ENABLE_IOCP),
1145275970Scy	{ "bufferevent_connect", test_bufferevent_connect,
1146275970Scy	  TT_FORK|TT_NEED_BASE|TT_ENABLE_IOCP, &basic_setup, (void*)"" },
1147275970Scy	{ "bufferevent_connect_defer", test_bufferevent_connect,
1148275970Scy	  TT_FORK|TT_NEED_BASE|TT_ENABLE_IOCP, &basic_setup, (void*)"defer" },
1149275970Scy	{ "bufferevent_connect_lock", test_bufferevent_connect,
1150275970Scy	  TT_FORK|TT_NEED_BASE|TT_NEED_THREADS|TT_ENABLE_IOCP, &basic_setup,
1151275970Scy	  (void*)"lock" },
1152275970Scy	{ "bufferevent_connect_lock_defer", test_bufferevent_connect,
1153275970Scy	  TT_FORK|TT_NEED_BASE|TT_NEED_THREADS|TT_ENABLE_IOCP, &basic_setup,
1154275970Scy	  (void*)"defer lock" },
1155275970Scy	{ "bufferevent_connect_fail", test_bufferevent_connect_fail,
1156275970Scy	  TT_FORK|TT_NEED_BASE|TT_ENABLE_IOCP, &basic_setup, NULL },
1157275970Scy	{ "bufferevent_connect_nonblocking", test_bufferevent_connect,
1158275970Scy	  TT_FORK|TT_NEED_BASE|TT_ENABLE_IOCP, &basic_setup,
1159275970Scy	  (void*)"unset_connectex" },
1160275970Scy
1161275970Scy	END_OF_TESTCASES,
1162275970Scy};
1163