mirror of
https://github.com/libevent/libevent.git
synced 2025-01-20 05:02:55 +08:00
f74e7258fd
This is one of those patches which will either make matters far simpler after the bugs shake out, or will get reverted pretty quick once we realize that it is a stupid idea. We now post-process the config.h file into a new event-config.h file, whose macros are prefixed with _EVENT_ and which is thus safe for headers to include. Using this, we can define replacement timeval manipulation functions in evutil.h, and use them uniformly through our code. We can also detect which headers are needful in event.h, and include them as required. This is also the perfect time to remove the long-deprecated acconfig.h file, so that autoheader no longer warns. Should resolve the following issues: [ 1826530 ] Header files should have access to autoconf output. [ 1826545 ] acconfig.h is deprecated. [ 1826564 ] On some platforms, event.h can't be included alone. svn:r492
1124 lines
22 KiB
C
1124 lines
22 KiB
C
/*
|
|
* Copyright (c) 2003, 2004 Niels Provos <provos@citi.umich.edu>
|
|
* 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.
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
|
|
*/
|
|
|
|
#ifdef WIN32
|
|
#include <winsock2.h>
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#ifdef HAVE_SYS_TIME_H
|
|
#include <sys/time.h>
|
|
#endif
|
|
#include <sys/queue.h>
|
|
#ifndef WIN32
|
|
#include <sys/socket.h>
|
|
#include <sys/signal.h>
|
|
#include <unistd.h>
|
|
#include <netdb.h>
|
|
#endif
|
|
#include <fcntl.h>
|
|
#include <signal.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#include "event.h"
|
|
#include "evutil.h"
|
|
#include "event-internal.h"
|
|
#include "log.h"
|
|
|
|
#include "regress.h"
|
|
#include "regress.gen.h"
|
|
|
|
int pair[2];
|
|
int test_ok;
|
|
static int called;
|
|
static char wbuf[4096];
|
|
static char rbuf[4096];
|
|
static int woff;
|
|
static int roff;
|
|
static int usepersist;
|
|
static struct timeval tset;
|
|
static struct timeval tcalled;
|
|
static struct event_base *global_base;
|
|
|
|
#define TEST1 "this is a test"
|
|
#define SECONDS 1
|
|
|
|
#ifndef SHUT_WR
|
|
#define SHUT_WR 1
|
|
#endif
|
|
|
|
#ifdef WIN32
|
|
#define write(fd,buf,len) send((fd),(buf),(len),0)
|
|
#define read(fd,buf,len) recv((fd),(buf),(len),0)
|
|
#endif
|
|
|
|
void
|
|
simple_read_cb(int fd, short event, void *arg)
|
|
{
|
|
char buf[256];
|
|
int len;
|
|
|
|
len = read(fd, buf, sizeof(buf));
|
|
|
|
if (len) {
|
|
if (!called) {
|
|
if (event_add(arg, NULL) == -1)
|
|
exit(1);
|
|
}
|
|
} else if (called == 1)
|
|
test_ok = 1;
|
|
|
|
called++;
|
|
}
|
|
|
|
void
|
|
simple_write_cb(int fd, short event, void *arg)
|
|
{
|
|
int len;
|
|
|
|
len = write(fd, TEST1, strlen(TEST1) + 1);
|
|
if (len == -1)
|
|
test_ok = 0;
|
|
else
|
|
test_ok = 1;
|
|
}
|
|
|
|
void
|
|
multiple_write_cb(int fd, short event, void *arg)
|
|
{
|
|
struct event *ev = arg;
|
|
int len;
|
|
|
|
len = 128;
|
|
if (woff + len >= sizeof(wbuf))
|
|
len = sizeof(wbuf) - woff;
|
|
|
|
len = write(fd, wbuf + woff, len);
|
|
if (len == -1) {
|
|
fprintf(stderr, "%s: write\n", __func__);
|
|
if (usepersist)
|
|
event_del(ev);
|
|
return;
|
|
}
|
|
|
|
woff += len;
|
|
|
|
if (woff >= sizeof(wbuf)) {
|
|
shutdown(fd, SHUT_WR);
|
|
if (usepersist)
|
|
event_del(ev);
|
|
return;
|
|
}
|
|
|
|
if (!usepersist) {
|
|
if (event_add(ev, NULL) == -1)
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
void
|
|
multiple_read_cb(int fd, short event, void *arg)
|
|
{
|
|
struct event *ev = arg;
|
|
int len;
|
|
|
|
len = read(fd, rbuf + roff, sizeof(rbuf) - roff);
|
|
if (len == -1)
|
|
fprintf(stderr, "%s: read\n", __func__);
|
|
if (len <= 0) {
|
|
if (usepersist)
|
|
event_del(ev);
|
|
return;
|
|
}
|
|
|
|
roff += len;
|
|
if (!usepersist) {
|
|
if (event_add(ev, NULL) == -1)
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
void
|
|
timeout_cb(int fd, short event, void *arg)
|
|
{
|
|
struct timeval tv;
|
|
int diff;
|
|
|
|
gettimeofday(&tcalled, NULL);
|
|
if (evutil_timercmp(&tcalled, &tset, >))
|
|
evutil_timersub(&tcalled, &tset, &tv);
|
|
else
|
|
evutil_timersub(&tset, &tcalled, &tv);
|
|
|
|
diff = tv.tv_sec*1000 + tv.tv_usec/1000 - SECONDS * 1000;
|
|
if (diff < 0)
|
|
diff = -diff;
|
|
|
|
if (diff < 100)
|
|
test_ok = 1;
|
|
}
|
|
|
|
void
|
|
signal_cb(int fd, short event, void *arg)
|
|
{
|
|
struct event *ev = arg;
|
|
|
|
signal_del(ev);
|
|
test_ok = 1;
|
|
}
|
|
|
|
struct both {
|
|
struct event ev;
|
|
int nread;
|
|
};
|
|
|
|
void
|
|
combined_read_cb(int fd, short event, void *arg)
|
|
{
|
|
struct both *both = arg;
|
|
char buf[128];
|
|
int len;
|
|
|
|
len = read(fd, buf, sizeof(buf));
|
|
if (len == -1)
|
|
fprintf(stderr, "%s: read\n", __func__);
|
|
if (len <= 0)
|
|
return;
|
|
|
|
both->nread += len;
|
|
if (event_add(&both->ev, NULL) == -1)
|
|
exit(1);
|
|
}
|
|
|
|
void
|
|
combined_write_cb(int fd, short event, void *arg)
|
|
{
|
|
struct both *both = arg;
|
|
char buf[128];
|
|
int len;
|
|
|
|
len = sizeof(buf);
|
|
if (len > both->nread)
|
|
len = both->nread;
|
|
|
|
len = write(fd, buf, len);
|
|
if (len == -1)
|
|
fprintf(stderr, "%s: write\n", __func__);
|
|
if (len <= 0) {
|
|
shutdown(fd, SHUT_WR);
|
|
return;
|
|
}
|
|
|
|
both->nread -= len;
|
|
if (event_add(&both->ev, NULL) == -1)
|
|
exit(1);
|
|
}
|
|
|
|
/* Test infrastructure */
|
|
|
|
int
|
|
setup_test(char *name)
|
|
{
|
|
|
|
fprintf(stdout, "%s", name);
|
|
|
|
if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) {
|
|
fprintf(stderr, "%s: socketpair\n", __func__);
|
|
exit(1);
|
|
}
|
|
|
|
#ifdef HAVE_FCNTL
|
|
if (fcntl(pair[0], F_SETFL, O_NONBLOCK) == -1)
|
|
fprintf(stderr, "fcntl(O_NONBLOCK)");
|
|
|
|
if (fcntl(pair[1], F_SETFL, O_NONBLOCK) == -1)
|
|
fprintf(stderr, "fcntl(O_NONBLOCK)");
|
|
#endif
|
|
|
|
test_ok = 0;
|
|
called = 0;
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
cleanup_test(void)
|
|
{
|
|
#ifndef WIN32
|
|
close(pair[0]);
|
|
close(pair[1]);
|
|
#else
|
|
CloseHandle((HANDLE)pair[0]);
|
|
CloseHandle((HANDLE)pair[1]);
|
|
#endif
|
|
if (test_ok)
|
|
fprintf(stdout, "OK\n");
|
|
else {
|
|
fprintf(stdout, "FAILED\n");
|
|
exit(1);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
test_simpleread(void)
|
|
{
|
|
struct event ev;
|
|
|
|
/* Very simple read test */
|
|
setup_test("Simple read: ");
|
|
|
|
write(pair[0], TEST1, strlen(TEST1)+1);
|
|
shutdown(pair[0], SHUT_WR);
|
|
|
|
event_set(&ev, pair[1], EV_READ, simple_read_cb, &ev);
|
|
if (event_add(&ev, NULL) == -1)
|
|
exit(1);
|
|
event_dispatch();
|
|
|
|
cleanup_test();
|
|
}
|
|
|
|
void
|
|
test_simplewrite(void)
|
|
{
|
|
struct event ev;
|
|
|
|
/* Very simple write test */
|
|
setup_test("Simple write: ");
|
|
|
|
event_set(&ev, pair[0], EV_WRITE, simple_write_cb, &ev);
|
|
if (event_add(&ev, NULL) == -1)
|
|
exit(1);
|
|
event_dispatch();
|
|
|
|
cleanup_test();
|
|
}
|
|
|
|
void
|
|
test_multiple(void)
|
|
{
|
|
struct event ev, ev2;
|
|
int i;
|
|
|
|
/* Multiple read and write test */
|
|
setup_test("Multiple read/write: ");
|
|
memset(rbuf, 0, sizeof(rbuf));
|
|
for (i = 0; i < sizeof(wbuf); i++)
|
|
wbuf[i] = i;
|
|
|
|
roff = woff = 0;
|
|
usepersist = 0;
|
|
|
|
event_set(&ev, pair[0], EV_WRITE, multiple_write_cb, &ev);
|
|
if (event_add(&ev, NULL) == -1)
|
|
exit(1);
|
|
event_set(&ev2, pair[1], EV_READ, multiple_read_cb, &ev2);
|
|
if (event_add(&ev2, NULL) == -1)
|
|
exit(1);
|
|
event_dispatch();
|
|
|
|
if (roff == woff)
|
|
test_ok = memcmp(rbuf, wbuf, sizeof(wbuf)) == 0;
|
|
|
|
cleanup_test();
|
|
}
|
|
|
|
void
|
|
test_persistent(void)
|
|
{
|
|
struct event ev, ev2;
|
|
int i;
|
|
|
|
/* Multiple read and write test with persist */
|
|
setup_test("Persist read/write: ");
|
|
memset(rbuf, 0, sizeof(rbuf));
|
|
for (i = 0; i < sizeof(wbuf); i++)
|
|
wbuf[i] = i;
|
|
|
|
roff = woff = 0;
|
|
usepersist = 1;
|
|
|
|
event_set(&ev, pair[0], EV_WRITE|EV_PERSIST, multiple_write_cb, &ev);
|
|
if (event_add(&ev, NULL) == -1)
|
|
exit(1);
|
|
event_set(&ev2, pair[1], EV_READ|EV_PERSIST, multiple_read_cb, &ev2);
|
|
if (event_add(&ev2, NULL) == -1)
|
|
exit(1);
|
|
event_dispatch();
|
|
|
|
if (roff == woff)
|
|
test_ok = memcmp(rbuf, wbuf, sizeof(wbuf)) == 0;
|
|
|
|
cleanup_test();
|
|
}
|
|
|
|
void
|
|
test_combined(void)
|
|
{
|
|
struct both r1, r2, w1, w2;
|
|
|
|
setup_test("Combined read/write: ");
|
|
memset(&r1, 0, sizeof(r1));
|
|
memset(&r2, 0, sizeof(r2));
|
|
memset(&w1, 0, sizeof(w1));
|
|
memset(&w2, 0, sizeof(w2));
|
|
|
|
w1.nread = 4096;
|
|
w2.nread = 8192;
|
|
|
|
event_set(&r1.ev, pair[0], EV_READ, combined_read_cb, &r1);
|
|
event_set(&w1.ev, pair[0], EV_WRITE, combined_write_cb, &w1);
|
|
event_set(&r2.ev, pair[1], EV_READ, combined_read_cb, &r2);
|
|
event_set(&w2.ev, pair[1], EV_WRITE, combined_write_cb, &w2);
|
|
if (event_add(&r1.ev, NULL) == -1)
|
|
exit(1);
|
|
if (event_add(&w1.ev, NULL))
|
|
exit(1);
|
|
if (event_add(&r2.ev, NULL))
|
|
exit(1);
|
|
if (event_add(&w2.ev, NULL))
|
|
exit(1);
|
|
|
|
event_dispatch();
|
|
|
|
if (r1.nread == 8192 && r2.nread == 4096)
|
|
test_ok = 1;
|
|
|
|
cleanup_test();
|
|
}
|
|
|
|
void
|
|
test_simpletimeout(void)
|
|
{
|
|
struct timeval tv;
|
|
struct event ev;
|
|
|
|
setup_test("Simple timeout: ");
|
|
|
|
tv.tv_usec = 0;
|
|
tv.tv_sec = SECONDS;
|
|
evtimer_set(&ev, timeout_cb, NULL);
|
|
evtimer_add(&ev, &tv);
|
|
|
|
gettimeofday(&tset, NULL);
|
|
event_dispatch();
|
|
|
|
cleanup_test();
|
|
}
|
|
|
|
#ifndef WIN32
|
|
void
|
|
test_simplesignal(void)
|
|
{
|
|
struct event ev;
|
|
struct itimerval itv;
|
|
|
|
setup_test("Simple signal: ");
|
|
signal_set(&ev, SIGALRM, signal_cb, &ev);
|
|
signal_add(&ev, NULL);
|
|
|
|
memset(&itv, 0, sizeof(itv));
|
|
itv.it_value.tv_sec = 1;
|
|
if (setitimer(ITIMER_REAL, &itv, NULL) == -1)
|
|
goto skip_simplesignal;
|
|
|
|
event_dispatch();
|
|
skip_simplesignal:
|
|
if (signal_del(&ev) == -1)
|
|
test_ok = 0;
|
|
|
|
cleanup_test();
|
|
}
|
|
|
|
void
|
|
test_immediatesignal(void)
|
|
{
|
|
struct event ev;
|
|
|
|
printf("Immediate signal: ");
|
|
signal_set(&ev, SIGUSR1, signal_cb, &ev);
|
|
signal_add(&ev, NULL);
|
|
raise(SIGUSR1);
|
|
event_loop(EVLOOP_NONBLOCK);
|
|
signal_del(&ev);
|
|
cleanup_test();
|
|
}
|
|
|
|
void
|
|
test_signal_dealloc(void)
|
|
{
|
|
/* make sure that signal_event is event_del'ed and pipe closed */
|
|
struct event ev;
|
|
struct event_base *base = event_init();
|
|
printf("Signal dealloc: ");
|
|
signal_set(&ev, SIGUSR1, signal_cb, &ev);
|
|
signal_add(&ev, NULL);
|
|
signal_del(&ev);
|
|
event_base_free(base);
|
|
cleanup_test();
|
|
}
|
|
|
|
void
|
|
test_signal_pipeloss(void)
|
|
{
|
|
/* make sure that the base1 pipe is closed correctly. */
|
|
struct event_base *base1, *base2;
|
|
int pipe1;
|
|
printf("Signal pipeloss: ");
|
|
base1 = event_init();
|
|
pipe1 = base1->sig.ev_signal_pair[0];
|
|
base2 = event_init();
|
|
event_base_free(base2);
|
|
event_base_free(base1);
|
|
if (close(pipe1) != -1 || errno!=EBADF) {
|
|
/* fd must be closed, so second close gives -1, EBADF */
|
|
printf("signal pipe not closed. ");
|
|
test_ok = 0;
|
|
} else {
|
|
test_ok = 1;
|
|
}
|
|
cleanup_test();
|
|
}
|
|
|
|
/*
|
|
* make two bases to catch signals, use both of them. this only works
|
|
* for event mechanisms that use our signal pipe trick. kqueue handles
|
|
* signals internally, and it looks like the first kqueue always gets the
|
|
* signal.
|
|
*/
|
|
void
|
|
test_signal_switchbase(void)
|
|
{
|
|
struct event ev1, ev2;
|
|
struct event_base *base1, *base2;
|
|
printf("Signal switchbase: ");
|
|
base1 = event_init();
|
|
base2 = event_init();
|
|
signal_set(&ev1, SIGUSR1, signal_cb, &ev1);
|
|
signal_set(&ev2, SIGUSR1, signal_cb, &ev2);
|
|
if (event_base_set(base1, &ev1) ||
|
|
event_base_set(base2, &ev2) ||
|
|
event_add(&ev1, NULL) ||
|
|
event_add(&ev2, NULL)) {
|
|
fprintf(stderr, "%s: cannot set base, add\n", __func__);
|
|
exit(1);
|
|
}
|
|
|
|
test_ok = 0;
|
|
/* can handle signal before loop is called */
|
|
raise(SIGUSR1);
|
|
event_base_loop(base2, EVLOOP_NONBLOCK);
|
|
event_base_loop(base1, EVLOOP_NONBLOCK);
|
|
if (test_ok) {
|
|
test_ok = 0;
|
|
/* set base1 to handle signals */
|
|
event_base_loop(base1, EVLOOP_NONBLOCK);
|
|
raise(SIGUSR1);
|
|
event_base_loop(base1, EVLOOP_NONBLOCK);
|
|
event_base_loop(base2, EVLOOP_NONBLOCK);
|
|
}
|
|
event_base_free(base1);
|
|
event_base_free(base2);
|
|
cleanup_test();
|
|
}
|
|
#endif
|
|
|
|
void
|
|
test_free_active_base(void)
|
|
{
|
|
struct event_base *base1;
|
|
struct event ev1;
|
|
setup_test("Free active base: ");
|
|
base1 = event_init();
|
|
event_set(&ev1, pair[1], EV_READ, simple_read_cb, &ev1);
|
|
event_base_set(base1, &ev1);
|
|
event_add(&ev1, NULL);
|
|
/* event_del(&ev1); */
|
|
event_base_free(base1);
|
|
test_ok = 1;
|
|
cleanup_test();
|
|
}
|
|
|
|
void
|
|
test_loopexit(void)
|
|
{
|
|
struct timeval tv, tv_start, tv_end;
|
|
struct event ev;
|
|
|
|
setup_test("Loop exit: ");
|
|
|
|
tv.tv_usec = 0;
|
|
tv.tv_sec = 60*60*24;
|
|
evtimer_set(&ev, timeout_cb, NULL);
|
|
evtimer_add(&ev, &tv);
|
|
|
|
tv.tv_usec = 0;
|
|
tv.tv_sec = 1;
|
|
event_loopexit(&tv);
|
|
|
|
gettimeofday(&tv_start, NULL);
|
|
event_dispatch();
|
|
gettimeofday(&tv_end, NULL);
|
|
evutil_timersub(&tv_end, &tv_start, &tv_end);
|
|
|
|
evtimer_del(&ev);
|
|
|
|
if (tv.tv_sec < 2)
|
|
test_ok = 1;
|
|
|
|
cleanup_test();
|
|
}
|
|
|
|
void
|
|
test_evbuffer(void) {
|
|
|
|
struct evbuffer *evb = evbuffer_new();
|
|
setup_test("Testing Evbuffer: ");
|
|
|
|
evbuffer_add_printf(evb, "%s/%d", "hello", 1);
|
|
|
|
if (EVBUFFER_LENGTH(evb) == 7 &&
|
|
strcmp((char*)EVBUFFER_DATA(evb), "hello/1") == 0)
|
|
test_ok = 1;
|
|
|
|
evbuffer_free(evb);
|
|
|
|
cleanup_test();
|
|
}
|
|
|
|
void
|
|
test_evbuffer_find(void)
|
|
{
|
|
u_char* p;
|
|
char* test1 = "1234567890\r\n";
|
|
char* test2 = "1234567890\r";
|
|
#define EVBUFFER_INITIAL_LENGTH 256
|
|
char test3[EVBUFFER_INITIAL_LENGTH];
|
|
unsigned int i;
|
|
struct evbuffer * buf = evbuffer_new();
|
|
|
|
/* make sure evbuffer_find doesn't match past the end of the buffer */
|
|
fprintf(stdout, "Testing evbuffer_find 1: ");
|
|
evbuffer_add(buf, (u_char*)test1, strlen(test1));
|
|
evbuffer_drain(buf, strlen(test1));
|
|
evbuffer_add(buf, (u_char*)test2, strlen(test2));
|
|
p = evbuffer_find(buf, (u_char*)"\r\n", 2);
|
|
if (p == NULL) {
|
|
fprintf(stdout, "OK\n");
|
|
} else {
|
|
fprintf(stdout, "FAILED\n");
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
* drain the buffer and do another find; in r309 this would
|
|
* read past the allocated buffer causing a valgrind error.
|
|
*/
|
|
fprintf(stdout, "Testing evbuffer_find 2: ");
|
|
evbuffer_drain(buf, strlen(test2));
|
|
for (i = 0; i < EVBUFFER_INITIAL_LENGTH; ++i)
|
|
test3[i] = 'a';
|
|
test3[EVBUFFER_INITIAL_LENGTH - 1] = 'x';
|
|
evbuffer_add(buf, (u_char *)test3, EVBUFFER_INITIAL_LENGTH);
|
|
p = evbuffer_find(buf, (u_char *)"xy", 2);
|
|
if (p == NULL) {
|
|
printf("OK\n");
|
|
} else {
|
|
fprintf(stdout, "FAILED\n");
|
|
exit(1);
|
|
}
|
|
|
|
/* simple test for match at end of allocated buffer */
|
|
fprintf(stdout, "Testing evbuffer_find 3: ");
|
|
p = evbuffer_find(buf, (u_char *)"ax", 2);
|
|
if (p != NULL && strncmp((char*)p, "ax", 2) == 0) {
|
|
printf("OK\n");
|
|
} else {
|
|
fprintf(stdout, "FAILED\n");
|
|
exit(1);
|
|
}
|
|
|
|
evbuffer_free(buf);
|
|
}
|
|
|
|
void
|
|
readcb(struct bufferevent *bev, void *arg)
|
|
{
|
|
if (EVBUFFER_LENGTH(bev->input) == 8333) {
|
|
bufferevent_disable(bev, EV_READ);
|
|
test_ok++;
|
|
}
|
|
}
|
|
|
|
void
|
|
writecb(struct bufferevent *bev, void *arg)
|
|
{
|
|
if (EVBUFFER_LENGTH(bev->output) == 0)
|
|
test_ok++;
|
|
}
|
|
|
|
void
|
|
errorcb(struct bufferevent *bev, short what, void *arg)
|
|
{
|
|
test_ok = -2;
|
|
}
|
|
|
|
void
|
|
test_bufferevent(void)
|
|
{
|
|
struct bufferevent *bev1, *bev2;
|
|
char buffer[8333];
|
|
int i;
|
|
|
|
setup_test("Bufferevent: ");
|
|
|
|
bev1 = bufferevent_new(pair[0], readcb, writecb, errorcb, NULL);
|
|
bev2 = bufferevent_new(pair[1], readcb, writecb, errorcb, NULL);
|
|
|
|
bufferevent_disable(bev1, EV_READ);
|
|
bufferevent_enable(bev2, EV_READ);
|
|
|
|
for (i = 0; i < sizeof(buffer); i++)
|
|
buffer[i] = i;
|
|
|
|
bufferevent_write(bev1, buffer, sizeof(buffer));
|
|
|
|
event_dispatch();
|
|
|
|
bufferevent_free(bev1);
|
|
bufferevent_free(bev2);
|
|
|
|
if (test_ok != 2)
|
|
test_ok = 0;
|
|
|
|
cleanup_test();
|
|
}
|
|
|
|
struct test_pri_event {
|
|
struct event ev;
|
|
int count;
|
|
};
|
|
|
|
void
|
|
test_priorities_cb(int fd, short what, void *arg)
|
|
{
|
|
struct test_pri_event *pri = arg;
|
|
struct timeval tv;
|
|
|
|
if (pri->count == 3) {
|
|
event_loopexit(NULL);
|
|
return;
|
|
}
|
|
|
|
pri->count++;
|
|
|
|
evutil_timerclear(&tv);
|
|
event_add(&pri->ev, &tv);
|
|
}
|
|
|
|
void
|
|
test_priorities(int npriorities)
|
|
{
|
|
char buf[32];
|
|
struct test_pri_event one, two;
|
|
struct timeval tv;
|
|
|
|
snprintf(buf, sizeof(buf), "Testing Priorities %d: ", npriorities);
|
|
setup_test(buf);
|
|
|
|
event_base_priority_init(global_base, npriorities);
|
|
|
|
memset(&one, 0, sizeof(one));
|
|
memset(&two, 0, sizeof(two));
|
|
|
|
timeout_set(&one.ev, test_priorities_cb, &one);
|
|
if (event_priority_set(&one.ev, 0) == -1) {
|
|
fprintf(stderr, "%s: failed to set priority", __func__);
|
|
exit(1);
|
|
}
|
|
|
|
timeout_set(&two.ev, test_priorities_cb, &two);
|
|
if (event_priority_set(&two.ev, npriorities - 1) == -1) {
|
|
fprintf(stderr, "%s: failed to set priority", __func__);
|
|
exit(1);
|
|
}
|
|
|
|
evutil_timerclear(&tv);
|
|
|
|
if (event_add(&one.ev, &tv) == -1)
|
|
exit(1);
|
|
if (event_add(&two.ev, &tv) == -1)
|
|
exit(1);
|
|
|
|
event_dispatch();
|
|
|
|
event_del(&one.ev);
|
|
event_del(&two.ev);
|
|
|
|
if (npriorities == 1) {
|
|
if (one.count == 3 && two.count == 3)
|
|
test_ok = 1;
|
|
} else if (npriorities == 2) {
|
|
/* Two is called once because event_loopexit is priority 1 */
|
|
if (one.count == 3 && two.count == 1)
|
|
test_ok = 1;
|
|
} else {
|
|
if (one.count == 3 && two.count == 0)
|
|
test_ok = 1;
|
|
}
|
|
|
|
cleanup_test();
|
|
}
|
|
|
|
static void
|
|
test_multiple_cb(int fd, short event, void *arg)
|
|
{
|
|
if (event & EV_READ)
|
|
test_ok |= 1;
|
|
else if (event & EV_WRITE)
|
|
test_ok |= 2;
|
|
}
|
|
|
|
void
|
|
test_multiple_events_for_same_fd(void)
|
|
{
|
|
struct event e1, e2;
|
|
|
|
setup_test("Multiple events for same fd: ");
|
|
|
|
event_set(&e1, pair[0], EV_READ, test_multiple_cb, NULL);
|
|
event_add(&e1, NULL);
|
|
event_set(&e2, pair[0], EV_WRITE, test_multiple_cb, NULL);
|
|
event_add(&e2, NULL);
|
|
event_loop(EVLOOP_ONCE);
|
|
event_del(&e2);
|
|
write(pair[1], TEST1, strlen(TEST1)+1);
|
|
event_loop(EVLOOP_ONCE);
|
|
event_del(&e1);
|
|
|
|
if (test_ok != 3)
|
|
test_ok = 0;
|
|
|
|
cleanup_test();
|
|
}
|
|
|
|
int decode_int(uint32_t *pnumber, struct evbuffer *evbuf);
|
|
|
|
void
|
|
read_once_cb(int fd, short event, void *arg)
|
|
{
|
|
char buf[256];
|
|
int len;
|
|
|
|
len = read(fd, buf, sizeof(buf));
|
|
|
|
if (called) {
|
|
test_ok = 0;
|
|
} else if (len) {
|
|
/* Assumes global pair[0] can be used for writing */
|
|
write(pair[0], TEST1, strlen(TEST1)+1);
|
|
test_ok = 1;
|
|
}
|
|
|
|
called++;
|
|
}
|
|
|
|
void
|
|
test_want_only_once(void)
|
|
{
|
|
struct event ev;
|
|
struct timeval tv;
|
|
|
|
/* Very simple read test */
|
|
setup_test("Want read only once: ");
|
|
|
|
write(pair[0], TEST1, strlen(TEST1)+1);
|
|
|
|
/* Setup the loop termination */
|
|
evutil_timerclear(&tv);
|
|
tv.tv_sec = 1;
|
|
event_loopexit(&tv);
|
|
|
|
event_set(&ev, pair[1], EV_READ, read_once_cb, &ev);
|
|
if (event_add(&ev, NULL) == -1)
|
|
exit(1);
|
|
event_dispatch();
|
|
|
|
cleanup_test();
|
|
}
|
|
|
|
#define TEST_MAX_INT 6
|
|
|
|
void
|
|
evtag_int_test(void)
|
|
{
|
|
struct evbuffer *tmp = evbuffer_new();
|
|
uint32_t integers[TEST_MAX_INT] = {
|
|
0xaf0, 0x1000, 0x1, 0xdeadbeef, 0x00, 0xbef000
|
|
};
|
|
uint32_t integer;
|
|
int i;
|
|
|
|
for (i = 0; i < TEST_MAX_INT; i++) {
|
|
int oldlen, newlen;
|
|
oldlen = EVBUFFER_LENGTH(tmp);
|
|
encode_int(tmp, integers[i]);
|
|
newlen = EVBUFFER_LENGTH(tmp);
|
|
fprintf(stdout, "\t\tencoded 0x%08x with %d bytes\n",
|
|
integers[i], newlen - oldlen);
|
|
}
|
|
|
|
for (i = 0; i < TEST_MAX_INT; i++) {
|
|
if (decode_int(&integer, tmp) == -1) {
|
|
fprintf(stderr, "decode %d failed", i);
|
|
exit(1);
|
|
}
|
|
if (integer != integers[i]) {
|
|
fprintf(stderr, "got %x, wanted %x",
|
|
integer, integers[i]);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
if (EVBUFFER_LENGTH(tmp) != 0) {
|
|
fprintf(stderr, "trailing data");
|
|
exit(1);
|
|
}
|
|
evbuffer_free(tmp);
|
|
|
|
fprintf(stdout, "\t%s: OK\n", __func__);
|
|
}
|
|
|
|
void
|
|
evtag_fuzz(void)
|
|
{
|
|
u_char buffer[4096];
|
|
struct evbuffer *tmp = evbuffer_new();
|
|
struct timeval tv;
|
|
int i, j;
|
|
|
|
int not_failed = 0;
|
|
for (j = 0; j < 100; j++) {
|
|
for (i = 0; i < sizeof(buffer); i++)
|
|
buffer[i] = rand();
|
|
evbuffer_drain(tmp, -1);
|
|
evbuffer_add(tmp, buffer, sizeof(buffer));
|
|
|
|
if (evtag_unmarshal_timeval(tmp, 0, &tv) != -1)
|
|
not_failed++;
|
|
}
|
|
|
|
/* The majority of decodes should fail */
|
|
if (not_failed >= 10) {
|
|
fprintf(stderr, "evtag_unmarshal should have failed");
|
|
exit(1);
|
|
}
|
|
|
|
/* Now insert some corruption into the tag length field */
|
|
evbuffer_drain(tmp, -1);
|
|
evutil_timerclear(&tv);
|
|
tv.tv_sec = 1;
|
|
evtag_marshal_timeval(tmp, 0, &tv);
|
|
evbuffer_add(tmp, buffer, sizeof(buffer));
|
|
|
|
EVBUFFER_DATA(tmp)[1] = 0xff;
|
|
if (evtag_unmarshal_timeval(tmp, 0, &tv) != -1) {
|
|
fprintf(stderr, "evtag_unmarshal_timeval should have failed");
|
|
exit(1);
|
|
}
|
|
|
|
evbuffer_free(tmp);
|
|
|
|
fprintf(stdout, "\t%s: OK\n", __func__);
|
|
}
|
|
|
|
void
|
|
evtag_test(void)
|
|
{
|
|
fprintf(stdout, "Testing Tagging:\n");
|
|
|
|
evtag_init();
|
|
evtag_int_test();
|
|
evtag_fuzz();
|
|
|
|
fprintf(stdout, "OK\n");
|
|
}
|
|
|
|
void
|
|
rpc_test(void)
|
|
{
|
|
struct msg *msg, *msg2;
|
|
struct kill *attack;
|
|
struct run *run;
|
|
struct evbuffer *tmp = evbuffer_new();
|
|
struct timeval tv_start, tv_end;
|
|
int i;
|
|
|
|
fprintf(stdout, "Testing RPC: ");
|
|
|
|
msg = msg_new();
|
|
EVTAG_ASSIGN(msg, from_name, "niels");
|
|
EVTAG_ASSIGN(msg, to_name, "phoenix");
|
|
|
|
if (EVTAG_GET(msg, attack, &attack) == -1) {
|
|
fprintf(stderr, "Failed to set kill message.\n");
|
|
exit(1);
|
|
}
|
|
|
|
EVTAG_ASSIGN(attack, weapon, "feather");
|
|
EVTAG_ASSIGN(attack, action, "tickle");
|
|
|
|
gettimeofday(&tv_start, NULL);
|
|
for (i = 0; i < 1000; ++i) {
|
|
run = EVTAG_ADD(msg, run);
|
|
if (run == NULL) {
|
|
fprintf(stderr, "Failed to add run message.\n");
|
|
exit(1);
|
|
}
|
|
EVTAG_ASSIGN(run, how, "very fast but with some data in it");
|
|
}
|
|
|
|
if (msg_complete(msg) == -1) {
|
|
fprintf(stderr, "Failed to make complete message.\n");
|
|
exit(1);
|
|
}
|
|
|
|
evtag_marshal_msg(tmp, 0, msg);
|
|
|
|
msg2 = msg_new();
|
|
if (evtag_unmarshal_msg(tmp, 0, msg2) == -1) {
|
|
fprintf(stderr, "Failed to unmarshal message.\n");
|
|
exit(1);
|
|
}
|
|
|
|
gettimeofday(&tv_end, NULL);
|
|
evutil_timersub(&tv_end, &tv_start, &tv_end);
|
|
fprintf(stderr, "(%.1f us/add) ",
|
|
(float)tv_end.tv_sec/(float)i * 1000000.0 +
|
|
tv_end.tv_usec / (float)i);
|
|
|
|
if (!EVTAG_HAS(msg2, from_name) ||
|
|
!EVTAG_HAS(msg2, to_name) ||
|
|
!EVTAG_HAS(msg2, attack)) {
|
|
fprintf(stderr, "Missing data structures.\n");
|
|
exit(1);
|
|
}
|
|
|
|
if (EVTAG_LEN(msg2, run) != i) {
|
|
fprintf(stderr, "Wrong number of run messages.\n");
|
|
exit(1);
|
|
}
|
|
|
|
msg_free(msg);
|
|
msg_free(msg2);
|
|
|
|
evbuffer_free(tmp);
|
|
|
|
fprintf(stdout, "OK\n");
|
|
}
|
|
|
|
int
|
|
main (int argc, char **argv)
|
|
{
|
|
#ifdef WIN32
|
|
WORD wVersionRequested;
|
|
WSADATA wsaData;
|
|
int err;
|
|
|
|
wVersionRequested = MAKEWORD( 2, 2 );
|
|
|
|
err = WSAStartup( wVersionRequested, &wsaData );
|
|
#endif
|
|
|
|
setvbuf(stdout, NULL, _IONBF, 0);
|
|
|
|
/* Initalize the event library */
|
|
global_base = event_init();
|
|
|
|
/* use the global event base and need to be called first */
|
|
test_priorities(1);
|
|
test_priorities(2);
|
|
test_priorities(3);
|
|
|
|
test_evbuffer();
|
|
test_evbuffer_find();
|
|
|
|
test_bufferevent();
|
|
|
|
test_free_active_base();
|
|
|
|
http_suite();
|
|
|
|
rpc_suite();
|
|
|
|
dns_suite();
|
|
|
|
test_simpleread();
|
|
|
|
test_simplewrite();
|
|
|
|
test_multiple();
|
|
|
|
test_persistent();
|
|
|
|
test_combined();
|
|
|
|
test_simpletimeout();
|
|
#ifndef WIN32
|
|
test_simplesignal();
|
|
test_immediatesignal();
|
|
#endif
|
|
test_loopexit();
|
|
|
|
test_multiple_events_for_same_fd();
|
|
|
|
test_want_only_once();
|
|
|
|
evtag_test();
|
|
|
|
rpc_test();
|
|
|
|
#ifndef WIN32
|
|
test_signal_dealloc();
|
|
test_signal_pipeloss();
|
|
test_signal_switchbase();
|
|
#endif
|
|
|
|
return (0);
|
|
}
|
|
|