Mailing List Archive

r98 - in trunk/varnish-cache/contrib/libevent: . WIN32-Code WIN32-Prj test
Author: des
Date: 2006-04-03 13:59:08 +0200 (Mon, 03 Apr 2006)
New Revision: 98

Added:
trunk/varnish-cache/contrib/libevent/event_rpcgen.py
trunk/varnish-cache/contrib/libevent/event_tagging.c
trunk/varnish-cache/contrib/libevent/http.c
trunk/varnish-cache/contrib/libevent/http.h
trunk/varnish-cache/contrib/libevent/strlcpy.c
trunk/varnish-cache/contrib/libevent/test/regress.h
trunk/varnish-cache/contrib/libevent/test/regress.rpc
trunk/varnish-cache/contrib/libevent/test/regress_http.c
Modified:
trunk/varnish-cache/contrib/libevent/Makefile.am
trunk/varnish-cache/contrib/libevent/README
trunk/varnish-cache/contrib/libevent/WIN32-Code/win32.c
trunk/varnish-cache/contrib/libevent/WIN32-Prj/libevent.dsp
trunk/varnish-cache/contrib/libevent/buffer.c
trunk/varnish-cache/contrib/libevent/configure.in
trunk/varnish-cache/contrib/libevent/devpoll.c
trunk/varnish-cache/contrib/libevent/epoll.c
trunk/varnish-cache/contrib/libevent/evbuffer.c
trunk/varnish-cache/contrib/libevent/event.3
trunk/varnish-cache/contrib/libevent/event.c
trunk/varnish-cache/contrib/libevent/event.h
trunk/varnish-cache/contrib/libevent/kqueue.c
trunk/varnish-cache/contrib/libevent/log.h
trunk/varnish-cache/contrib/libevent/poll.c
trunk/varnish-cache/contrib/libevent/rtsig.c
trunk/varnish-cache/contrib/libevent/select.c
trunk/varnish-cache/contrib/libevent/signal.c
trunk/varnish-cache/contrib/libevent/test/Makefile.am
trunk/varnish-cache/contrib/libevent/test/regress.c
trunk/varnish-cache/contrib/libevent/test/test-weof.c
trunk/varnish-cache/contrib/libevent/test/test.sh
Log:
Update from libevent CVS trunk.

Modified: trunk/varnish-cache/contrib/libevent/Makefile.am
===================================================================
--- trunk/varnish-cache/contrib/libevent/Makefile.am 2006-04-03 11:05:53 UTC (rev 97)
+++ trunk/varnish-cache/contrib/libevent/Makefile.am 2006-04-03 11:59:08 UTC (rev 98)
@@ -2,9 +2,11 @@

SUBDIRS = . sample test

+bin_SCRIPTS = event_rpcgen.py
+
EXTRA_DIST = acconfig.h event.h event-internal.h log.h evsignal.h event.3 \
kqueue.c epoll_sub.c epoll.c select.c rtsig.c poll.c signal.c \
- devpoll.c \
+ devpoll.c event_rpcgen.py \
sample/Makefile.am sample/Makefile.in sample/event-test.c \
sample/signal-test.c sample/time-test.c \
test/Makefile.am test/Makefile.in test/bench.c test/regress.c \
@@ -21,9 +23,10 @@

lib_LTLIBRARIES = libevent.la

-libevent_la_SOURCES = event.c buffer.c evbuffer.c log.c
+libevent_la_SOURCES = event.c buffer.c evbuffer.c log.c event_tagging.c \
+ http.c http.h
libevent_la_LIBADD = @LTLIBOBJS@
-libevent_la_LDFLAGS = -release @VERSION@ -version-info 1:2:0
+libevent_la_LDFLAGS = -release @VERSION@ -version-info 1:3:0

include_HEADERS = event.h


Modified: trunk/varnish-cache/contrib/libevent/README
===================================================================
--- trunk/varnish-cache/contrib/libevent/README 2006-04-03 11:05:53 UTC (rev 97)
+++ trunk/varnish-cache/contrib/libevent/README 2006-04-03 11:59:08 UTC (rev 98)
@@ -12,6 +12,10 @@

Before, reporting any problems, please run the regression tests.

+To enable the low-level tracing build the library as:
+
+CFLAGS=-DUSE_DEBUG ./configure [...]
+
Acknowledgements:
-----------------

@@ -24,5 +28,8 @@
Mike Davis
William Ahern
Alexander von Gernler
+ Artur Grabowski
+ Stas Bekman
+ Tassilo von Parseval

If I have forgotten your name, please contact me.

Modified: trunk/varnish-cache/contrib/libevent/WIN32-Code/win32.c
===================================================================
--- trunk/varnish-cache/contrib/libevent/WIN32-Code/win32.c 2006-04-03 11:05:53 UTC (rev 97)
+++ trunk/varnish-cache/contrib/libevent/WIN32-Code/win32.c 2006-04-03 11:59:08 UTC (rev 98)
@@ -81,6 +81,7 @@
int win32_del (void *, struct event *);
int win32_recalc (struct event_base *base, void *, int);
int win32_dispatch (struct event_base *base, void *, struct timeval *);
+void win32_dealloc (void *);

struct eventop win32ops = {
"win32",
@@ -88,7 +89,8 @@
win32_insert,
win32_del,
win32_recalc,
- win32_dispatch
+ win32_dispatch,
+ win32_dealloc
};

#define FD_SET_ALLOC_SIZE(n) ((sizeof(struct win_fd_set) + ((n)-1)*sizeof(SOCKET)))
@@ -135,6 +137,8 @@
if (set->fd_count == op->fd_setsz) {
if (realloc_fd_sets(op, op->fd_setsz*2))
return (-1);
+ /* set pointer will have changed and needs reiniting! */
+ set = read ? op->readset_in : op->writeset_in;
}
set->fd_array[set->fd_count] = s;
return (set->fd_count++);
@@ -363,7 +367,28 @@
return (0);
}

+void
+win32_dealloc(void *arg)
+{
+ struct win32op *win32op = arg;

+ if (win32op->readset_in)
+ free(win32op->readset_in);
+ if (win32op->writeset_in)
+ free(win32op->writeset_in);
+ if (win32op->readset_out)
+ free(win32op->readset_out);
+ if (win32op->writeset_out)
+ free(win32op->writeset_out);
+ if (win32op->exset_out)
+ free(win32op->exset_out);
+ if (win32op->events)
+ free(win32op->events);
+
+ memset(win32op, 0, sizeof(win32op));
+ free(win32op);
+}
+
static int
signal_handler(int sig)
{

Modified: trunk/varnish-cache/contrib/libevent/WIN32-Prj/libevent.dsp
===================================================================
--- trunk/varnish-cache/contrib/libevent/WIN32-Prj/libevent.dsp 2006-04-03 11:05:53 UTC (rev 97)
+++ trunk/varnish-cache/contrib/libevent/WIN32-Prj/libevent.dsp 2006-04-03 11:59:08 UTC (rev 98)
@@ -85,7 +85,7 @@
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File

-SOURCE=..\err.c
+SOURCE=..\log.c
# End Source File
# Begin Source File


Modified: trunk/varnish-cache/contrib/libevent/buffer.c
===================================================================
--- trunk/varnish-cache/contrib/libevent/buffer.c 2006-04-03 11:05:53 UTC (rev 97)
+++ trunk/varnish-cache/contrib/libevent/buffer.c 2006-04-03 11:59:08 UTC (rev 98)
@@ -117,45 +117,55 @@
}

res = evbuffer_add(outbuf, inbuf->buffer, inbuf->off);
- if (res == 0)
+ if (res == 0) {
+ /* We drain the input buffer on success */
evbuffer_drain(inbuf, inbuf->off);
+ }

return (res);
}

int
-evbuffer_add_printf(struct evbuffer *buf, char *fmt, ...)
+evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap)
{
- int res = -1;
- char *msg;
-#ifndef HAVE_VASPRINTF
- static char buffer[4096];
-#endif
- va_list ap;
+ char *buffer;
+ size_t space;
+ size_t oldoff = buf->off;
+ int sz;

- va_start(ap, fmt);
+ for (;;) {
+ buffer = buf->buffer + buf->off;
+ space = buf->totallen - buf->misalign - buf->off;

-#ifdef HAVE_VASPRINTF
- if (vasprintf(&msg, fmt, ap) == -1)
- goto end;
+#ifdef WIN32
+ sz = vsnprintf(buffer, space - 1, fmt, ap);
+ buffer[space - 1] = '\0';
#else
-# ifdef WIN32
- _vsnprintf(buffer, sizeof(buffer) - 1, fmt, ap);
- buffer[sizeof(buffer)-1] = '\0';
-# else /* ! WIN32 */
- vsnprintf(buffer, sizeof(buffer), fmt, ap);
-# endif
- msg = buffer;
+ sz = vsnprintf(buffer, space, fmt, ap);
#endif
-
- res = strlen(msg);
- if (evbuffer_add(buf, msg, res) == -1)
- res = -1;
-#ifdef HAVE_VASPRINTF
- free(msg);
+ if (sz == -1)
+ return (-1);
+ if (sz < space) {
+ buf->off += sz;
+ if (buf->cb != NULL)
+ (*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
+ return (sz);
+ }
+ if (evbuffer_expand(buf, sz + 1) == -1)
+ return (-1);

-end:
-#endif
+ }
+ /* NOTREACHED */
+}
+
+int
+evbuffer_add_printf(struct evbuffer *buf, const char *fmt, ...)
+{
+ int res = -1;
+ va_list ap;
+
+ va_start(ap, fmt);
+ res = evbuffer_add_vprintf(buf, fmt, ap);
va_end(ap);

return (res);
@@ -184,16 +194,16 @@
char *
evbuffer_readline(struct evbuffer *buffer)
{
- char *data = EVBUFFER_DATA(buffer);
+ u_char *data = EVBUFFER_DATA(buffer);
size_t len = EVBUFFER_LENGTH(buffer);
char *line;
- int i;
+ u_int i;

for (i = 0; i < len; i++) {
if (data[i] == '\r' || data[i] == '\n')
break;
}
-
+
if (i == len)
return (NULL);

@@ -332,8 +342,21 @@
#endif

#ifdef FIONREAD
- if (ioctl(fd, FIONREAD, &n) == -1 || n == 0)
+ if (ioctl(fd, FIONREAD, &n) == -1 || n == 0) {
n = EVBUFFER_MAX_READ;
+ } else if (n > EVBUFFER_MAX_READ && n > howmuch) {
+ /*
+ * It's possible that a lot of data is available for
+ * reading. We do not want to exhaust resources
+ * before the reader has a chance to do something
+ * about it. If the reader does not tell us how much
+ * data we should read, we artifically limit it.
+ */
+ if (n > buf->totallen << 2)
+ n = buf->totallen << 2;
+ if (n < EVBUFFER_MAX_READ)
+ n = EVBUFFER_MAX_READ;
+ }
#endif
if (howmuch < 0 || howmuch > n)
howmuch = n;
@@ -397,7 +420,7 @@
}

u_char *
-evbuffer_find(struct evbuffer *buffer, u_char *what, size_t len)
+evbuffer_find(struct evbuffer *buffer, const u_char *what, size_t len)
{
size_t remain = buffer->off;
u_char *search = buffer->buffer;

Modified: trunk/varnish-cache/contrib/libevent/configure.in
===================================================================
--- trunk/varnish-cache/contrib/libevent/configure.in 2006-04-03 11:05:53 UTC (rev 97)
+++ trunk/varnish-cache/contrib/libevent/configure.in 2006-04-03 11:59:08 UTC (rev 98)
@@ -2,7 +2,7 @@
dnl Dug Song <dugsong at monkey.org>
AC_INIT(event.c)

-AM_INIT_AUTOMAKE(libevent,1.1a)
+AM_INIT_AUTOMAKE(libevent,1.2)
AM_CONFIG_HEADER(config.h)
AM_MAINTAINER_MODE

@@ -117,7 +117,8 @@
AC_HEADER_TIME

dnl Checks for library functions.
-AC_CHECK_FUNCS(gettimeofday vasprintf fcntl)
+AC_CHECK_FUNCS(gettimeofday vasprintf fcntl clock_gettime)
+AC_REPLACE_FUNCS(strlcpy)

AC_MSG_CHECKING(for F_SETFD in fcntl.h)
AC_EGREP_CPP(yes,

Modified: trunk/varnish-cache/contrib/libevent/devpoll.c
===================================================================
--- trunk/varnish-cache/contrib/libevent/devpoll.c 2006-04-03 11:05:53 UTC (rev 97)
+++ trunk/varnish-cache/contrib/libevent/devpoll.c 2006-04-03 11:59:08 UTC (rev 98)
@@ -76,6 +76,7 @@
int devpoll_del (void *, struct event *);
int devpoll_recalc (struct event_base *, void *, int);
int devpoll_dispatch (struct event_base *, void *, struct timeval *);
+void devpoll_dealloc (void *);

struct eventop devpollops = {
"devpoll",
@@ -83,7 +84,8 @@
devpoll_add,
devpoll_del,
devpoll_recalc,
- devpoll_dispatch
+ devpoll_dispatch,
+ devpoll_dealloc
};

#define NEVENT 32000
@@ -401,3 +403,21 @@

return (0);
}
+
+void
+devpoll_dealloc(void *arg)
+{
+ struct devpollop *devpollop = arg;
+
+ if (devpollop->fds)
+ free(devpollop->fds);
+ if (devpollop->events)
+ free(devpollop->events);
+ if (devpollop->changes)
+ free(devpollop->changes);
+ if (devpollop->dpfd >= 0)
+ close(devpollop->dpfd);
+
+ memset(devpollop, 0, sizeof(struct devpollop));
+ free(devpollop);
+}

Modified: trunk/varnish-cache/contrib/libevent/epoll.c
===================================================================
--- trunk/varnish-cache/contrib/libevent/epoll.c 2006-04-03 11:05:53 UTC (rev 97)
+++ trunk/varnish-cache/contrib/libevent/epoll.c 2006-04-03 11:59:08 UTC (rev 98)
@@ -76,6 +76,7 @@
int epoll_del (void *, struct event *);
int epoll_recalc (struct event_base *, void *, int);
int epoll_dispatch (struct event_base *, void *, struct timeval *);
+void epoll_dealloc (void *);

struct eventop epollops = {
"epoll",
@@ -83,7 +84,8 @@
epoll_add,
epoll_del,
epoll_recalc,
- epoll_dispatch
+ epoll_dispatch,
+ epoll_dealloc
};

#ifdef HAVE_SETFD
@@ -109,8 +111,14 @@
return (NULL);

if (getrlimit(RLIMIT_NOFILE, &rl) == 0 &&
- rl.rlim_cur != RLIM_INFINITY)
- nfiles = rl.rlim_cur;
+ rl.rlim_cur != RLIM_INFINITY) {
+ /*
+ * Solaris is somewhat retarded - it's important to drop
+ * backwards compatibility when making changes. So, don't
+ * dare to put rl.rlim_cur here.
+ */
+ nfiles = rl.rlim_cur - 1;
+ }

/* Initalize the kernel queue */

@@ -343,3 +351,19 @@

return (0);
}
+
+void
+epoll_dealloc(void *arg)
+{
+ struct epollop *epollop = arg;
+
+ if (epollop->fds)
+ free(epollop->fds);
+ if (epollop->events)
+ free(epollop->events);
+ if (epollop->epfd >= 0)
+ close(epollop->epfd);
+
+ memset(epollop, 0, sizeof(struct epollop));
+ free(epollop);
+}

Modified: trunk/varnish-cache/contrib/libevent/evbuffer.c
===================================================================
--- trunk/varnish-cache/contrib/libevent/evbuffer.c 2006-04-03 11:05:53 UTC (rev 97)
+++ trunk/varnish-cache/contrib/libevent/evbuffer.c 2006-04-03 11:59:08 UTC (rev 98)
@@ -74,7 +74,7 @@
void *arg) {
struct bufferevent *bufev = arg;
/*
- * If we are below the watermak then reschedule reading if it's
+ * If we are below the watermark then reschedule reading if it's
* still enabled.
*/
if (bufev->wm_read.high == 0 || now < bufev->wm_read.high) {
@@ -92,13 +92,21 @@
int res = 0;
short what = EVBUFFER_READ;
size_t len;
+ int howmuch = -1;

if (event == EV_TIMEOUT) {
what |= EVBUFFER_TIMEOUT;
goto error;
}

- res = evbuffer_read(bufev->input, fd, -1);
+ /*
+ * If we have a high watermark configured then we don't want to
+ * read more data than would make us reach the watermark.
+ */
+ if (bufev->wm_read.high != 0)
+ howmuch = bufev->wm_read.high;
+
+ res = evbuffer_read(bufev->input, fd, howmuch);
if (res == -1) {
if (errno == EAGAIN || errno == EINTR)
goto reschedule;
@@ -226,7 +234,12 @@

bufev->cbarg = cbarg;

- bufev->enabled = EV_READ | EV_WRITE;
+ /*
+ * Set to EV_WRITE so that using bufferevent_write is going to
+ * trigger a callback. Reading needs to be explicitly enabled
+ * because otherwise no data will be available.
+ */
+ bufev->enabled = EV_WRITE;

return (bufev);
}
@@ -372,3 +385,16 @@
bufferevent_read_pressure_cb(bufev->input,
0, EVBUFFER_LENGTH(bufev->input), bufev);
}
+
+int
+bufferevent_base_set(struct event_base *base, struct bufferevent *bufev)
+{
+ int res;
+
+ res = event_base_set(base, &bufev->ev_read);
+ if (res == -1)
+ return (res);
+
+ res = event_base_set(base, &bufev->ev_write);
+ return (res);
+}

Modified: trunk/varnish-cache/contrib/libevent/event.3
===================================================================
--- trunk/varnish-cache/contrib/libevent/event.3 2006-04-03 11:05:53 UTC (rev 97)
+++ trunk/varnish-cache/contrib/libevent/event.3 2006-04-03 11:59:08 UTC (rev 98)
@@ -34,9 +34,11 @@
.Nm event_dispatch ,
.Nm event_loop ,
.Nm event_loopexit ,
+.Nm event_set ,
+.Nm event_base_dispatch ,
.Nm event_base_loop ,
.Nm event_base_loopexit ,
-.Nm event_set ,
+.Nm event_base_set ,
.Nm event_add ,
.Nm event_del ,
.Nm event_once ,
@@ -46,12 +48,12 @@
.Nm event_priority_set ,
.Nm evtimer_set ,
.Nm evtimer_add ,
-.Nm evtimer_del
+.Nm evtimer_del ,
.Nm evtimer_pending ,
.Nm evtimer_initialized ,
.Nm signal_set ,
.Nm signal_add ,
-.Nm signal_del
+.Nm signal_del ,
.Nm signal_pending ,
.Nm signal_initialized ,
.Nm bufferevent_new ,
@@ -62,16 +64,20 @@
.Nm bufferevent_enable ,
.Nm bufferevent_disable ,
.Nm bufferevent_settimeout ,
+.Nm bufferevent_base_set ,
.Nm evbuffer_new ,
.Nm evbuffer_free ,
.Nm evbuffer_add ,
.Nm evbuffer_add_buffer ,
.Nm evbuffer_add_printf ,
+.Nm evbuffer_add_vprintf ,
.Nm evbuffer_drain ,
.Nm evbuffer_write ,
.Nm evbuffer_read ,
.Nm evbuffer_find ,
-.Nm evbuffer_readline
+.Nm evbuffer_readline ,
+.Nm evhttp_start ,
+.Nm evhttp_free
.Nd execute a function when a specific event occurs
.Sh SYNOPSIS
.Fd #include <sys/time.h>
@@ -84,13 +90,17 @@
.Fn "event_loop" "int flags"
.Ft int
.Fn "event_loopexit" "struct timeval *tv"
-.Ft int
-.Fn "event_base_loop" "struct event_base *" "int flags"
-.Ft int
-.Fn "event_base_loopexit" "struct event_base *" "struct timeval *tv"
.Ft void
.Fn "event_set" "struct event *ev" "int fd" "short event" "void (*fn)(int, short, void *)" "void *arg"
.Ft int
+.Fn "event_base_dispatch" "struct event_base *base"
+.Ft int
+.Fn "event_base_loop" "struct event_base *base" "int flags"
+.Ft int
+.Fn "event_base_loopexit" "struct event_base *base" "struct timeval *tv"
+.Ft int
+.Fn "event_base_set" "struct event_base *base" "struct event *"
+.Ft int
.Fn "event_add" "struct event *ev" "struct timeval *tv"
.Ft int
.Fn "event_del" "struct event *ev"
@@ -140,6 +150,8 @@
.Fn "bufferevent_disable" "struct bufferevent *bufev" "short event"
.Ft void
.Fn "bufferevent_settimeout" "struct bufferevent *bufev" "int timeout_read" "int timeout_write"
+.Ft int
+.Fn "bufferevent_base_set" "struct event_base *base" "struct bufferevent *bufev"
.Ft "struct evbuffer *"
.Fn "evbuffer_new" "void"
.Ft void
@@ -150,6 +162,8 @@
.Fn "evbuffer_add_buffer" "struct evbuffer *dst" "struct evbuffer *src"
.Ft int
.Fn "evbuffer_add_printf" "struct evbuffer *buf" "char *fmt" "..."
+.Ft int
+.Fn "evbuffer_add_vprintf" "struct evbuffer *buf" "const char *fmt" "va_list ap"
.Ft void
.Fn "evbuffer_drain" "struct evbuffer *buf" "size_t size"
.Ft int
@@ -160,6 +174,10 @@
.Fn "evbuffer_find" "struct evbuffer *buf" "u_char *data" "size_t size"
.Ft "char *"
.Fn "evbuffer_readline" "struct evbuffer *buf"
+.Ft "struct evhttp *"
+.Fn "evhttp_start" "const char *address" "u_short port"
+.Ft "void"
+.Fn "evhttp_free" "struct evhttp* http"
.Ft int
.Fa (*event_sigcb)(void) ;
.Ft int
@@ -193,24 +211,26 @@
to indicate that a signal has been received.
The application sets
.Va event_sigcb
-to a callback function. After the signal handler sets
+to a callback function.
+After the signal handler sets
.Va event_gotsig ,
.Nm event_dispatch
-will execute the callback function to process received signals. The
-callback returns 1 when no events are registered any more. It can
-return -1 to indicate an error to the
+will execute the callback function to process received signals.
+The callback returns 1 when no events are registered any more.
+It can return -1 to indicate an error to the
.Nm event
library, causing
.Fn event_dispatch
to terminate with
.Va errno
set to
-.Er EINTR.
+.Er EINTR .
.Pp
The
.Nm event_loop
function provides an interface for single pass execution of pending
-events. The flags
+events.
+The flags
.Va EVLOOP_ONCE
and
.Va EVLOOP_NONBLOCK
@@ -243,14 +263,14 @@
argument given in the
.Fa arg
argument.
-The
+The
.Fa fd
indicates the file descriptor that should be monitored for events.
The events can be either
.Va EV_READ ,
.Va EV_WRITE ,
-or both.
-Indicating that an application can read or write from the file descriptor
+or both,
+indicating that an application can read or write from the file descriptor
respectively without blocking.
.Pp
The function
@@ -278,18 +298,18 @@
.Fn event_del
and does not need to be reinitialized unless the function called and/or
the argument to it are to be changed.
-However, when an
+However, when an
.Fa ev
-structure has been added to libevent using
+structure has been added to libevent using
.Fn event_add
-the structure must persist until the event occurs (assuming
-.Fa EV_PERSIST
-is not set) or is removed
-using
-.Fn event_del .
+the structure must persist until the event occurs (assuming
+.Fa EV_PERSIST
+is not set) or is removed
+using
+.Fn event_del .
You may not reuse the same
-.Fa ev
-structure for multiple monitored descriptors; each descriptor
+.Fa ev
+structure for multiple monitored descriptors; each descriptor
needs its own
.Fa ev .
.Pp
@@ -297,13 +317,15 @@
.Fn event_add
schedules the execution of the
.Fa ev
-event when the event specified in
+event when the event specified in
.Fn event_set
occurs or in at least the time specified in the
.Fa tv .
If
.Fa tv
-is NULL, no timeout occurs and the function will only be called
+is
+.Dv NULL ,
+no timeout occurs and the function will only be called
if a matching event occurs on the file descriptor.
The event in the
.Fa ev
@@ -335,7 +357,7 @@
structure.
This function supports
.Fa EV_TIMEOUT ,
-.Fa EV_READ
+.Fa EV_READ ,
and
.Fa EV_WRITE .
.Pp
@@ -346,10 +368,10 @@
is pending to run.
If
.Va EV_TIMEOUT
-was specified and
+was specified and
.Fa tv
is not
-.Va NULL ,
+.Dv NULL ,
the expiration time of the event will be returned in
.Fa tv .
.Pp
@@ -365,10 +387,9 @@
and
.Fn evtimer_pending
are abbreviations for common situations where only a timeout is required.
-The file descriptor passed will be -1, and the event type will be
+The file descriptor passed will be \-1, and the event type will be
.Va EV_TIMEOUT .
.Pp
-.Pp
The functions
.Fn signal_set ,
.Fn signal_add ,
@@ -385,23 +406,23 @@
.Va EV_PERSIST .
.Pp
It is possible to disable support for
-.Va epoll , kqueue , devpoll, poll
+.Va epoll , kqueue , devpoll , poll
or
.Va select
by setting the environment variable
-.Va EVENT_NOEPOLL , EVENT_NOKQUEUE , EVENT_NODEVPOLL, EVENT_NOPOLL
+.Va EVENT_NOEPOLL , EVENT_NOKQUEUE , EVENT_NODEVPOLL , EVENT_NOPOLL
or
-.Va EVENT_NOSELECT .
+.Va EVENT_NOSELECT ,
+respectively.
By setting the environment variable
.Va EVENT_SHOW_METHOD ,
.Nm libevent
displays the kernel notification method that it uses.
-.Pp
.Sh EVENT PRIORITIES
By default
.Nm libevent
schedules all active events with the same priority.
-However, sometime it is desirable to process some events with a higher
+However, sometimes it is desirable to process some events with a higher
priority than others.
For that reason,
.Nm libevent
@@ -421,7 +442,6 @@
.Nm libevent
assigns the middle priority to all events unless their priority
is explicitly set.
-.Pp
.Sh THREAD SAFE EVENTS
.Nm Libevent
has experimental support for thread-safe events.
@@ -429,24 +449,27 @@
.Fn event_init ,
an event base is returned.
This event base can be used in conjunction with calls to
-.Fn event_base_set
+.Fn event_base_set ,
.Fn event_base_dispatch ,
.Fn event_base_loop ,
+.Fn event_base_loopexit ,
and
-.Fn event_base_loopexit .
-.Fn event_base_set
+.Fn bufferevent_base_set .
+.Fn event_base_set
should be called after preparing an event with
-.Fn event_set ,
+.Fn event_set ,
as
-.Fn event_set
+.Fn event_set
assigns the provided event to the most recently created event base.
-.Pp
+.Fn bufferevent_base_set
+should be called after preparing a bufferevent with
+.Fn bufferevent_new .
.Sh BUFFERED EVENTS
.Nm libevent
provides an abstraction on top of the regular event callbacks.
This abstraction is called a
.Va "buffered event" .
-A buffered event provides input and output buffer that get filled
+A buffered event provides input and output buffers that get filled
and drained automatically.
The user of a buffered event no longer deals directly with the IO,
but instead is reading from input and writing to output buffers.
@@ -454,21 +477,33 @@
A new bufferevent is created by
.Fn bufferevent_new .
The parameter
-.Fa "fd"
+.Fa fd
specifies the file descriptor from which data is read and written to.
-This file descriptor is not allowed to be a
+This file descriptor is not allowed to be a
.Xr pipe 2 .
The next three parameters are callbacks.
-The read and write callback have the following form
+The read and write callback have the following form:
.Ft void
-.Fn "(*cb)" "struct bufferevent *bufev" "void *arg"
+.Fn "(*cb)" "struct bufferevent *bufev" "void *arg" .
+The error callback has the following form:
+.Ft void
+.Fn "(*cb)" "struct bufferevent *bufev" "short what" "void *arg" .
The argument is specified by the fourth parameter
.Fa "cbarg" .
+A
+.Fa bufferevent struct
+pointer is returned on success, NULL on error.
.Pp
-By default the buffered event is read enabled and will try to read
-from the file descriptor.
-The write callback is executed whenever the output buffer is drained
-below the write low watermark which is
+Once initialized, the bufferevent structure can be used repeatedly with
+bufferevent_enable() and bufferevent_disable(). The flags parameter can
+be a combination of
+.Va EV_READ
+and
+.Va EV_WRITE .
+When read enabled the bufferevent will try to read from the file
+descriptor and call the read callback. The write callback is executed
+whenever the output buffer is drained below the write low watermark,
+which is
.Va 0
by default.
.Pp
@@ -482,18 +517,43 @@
function is used to read data from the input buffer.
Both functions return the amount of data written or read.
.Pp
+If multiple bases are in use, bufferevent_base_set() must be called before
+enabling the bufferevent for the first time.
+.Sh NON-BLOCKING HTTP SUPPORT
+.Nm libevent
+provides a very thin HTTP layer that can be used both to host an HTTP
+server and also to make HTTP requests.
+An HTTP server can be created by calling
+.Fn evhttp_start .
+When the HTTP server is no longer used, it can be freed via
+.Fn evhttp_free .
+.Pp
+To be notified of HTTP requests, a user needs to register callbacks with the
+HTTP server.
+This can be done by calling
+.Fn evhttp_set_cb .
+The second argument is the URI for which a callback is being registered.
+The corresponding callback will receive an
+.Va struct evhttp_request
+object that contains all information about the request.
+.Pp
+This section does not document all the possible function calls, please
+check
+.Va event.h
+for the public interfaces.
.Sh RETURN VALUES
Upon successful completion
.Fn event_add
and
.Fn event_del
return 0.
-Otherwise, -1 is returned and the global variable errno is
+Otherwise, \-1 is returned and the global variable errno is
set to indicate the error.
.Sh SEE ALSO
-.Xr timeout 9 ,
+.Xr kqueue 2 ,
+.Xr poll 2 ,
.Xr select 2 ,
-.Xr kqueue 2
+.Xr timeout 9
.Sh HISTORY
The
.Nm event
@@ -508,7 +568,6 @@
The
.Nm event
library was written by Niels Provos.
-.Pp
.Sh BUGS
This documentation is neither complete nor authoritative.
If you are in doubt about the usage of this API then

Modified: trunk/varnish-cache/contrib/libevent/event.c
===================================================================
--- trunk/varnish-cache/contrib/libevent/event.c 2006-04-03 11:05:53 UTC (rev 97)
+++ trunk/varnish-cache/contrib/libevent/event.c 2006-04-03 11:59:08 UTC (rev 98)
@@ -24,7 +24,9 @@
* (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 HAVE_CONFIG_H
#include "config.h"
+#endif

#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
@@ -46,6 +48,7 @@
#include <unistd.h>
#endif
#include <errno.h>
+#include <signal.h>
#include <string.h>
#include <assert.h>

@@ -107,8 +110,8 @@
struct event_base *current_base = NULL;

/* Handle signals - This is a deprecated interface */
-int (*event_sigcb)(void); /* Signal callback when gotsig is set */
-volatile int event_gotsig; /* Set in signal handler */
+int (*event_sigcb)(void); /* Signal callback when gotsig is set */
+volatile sig_atomic_t event_gotsig; /* Set in signal handler */

/* Prototypes */
static void event_queue_insert(struct event_base *, struct event *, int);
@@ -135,6 +138,23 @@
return (0);
}

+static int
+gettime(struct timeval *tp)
+{
+#ifdef HAVE_CLOCK_GETTIME
+ struct timespec ts;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
+ return (-1);
+ tp->tv_sec = ts.tv_sec;
+ tp->tv_usec = ts.tv_nsec / 1000;
+#else
+ gettimeofday(tp, NULL);
+#endif
+
+ return (0);
+}
+
RB_PROTOTYPE(event_tree, event, ev_timeout_node, compare);

RB_GENERATE(event_tree, event, ev_timeout_node, compare);
@@ -150,7 +170,7 @@

event_sigcb = NULL;
event_gotsig = 0;
- gettimeofday(&current_base->event_tv, NULL);
+ gettime(&current_base->event_tv);

RB_INIT(&current_base->timetree);
TAILQ_INIT(&current_base->eventqueue);
@@ -176,6 +196,33 @@
return (current_base);
}

+void
+event_base_free(struct event_base *base)
+{
+ int i;
+
+ if (base == NULL && current_base)
+ base = current_base;
+ if (base == current_base)
+ current_base = NULL;
+
+ assert(base);
+ assert(TAILQ_EMPTY(&base->eventqueue));
+ for (i=0; i < base->nactivequeues; ++i)
+ assert(TAILQ_EMPTY(base->activequeues[i]));
+
+ assert(RB_EMPTY(&base->timetree));
+
+ for (i = 0; i < base->nactivequeues; ++i)
+ free(base->activequeues[i]);
+ free(base->activequeues);
+
+ if (base->evsel->dealloc != NULL)
+ base->evsel->dealloc(base->evbase);
+
+ free(base);
+}
+
int
event_priority_init(int npriorities)
{
@@ -313,12 +360,12 @@
struct timeval tv;
int res, done;

- /* Calculate the initial events that we are waiting for */
- if (evsel->recalc(base, evbase, 0) == -1)
- return (-1);
-
done = 0;
while (!done) {
+ /* Calculate the initial events that we are waiting for */
+ if (evsel->recalc(base, evbase, 0) == -1)
+ return (-1);
+
/* Terminate the loop if we have been asked to */
if (base->event_gotterm) {
base->event_gotterm = 0;
@@ -338,7 +385,7 @@
}

/* Check if time is running backwards */
- gettimeofday(&tv, NULL);
+ gettime(&tv);
if (timercmp(&tv, &base->event_tv, <)) {
struct timeval off;
event_debug(("%s: time is running backwards, corrected",
@@ -372,9 +419,6 @@
done = 1;
} else if (flags & EVLOOP_NONBLOCK)
done = 1;
-
- if (evsel->recalc(base, evbase, 0) == -1)
- return (-1);
}

event_debug(("%s: asked to terminate loop.", __func__));
@@ -499,6 +543,7 @@
int
event_pending(struct event *ev, short event, struct timeval *tv)
{
+ struct timeval now, res;
int flags = 0;

if (ev->ev_flags & EVLIST_INSERTED)
@@ -513,8 +558,13 @@
event &= (EV_TIMEOUT|EV_READ|EV_WRITE|EV_SIGNAL);

/* See if there is a timeout that we should report */
- if (tv != NULL && (flags & event & EV_TIMEOUT))
- *tv = ev->ev_timeout;
+ if (tv != NULL && (flags & event & EV_TIMEOUT)) {
+ gettime(&now);
+ timersub(&ev->ev_timeout, &now, &res);
+ /* correctly remap to real time */
+ gettimeofday(&now, NULL);
+ timeradd(&now, &res, tv);
+ }

return (flags & event);
}
@@ -558,7 +608,7 @@
event_queue_remove(base, ev, EVLIST_ACTIVE);
}

- gettimeofday(&now, NULL);
+ gettime(&now);
timeradd(&now, tv, &ev->ev_timeout);

event_debug((
@@ -654,7 +704,7 @@
return (0);
}

- if (gettimeofday(&now, NULL) == -1)
+ if (gettime(&now) == -1)
return (-1);

if (timercmp(&ev->ev_timeout, &now, <=)) {
@@ -690,7 +740,7 @@
struct timeval now;
struct event *ev, *next;

- gettimeofday(&now, NULL);
+ gettime(&now);

for (ev = RB_MIN(event_tree, &base->timetree); ev; ev = next) {
if (timercmp(&ev->ev_timeout, &now, >))

Modified: trunk/varnish-cache/contrib/libevent/event.h
===================================================================
--- trunk/varnish-cache/contrib/libevent/event.h 2006-04-03 11:05:53 UTC (rev 97)
+++ trunk/varnish-cache/contrib/libevent/event.h 2006-04-03 11:59:08 UTC (rev 98)
@@ -31,6 +31,8 @@
extern "C" {
#endif

+#include <stdarg.h>
+
#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
@@ -101,11 +103,25 @@
#define EVENT_SIGNAL(ev) (int)ev->ev_fd
#define EVENT_FD(ev) (int)ev->ev_fd

+/*
+ * Key-Value pairs. Can be used for HTTP headers but also for
+ * query argument parsing.
+ */
+struct evkeyval {
+ TAILQ_ENTRY(evkeyval) next;
+
+ char *key;
+ char *value;
+};
+
#ifdef _EVENT_DEFINED_TQENTRY
#undef TAILQ_ENTRY
+struct event_list;
+struct evkeyvalq;
#undef _EVENT_DEFINED_TQENTRY
#else
TAILQ_HEAD (event_list, event);
+TAILQ_HEAD (evkeyvalq, evkeyval);
#endif /* _EVENT_DEFINED_TQENTRY */
#ifdef _EVENT_DEFINED_RBENTRY
#undef RB_ENTRY
@@ -119,6 +135,7 @@
int (*del)(void *, struct event *);
int (*recalc)(struct event_base *, void *, int);
int (*dispatch)(struct event_base *, void *, struct timeval *);
+ void (*dealloc)(void *);
};

#define TIMEOUT_DEFAULT {5, 0}
@@ -126,6 +143,7 @@
void *event_init(void);
int event_dispatch(void);
int event_base_dispatch(struct event_base *);
+void event_base_free(struct event_base *);

#define _EVENT_LOG_DEBUG 0
#define _EVENT_LOG_MSG 1
@@ -263,13 +281,115 @@
int evbuffer_remove(struct evbuffer *, void *, size_t);
char *evbuffer_readline(struct evbuffer *);
int evbuffer_add_buffer(struct evbuffer *, struct evbuffer *);
-int evbuffer_add_printf(struct evbuffer *, char *fmt, ...);
+int evbuffer_add_printf(struct evbuffer *, const char *fmt, ...);
+int evbuffer_add_vprintf(struct evbuffer *, const char *fmt, va_list ap);
void evbuffer_drain(struct evbuffer *, size_t);
int evbuffer_write(struct evbuffer *, int);
int evbuffer_read(struct evbuffer *, int, int);
-u_char *evbuffer_find(struct evbuffer *, u_char *, size_t);
+u_char *evbuffer_find(struct evbuffer *, const u_char *, size_t);
void evbuffer_setcb(struct evbuffer *, void (*)(struct evbuffer *, size_t, size_t, void *), void *);

+/*
+ * Marshaling tagged data - We assume that all tags are inserted in their
+ * numeric order - so that unknown tags will always be higher than the
+ * known ones - and we can just ignore the end of an event buffer.
+ */
+
+void evtag_init(void);
+
+void evtag_marshal(struct evbuffer *evbuf, u_int8_t tag, const void *data,
+ u_int16_t len);
+
+void encode_int(struct evbuffer *evbuf, u_int32_t number);
+
+void evtag_marshal_int(struct evbuffer *evbuf, u_int8_t tag,
+ u_int32_t integer);
+
+void evtag_marshal_string(struct evbuffer *buf, u_int8_t tag,
+ const char *string);
+
+void evtag_marshal_timeval(struct evbuffer *evbuf, u_int8_t tag,
+ struct timeval *tv);
+
+void evtag_test(void);
+
+int evtag_unmarshal(struct evbuffer *src, u_int8_t *ptag,
+ struct evbuffer *dst);
+int evtag_peek(struct evbuffer *evbuf, u_int8_t *ptag);
+int evtag_peek_length(struct evbuffer *evbuf, u_int32_t *plength);
+int evtag_payload_length(struct evbuffer *evbuf, u_int32_t *plength);
+int evtag_consume(struct evbuffer *evbuf);
+
+int evtag_unmarshal_int(struct evbuffer *evbuf, u_int8_t need_tag,
+ u_int32_t *pinteger);
+
+int evtag_unmarshal_fixed(struct evbuffer *src, u_int8_t need_tag, void *data,
+ size_t len);
+
+int evtag_unmarshal_string(struct evbuffer *evbuf, u_int8_t need_tag,
+ char **pstring);
+
+int evtag_unmarshal_timeval(struct evbuffer *evbuf, u_int8_t need_tag,
+ struct timeval *ptv);
+
+/*
+ * Basic support for HTTP serving.
+ *
+ * As libevent is a library for dealing with event notification and most
+ * interesting applications are networked today, I have often found the
+ * need to write HTTP code. The following prototypes and definitions provide
+ * an application with a minimal interface for making HTTP requests and for
+ * creating a very simple HTTP server.
+ */
+
+/* Response codes */
+#define HTTP_OK 200
+#define HTTP_MOVEPERM 301
+#define HTTP_MOVETEMP 302
+#define HTTP_NOTFOUND 404
+
+struct evhttp;
+struct evhttp_request;
+
+/* Start an HTTP server on the specified address and port */
+struct evhttp *evhttp_start(const char *address, u_short port);
+
+/*
+ * Free the previously create HTTP server. Works only if no requests are
+ * currently being served.
+ */
+void evhttp_free(struct evhttp* http);
+
+/* Set a callback for a specified URI */
+void evhttp_set_cb(struct evhttp *, const char *,
+ void (*)(struct evhttp_request *, void *), void *);
+
+/* Set a callback for all requests that are not caught by specific callbacks */
+void evhttp_set_gencb(struct evhttp *,
+ void (*)(struct evhttp_request *, void *), void *);
+
+void evhttp_send_error(struct evhttp_request *, int, const char *);
+void evhttp_send_reply(struct evhttp_request *, int, const char *,
+ struct evbuffer *);
+
+/* Interfaces for making requests */
+enum evhttp_cmd_type { EVHTTP_REQ_GET, EVHTTP_REQ_POST, EVHTTP_REQ_HEAD };
+
+struct evhttp_request *evhttp_request_new(
+ void (*cb)(struct evhttp_request *, void *), void *arg);
+void evhttp_request_free(struct evhttp_request *req);
+const char *evhttp_request_uri(struct evhttp_request *req);
+
+/* Interfaces for dealing with HTTP headers */
+
+const char *evhttp_find_header(struct evkeyvalq *, const char *);
+int evhttp_remove_header(struct evkeyvalq *, const char *);
+int evhttp_add_header(struct evkeyvalq *, const char *, const char *);
+void evhttp_clear_headers(struct evkeyvalq *);
+
+/* Miscellaneous utility functions */
+void evhttp_parse_query(const char *uri, struct evkeyvalq *);
+char *evhttp_htmlescape(const char *html);
#ifdef __cplusplus
}
#endif

Added: trunk/varnish-cache/contrib/libevent/event_rpcgen.py
===================================================================
--- trunk/varnish-cache/contrib/libevent/event_rpcgen.py 2006-04-03 11:05:53 UTC (rev 97)
+++ trunk/varnish-cache/contrib/libevent/event_rpcgen.py 2006-04-03 11:59:08 UTC (rev 98)
@@ -0,0 +1,1378 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2005 Niels Provos <provos at citi.umich.edu>
+# All rights reserved.
+#
+# Generates marshalling code based on libevent.
+
+import sys
+import re
+
+#
+_NAME = "event_rpcgen.py"
+_VERSION = "0.1"
+_STRUCT_RE = '[a-z][a-z_0-9]*'
+
+# Globals
+line_count = 0
+
+leading = re.compile(r'^\s+')
+trailing = re.compile(r'\s+$')
+white = re.compile(r'^\s+')
+cppcomment = re.compile(r'\/\/.*$')
+cppdirect = []
+
+# Holds everything that makes a struct
+class Struct:
+ def __init__(self, name):
+ self._name = name
+ self._entries = []
+ self._tags = {}
+ print >>sys.stderr, ' Created struct: %s' % name
+
+ def AddEntry(self, entry):
+ if self._tags.has_key(entry.Tag()):
+ print >>sys.stderr, ( 'Entry "%s" duplicates tag number '
+ '%d from "%s" around line %d' ) % (
+ entry.Name(), entry.Tag(),
+ self._tags[entry.Tag()], line_count)
+ sys.exit(1)
+ self._entries.append(entry)
+ self._tags[entry.Tag()] = entry.Name()
+ print >>sys.stderr, ' Added entry: %s' % entry.Name()
+
+ def Name(self):
+ return self._name
+
+ def EntryTagName(self, entry):
+ """Creates the name inside an enumeration for distinguishing data
+ types."""
+ name = "%s_%s" % (self._name, entry.Name())
+ return name.upper()
+
+ def PrintIdented(self, file, ident, code):
+ """Takes an array, add indentation to each entry and prints it."""
+ for entry in code:
+ print >>file, '%s%s' % (ident, entry)
+
+ def PrintTags(self, file):
+ """Prints the tag definitions for a structure."""
+ print >>file, '/* Tag definition for %s */' % self._name
+ print >>file, 'enum %s_ {' % self._name.lower()
+ for entry in self._entries:
+ print >>file, ' %s=%d,' % (self.EntryTagName(entry),
+ entry.Tag())
+ print >>file, ' %s_MAX_TAGS' % (self._name.upper())
+ print >>file, '} %s_tags;\n' % (self._name.lower())
+
+ def PrintForwardDeclaration(self, file):
+ print >>file, 'struct %s;' % self._name
+
+ def PrintDeclaration(self, file):
+ print >>file, '/* Structure declaration for %s */' % self._name
+ print >>file, 'struct %s {' % self._name
+ for entry in self._entries:
+ dcl = entry.Declaration()
+ dcl.extend(
+ entry.AssignDeclaration('(*%s_assign)' % entry.Name()))
+ dcl.extend(
+ entry.GetDeclaration('(*%s_get)' % entry.Name()))
+ if entry.Array():
+ dcl.extend(
+ entry.AddDeclaration('(*%s_add)' % entry.Name()))
+ self.PrintIdented(file, ' ', dcl)
+ print >>file, ''
+ for entry in self._entries:
+ print >>file, ' u_int8_t %s_set;' % entry.Name()
+ print >>file, '};\n'
+
+ print >>file, (
+ 'struct %s *%s_new();\n' % (self._name, self._name) +
+ 'void %s_free(struct %s *);\n' % (self._name, self._name) +
+ 'void %s_clear(struct %s *);\n' % (self._name, self._name) +
+ 'void %s_marshal(struct evbuffer *, const struct %s *);\n' % (
+ self._name, self._name) +
+ 'int %s_unmarshal(struct %s *, struct evbuffer *);\n' % (
+ self._name, self._name) +
+ 'int %s_complete(struct %s *);' % (self._name, self._name)
+ )
+ print >>file, ('void evtag_marshal_%s(struct evbuffer *, u_int8_t, '
+ 'const struct %s *);') % ( self._name, self._name)
+ print >>file, ('int evtag_unmarshal_%s(struct evbuffer *, u_int8_t, '
+ 'struct %s *);') % ( self._name, self._name)
+
+ # Write a setting function of every variable
+ for entry in self._entries:
+ self.PrintIdented(file, '', entry.AssignDeclaration(
+ entry.AssignFuncName()))
+ self.PrintIdented(file, '', entry.GetDeclaration(
+ entry.GetFuncName()))
+ if entry.Array():
+ self.PrintIdented(file, '', entry.AddDeclaration(
+ entry.AddFuncName()))
+
+ print >>file, '/* --- %s done --- */\n' % self._name
+
+ def PrintCode(self, file):
+ print >>file, ('/*\n'
+ ' * Implementation of %s\n'
+ ' */\n') % self._name
+
+ # Creation
+ print >>file, ( 'struct %s *\n' % self._name +
+ '%s_new()\n' % self._name +
+ '{\n'
+ ' struct %s *tmp;\n' % self._name +
+ ' if ((tmp = malloc(sizeof(struct %s))) == NULL) {\n'
+ ' event_warn("%%s: malloc", __func__);\n'
+ ' return (NULL);\n' % self._name +
+ ' }'
+ )
+ for entry in self._entries:
+ self.PrintIdented(file, ' ', entry.CodeNew('tmp'))
+ print >>file, ' tmp->%s_set = 0;\n' % entry.Name()
+
+ print >>file, (' return (tmp);\n'
+ '}\n')
+
+ # Adding
+ for entry in self._entries:
+ if entry.Array():
+ self.PrintIdented(file, '', entry.CodeAdd())
+ print >>file, ''
+
+ # Assigning
+ for entry in self._entries:
+ self.PrintIdented(file, '', entry.CodeAssign())
+ print >>file, ''
+
+ # Getting
+ for entry in self._entries:
+ self.PrintIdented(file, '', entry.CodeGet())
+ print >>file, ''
+
+ # Clearing
+ print >>file, ( 'void\n'
+ '%s_clear(struct %s *tmp)\n' % (
+ self._name, self._name)+
+ '{'
+ )
+ for entry in self._entries:
+ self.PrintIdented(file, ' ', entry.CodeClear('tmp'))
+
+ print >>file, '}\n'
+
+ # Freeing
+ print >>file, ( 'void\n'
+ '%s_free(struct %s *tmp)\n' % (
+ self._name, self._name)+
+ '{'
+ )
+ for entry in self._entries:
+ self.PrintIdented(file, ' ', entry.CodeFree('tmp'))
+
+ print >>file, (' free(tmp);\n'
+ '}\n')
+
+ # Marshaling
+ print >>file, ('void\n'
+ '%s_marshal(struct evbuffer *evbuf, '
+ 'const struct %s *tmp)' % (self._name, self._name) +
+ '{')
+ for entry in self._entries:
+ indent = ' '
+ # Optional entries do not have to be set
+ if entry.Optional():
+ indent += ' '
+ print >>file, ' if (tmp->%s_set) {' % entry.Name()
+ self.PrintIdented(
+ file, indent,
+ entry.CodeMarshal('evbuf', self.EntryTagName(entry), 'tmp'))
+ if entry.Optional():
+ print >>file, ' }'
+
+ print >>file, '}\n'
+
+ # Unmarshaling
+ print >>file, ('int\n'
+ '%s_unmarshal(struct %s *tmp, '
+ ' struct evbuffer *evbuf)\n' % (
+ self._name, self._name) +
+ '{\n'
+ ' u_int8_t tag;\n'
+ ' while (EVBUFFER_LENGTH(evbuf) > 0) {\n'
+ ' if (evtag_peek(evbuf, &tag) == -1)\n'
+ ' return (-1);\n'
+ ' switch (tag) {\n'
+ )
+ for entry in self._entries:
+ print >>file, ' case %s:\n' % self.EntryTagName(entry)
+ if not entry.Array():
+ print >>file, (
+ ' if (tmp->%s_set)\n'
+ ' return (-1);'
+ ) % (entry.Name())
+
+ self.PrintIdented(
+ file, ' ',
+ entry.CodeUnmarshal('evbuf',
+ self.EntryTagName(entry), 'tmp'))
+
+ print >>file, ( ' tmp->%s_set = 1;\n' % entry.Name() +
+ ' break;\n' )
+ print >>file, ( ' default:\n'
+ ' return -1;\n'
+ ' }\n'
+ ' }\n' )
+ # Check if it was decoded completely
+ print >>file, ( ' if (%s_complete(tmp) == -1)\n' % self._name +
+ ' return (-1);')
+
+ # Successfully decoded
+ print >>file, ( ' return (0);\n'
+ '}\n')
+
+ # Checking if a structure has all the required data
+ print >>file, (
+ 'int\n'
+ '%s_complete(struct %s *msg)\n' % (self._name, self._name) +
+ '{' )
+ for entry in self._entries:
+ self.PrintIdented(
+ file, ' ',
+ entry.CodeComplete('msg'))
+ print >>file, (
+ ' return (0);\n'
+ '}\n' )
+
+ # Complete message unmarshaling
+ print >>file, (
+ 'int\n'
+ 'evtag_unmarshal_%s(struct evbuffer *evbuf, u_int8_t need_tag, '
+ ' struct %s *msg)'
+ ) % (self._name, self._name)
+ print >>file, (
+ '{\n'
+ ' u_int8_t tag;\n'
+ ' int res = -1;\n'
+ '\n'
+ ' struct evbuffer *tmp = evbuffer_new();\n'
+ '\n'
+ ' if (evtag_unmarshal(evbuf, &tag, tmp) == -1'
+ ' || tag != need_tag)\n'
+ ' goto error;\n'
+ '\n'
+ ' if (%s_unmarshal(msg, tmp) == -1)\n'
+ ' goto error;\n'
+ '\n'
+ ' res = 0;\n'
+ '\n'
+ ' error:\n'
+ ' evbuffer_free(tmp);\n'
+ ' return (res);\n'
+ '}\n' ) % self._name
+
+ # Complete message marshaling
+ print >>file, (
+ 'void\n'
+ 'evtag_marshal_%s(struct evbuffer *evbuf, u_int8_t tag, '
+ 'const struct %s *msg)\n' % (self._name, self._name) +
+ '{\n'
+ ' struct evbuffer *_buf = evbuffer_new();\n'
+ ' assert(_buf != NULL);\n'
+ ' evbuffer_drain(_buf, -1);\n'
+ ' %s_marshal(_buf, msg);\n' % self._name +
+ ' evtag_marshal(evbuf, tag, EVBUFFER_DATA(_buf), '
+ 'EVBUFFER_LENGTH(_buf));\n'
+ ' evbuffer_free(_buf);\n'
+ '}\n' )
+
+class Entry:
+ def __init__(self, type, name, tag):
+ self._type = type
+ self._name = name
+ self._tag = int(tag)
+ self._ctype = type
+ self._optional = 0
+ self._can_be_array = 0
+ self._array = 0
+ self._line_count = -1
+ self._struct = None
+
+ def SetStruct(self, struct):
+ self._struct = struct
+
+ def LineCount(self):
+ assert self._line_count != -1
+ return self._line_count
+
+ def SetLineCount(self, number):
+ self._line_count = number
+
+ def Array(self):
+ return self._array
+
+ def Optional(self):
+ return self._optional
+
+ def Tag(self):
+ return self._tag
+
+ def Name(self):
+ return self._name
+
+ def Type(self):
+ return self._type
+
+ def MakeArray(self, yes=1):
+ self._array = yes
+
+ def MakeOptional(self):
+ self._optional = 1
+
+ def GetFuncName(self):
+ return '%s_%s_get' % (self._struct.Name(), self._name)
+
+ def GetDeclaration(self, funcname):
+ code = [ 'int %s(struct %s *, %s *);' % (
+ funcname, self._struct.Name(), self._ctype ) ]
+ return code
+
+ def CodeGet(self):
+ code = [. 'int',
+ '%s_%s_get(struct %s *msg, %s *value)' % (
+ self._struct.Name(), self._name,
+ self._struct.Name(), self._ctype),
+ '{',
+ ' if (msg->%s_set != 1)' % self._name,
+ ' return (-1);',
+ ' *value = msg->%s_data;' % self._name,
+ ' return (0);',
+ '}' ]
+ return code
+
+ def AssignFuncName(self):
+ return '%s_%s_assign' % (self._struct.Name(), self._name)
+
+ def AddFuncName(self):
+ return '%s_%s_add' % (self._struct.Name(), self._name)
+
+ def AssignDeclaration(self, funcname):
+ code = [ 'int %s(struct %s *, const %s);' % (
+ funcname, self._struct.Name(), self._ctype ) ]
+ return code
+
+ def CodeAssign(self):
+ code = [. 'int',
+ '%s_%s_assign(struct %s *msg, const %s value)' % (
+ self._struct.Name(), self._name,
+ self._struct.Name(), self._ctype),
+ '{',
+ ' msg->%s_set = 1;' % self._name,
+ ' msg->%s_data = value;' % self._name,
+ ' return (0);',
+ '}' ]
+ return code
+
+ def CodeClear(self, structname):
+ code = [ '%s->%s_set = 0;' % (structname, self.Name()) ]
+
+ return code
+
+ def CodeComplete(self, structname):
+ if self.Optional():
+ return []
+
+ code = [ 'if (!%s->%s_set)' % (structname, self.Name()),
+ ' return (-1);' ]
+
+ return code
+
+ def CodeFree(self, name):
+ return []
+
+ def CodeNew(self, name):
+ code = [. '%s->%s_assign = %s_%s_assign;' % (
+ name, self._name, self._struct.Name(), self._name ),
+ '%s->%s_get = %s_%s_get;' % (
+ name, self._name, self._struct.Name(), self._name ),
+ ]
+ if self.Array():
+ code.append(
+ '%s->%s_add = %s_%s_add;' % (
+ name, self._name, self._struct.Name(), self._name ) )
+ return code
+
+ def Verify(self):
+ if self.Array() and not self._can_be_array:
+ print >>sys.stderr, (
+ 'Entry "%s" cannot be created as an array '
+ 'around line %d' ) % (self._name, self.LineCount())
+ sys.exit(1)
+ if not self._struct:
+ print >>sys.stderr, (
+ 'Entry "%s" does not know which struct it belongs to '
+ 'around line %d' ) % (self._name, self.LineCount())
+ sys.exit(1)
+ if self._optional and self._array:
+ print >>sys.stderr, ( 'Entry "%s" has illegal combination of '
+ 'optional and array around line %d' ) % (
+ self._name, self.LineCount() )
+ sys.exit(1)
+
+class EntryBytes(Entry):
+ def __init__(self, type, name, tag, length):
+ # Init base class
+ Entry.__init__(self, type, name, tag)
+
+ self._length = length
+ self._ctype = 'u_int8_t'
+
+ def GetDeclaration(self, funcname):
+ code = [ 'int %s(struct %s *, %s **);' % (
+ funcname, self._struct.Name(), self._ctype ) ]
+ return code
+
+ def AssignDeclaration(self, funcname):
+ code = [ 'int %s(struct %s *, const %s *);' % (
+ funcname, self._struct.Name(), self._ctype ) ]
+ return code
+
+ def Declaration(self):
+ dcl = ['u_int8_t %s_data[%s];' % (self._name, self._length)]
+
+ return dcl
+
+ def CodeGet(self):
+ name = self._name
+ code = [. 'int',
+ '%s_%s_get(struct %s *msg, %s **value)' % (
+ self._struct.Name(), name,
+ self._struct.Name(), self._ctype),
+ '{',
+ ' if (msg->%s_set != 1)' % name,
+ ' return (-1);',
+ ' *value = msg->%s_data;' % name,
+ ' return (0);',
+ '}' ]
+ return code
+
+ def CodeAssign(self):
+ name = self._name
+ code = [. 'int',
+ '%s_%s_assign(struct %s *msg, const %s *value)' % (
+ self._struct.Name(), name,
+ self._struct.Name(), self._ctype),
+ '{',
+ ' msg->%s_set = 1;' % name,
+ ' memcpy(msg->%s_data, value, %s);' % (
+ name, self._length),
+ ' return (0);',
+ '}' ]
+ return code
+
+ def CodeUnmarshal(self, buf, tag_name, var_name):
+ code = [. 'if (evtag_unmarshal_fixed(%s, %s, ' % (buf, tag_name) +
+ '%s->%s_data, ' % (var_name, self._name) +
+ 'sizeof(%s->%s_data)) == -1) {' % (
+ var_name, self._name),
+ ' event_warnx("%%s: failed to unmarshal %s", __func__);' % (
+ self._name ),
+ ' return (-1);',
+ '}'
+ ]
+ return code
+
+ def CodeMarshal(self, buf, tag_name, var_name):
+ code = [.'evtag_marshal(%s, %s, %s->%s_data, sizeof(%s->%s_data));' % (
+ buf, tag_name, var_name, self._name, var_name, self._name )]
+ return code
+
+ def CodeClear(self, structname):
+ code = [. '%s->%s_set = 0;' % (structname, self.Name()),
+ 'memset(%s->%s_data, 0, sizeof(%s->%s_data));' % (
+ structname, self._name, structname, self._name)]
+
+ return code
+
+ def CodeNew(self, name):
+ code = ['memset(%s->%s_data, 0, sizeof(%s->%s_data));' % (
+ name, self._name, name, self._name)]
+ code.extend(Entry.CodeNew(self, name))
+ return code
+
+ def Verify(self):
+ if not self._length:
+ print >>sys.stderr, 'Entry "%s" needs a length around line %d' % (
+ self._name, self.LineCount() )
+ sys.exit(1)
+
+ Entry.Verify(self)
+
+class EntryInt(Entry):
+ def __init__(self, type, name, tag):
+ # Init base class
+ Entry.__init__(self, type, name, tag)
+
+ self._ctype = 'u_int32_t'
+
+ def CodeUnmarshal(self, buf, tag_name, var_name):
+ code = [.'if (evtag_unmarshal_int(%s, %s, &%s->%s_data) == -1) {' % (
+ buf, tag_name, var_name, self._name),
+ ' event_warnx("%%s: failed to unmarshal %s", __func__);' % (
+ self._name ),
+ ' return (-1);',
+ '}' ]
+ return code
+
+ def CodeMarshal(self, buf, tag_name, var_name):
+ code = ['evtag_marshal_int(%s, %s, %s->%s_data);' % (
+ buf, tag_name, var_name, self._name)]
+ return code
+
+ def Declaration(self):
+ dcl = ['u_int32_t %s_data;' % self._name]
+
+ return dcl
+
+class EntryString(Entry):
+ def __init__(self, type, name, tag):
+ # Init base class
+ Entry.__init__(self, type, name, tag)
+
+ self._ctype = 'char *'
+
+ def CodeAssign(self):
+ name = self._name
+ code = [. 'int',
+ '%s_%s_assign(struct %s *msg, const %s value)' % (
+ self._struct.Name(), name,
+ self._struct.Name(), self._ctype),
+ '{',
+ ' if (msg->%s_data != NULL)' % name,
+ ' free(msg->%s_data);' % name,
+ ' if ((msg->%s_data = strdup(value)) == NULL)' % name,
+ ' return (-1);',
+ ' msg->%s_set = 1;' % name,
+ ' return (0);',
+ '}' ]
+ return code
+
+ def CodeUnmarshal(self, buf, tag_name, var_name):
+ code = [.'if (evtag_unmarshal_string(%s, %s, &%s->%s_data) == -1) {' % (
+ buf, tag_name, var_name, self._name),
+ ' event_warnx("%%s: failed to unmarshal %s", __func__);' % (
+ self._name ),
+ ' return (-1);',
+ '}'
+ ]
+ return code
+
+ def CodeMarshal(self, buf, tag_name, var_name):
+ code = ['evtag_marshal_string(%s, %s, %s->%s_data);' % (
+ buf, tag_name, var_name, self._name)]
+ return code
+
+ def CodeClear(self, structname):
+ code = [. 'if (%s->%s_set == 1) {' % (structname, self.Name()),
+ ' free (%s->%s_data);' % (structname, self.Name()),
+ ' %s->%s_data = NULL;' % (structname, self.Name()),
+ ' %s->%s_set = 0;' % (structname, self.Name()),
+ '}'
+ ]
+
+ return code
+
+ def CodeNew(self, name):
+ code = ['%s->%s_data = NULL;' % (name, self._name)]
+ code.extend(Entry.CodeNew(self, name))
+ return code
+
+ def CodeFree(self, name):
+ code = [.'if (%s->%s_data != NULL)' % (name, self._name),
+ ' free (%s->%s_data); ' % (name, self._name)]
+
+ return code
+
+ def Declaration(self):
+ dcl = ['char *%s_data;' % self._name]
+
+ return dcl
+
+class EntryStruct(Entry):
+ def __init__(self, type, name, tag, refname):
+ # Init base class
+ Entry.__init__(self, type, name, tag)
+
+ self._can_be_array = 1
+ self._refname = refname
+ self._ctype = 'struct %s' % refname
+
+ def GetDeclaration(self, funcname):
+ code = [ 'int %s(struct %s *, %s **);' % (
+ funcname, self._struct.Name(), self._ctype ) ]
+ return code
+
+ def AssignDeclaration(self, funcname):
+ code = [ 'int %s(struct %s *, const %s *);' % (
+ funcname, self._struct.Name(), self._ctype ) ]
+ return code
+
+ def CodeGet(self):
+ name = self._name
+ code = [. 'int',
+ '%s_%s_get(struct %s *msg, %s **value)' % (
+ self._struct.Name(), name,
+ self._struct.Name(), self._ctype),
+ '{',
+ ' if (msg->%s_set != 1) {' % name,
+ ' msg->%s_data = %s_new();' % (name, self._refname),
+ ' if (msg->%s_data == NULL)' % name,
+ ' return (-1);',
+ ' msg->%s_set = 1;' % name,
+ ' }',
+ ' *value = msg->%s_data;' % name,
+ ' return (0);',
+ '}' ]
+ return code
+
+ def CodeAssign(self):
+ name = self._name
+ code = [. 'int',
+ '%s_%s_assign(struct %s *msg, const %s *value)' % (
+ self._struct.Name(), name,
+ self._struct.Name(), self._ctype),
+ '{',
+ ' struct evbuffer *tmp = NULL;',
+ ' if (msg->%s_set) {' % name,
+ ' %s_clear(msg->%s_data);' % (self._refname, name),
+ ' msg->%s_set = 0;' % name,
+ ' } else {',
+ ' msg->%s_data = %s_new();' % (name, self._refname),
+ ' if (msg->%s_data == NULL) {' % name,
+ ' event_warn("%%s: %s_new()", __func__);' % (
+ self._refname),
+ ' goto error;',
+ ' }',
+ ' }',
+ ' if ((tmp = evbuffer_new()) == NULL) {',
+ ' event_warn("%s: evbuffer_new()", __func__);',
+ ' goto error;',
+ ' }',
+ ' %s_marshal(tmp, value); ' % self._refname,
+ ' if (%s_unmarshal(msg->%s_data, tmp) == -1) {' % (
+ self._refname, name ),
+ ' event_warnx("%%s: %s_unmarshal", __func__);' % (
+ self._refname),
+ ' goto error;',
+ ' }',
+ ' msg->%s_set = 1;' % name,
+ ' evbuffer_free(tmp);',
+ ' return (0);',
+ ' error:',
+ ' if (tmp != NULL)',
+ ' evbuffer_free(tmp);',
+ ' if (msg->%s_data != NULL) {' % name,
+ ' %s_free(msg->%s_data);' % (self._refname, name),
+ ' msg->%s_data = NULL;' % name,
+ ' }',
+ ' return (-1);',
+ '}' ]
+ return code
+
+ def CodeComplete(self, structname):
+ if self.Optional():
+ code = [. 'if (%s->%s_set && %s_complete(%s->%s_data) == -1)' % (
+ structname, self.Name(),
+ self._refname, structname, self.Name()),
+ ' return (-1);' ]
+ else:
+ code = [ 'if (%s_complete(%s->%s_data) == -1)' % (
+ self._refname, structname, self.Name()),
+ ' return (-1);' ]
+
+ return code
+
+ def CodeUnmarshal(self, buf, tag_name, var_name):
+ code = [.'%s->%s_data = %s_new();' % (
+ var_name, self._name, self._refname),
+ 'if (%s->%s_data == NULL)' % (var_name, self._name),
+ ' return (-1);',
+ 'if (evtag_unmarshal_%s(%s, %s, %s->%s_data) == -1) {' % (
+ self._refname, buf, tag_name, var_name, self._name),
+ ' event_warnx("%%s: failed to unmarshal %s", __func__);' % (
+ self._name ),
+ ' return (-1);',
+ '}'
+ ]
+ return code
+
+ def CodeMarshal(self, buf, tag_name, var_name):
+ code = [.'evtag_marshal_%s(%s, %s, %s->%s_data);' % (
+ self._refname, buf, tag_name, var_name, self._name)]
+ return code
+
+ def CodeClear(self, structname):
+ code = [. 'if (%s->%s_set == 1) {' % (structname, self.Name()),
+ ' %s_free(%s->%s_data);' % (
+ self._refname, structname, self.Name()),
+ ' %s->%s_data = NULL;' % (structname, self.Name()),
+ ' %s->%s_set = 0;' % (structname, self.Name()),
+ '}'
+ ]
+
+ return code
+
+ def CodeNew(self, name):
+ code = ['%s->%s_data = NULL;' % (name, self._name)]
+ code.extend(Entry.CodeNew(self, name))
+ return code
+
+ def CodeFree(self, name):
+ code = [.'if (%s->%s_data != NULL)' % (name, self._name),
+ ' %s_free(%s->%s_data); ' % (
+ self._refname, name, self._name)]
+
+ return code
+
+ def Declaration(self):
+ dcl = ['struct %s *%s_data;' % (self._refname, self._name)]
+
+ return dcl
+
+class EntryVarBytes(Entry):
+ def __init__(self, type, name, tag):
+ # Init base class
+ Entry.__init__(self, type, name, tag)
+
+ self._ctype = 'u_int8_t *'
+
+ def GetDeclaration(self, funcname):
+ code = [ 'int %s(struct %s *, %s *, u_int32_t *);' % (
+ funcname, self._struct.Name(), self._ctype ) ]
+ return code
+
+ def AssignDeclaration(self, funcname):
+ code = [ 'int %s(struct %s *, const %s, u_int32_t);' % (
+ funcname, self._struct.Name(), self._ctype ) ]
+ return code
+
+ def CodeAssign(self):
+ name = self._name
+ code = [. 'int',
+ '%s_%s_assign(struct %s *msg, '
+ 'const %s value, u_int32_t len)' % (
+ self._struct.Name(), name,
+ self._struct.Name(), self._ctype),
+ '{',
+ ' if (msg->%s_data != NULL)' % name,
+ ' free (msg->%s_data);' % name,
+ ' msg->%s_data = malloc(len);' % name,
+ ' if (msg->%s_data == NULL)' % name,
+ ' return (-1);',
+ ' msg->%s_set = 1;' % name,
+ ' msg->%s_length = len;' % name,
+ ' memcpy(msg->%s_data, value, len);' % name,
+ ' return (0);',
+ '}' ]
+ return code
+
+ def CodeGet(self):
+ name = self._name
+ code = [. 'int',
+ '%s_%s_get(struct %s *msg, %s *value, u_int32_t *plen)' % (
+ self._struct.Name(), name,
+ self._struct.Name(), self._ctype),
+ '{',
+ ' if (msg->%s_set != 1)' % name,
+ ' return (-1);',
+ ' *value = msg->%s_data;' % name,
+ ' *plen = msg->%s_length;' % name,
+ ' return (0);',
+ '}' ]
+ return code
+
+ def CodeUnmarshal(self, buf, tag_name, var_name):
+ code = [.'if (evtag_payload_length(%s, &%s->%s_length) == -1)' % (
+ buf, var_name, self._name),
+ ' return (-1);',
+ # We do not want DoS opportunities
+ 'if (%s->%s_length > EVBUFFER_LENGTH(%s))' % (
+ var_name, self._name, buf),
+ ' return (-1);',
+ 'if ((%s->%s_data = malloc(%s->%s_length)) == NULL)' % (
+ var_name, self._name, var_name, self._name),
+ ' return (-1);',
+ 'if (evtag_unmarshal_fixed(%s, %s, %s->%s_data, '
+ '%s->%s_length) == -1) {' % (
+ buf, tag_name, var_name, self._name, var_name, self._name),
+ ' event_warnx("%%s: failed to unmarshal %s", __func__);' % (
+ self._name ),
+ ' return (-1);',
+ '}'
+ ]
+ return code
+
+ def CodeMarshal(self, buf, tag_name, var_name):
+ code = [.'evtag_marshal(%s, %s, %s->%s_data, %s->%s_length);' % (
+ buf, tag_name, var_name, self._name, var_name, self._name)]
+ return code
+
+ def CodeClear(self, structname):
+ code = [. 'if (%s->%s_set == 1) {' % (structname, self.Name()),
+ ' free (%s->%s_data);' % (structname, self.Name()),
+ ' %s->%s_data = NULL;' % (structname, self.Name()),
+ ' %s->%s_length = 0;' % (structname, self.Name()),
+ ' %s->%s_set = 0;' % (structname, self.Name()),
+ '}'
+ ]
+
+ return code
+
+ def CodeNew(self, name):
+ code = [.'%s->%s_data = NULL;' % (name, self._name),
+ '%s->%s_length = 0;' % (name, self._name) ]
+ code.extend(Entry.CodeNew(self, name))
+ return code
+
+ def CodeFree(self, name):
+ code = [.'if (%s->%s_data != NULL)' % (name, self._name),
+ ' free (%s->%s_data); ' % (name, self._name)]
+
+ return code
+
+ def Declaration(self):
+ dcl = ['u_int8_t *%s_data;' % self._name,
+ 'u_int32_t %s_length;' % self._name]
+
+ return dcl
+
+class EntryArray(Entry):
+ def __init__(self, entry):
+ # Init base class
+ Entry.__init__(self, entry._type, entry._name, entry._tag)
+
+ self._entry = entry
+ self._refname = entry._refname
+ self._ctype = 'struct %s' % self._refname
+
+ def GetDeclaration(self, funcname):
+ """Allows direct access to elements of the array."""
+ code = [ 'int %s(struct %s *, int, %s **);' % (
+ funcname, self._struct.Name(), self._ctype ) ]
+ return code
+
+ def AssignDeclaration(self, funcname):
+ code = [ 'int %s(struct %s *, int, const %s *);' % (
+ funcname, self._struct.Name(), self._ctype ) ]
+ return code
+
+ def AddDeclaration(self, funcname):
+ code = [ '%s *%s(struct %s *);' % (
+ self._ctype, funcname, self._struct.Name() ) ]
+ return code
+
+ def CodeGet(self):
+ name = self._name
+ code = [. 'int',
+ '%s_%s_get(struct %s *msg, int offset, %s **value)' % (
+ self._struct.Name(), name,
+ self._struct.Name(), self._ctype),
+ '{',
+ ' if (msg->%s_set != 1)' % name,
+ ' return (-1);',
+ ' if (offset >= msg->%s_length)' % name,
+ ' return (-1);',
+ ' *value = msg->%s_data[offset];' % name,
+ ' return (0);',
+ '}' ]
+ return code
+
+ def CodeAssign(self):
+ name = self._name
+ code = [. 'int',
+ '%s_%s_assign(struct %s *msg, int off, const %s *value)' % (
+ self._struct.Name(), name,
+ self._struct.Name(), self._ctype),
+ '{',
+ ' struct evbuffer *tmp = NULL;',
+ ' if (msg->%s_set != 1)' % name,
+ ' return (-1);',
+ ' if (off >= msg->%s_length)' % name,
+ ' return (-1);',
+ '',
+ ' %s_clear(msg->%s_data[off]);' % (self._refname, name),
+ ' if ((tmp = evbuffer_new()) == NULL) {',
+ ' event_warn("%s: evbuffer_new()", __func__);',
+ ' goto error;',
+ ' }',
+ ' %s_marshal(tmp, value); ' % self._refname,
+ ' if (%s_unmarshal(msg->%s_data[off], tmp) == -1) {' % (
+ self._refname, name ),
+ ' event_warnx("%%s: %s_unmarshal", __func__);' % (
+ self._refname),
+ ' goto error;',
+ ' }',
+ ' evbuffer_free(tmp);',
+ ' return (0);',
+ ' error:',
+ ' if (tmp != NULL)',
+ ' evbuffer_free(tmp);',
+ ' %s_clear(msg->%s_data[off]);' % (self._refname, name),
+ ' return (-1);',
+ '}' ]
+ return code
+
+ def CodeAdd(self):
+ name = self._name
+ code = [.
+ '%s *' % self._ctype,
+ '%s_%s_add(struct %s *msg)' % (
+ self._struct.Name(), name, self._struct.Name()),
+ '{',
+ ' msg->%s_length++;' % name,
+ ' msg->%s_data = (struct %s**)realloc(msg->%s_data, '
+ ' msg->%s_length * sizeof(struct %s*));' % (
+ name, self._refname, name, name, self._refname ),
+ ' if (msg->%s_data == NULL)' % name,
+ ' return (NULL);',
+ ' msg->%s_data[msg->%s_length - 1] = %s_new();' % (
+ name, name, self._refname),
+ ' if (msg->%s_data[msg->%s_length - 1] == NULL) {' % (name, name),
+ ' msg->%s_length--; ' % name,
+ ' return (NULL);',
+ ' }',
+ ' msg->%s_set = 1;' % name,
+ ' return (msg->%s_data[msg->%s_length - 1]);' % (name, name),
+ '}'
+ ]
+ return code
+
+ def CodeComplete(self, structname):
+ code = []
+ if self.Optional():
+ code.append( 'if (%s->%s_set)' % (structname, self.Name()))
+
+ code.extend([.'{',
+ ' int i;',
+ ' for (i = 0; i < %s->%s_length; ++i) {' % (
+ structname, self.Name()),
+ ' if (%s_complete(%s->%s_data[i]) == -1)' % (
+ self._refname, structname, self.Name()),
+ ' return (-1);',
+ ' }',
+ '}'
+ ])
+
+ return code
+
+ def CodeUnmarshal(self, buf, tag_name, var_name):
+ code = [.'if (%s_%s_add(%s) == NULL)' % (
+ self._struct.Name(), self._name, var_name),
+ ' return (-1);',
+ 'if (evtag_unmarshal_%s(%s, %s, '
+ '%s->%s_data[%s->%s_length - 1]) == -1) {' % (
+ self._refname, buf, tag_name, var_name, self._name,
+ var_name, self._name),
+ ' %s->%s_length--; ' % (var_name, self._name),
+ ' event_warnx("%%s: failed to unmarshal %s", __func__);' % (
+ self._name ),
+ ' return (-1);',
+ '}'
+ ]
+ return code
+
+ def CodeMarshal(self, buf, tag_name, var_name):
+ code = [.'{',
+ ' int i;',
+ ' for (i = 0; i < %s->%s_length; ++i) {' % (
+ var_name, self._name),
+ ' evtag_marshal_%s(%s, %s, %s->%s_data[i]);' % (
+ self._refname, buf, tag_name, var_name, self._name),
+ ' }',
+ '}'
+ ]
+ return code
+
+ def CodeClear(self, structname):
+ code = [. 'if (%s->%s_set == 1) {' % (structname, self.Name()),
+ ' int i;',
+ ' for (i = 0; i < %s->%s_length; ++i) {' % (
+ structname, self.Name()),
+ ' %s_free(%s->%s_data[i]);' % (
+ self._refname, structname, self.Name()),
+ ' }',
+ ' free(%s->%s_data);' % (structname, self.Name()),
+ ' %s->%s_data = NULL;' % (structname, self.Name()),
+ ' %s->%s_set = 0;' % (structname, self.Name()),
+ ' %s->%s_length = 0;' % (structname, self.Name()),
+ '}'
+ ]
+
+ return code
+
+ def CodeNew(self, name):
+ code = [.'%s->%s_data = NULL;' % (name, self._name),
+ '%s->%s_length = 0;' % (name, self._name)]
+ code.extend(Entry.CodeNew(self, name))
+ return code
+
+ def CodeFree(self, name):
+ code = [.'if (%s->%s_data != NULL) {' % (name, self._name),
+ ' int i;',
+ ' for (i = 0; i < %s->%s_length; ++i) {' % (
+ name, self._name),
+ ' %s_free(%s->%s_data[i]); ' % (
+ self._refname, name, self._name),
+ ' %s->%s_data[i] = NULL;' % (name, self._name),
+ ' }',
+ ' free(%s->%s_data);' % (name, self._name),
+ ' %s->%s_data = NULL;' % (name, self._name),
+ ' %s->%s_length = 0;' % (name, self._name),
+ '}'
+ ]
+
+ return code
+
+ def Declaration(self):
+ dcl = ['struct %s **%s_data;' % (self._refname, self._name),
+ 'int %s_length;' % self._name]
+
+ return dcl
+
+def NormalizeLine(line):
+ global leading
+ global trailing
+ global white
+ global cppcomment
+
+ line = cppcomment.sub('', line)
+ line = leading.sub('', line)
+ line = trailing.sub('', line)
+ line = white.sub(' ', line)
+
+ return line
+
+def ProcessOneEntry(newstruct, entry):
+ optional = 0
+ array = 0
+ type = ''
+ name = ''
+ tag = ''
+ tag_set = None
+ separator = ''
+ fixed_length = ''
+
+ tokens = entry.split(' ')
+ while tokens:
+ token = tokens[0]
+ tokens = tokens[1:]
+
+ if not type:
+ if not optional and token == 'optional':
+ optional = 1
+ continue
+
+ if not array and token == 'array':
+ array = 1
+ continue
+
+ if not type:
+ type = token
+ continue
+
+ if not name:
+ res = re.match(r'^([^\[\]]+)(\[.*\])?$', token)
+ if not res:
+ print >>sys.stderr, 'Cannot parse name: \"%s\" around %d' % (
+ entry, line_count)
+ sys.exit(1)
+ name = res.group(1)
+ fixed_length = res.group(2)
+ if fixed_length:
+ fixed_length = fixed_length[1:-1]
+ continue
+
+ if not separator:
+ separator = token
+ if separator != '=':
+ print >>sys.stderr, 'Expected "=" after name \"%s\" got %s' % (
+ name, token)
+ sys.exit(1)
+ continue
+
+ if not tag_set:
+ tag_set = 1
+ if not re.match(r'^[0-9]+$', token):
+ print >>sys.stderr, 'Expected tag number: \"%s\"' % entry
+ sys.exit(1)
+ tag = int(token)
+ continue
+
+ print >>sys.stderr, 'Cannot parse \"%s\"' % entry
+ sys.exit(1)
+
+ if not tag_set:
+ print >>sys.stderr, 'Need tag number: \"%s\"' % entry
+ sys.exit(1)
+
+ # Create the right entry
+ if type == 'bytes':
+ if fixed_length:
+ newentry = EntryBytes(type, name, tag, fixed_length)
+ else:
+ newentry = EntryVarBytes(type, name, tag)
+ elif type == 'int' and not fixed_length:
+ newentry = EntryInt(type, name, tag)
+ elif type == 'string' and not fixed_length:
+ newentry = EntryString(type, name, tag)
+ else:
+ res = re.match(r'^struct\[(%s)\]$' % _STRUCT_RE, type, re.IGNORECASE)
+ if res:
+ # References another struct defined in our file
+ newentry = EntryStruct(type, name, tag, res.group(1))
+ else:
+ print >>sys.stderr, 'Bad type: "%s" in "%s"' % (type, entry)
+ sys.exit(1)
+
+ structs = []
+
+ if optional:
+ newentry.MakeOptional()
+ if array:
+ newentry.MakeArray()
+
+ newentry.SetStruct(newstruct)
+ newentry.SetLineCount(line_count)
+ newentry.Verify()
+
+ if array:
+ # We need to encapsulate this entry into a struct
+ newname = newentry.Name()+ '_array'
+
+ # Now borgify the new entry.
+ newentry = EntryArray(newentry)
+ newentry.SetStruct(newstruct)
+ newentry.SetLineCount(line_count)
+ newentry.MakeArray()
+
+ newstruct.AddEntry(newentry)
+
+ return structs
+
+def ProcessStruct(data):
+ tokens = data.split(' ')
+
+ # First three tokens are: 'struct' 'name' '{'
+ newstruct = Struct(tokens[1])
+
+ inside = ' '.join(tokens[3:-1])
+
+ tokens = inside.split(';')
+
+ structs = []
+
+ for entry in tokens:
+ entry = NormalizeLine(entry)
+ if not entry:
+ continue
+
+ # It's possible that new structs get defined in here
+ structs.extend(ProcessOneEntry(newstruct, entry))
+
+ structs.append(newstruct)
+ return structs
+
+def GetNextStruct(file):
+ global line_count
+ global cppdirect
+
+ got_struct = 0
+
+ processed_lines = []
+
+ have_c_comment = 0
+ data = ''
+ for line in file:
+ line_count += 1
+ line = line[:-1]
+
+ if not have_c_comment and re.search(r'/\*', line):
+ if re.search(r'/\*.*\*/', line):
+ line = re.sub(r'/\*.*\*/', '', line)
+ else:
+ line = re.sub(r'/\*.*$', '', line)
+ have_c_comment = 1
+
+ if have_c_comment:
+ if not re.search(r'\*/', line):
+ continue
+ have_c_comment = 0
+ line = re.sub(r'^.*\*/', '', line)
+
+ line = NormalizeLine(line)
+
+ if not line:
+ continue
+
+ if not got_struct:
+ if re.match(r'#include ["<].*[>"]', line):
+ cppdirect.append(line)
+ continue
+
+ if re.match(r'^#(if( |def)|endif)', line):
+ cppdirect.append(line)
+ continue
+
+ if not re.match(r'^struct %s {$' % _STRUCT_RE,
+ line, re.IGNORECASE):
+ print >>sys.stderr, 'Missing struct on line %d: %s' % (
+ line_count, line)
+ sys.exit(1)
+ else:
+ got_struct = 1
+ data += line
+ continue
+
+ # We are inside the struct
+ tokens = line.split('}')
+ if len(tokens) == 1:
+ data += ' ' + line
+ continue
+
+ if len(tokens[1]):
+ print >>sys.stderr, 'Trailing garbage after struct on line %d' % (
+ line_count )
+ sys.exit(1)
+
+ # We found the end of the struct
+ data += ' %s}' % tokens[0]
+ break
+
+ # Remove any comments, that might be in there
+ data = re.sub(r'/\*.*\*/', '', data)
+
+ return data
+
+
+def Parse(file):
+ """Parses the input file and returns C code and corresponding header
+ file."""
+
+ entities = []
+
+ while 1:
+ # Just gets the whole struct nicely formatted
+ data = GetNextStruct(file)
+
+ if not data:
+ break
+
+ entities.extend(ProcessStruct(data))
+
+ return entities
+
+def GuardName(name):
+ name = '_'.join(name.split('.'))
+ name = '_'.join(name.split('/'))
+ guard = '_'+name.upper()+'_'
+
+ return guard
+
+def HeaderPreamble(name):
+ guard = GuardName(name)
+ pre = (
+ '/*\n'
+ ' * Automatically generated from %s\n'
+ ' */\n\n'
+ '#ifndef %s\n'
+ '#define %s\n\n' ) % (
+ name, guard, guard)
+ pre += (
+ '#define EVTAG_HAS(msg, member) ((msg)->member##_set == 1)\n'
+ '#define EVTAG_ASSIGN(msg, member, args...) '
+ '(*(msg)->member##_assign)(msg, ## args)\n'
+ '#define EVTAG_GET(msg, member, args...) '
+ '(*(msg)->member##_get)(msg, ## args)\n'
+ '#define EVTAG_ADD(msg, member) (*(msg)->member##_add)(msg)\n'
+ '#define EVTAG_LEN(msg, member) ((msg)->member##_length)\n'
+ )
+
+ return pre
+
+
+def HeaderPostamble(name):
+ guard = GuardName(name)
+ return '#endif /* %s */' % guard
+
+def BodyPreamble(name):
+ global _NAME
+ global _VERSION
+
+ header_file = '.'.join(name.split('.')[:-1]) + '.gen.h'
+
+ pre = ( '/*\n'
+ ' * Automatically generated from %s\n'
+ ' * by %s/%s. DO NOT EDIT THIS FILE.\n'
+ ' */\n\n' ) % (name, _NAME, _VERSION)
+ pre += ( '#include <sys/types.h>\n'
+ '#include <sys/time.h>\n'
+ '#include <stdlib.h>\n'
+ '#include <string.h>\n'
+ '#include <assert.h>\n'
+ '#include <event.h>\n\n' )
+
+ for include in cppdirect:
+ pre += '%s\n' % include
+
+ pre += '\n#include "%s"\n\n' % header_file
+
+ pre += 'void event_err(int eval, const char *fmt, ...);\n'
+ pre += 'void event_warn(const char *fmt, ...);\n'
+ pre += 'void event_errx(int eval, const char *fmt, ...);\n'
+ pre += 'void event_warnx(const char *fmt, ...);\n\n'
+
+ return pre
+
+def main(argv):
+ filename = argv[1]
+
+ if filename.split('.')[-1] != 'rpc':
+ ext = filename.split('.')[-1]
+ print >>sys.stderr, 'Unrecognized file extension: %s' % ext
+ sys.exit(1)
+
+ print >>sys.stderr, 'Reading \"%s\"' % filename
+
+ fp = open(filename, 'r')
+ entities = Parse(fp)
+ fp.close()
+
+ header_file = '.'.join(filename.split('.')[:-1]) + '.gen.h'
+ impl_file = '.'.join(filename.split('.')[:-1]) + '.gen.c'
+
+ print >>sys.stderr, '... creating "%s"' % header_file
+ header_fp = open(header_file, 'w')
+ print >>header_fp, HeaderPreamble(filename)
+
+ # Create forward declarations: allows other structs to reference
+ # each other
+ for entry in entities:
+ entry.PrintForwardDeclaration(header_fp)
+ print >>header_fp, ''
+
+ for entry in entities:
+ entry.PrintTags(header_fp)
+ entry.PrintDeclaration(header_fp)
+ print >>header_fp, HeaderPostamble(filename)
+ header_fp.close()
+
+ print >>sys.stderr, '... creating "%s"' % impl_file
+ impl_fp = open(impl_file, 'w')
+ print >>impl_fp, BodyPreamble(filename)
+ for entry in entities:
+ entry.PrintCode(impl_fp)
+ impl_fp.close()
+
+if __name__ == '__main__':
+ main(sys.argv)


Property changes on: trunk/varnish-cache/contrib/libevent/event_rpcgen.py
___________________________________________________________________
Name: svn:executable
+ *
Name: svn:mime-type
+ text/script

Added: trunk/varnish-cache/contrib/libevent/event_tagging.c
===================================================================
--- trunk/varnish-cache/contrib/libevent/event_tagging.c 2006-04-03 11:05:53 UTC (rev 97)
+++ trunk/varnish-cache/contrib/libevent/event_tagging.c 2006-04-03 11:59:08 UTC (rev 98)
@@ -0,0 +1,360 @@
+/*
+ * Copyright (c) 2003, 2004 Niels Provos <provos at 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.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/ioctl.h>
+#include <sys/tree.h>
+#include <sys/queue.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "event.h"
+
+int decode_int(u_int32_t *pnumber, struct evbuffer *evbuf);
+
+static struct evbuffer *_buf;
+
+void
+evtag_init()
+{
+ if ((_buf = evbuffer_new()) == NULL)
+ err(1, "%s: malloc", __func__);
+}
+
+/*
+ * We encode integer's by nibbles; the first nibble contains the number
+ * of significant nibbles - 1; this allows us to encode up to 64-bit
+ * integers. This function is byte-order independent.
+ */
+
+void
+encode_int(struct evbuffer *evbuf, u_int32_t number)
+{
+ int off = 1, nibbles = 0;
+ u_int8_t data[5];
+
+ memset(data, 0, sizeof(data));
+ while (number) {
+ if (off & 0x1)
+ data[off/2] = (data[off/2] & 0xf0) | (number & 0x0f);
+ else
+ data[off/2] = (data[off/2] & 0x0f) |
+ ((number & 0x0f) << 4);
+ number >>= 4;
+ off++;
+ }
+
+ if (off > 2)
+ nibbles = off - 2;
+
+ /* Off - 1 is the number of encoded nibbles */
+ data[0] = (data[0] & 0x0f) | ((nibbles & 0x0f) << 4);
+
+ evbuffer_add(evbuf, data, (off + 1) / 2);
+}
+
+/*
+ * Marshal a data type, the general format is as follows:
+ *
+ * tag number: one byte; length: var bytes; payload: var bytes
+ */
+
+void
+evtag_marshal(struct evbuffer *evbuf, u_int8_t tag,
+ const void *data, u_int16_t len)
+{
+ evbuffer_add(evbuf, &tag, sizeof(tag));
+ encode_int(evbuf, len);
+ evbuffer_add(evbuf, (void *)data, len);
+}
+
+/* Marshaling for integers */
+void
+evtag_marshal_int(struct evbuffer *evbuf, u_int8_t tag, u_int32_t integer)
+{
+ evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
+ encode_int(_buf, integer);
+
+ evbuffer_add(evbuf, &tag, sizeof(tag));
+ encode_int(evbuf, EVBUFFER_LENGTH(_buf));
+ evbuffer_add_buffer(evbuf, _buf);
+}
+
+void
+evtag_marshal_string(struct evbuffer *buf, u_int8_t tag, const char *string)
+{
+ evtag_marshal(buf, tag, string, strlen(string));
+}
+
+void
+evtag_marshal_timeval(struct evbuffer *evbuf, u_int8_t tag, struct timeval *tv)
+{
+ evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
+
+ encode_int(_buf, tv->tv_sec);
+ encode_int(_buf, tv->tv_usec);
+
+ evtag_marshal(evbuf, tag, EVBUFFER_DATA(_buf),
+ EVBUFFER_LENGTH(_buf));
+}
+
+static int __inline
+decode_int_internal(u_int32_t *pnumber, struct evbuffer *evbuf, int dodrain)
+{
+ u_int32_t number = 0;
+ u_int8_t *data = EVBUFFER_DATA(evbuf);
+ int len = EVBUFFER_LENGTH(evbuf);
+ int nibbles = 0, off;
+
+ if (!len)
+ return (-1);
+
+ nibbles = ((data[0] & 0xf0) >> 4) + 1;
+ if (nibbles > 8 || (nibbles >> 1) > len - 1)
+ return (-1);
+
+ off = nibbles;
+ while (off > 0) {
+ number <<= 4;
+ if (off & 0x1)
+ number |= data[off >> 1] & 0x0f;
+ else
+ number |= (data[off >> 1] & 0xf0) >> 4;
+ off--;
+ }
+
+ len = (nibbles >> 1) + 1;
+ if (dodrain)
+ evbuffer_drain(evbuf, len);
+
+ *pnumber = number;
+
+ return (len);
+}
+
+int
+decode_int(u_int32_t *pnumber, struct evbuffer *evbuf)
+{
+ return (decode_int_internal(pnumber, evbuf, 1) == -1 ? -1 : 0);
+}
+
+int
+evtag_peek(struct evbuffer *evbuf, u_int8_t *ptag)
+{
+ if (EVBUFFER_LENGTH(evbuf) < 2)
+ return (-1);
+ *ptag = EVBUFFER_DATA(evbuf)[0];
+
+ return (0);
+}
+
+int
+evtag_peek_length(struct evbuffer *evbuf, u_int32_t *plength)
+{
+ struct evbuffer tmp;
+ int res;
+
+ if (EVBUFFER_LENGTH(evbuf) < 2)
+ return (-1);
+
+ tmp = *evbuf;
+ tmp.buffer += 1;
+ tmp.off -= 1;
+
+ res = decode_int_internal(plength, &tmp, 0);
+ if (res == -1)
+ return (-1);
+
+ *plength += res + 1;
+
+ return (0);
+}
+
+int
+evtag_payload_length(struct evbuffer *evbuf, u_int32_t *plength)
+{
+ struct evbuffer tmp;
+ int res;
+
+ if (EVBUFFER_LENGTH(evbuf) < 2)
+ return (-1);
+
+ tmp = *evbuf;
+ tmp.buffer += 1;
+ tmp.off -= 1;
+
+ res = decode_int_internal(plength, &tmp, 0);
+ if (res == -1)
+ return (-1);
+
+ return (0);
+}
+
+int
+evtag_consume(struct evbuffer *evbuf)
+{
+ u_int32_t len;
+ evbuffer_drain(evbuf, 1);
+ if (decode_int(&len, evbuf) == -1)
+ return (-1);
+ evbuffer_drain(evbuf, len);
+
+ return (0);
+}
+
+/* Reads the data type from an event buffer */
+
+int
+evtag_unmarshal(struct evbuffer *src, u_int8_t *ptag, struct evbuffer *dst)
+{
+ u_int8_t tag;
+ u_int16_t len;
+ u_int32_t integer;
+
+ if (evbuffer_remove(src, &tag, sizeof(tag)) != sizeof(tag))
+ return (-1);
+ if (decode_int(&integer, src) == -1)
+ return (-1);
+ len = integer;
+
+ if (EVBUFFER_LENGTH(src) < len)
+ return (-1);
+
+ if (evbuffer_add(dst, EVBUFFER_DATA(src), len) == -1)
+ return (-1);
+
+ evbuffer_drain(src, len);
+
+ *ptag = tag;
+ return (len);
+}
+
+/* Marshaling for integers */
+
+int
+evtag_unmarshal_int(struct evbuffer *evbuf, u_int8_t need_tag,
+ u_int32_t *pinteger)
+{
+ u_int8_t tag;
+ u_int16_t len;
+ u_int32_t integer;
+
+ if (evbuffer_remove(evbuf, &tag, sizeof(tag)) != sizeof(tag) ||
+ tag != need_tag)
+ return (-1);
+ if (decode_int(&integer, evbuf) == -1)
+ return (-1);
+ len = integer;
+
+ if (EVBUFFER_LENGTH(evbuf) < len)
+ return (-1);
+
+ evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
+ if (evbuffer_add(_buf, EVBUFFER_DATA(evbuf), len) == -1)
+ return (-1);
+
+ evbuffer_drain(evbuf, len);
+
+ return (decode_int(pinteger, _buf));
+}
+
+/* Unmarshal a fixed length tag */
+
+int
+evtag_unmarshal_fixed(struct evbuffer *src, u_int8_t need_tag, void *data,
+ size_t len)
+{
+ u_int8_t tag;
+
+ /* Initialize this event buffer so that we can read into it */
+ evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
+
+ /* Now unmarshal a tag and check that it matches the tag we want */
+ if (evtag_unmarshal(src, &tag, _buf) == -1 || tag != need_tag)
+ return (-1);
+
+ if (EVBUFFER_LENGTH(_buf) != len)
+ return (-1);
+
+ memcpy(data, EVBUFFER_DATA(_buf), len);
+ return (0);
+}
+
+int
+evtag_unmarshal_string(struct evbuffer *evbuf, u_int8_t need_tag,
+ char **pstring)
+{
+ u_int8_t tag;
+
+ evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
+
+ if (evtag_unmarshal(evbuf, &tag, _buf) == -1 || tag != need_tag)
+ return (-1);
+
+ *pstring = calloc(EVBUFFER_LENGTH(_buf) + 1, 1);
+ if (*pstring == NULL)
+ err(1, "%s: calloc", __func__);
+ evbuffer_remove(_buf, *pstring, EVBUFFER_LENGTH(_buf));
+
+ return (0);
+}
+
+int
+evtag_unmarshal_timeval(struct evbuffer *evbuf, u_int8_t need_tag,
+ struct timeval *ptv)
+{
+ u_int8_t tag;
+ u_int32_t integer;
+
+ evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
+ if (evtag_unmarshal(evbuf, &tag, _buf) == -1 || tag != need_tag)
+ return (-1);
+
+ if (decode_int(&integer, _buf) == -1)
+ return (-1);
+ ptv->tv_sec = integer;
+ if (decode_int(&integer, _buf) == -1)
+ return (-1);
+ ptv->tv_usec = integer;
+
+ return (0);
+}

Added: trunk/varnish-cache/contrib/libevent/http.c
===================================================================
--- trunk/varnish-cache/contrib/libevent/http.c 2006-04-03 11:05:53 UTC (rev 97)
+++ trunk/varnish-cache/contrib/libevent/http.c 2006-04-03 11:59:08 UTC (rev 98)
@@ -0,0 +1,1492 @@
+/*
+ * Copyright (c) 2002-2006 Niels Provos <provos at 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.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_IOCCOM_H
+#include <sys/ioccom.h>
+#endif
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/tree.h>
+#include <sys/wait.h>
+#include <sys/queue.h>
+
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <signal.h>
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+#include <unistd.h>
+#include <getopt.h>
+#include <fcntl.h>
+
+#undef timeout_pending
+#undef timeout_initialized
+
+#include "event.h"
+#include "log.h"
+#include "http.h"
+
+extern int debug;
+
+static int make_socket_ai(int (*f)(int, const struct sockaddr *, socklen_t),
+ struct addrinfo *);
+static int make_socket(int (*)(int, const struct sockaddr *, socklen_t),
+ const char *, short);
+static void name_from_addr(struct sockaddr *, socklen_t, char **, char **);
+
+void evhttp_write(int, short, void *);
+
+static const char *
+html_replace(char ch)
+{
+ static char buf[2];
+
+ switch (ch) {
+ case '<':
+ return "&lt;";
+ case '>':
+ return "&gt;";
+ case '"':
+ return "&quot;";
+ case '\'':
+ return "&#039;";
+ case '&':
+ return "&amp;";
+ default:
+ break;
+ }
+
+ /* Echo the character back */
+ buf[0] = ch;
+ buf[1] = '\0';
+
+ return buf;
+}
+
+/*
+ * Replaces <, >, ", ' and & with &lt;, &gt;, &quot;,
+ * &#039; and &amp; correspondingly.
+ *
+ * The returned string needs to be freed by the caller.
+ */
+
+char *
+evhttp_htmlescape(const char *html)
+{
+ int i, new_size = 0;
+ char *escaped_html, *p;
+
+ for (i = 0; i < strlen(html); ++i)
+ new_size += strlen(html_replace(html[i]));
+
+ p = escaped_html = malloc(new_size + 1);
+ if (escaped_html == NULL)
+ event_err(1, "%s: malloc(%d)", __func__, new_size + 1);
+ for (i = 0; i < strlen(html); ++i) {
+ const char *replaced = html_replace(html[i]);
+ /* this is length checked */
+ strcpy(p, replaced);
+ p += strlen(replaced);
+ }
+
+ *p = '\0';
+
+ return (escaped_html);
+}
+
+const char *
+evhttp_method(enum evhttp_cmd_type type)
+{
+ const char *method;
+
+ switch (type) {
+ case EVHTTP_REQ_GET:
+ method = "GET";
+ break;
+ case EVHTTP_REQ_POST:
+ method = "POST";
+ break;
+ case EVHTTP_REQ_HEAD:
+ method = "HEAD";
+ break;
+ default:
+ method = NULL;
+ break;
+ }
+
+ return (method);
+}
+
+void
+evhttp_form_response(struct evbuffer *buf, struct evhttp_request *req)
+{
+ /* Clean out the buffer */
+ evbuffer_drain(buf, buf->off);
+
+ /* Create the header fields */
+ evhttp_make_header(buf, req);
+
+ /* Append the response buffer */
+ evbuffer_add(buf,
+ EVBUFFER_DATA(req->buffer), EVBUFFER_LENGTH(req->buffer));
+}
+
+void
+evhttp_write_buffer(struct evhttp_request *req, struct evbuffer *buffer,
+ void (*cb)(struct evhttp_request *, void *), void *arg)
+{
+ struct timeval tv;
+
+ event_debug(("%s: preparing to write buffer\n", __func__));
+
+ if (req->buffer != buffer) {
+ if (req->buffer != NULL)
+ evbuffer_free(req->buffer);
+ req->buffer = buffer;
+ }
+
+ /* Set call back */
+
+ req->cb = cb;
+ req->cb_arg = arg;
+
+ event_set(&req->ev, req->fd, EV_WRITE, evhttp_write, req);
+ timerclear(&tv);
+ tv.tv_sec = HTTP_WRITE_TIMEOUT;
+ event_add(&req->ev, &tv);
+}
+
+/*
+ * Create the headers need for an HTTP reply
+ */
+static void
+evhttp_make_header_request(struct evbuffer *buf, struct evhttp_request *req)
+{
+ static char line[1024];
+ const char *method;
+
+ evhttp_remove_header(req->output_headers, "Accept-Encoding");
+ evhttp_remove_header(req->output_headers, "Proxy-Connection");
+ evhttp_remove_header(req->output_headers, "Connection");
+ evhttp_add_header(req->output_headers, "Connection", "close");
+ req->minor = 0;
+
+ /* Generate request line */
+ method = evhttp_method(req->type);
+ snprintf(line, sizeof(line), "%s %s HTTP/%d.%d\r\n",
+ method, req->uri, req->major, req->minor);
+ evbuffer_add(buf, line, strlen(line));
+
+ /* Add the content length on a post request if missing */
+ if (req->type == EVHTTP_REQ_POST &&
+ evhttp_find_header(req->output_headers, "Content-Length") == NULL){
+ char size[12];
+ snprintf(size, sizeof(size), "%d",
+ EVBUFFER_LENGTH(req->buffer));
+ evhttp_add_header(req->output_headers, "Content-Length", size);
+ }
+}
+
+/*
+ * Create the headers needed for an HTTP reply
+ */
+static void
+evhttp_make_header_response(struct evbuffer *buf, struct evhttp_request *req)
+{
+ static char line[1024];
+ snprintf(line, sizeof(line), "HTTP/%d.%d %d %s\r\n",
+ req->major, req->minor, req->response_code,
+ req->response_code_line);
+ evbuffer_add(buf, line, strlen(line));
+
+ /* Potentially add headers */
+ if (evhttp_find_header(req->output_headers, "Content-Type") == NULL) {
+ evhttp_add_header(req->output_headers,
+ "Content-Type", "text/html; charset=ISO-8859-1");
+ }
+}
+
+void
+evhttp_make_header(struct evbuffer *buf, struct evhttp_request *req)
+{
+ static char line[1024];
+ struct evkeyval *header;
+
+ /*
+ * Depending if this is a HTTP request or response, we might need to
+ * add some new headers or remove existing headers.
+ */
+ if (req->kind == EVHTTP_REQUEST) {
+ evhttp_make_header_request(buf, req);
+ } else {
+ evhttp_make_header_response(buf, req);
+ }
+
+ TAILQ_FOREACH(header, req->output_headers, next) {
+ snprintf(line, sizeof(line), "%s: %s\r\n",
+ header->key, header->value);
+ evbuffer_add(buf, line, strlen(line));
+ }
+ evbuffer_add(buf, "\r\n", 2);
+
+ if (req->kind == EVHTTP_REQUEST) {
+ int len = EVBUFFER_LENGTH(req->buffer);
+
+ /* Add the POST data */
+ if (len > 0)
+ evbuffer_add(buf, EVBUFFER_DATA(req->buffer), len);
+ }
+}
+
+/* Separated host, port and file from URI */
+
+int
+evhttp_hostportfile(char *url, char **phost, u_short *pport, char **pfile)
+{
+ static char host[1024];
+ static char file[1024];
+ char *p, *p2;
+ int len;
+ u_short port;
+
+ len = strlen(HTTP_PREFIX);
+ if (strncasecmp(url, HTTP_PREFIX, len))
+ return (-1);
+
+ url += len;
+
+ /* We might overrun */
+ if (strlcpy(host, url, sizeof (host)) >= sizeof(host))
+ return (-1);
+
+ p = strchr(host, '/');
+ if (p != NULL) {
+ *p = '\0';
+ p2 = p + 1;
+ } else
+ p2 = NULL;
+
+ if (pfile != NULL) {
+ /* Generate request file */
+ if (p2 == NULL)
+ p2 = "";
+ snprintf(file, sizeof(file), "/%s", p2);
+ }
+
+ p = strchr(host, ':');
+ if (p != NULL) {
+ *p = '\0';
+ port = atoi(p + 1);
+
+ if (port == 0)
+ return (-1);
+ } else
+ port = HTTP_DEFAULTPORT;
+
+ if (phost != NULL)
+ *phost = host;
+ if (pport != NULL)
+ *pport = port;
+ if (pfile != NULL)
+ *pfile = file;
+
+ return (0);
+}
+
+void
+evhttp_fail(struct evhttp_request *req)
+{
+ evhttp_request_free(req);
+}
+
+void
+evhttp_write(int fd, short what, void *arg)
+{
+ struct evhttp_request *req = arg;
+ struct timeval tv;
+ int n;
+
+ if (what == EV_TIMEOUT) {
+ evhttp_fail(req);
+ return;
+ }
+
+ n = evbuffer_write(req->buffer, fd);
+ if (n == -1) {
+ event_warn("%s: evbuffer_write", __func__);
+ evhttp_fail(req);
+ return;
+ }
+
+ if (n == 0) {
+ event_warnx("%s: write nothing\n", __func__);
+ evhttp_fail(req);
+ return;
+ }
+
+ if (EVBUFFER_LENGTH(req->buffer) != 0) {
+ timerclear(&tv);
+ tv.tv_sec = HTTP_WRITE_TIMEOUT;
+ event_add(&req->ev, &tv);
+ return;
+ }
+
+ /* Activate our call back */
+ (*req->cb)(req, req->cb_arg);
+}
+
+/*
+ * Reads data into a buffer structure until no more data
+ * can be read on the file descriptor or we have read all
+ * the data that we wanted to read.
+ * Execute callback when done.
+ */
+
+void
+evhttp_read(int fd, short what, void *arg)
+{
+ struct evhttp_request *req = arg;
+ struct timeval tv;
+ int n;
+
+ if (what == EV_TIMEOUT) {
+ evhttp_fail(req);
+ return;
+ }
+
+ n = evbuffer_read(req->buffer, fd, req->ntoread);
+ event_debug(("%s: got %d on %d\n", __func__, n, req->fd));
+
+ if (n == -1) {
+ event_warn("%s: evbuffer_read", __func__);
+ evhttp_fail(req);
+ }
+
+ /* Adjust the amount of data that we have left to read */
+ if (req->ntoread > 0)
+ req->ntoread -= n;
+
+ if (n == 0 || req->ntoread == 0) {
+ (*req->cb)(req, req->cb_arg);
+ return;
+ }
+
+ timerclear(&tv);
+ tv.tv_sec = HTTP_READ_TIMEOUT;
+ event_add(&req->ev, &tv);
+}
+
+void
+evhttp_write_requestcb(struct evhttp_request *req, void *arg)
+{
+ /* Restore the original callbacks */
+ req->cb = req->save_cb;
+ req->cb_arg = req->save_cbarg;
+
+ /* This is after writing the request to the server */
+
+ /* We are done writing our header and are now expecting the response */
+ req->kind = EVHTTP_RESPONSE;
+
+ evhttp_start_read(req);
+}
+
+/*
+ * Clean up a connection object
+ */
+
+void
+evhttp_connection_free(struct evhttp_connection *evcon)
+{
+ event_del(&evcon->ev);
+
+ if (evcon->fd != -1)
+ close(evcon->fd);
+
+ if (evcon->address != NULL)
+ free(evcon->address);
+
+ free(evcon);
+}
+
+/*
+ * Call back for asynchronous connection attempt.
+ */
+
+void
+evhttp_connectioncb(int fd, short what, void *arg)
+{
+ struct evhttp_connection *evcon = arg;
+ int error;
+ socklen_t errsz = sizeof(error);
+
+ if (what == EV_TIMEOUT) {
+ event_warnx("%s: connection timeout for \"%s:%d\" on %d\n",
+ __func__, evcon->address, evcon->port, evcon->fd);
+ goto cleanup;
+ }
+
+ /* Check if the connection completed */
+ if (getsockopt(evcon->fd, SOL_SOCKET, SO_ERROR, &error,
+ &errsz) == -1) {
+ event_warn("%s: getsockopt for \"%s:%d\" on %d",
+ __func__, evcon->address, evcon->port, evcon->fd);
+ goto cleanup;
+ }
+
+ if (error) {
+ event_warnx("%s: connect failed for \"%s:%d\" on %d: %s\n",
+ __func__, evcon->address, evcon->port, evcon->fd,
+ strerror(error));
+ goto cleanup;
+ }
+
+ /* We are connected to the server now */
+ event_debug(("%s: connected to \"%s:%d\" on %d\n",
+ __func__, evcon->address, evcon->port, evcon->fd));
+
+ /* We are turning the connection object over to the user */
+ (*evcon->cb)(evcon, evcon->cb_arg);
+ return;
+
+ cleanup:
+ /* Signal error to the user */
+ (*evcon->cb)(NULL, evcon->cb_arg);
+
+ evhttp_connection_free(evcon);
+ return;
+}
+
+/*
+ * Check if we got a valid response code.
+ */
+
+int
+evhttp_valid_response_code(int code)
+{
+ if (code == 0)
+ return (0);
+
+ return (1);
+}
+
+/* Parses the status line of a web server */
+
+int
+evhttp_parse_response_line(struct evhttp_request *req, char *line)
+{
+ char *protocol;
+ char *number;
+ char *readable;
+
+ protocol = strsep(&line, " ");
+ if (line == NULL)
+ return (-1);
+ number = strsep(&line, " ");
+ if (line == NULL)
+ return (-1);
+ readable = line;
+
+ if (strcmp(protocol, "HTTP/1.0") == 0) {
+ req->major = 1;
+ req->minor = 0;
+ } else if (strcmp(protocol, "HTTP/1.1") == 0) {
+ req->major = 1;
+ req->minor = 1;
+ } else {
+ event_warnx("%s: bad protocol \"%s\" on %d\n",
+ __func__, protocol, req->fd);
+ return (-1);
+ }
+
+ req->response_code = atoi(number);
+ if (!evhttp_valid_response_code(req->response_code)) {
+ event_warnx("%s: bad response code \"%s\" on %d\n",
+ __func__, number, req->fd);
+ return (-1);
+ }
+
+ if ((req->response_code_line = strdup(readable)) == NULL)
+ event_err(1, "%s: strdup", __func__);
+
+ return (0);
+}
+
+/* Parse the first line of a HTTP request */
+
+int
+evhttp_parse_request_line(struct evhttp_request *req, char *line)
+{
+ char *method;
+ char *uri;
+ char *version;
+
+ /* Parse the request line */
+ method = strsep(&line, " ");
+ if (line == NULL)
+ return (-1);
+ uri = strsep(&line, " ");
+ if (line == NULL)
+ return (-1);
+ version = strsep(&line, " ");
+ if (line != NULL)
+ return (-1);
+
+ /* First line */
+ if (strcmp(method, "GET") == 0) {
+ req->type = EVHTTP_REQ_GET;
+ } else if (strcmp(method, "POST") == 0) {
+ req->type = EVHTTP_REQ_POST;
+ } else if (strcmp(method, "HEAD") == 0) {
+ req->type = EVHTTP_REQ_HEAD;
+ } else {
+ event_warnx("%s: bad method %s on fd %d\n",
+ __func__, method, req->fd);
+ return (-1);
+ }
+
+ if (strcmp(version, "HTTP/1.0") == 0) {
+ req->major = 1;
+ req->minor = 0;
+ } else if (strcmp(version, "HTTP/1.1") == 0) {
+ req->major = 1;
+ req->minor = 1;
+ } else {
+ event_warnx("%s: bad version %s on %d\n",
+ __func__, version, req->fd);
+ return (-1);
+ }
+
+ if ((req->uri = strdup(uri)) == NULL) {
+ event_warn("%s: strdup", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+const char *
+evhttp_find_header(struct evkeyvalq *headers, const char *key)
+{
+ struct evkeyval *header;
+
+ TAILQ_FOREACH(header, headers, next) {
+ if (strcasecmp(header->key, key) == 0)
+ return (header->value);
+ }
+
+ return (NULL);
+}
+
+void
+evhttp_clear_headers(struct evkeyvalq *headers)
+{
+ struct evkeyval *header;
+
+ for (header = TAILQ_FIRST(headers);
+ header != NULL;
+ header = TAILQ_FIRST(headers)) {
+ TAILQ_REMOVE(headers, header, next);
+ free(header->key);
+ free(header->value);
+ free(header);
+ }
+}
+
+/*
+ * Returns 0, if the header was successfully removed.
+ * Returns -1, if the header could not be found.
+ */
+
+int
+evhttp_remove_header(struct evkeyvalq *headers, const char *key)
+{
+ struct evkeyval *header;
+
+ TAILQ_FOREACH(header, headers, next) {
+ if (strcasecmp(header->key, key) == 0)
+ break;
+ }
+
+ if (header == NULL)
+ return (-1);
+
+ /* Free and remove the header that we found */
+ TAILQ_REMOVE(headers, header, next);
+ free(header->key);
+ free(header->value);
+ free(header);
+
+ return (0);
+}
+
+int
+evhttp_add_header(struct evkeyvalq *headers, const char *key, const char *value)
+{
+ struct evkeyval *header;
+
+ header = calloc(1, sizeof(struct evkeyval));
+ if (header == NULL) {
+ event_warn("%s: calloc", __func__);
+ return (-1);
+ }
+ if ((header->key = strdup(key)) == NULL) {
+ free(header);
+ event_warn("%s: strdup", __func__);
+ return (-1);
+ }
+ if ((header->value = strdup(value)) == NULL) {
+ free(header->key);
+ free(header);
+ event_warn("%s: strdup", __func__);
+ return (-1);
+ }
+
+ TAILQ_INSERT_TAIL(headers, header, next);
+
+ return (0);
+}
+
+/*
+ * Parses header lines from a request or a response into the specified
+ * request object given an event buffer.
+ *
+ * Returns
+ * -1 on error
+ * 0 when we need to read more headers
+ * 1 when all headers have been read.
+ */
+
+int
+evhttp_parse_lines(struct evhttp_request *req, struct evbuffer* buffer)
+{
+ u_char *endp;
+ int done = 0;
+
+ struct evkeyvalq* headers = req->input_headers;
+ while ((endp = evbuffer_find(buffer, "\r\n", 2)) != NULL) {
+ char *skey, *svalue;
+
+ if (strncmp(EVBUFFER_DATA(buffer), "\r\n", 2) == 0) {
+ evbuffer_drain(buffer, 2);
+ /* Last header - Done */
+ done = 1;
+ break;
+ }
+
+ *endp = '\0';
+ endp += 2;
+
+ event_debug(("%s: Got: %s\n", __func__, EVBUFFER_DATA(buffer)));
+
+ /* Processing of header lines */
+ if (req->got_firstline == 0) {
+ switch (req->kind) {
+ case EVHTTP_REQUEST:
+ if (evhttp_parse_request_line(req, EVBUFFER_DATA(buffer)) == -1)
+ return (-1);
+ break;
+ case EVHTTP_RESPONSE:
+ if (evhttp_parse_response_line(req, EVBUFFER_DATA(buffer)) == -1)
+ return (-1);
+ break;
+ default:
+ return (-1);
+ }
+ req->got_firstline = 1;
+ } else {
+ /* Regular header */
+ svalue = EVBUFFER_DATA(buffer);
+ skey = strsep(&svalue, ":");
+ if (svalue == NULL)
+ return (-1);
+
+ svalue += strspn(svalue, " ");
+
+ if (evhttp_add_header(headers, skey, svalue) == -1)
+ return (-1);
+ }
+
+ /* Move the uncompleted headers forward */
+ evbuffer_drain(buffer, endp - EVBUFFER_DATA(buffer));
+ }
+
+ return (done);
+}
+
+void
+evhttp_get_body(struct evhttp_request *req)
+{
+ struct timeval tv;
+ const char *content_length;
+ const char *connection;
+ struct evkeyvalq *headers = req->input_headers;
+
+ /* If this is a request without a body, then we are done */
+ if (req->kind == EVHTTP_REQUEST && req->type != EVHTTP_REQ_POST) {
+ (*req->cb)(req, req->cb_arg);
+ return;
+ }
+
+ content_length = evhttp_find_header(headers, "Content-Length");
+ connection = evhttp_find_header(headers, "Connection");
+
+ if (content_length == NULL && connection == NULL)
+ req->ntoread = -1;
+ else if (content_length == NULL &&
+ strcasecmp(connection, "Close") != 0) {
+ /* Bad combination, we don't know when it will end */
+ event_warnx("%s: we got no content length, but the server"
+ " wants to keep the connection open: %s.\n",
+ __func__, connection);
+ evhttp_fail(req);
+ return;
+ } else if (content_length == NULL)
+ req->ntoread = -1;
+ else
+ req->ntoread = atoi(content_length);
+
+ event_debug(("%s: bytes to read: %d (in buffer %d)\n",
+ __func__, req->ntoread, EVBUFFER_LENGTH(req->buffer)));
+
+ if (req->ntoread > 0)
+ req->ntoread -= EVBUFFER_LENGTH(req->buffer);
+
+ if (req->ntoread == 0) {
+ (*req->cb)(req, req->cb_arg);
+ return;
+ }
+
+ event_set(&req->ev, req->fd, EV_READ, evhttp_read, req);
+ timerclear(&tv);
+ tv.tv_sec = HTTP_READ_TIMEOUT;
+ event_add(&req->ev, &tv);
+}
+
+void
+evhttp_read_header(int fd, short what, void *arg)
+{
+ struct timeval tv;
+ struct evhttp_request *req = arg;
+ int n, res;
+
+ if (what == EV_TIMEOUT) {
+ event_warnx("%s: timeout on %d\n", __func__, fd);
+ evhttp_request_free(req);
+ return;
+ }
+
+ n = evbuffer_read(req->buffer, fd, -1);
+ if (n == 0) {
+ event_warnx("%s: no more data on %d\n", __func__, fd);
+ evhttp_request_free(req);
+ return;
+ }
+ if (n == -1) {
+ event_warnx("%s: bad read on %d\n", __func__, fd);
+ evhttp_request_free(req);
+ return;
+ }
+
+ res = evhttp_parse_lines(req, req->buffer);
+ if (res == -1) {
+ /* Error while reading, terminate */
+ event_warnx("%s: bad header lines on %d\n", __func__, fd);
+ evhttp_request_free(req);
+ return;
+ } else if (res == 0) {
+ /* Need more header lines */
+ timerclear(&tv);
+ tv.tv_sec = HTTP_READ_TIMEOUT;
+ event_add(&req->ev, &tv);
+ return;
+ }
+
+ /* Done reading headers, do the real work */
+ switch (req->kind) {
+ case EVHTTP_REQUEST:
+ event_debug(("%s: checking for post data on %d\n",
+ __func__, req->fd));
+ evhttp_get_body(req);
+ break;
+
+ case EVHTTP_RESPONSE:
+ event_debug(("%s: starting to read body for \"%s\" on %d\n",
+ __func__, req->remote_host, req->fd));
+ evhttp_get_body(req);
+ break;
+
+ default:
+ event_warnx("%s: bad header on %d\n", __func__, fd);
+ evhttp_fail(req);
+ break;
+ }
+}
+
+/*
+ * Creates a TCP connection to the specified port and executes a callback
+ * when finished. Failure or sucess is indicate by the passed connection
+ * object.
+ *
+ * Although this interface accepts a hostname, it is intended to take
+ * only numeric hostnames so that non-blocking DNS resolution can
+ * happen elsewhere.
+ */
+
+struct evhttp_connection *
+evhttp_connect(const char *address, unsigned short port,
+ void (*cb)(struct evhttp_connection *, void *), void *cb_arg)
+{
+ struct evhttp_connection *evcon = NULL;
+ struct timeval tv;
+
+ event_debug(("Attempting connection to %s:%d\n", address, port));
+
+ if ((evcon = calloc(1, sizeof(struct evhttp_connection))) == NULL) {
+ event_warn("%s: calloc failed", __func__);
+ goto error;
+ }
+
+ evcon->fd = -1;
+ evcon->port = port;
+ if ((evcon->address = strdup(address)) == NULL) {
+ event_warn("%s: strdup failed", __func__);
+ goto error;
+ }
+
+ /* Let the user name when something interesting happened */
+ evcon->cb = cb;
+ evcon->cb_arg = cb_arg;
+
+ /* Do async connection to HTTP server */
+ if ((evcon->fd = make_socket(connect, address, port)) == -1) {
+ event_warn("%s: failed to connect to \"%s:%d\"",
+ __func__, address, port);
+ goto error;
+ }
+
+ /* Set up a callback for successful connection setup */
+ event_set(&evcon->ev, evcon->fd, EV_WRITE, evhttp_connectioncb, evcon);
+ timerclear(&tv);
+ tv.tv_sec = HTTP_CONNECT_TIMEOUT;
+ event_add(&evcon->ev, &tv);
+
+ return (evcon);
+
+ error:
+ if (evcon != NULL)
+ evhttp_connection_free(evcon);
+ return (NULL);
+}
+
+/*
+ * Starts an HTTP request on the provided evhttp_connection object.
+ *
+ * In theory we might use this to queue requests on the connection
+ * object.
+ */
+
+int
+evhttp_make_request(struct evhttp_connection *evcon,
+ struct evhttp_request *req,
+ enum evhttp_cmd_type type, const char *uri)
+{
+ struct evbuffer *evbuf = evbuffer_new();
+
+ if (evbuf == NULL)
+ return (-1);
+
+ /* We are making a request */
+ req->fd = evcon->fd;
+ req->kind = EVHTTP_REQUEST;
+ req->type = type;
+ if (req->uri != NULL)
+ free(req->uri);
+ if ((req->uri = strdup(uri)) == NULL)
+ goto error;
+
+ /* Set the protocol version if it is not supplied */
+ if (!req->major && !req->minor) {
+ req->major = 1;
+ req->minor = 1;
+ }
+
+ /* Create the header from the store arguments */
+ evhttp_make_header(evbuf, req);
+
+ /* Schedule the write */
+ req->save_cb = req->cb;
+ req->save_cbarg = req->cb_arg;
+
+ /* evbuf is being freed when the request finishes */
+ evhttp_write_buffer(req, evbuf, evhttp_write_requestcb, NULL);
+ return (0);
+
+ error:
+ evbuffer_free(evbuf);
+ return (-1);
+}
+
+/*
+ * Reads data from file descriptor into request structure
+ * Request structure needs to be set up correctly.
+ */
+
+void
+evhttp_start_read(struct evhttp_request *req)
+{
+ struct timeval tv;
+
+ /* Set up an event to read the headers */
+ event_set(&req->ev, req->fd, EV_READ, evhttp_read_header, req);
+
+ timerclear(&tv);
+ tv.tv_sec = HTTP_READ_TIMEOUT;
+ event_add(&req->ev, &tv);
+}
+
+void
+evhttp_send_done(struct evhttp_request *req, void *arg)
+{
+ evhttp_request_free(req);
+}
+
+/*
+ * Returns an error page.
+ */
+
+void
+evhttp_send_error(struct evhttp_request *req, int error, const char *reason)
+{
+ char *fmt = "<HTML><HEAD>\n"
+ "<TITLE>%d %s</TITLE>\n"
+ "</HEAD><BODY>\n"
+ "<H1>Method Not Implemented</H1>\n"
+ "Invalid method in request<P>\n"
+ "</BODY></HTML>\n";
+
+ struct evbuffer *buf = evbuffer_new();
+
+ evhttp_response_code(req, error, reason);
+
+ evbuffer_add_printf(buf, fmt, error, reason);
+
+ evhttp_send_page(req, buf);
+
+ evbuffer_free(buf);
+}
+
+/* Requires that headers and response code are already set up */
+
+static __inline void
+evhttp_send(struct evhttp_request *req, struct evbuffer *databuf)
+{
+ struct evbuffer *buf = req->buffer;
+
+ evbuffer_drain(buf, -1);
+
+ /* Adds headers to the response */
+ evhttp_make_header(buf, req);
+
+ evbuffer_add_buffer(buf, databuf);
+
+ evhttp_write_buffer(req, buf, evhttp_send_done, NULL);
+}
+
+void
+evhttp_send_reply(struct evhttp_request *req, int code, const char *reason,
+ struct evbuffer *databuf)
+{
+ evhttp_response_code(req, code, reason);
+
+ evhttp_send(req, databuf);
+}
+
+void
+evhttp_response_code(struct evhttp_request *req, int code, const char *reason)
+{
+ req->kind = EVHTTP_RESPONSE;
+ req->response_code = code;
+ if (req->response_code_line != NULL)
+ free(req->response_code_line);
+ req->response_code_line = strdup(reason);
+}
+
+void
+evhttp_send_page(struct evhttp_request *req, struct evbuffer *databuf)
+{
+ if (req->kind != EVHTTP_RESPONSE)
+ evhttp_response_code(req, 200, "OK");
+
+ evhttp_clear_headers(req->output_headers);
+ evhttp_add_header(req->output_headers, "Content-Type", "text/html");
+ evhttp_add_header(req->output_headers, "Connection", "close");
+
+ evhttp_send(req, databuf);
+}
+
+/*
+ * Helper function to parse out arguments in a query.
+ * The arguments are separated by key and value.
+ */
+
+void
+evhttp_parse_query(const char *uri, struct evkeyvalq *headers)
+{
+ char *line;
+ char *argument;
+ char *p;
+
+ TAILQ_INIT(headers);
+
+ /* No arguments - we are done */
+ if (strchr(uri, '?') == NULL)
+ return;
+
+ if ((line = strdup(uri)) == NULL)
+ event_err(1, "%s: strdup", __func__);
+
+
+ argument = line;
+
+ /* We already know that there has to be a ? */
+ strsep(&argument, "?");
+
+ p = argument;
+ while (p != NULL && *p != '\0') {
+ char *key, *value;
+ argument = strsep(&p, "&");
+
+ value = argument;
+ key = strsep(&value, "=");
+ if (value == NULL)
+ goto error;
+
+ event_warnx("Got: %s -> %s\n", key, value);
+ evhttp_add_header(headers, key, value);
+ }
+
+ error:
+ free(line);
+}
+
+void
+evhttp_handle_request(struct evhttp_request *req, void *arg)
+{
+ struct evhttp *http = arg;
+ struct evhttp_cb *cb;
+
+ /* Test for different URLs */
+ TAILQ_FOREACH(cb, &http->callbacks, next) {
+ int res;
+ char *p = strchr(req->uri, '?');
+ if (p == NULL)
+ res = strcmp(cb->what, req->uri) == 0;
+ else
+ res = strncmp(cb->what, req->uri,
+ (size_t)(p - req->uri)) == 0;
+ if (res) {
+ (*cb->cb)(req, cb->cbarg);
+ return;
+ }
+ }
+
+ /* Generic call back */
+ if (http->gencb) {
+ (*http->gencb)(req, http->gencbarg);
+ return;
+ } else {
+ /* We need to send a 404 here */
+ char *fmt = "<html><head>"
+ "<title>404 Not Found</title>"
+ "</head><body>"
+ "<h1>Not Found</h1>"
+ "<p>The requested URL %s was not found on this server.</p>"
+ "</body></html>\n";
+
+ char *escaped_html = evhttp_htmlescape(req->uri);
+ struct evbuffer *buf = evbuffer_new();
+
+ evhttp_response_code(req, HTTP_NOTFOUND, "Not Found");
+
+ evbuffer_add_printf(buf, fmt, escaped_html);
+
+ free(escaped_html);
+
+ evhttp_send_page(req, buf);
+
+ evbuffer_free(buf);
+ }
+}
+
+static void
+accept_socket(int fd, short what, void *arg)
+{
+ struct evhttp *http = arg;
+ struct sockaddr_storage ss;
+ socklen_t addrlen = sizeof(ss);
+ int nfd;
+
+ if ((nfd = accept(fd, (struct sockaddr *)&ss, &addrlen)) == -1) {
+ event_warn("%s: bad accept", __func__);
+ return;
+ }
+
+ evhttp_get_request(nfd, (struct sockaddr *)&ss, addrlen,
+ evhttp_handle_request, http);
+}
+
+static int
+bind_socket(struct evhttp *http, const char *address, u_short port)
+{
+ struct event *ev = &http->bind_ev;
+ int fd;
+
+ if ((fd = make_socket(bind, address, port)) == -1)
+ return (-1);
+
+ if (listen(fd, 10) == -1) {
+ event_warn("%s: listen", __func__);
+ return (-1);
+ }
+
+ /* Schedule the socket for accepting */
+ event_set(ev, fd, EV_READ | EV_PERSIST, accept_socket, http);
+ event_add(ev, NULL);
+
+ event_debug(("Bound to port %d - Awaiting connections ... ", port));
+
+ return (0);
+}
+
+/*
+ * Start a web server on the specified address and port.
+ */
+
+struct evhttp *
+evhttp_start(const char *address, u_short port)
+{
+ struct evhttp *http;
+
+ if ((http = calloc(1, sizeof(struct evhttp))) == NULL) {
+ event_warn("%s: calloc", __func__);
+ return (NULL);
+ }
+
+ TAILQ_INIT(&http->callbacks);
+
+ if (bind_socket(http, address, port) == -1) {
+ free(http);
+ return (NULL);
+ }
+
+ return (http);
+}
+
+void
+evhttp_free(struct evhttp* http)
+{
+ struct evhttp_cb *http_cb;
+ int fd = http->bind_ev.ev_fd;
+
+ /* Remove the accepting part */
+ event_del(&http->bind_ev);
+ close(fd);
+
+ while ((http_cb = TAILQ_FIRST(&http->callbacks)) != NULL) {
+ TAILQ_REMOVE(&http->callbacks, http_cb, next);
+ free(http_cb->what);
+ free(http_cb);
+ }
+
+ free(http);
+}
+
+void
+evhttp_set_cb(struct evhttp *http, const char *uri,
+ void (*cb)(struct evhttp_request *, void *), void *cbarg)
+{
+ struct evhttp_cb *http_cb;
+
+ if ((http_cb = calloc(1, sizeof(struct evhttp_cb))) == NULL)
+ event_err(1, "%s: calloc", __func__);
+
+ http_cb->what = strdup(uri);
+ http_cb->cb = cb;
+ http_cb->cbarg = cbarg;
+
+ TAILQ_INSERT_TAIL(&http->callbacks, http_cb, next);
+}
+
+void
+evhttp_set_gencb(struct evhttp *http,
+ void (*cb)(struct evhttp_request *, void *), void *cbarg)
+{
+ http->gencb = cb;
+ http->gencbarg = cbarg;
+}
+
+/*
+ * Request related functions
+ */
+
+struct evhttp_request *
+evhttp_request_new(void (*cb)(struct evhttp_request *, void *), void *arg)
+{
+ struct evhttp_request *req = NULL;
+
+ /* Allocate request structure */
+ if ((req = calloc(1, sizeof(struct evhttp_request))) == NULL) {
+ event_warn("%s: calloc", __func__);
+ goto error;
+ }
+
+ req->fd = -1;
+ req->kind = EVHTTP_RESPONSE;
+ req->input_headers = calloc(1, sizeof(struct evkeyvalq));
+ if (req->input_headers == NULL) {
+ event_warn("%s: calloc", __func__);
+ goto error;
+ }
+ TAILQ_INIT(req->input_headers);
+
+ req->output_headers = calloc(1, sizeof(struct evkeyvalq));
+ if (req->output_headers == NULL) {
+ event_warn("%s: calloc", __func__);
+ goto error;
+ }
+ TAILQ_INIT(req->output_headers);
+
+ req->buffer = evbuffer_new();
+
+ req->cb = cb;
+ req->cb_arg = arg;
+
+ return (req);
+
+ error:
+ if (req != NULL)
+ evhttp_request_free(req);
+ return (NULL);
+}
+
+void
+evhttp_request_free(struct evhttp_request *req)
+{
+ if (req->fd != -1)
+ close(req->fd);
+ if (req->remote_host != NULL)
+ free(req->remote_host);
+ if (req->uri != NULL)
+ free(req->uri);
+ if (req->response_code_line != NULL)
+ free(req->response_code_line);
+
+ if (event_initialized(&req->ev))
+ event_del(&req->ev);
+
+ evhttp_clear_headers(req->input_headers);
+ free(req->input_headers);
+
+ evhttp_clear_headers(req->output_headers);
+ free(req->output_headers);
+
+ if (req->buffer != NULL)
+ evbuffer_free(req->buffer);
+ free(req);
+}
+
+/*
+ * Allows for inspection of the request URI
+ */
+
+const char *
+evhttp_request_uri(struct evhttp_request *req) {
+ if (req->uri == NULL)
+ event_debug(("%s: request %p has no uri\n", req));
+ return (req->uri);
+}
+
+/*
+ * Takes a file descriptor to read a request from.
+ * The callback is executed once the whole request has been read.
+ */
+
+void
+evhttp_get_request(int fd, struct sockaddr *sa, socklen_t salen,
+ void (*cb)(struct evhttp_request *, void *), void *arg)
+{
+ struct evhttp_request *req;
+ char *hostname, *portname;
+
+ name_from_addr(sa, salen, &hostname, &portname);
+ event_debug(("%s: new request from %s:%s on %d\n",
+ __func__, hostname, portname, fd));
+
+ if ((req = evhttp_request_new(cb, arg)) == NULL)
+ return;
+
+ req->fd = fd;
+ req->kind = EVHTTP_REQUEST;
+
+ if ((req->remote_host = strdup(hostname)) == NULL)
+ event_err(1, "%s: strdup", __func__);
+ req->remote_port = atoi(portname);
+
+ evhttp_start_read(req);
+}
+
+
+/*
+ * Network helper functions that we do not want to export to the rest of
+ * the world.
+ */
+
+static struct addrinfo *
+addr_from_name(char *address)
+{
+ struct addrinfo ai, *aitop;
+
+ memset(&ai, 0, sizeof (ai));
+ ai.ai_family = AF_INET;
+ ai.ai_socktype = SOCK_RAW;
+ ai.ai_flags = 0;
+ if (getaddrinfo(address, NULL, &ai, &aitop) != 0) {
+ event_warn("getaddrinfo");
+ return (NULL);
+ }
+
+ return (aitop);
+}
+
+static void
+name_from_addr(struct sockaddr *sa, socklen_t salen,
+ char **phost, char **pport)
+{
+ static char ntop[NI_MAXHOST];
+ static char strport[NI_MAXSERV];
+
+ if (getnameinfo(sa, salen,
+ ntop, sizeof(ntop), strport, sizeof(strport),
+ NI_NUMERICHOST|NI_NUMERICSERV) != 0)
+ event_err(1, "getnameinfo failed");
+
+ *phost = ntop;
+ *pport = strport;
+}
+
+/* Either connect or bind */
+
+static int
+make_socket_ai(int (*f)(int, const struct sockaddr *, socklen_t),
+ struct addrinfo *ai)
+{
+ struct linger linger;
+ int fd, on = 1;
+ int serrno;
+
+ /* Create listen socket */
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd == -1) {
+ event_warn("socket");
+ return (-1);
+ }
+
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
+ event_warn("fcntl(O_NONBLOCK)");
+ goto out;
+ }
+
+ if (fcntl(fd, F_SETFD, 1) == -1) {
+ event_warn("fcntl(F_SETFD)");
+ goto out;
+ }
+
+ setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on));
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(on));
+ linger.l_onoff = 1;
+ linger.l_linger = 5;
+ setsockopt(fd, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger));
+
+ if ((f)(fd, ai->ai_addr, ai->ai_addrlen) == -1) {
+ if (errno != EINPROGRESS) {
+ goto out;
+ }
+ }
+
+ return (fd);
+
+ out:
+ serrno = errno;
+ close(fd);
+ errno = serrno;
+ return (-1);
+}
+
+static int
+make_socket(int (*f)(int, const struct sockaddr *, socklen_t),
+ const char *address, short port)
+{
+ struct addrinfo ai, *aitop;
+ char strport[NI_MAXSERV];
+ int fd;
+
+ memset(&ai, 0, sizeof (ai));
+ ai.ai_family = AF_INET;
+ ai.ai_socktype = SOCK_STREAM;
+ ai.ai_flags = f != connect ? AI_PASSIVE : 0;
+ snprintf(strport, sizeof (strport), "%d", port);
+ if (getaddrinfo(address, strport, &ai, &aitop) != 0) {
+ event_warn("getaddrinfo");
+ return (-1);
+ }
+
+ fd = make_socket_ai(f, aitop);
+
+ freeaddrinfo(aitop);
+
+ return (fd);
+}

Added: trunk/varnish-cache/contrib/libevent/http.h
===================================================================
--- trunk/varnish-cache/contrib/libevent/http.h 2006-04-03 11:05:53 UTC (rev 97)
+++ trunk/varnish-cache/contrib/libevent/http.h 2006-04-03 11:59:08 UTC (rev 98)
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2001 Niels Provos <provos at citi.umich.edu>
+ * All rights reserved.
+ *
+ * This header file contains definitions for dealing with HTTP requests
+ * that are internal to libevent. As user of the library, you should not
+ * need to know about these.
+ */
+
+#ifndef _HTTP_H_
+#define _HTTP_H_
+
+#define HTTP_CONNECT_TIMEOUT 45
+#define HTTP_WRITE_TIMEOUT 50
+#define HTTP_READ_TIMEOUT 50
+
+#define HTTP_PREFIX "http://"
+#define HTTP_DEFAULTPORT 80
+
+struct evbuffer;
+struct addrinfo;
+
+/* A stupid connection object - maybe make this a bufferevent later */
+
+struct evhttp_connection {
+ int fd;
+ struct event ev;
+
+ char *address;
+ u_short port;
+
+ void (*cb)(struct evhttp_connection *, void *);
+ void *cb_arg;
+};
+
+enum evhttp_request_kind { EVHTTP_REQUEST, EVHTTP_RESPONSE };
+
+struct evhttp_request {
+ struct evkeyvalq *input_headers;
+ struct evkeyvalq *output_headers;
+
+ char *remote_host;
+ u_short remote_port;
+
+ enum evhttp_request_kind kind;
+ enum evhttp_cmd_type type;
+
+ char *uri; /* uri after HTTP request was parsed */
+
+ char major; /* HTTP Major number */
+ char minor; /* HTTP Minor number */
+
+ int got_firstline;
+ int response_code; /* HTTP Response code */
+ char *response_code_line; /* Readable response */
+
+ int fd;
+
+ struct event ev;
+
+ struct evbuffer *buffer;
+ int ntoread;
+
+ /* Callback */
+ void (*cb)(struct evhttp_request *, void *);
+ void *cb_arg;
+
+ void (*save_cb)(struct evhttp_request *, void *);
+ void *save_cbarg;
+};
+
+struct evhttp_cb {
+ TAILQ_ENTRY(evhttp_cb) next;
+
+ char *what;
+
+ void (*cb)(struct evhttp_request *req, void *);
+ void *cbarg;
+};
+
+struct evhttp {
+ struct event bind_ev;
+
+ TAILQ_HEAD(httpcbq, evhttp_cb) callbacks;
+
+ void (*gencb)(struct evhttp_request *req, void *);
+ void *gencbarg;
+};
+
+void evhttp_get_request(int, struct sockaddr *, socklen_t,
+ void (*)(struct evhttp_request *, void *), void *);
+
+/*
+ * Starts a connection to the specified address and invokes the callback
+ * if everything is fine.
+ */
+struct evhttp_connection *evhttp_connect(
+ const char *address, unsigned short port,
+ void (*cb)(struct evhttp_connection *, void *), void *cb_arg);
+
+/* Frees an http connection */
+void evhttp_connection_free(struct evhttp_connection *evcon);
+
+int evhttp_make_request(struct evhttp_connection *evcon,
+ struct evhttp_request *req,
+ enum evhttp_cmd_type type, const char *uri);
+
+int evhttp_hostportfile(char *, char **, u_short *, char **);
+
+int evhttp_parse_lines(struct evhttp_request *, struct evbuffer*);
+
+void evhttp_start_read(struct evhttp_request *);
+void evhttp_read_header(int, short, void *);
+void evhttp_make_header(struct evbuffer *, struct evhttp_request *);
+
+void evhttp_form_response(struct evbuffer *, struct evhttp_request *);
+void evhttp_write_buffer(struct evhttp_request *, struct evbuffer *,
+ void (*)(struct evhttp_request *, void *), void *);
+
+/* response sending HTML the data in the buffer */
+void evhttp_response_code(struct evhttp_request *, int, const char *);
+void evhttp_send_page(struct evhttp_request *, struct evbuffer *);
+void evhttp_fail(struct evhttp_request *);
+
+#endif /* _HTTP_H */

Modified: trunk/varnish-cache/contrib/libevent/kqueue.c
===================================================================
--- trunk/varnish-cache/contrib/libevent/kqueue.c 2006-04-03 11:05:53 UTC (rev 97)
+++ trunk/varnish-cache/contrib/libevent/kqueue.c 2006-04-03 11:59:08 UTC (rev 98)
@@ -75,6 +75,7 @@
int kq_recalc (struct event_base *, void *, int);
int kq_dispatch (struct event_base *, void *, struct timeval *);
int kq_insert (struct kqop *, struct kevent *);
+void kq_dealloc (void *);

const struct eventop kqops = {
"kqueue",
@@ -82,7 +83,8 @@
kq_add,
kq_del,
kq_recalc,
- kq_dispatch
+ kq_dispatch,
+ kq_dealloc
};

void *
@@ -266,10 +268,8 @@
if (!which)
continue;

- if (!(ev->ev_events & EV_PERSIST)) {
- ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
+ if (!(ev->ev_events & EV_PERSIST))
event_del(ev);
- }

event_active(ev, which,
ev->ev_events & EV_SIGNAL ? events[i].data : 1);
@@ -396,3 +396,18 @@

return (0);
}
+
+void
+kq_dealloc(void *arg)
+{
+ struct kqop *kqop = arg;
+
+ if (kqop->changes)
+ free(kqop->changes);
+ if (kqop->events)
+ free(kqop->events);
+ if (kqop->kq)
+ close(kqop->kq);
+ memset(kqop, 0, sizeof(struct kqop));
+ free(kqop);
+}

Modified: trunk/varnish-cache/contrib/libevent/log.h
===================================================================
--- trunk/varnish-cache/contrib/libevent/log.h 2006-04-03 11:05:53 UTC (rev 97)
+++ trunk/varnish-cache/contrib/libevent/log.h 2006-04-03 11:59:08 UTC (rev 98)
@@ -33,11 +33,11 @@
void event_warnx(const char *fmt, ...);
void event_msgx(const char *fmt, ...);
void _event_debugx(const char *fmt, ...);
-#undef USE_DEBUG
+
#ifdef USE_DEBUG
#define event_debug(x) _event_debugx x
#else
-#define event_debug(x)
+#define event_debug(x) do {;} while (0)
#endif

#endif

Modified: trunk/varnish-cache/contrib/libevent/poll.c
===================================================================
--- trunk/varnish-cache/contrib/libevent/poll.c 2006-04-03 11:05:53 UTC (rev 97)
+++ trunk/varnish-cache/contrib/libevent/poll.c 2006-04-03 11:59:08 UTC (rev 98)
@@ -74,14 +74,16 @@
int poll_del (void *, struct event *);
int poll_recalc (struct event_base *, void *, int);
int poll_dispatch (struct event_base *, void *, struct timeval *);
+void poll_dealloc (void *);

-struct eventop pollops = {
+const struct eventop pollops = {
"poll",
poll_init,
poll_add,
poll_del,
poll_recalc,
- poll_dispatch
+ poll_dispatch,
+ poll_dealloc
};

void *
@@ -89,11 +91,11 @@
{
struct pollop *pollop;

- /* Disable kqueue when this environment variable is set */
+ /* Disable poll when this environment variable is set */
if (getenv("EVENT_NOPOLL"))
return (NULL);

- if (!(pollop = calloc(1, sizeof(struct pollop))))
+ if (!(pollop = calloc(1, sizeof(struct pollop))))
return (NULL);

evsignal_init(&pollop->evsigmask);
@@ -182,7 +184,7 @@
return (0);

for (i = 0; i < nfds; i++) {
- int what = pop->event_set[i].revents;
+ int what = pop->event_set[i].revents;
struct event *r_ev = NULL, *w_ev = NULL;
if (!what)
continue;
@@ -355,3 +357,21 @@
poll_check_ok(pop);
return (0);
}
+
+void
+poll_dealloc(void *arg)
+{
+ struct pollop *pop = arg;
+
+ if (pop->event_set)
+ free(pop->event_set);
+ if (pop->event_r_back)
+ free(pop->event_r_back);
+ if (pop->event_w_back)
+ free(pop->event_w_back);
+ if (pop->idxplus1_by_fd)
+ free(pop->idxplus1_by_fd);
+
+ memset(pop, 0, sizeof(struct pollop));
+ free(pop);
+}

Modified: trunk/varnish-cache/contrib/libevent/rtsig.c
===================================================================
--- trunk/varnish-cache/contrib/libevent/rtsig.c 2006-04-03 11:05:53 UTC (rev 97)
+++ trunk/varnish-cache/contrib/libevent/rtsig.c 2006-04-03 11:59:08 UTC (rev 98)
@@ -1,3 +1,152 @@
+/*
+ * Copyright (c) 2006 Mathew Mills <mathewmills at mac.com>
+ * 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.
+ */
+/*
+ * Meta-level comments: You know that a kernel interface is wrong if
+ * supporting it requires three times more code than any of the other
+ * kernel interfaces supported in libevent. Niels - 2006-02-22
+ */
+/**
+
+ "RTSIG" is a shorthand for using O_ASYNC to make descriptors send
+ signals when readable/writable and to use POSIX real-time signals
+ witch are queued unlike normal signals. At first blush this may
+ seem like a alternative to epoll, but a number of problems arise
+ when attempting to build an eventloop entirely out of rtsig.
+ Still, we can use rtsig in combination with poll() to
+ provide an eventloop that allows for many thousands of sockets
+ without huge overheads implicit with using select() or poll()
+ alone. epoll and kqueue are far superior to rtsig and should be
+ used where available, but rtsig has been in standard Linux kernels
+ for a long time and have a huge installation base. epoll requires
+ special patches for 2.4 kernels and 2.6 kernels are not yet nearly
+ so ubiquitous.
+
+ rtsig problems:
+ - O_ASYNC mechanisms work only on sockets - not pipes or tty's
+
+ - O_ASYNC signals are edge-triggered, POLLIN on packet arriving
+ or socket close; POLLOUT when a socket transitions from
+ non-writable to writable. Being edge-triggered means the
+ event-handler callbacks must transition the level ( reading
+ completely the socket buffer contents ) or it will be unable to
+ reliably receive notification again.
+
+ - rtsig implementations must be intimately involved in how a
+ process dispatches signals.
+
+ - delivering signals per-event can be expensive, CPU-wise, but
+ sigtimedwait() blocks on signals only and means non-sockets
+ cannot be serviced.
+
+ Theory of operation:
+ This libevent module uses rtsig to allow us to manage a set of
+ poll-event descriptors. We can drop uninteresting fd's from the
+ pollset if the fd will send a signal when it becomes interesting
+ again.
+
+ poll() offers us level-triggering and, when we have verified the
+ level of a socket, we can trust the edge-trigger nature of the
+ ASYNC signal.
+
+ As an eventloop we must poll for external events but leverage
+ kernel functionality to sleep between events ( until the loop's
+ next scheduled timed event ).
+
+ If we are polling on any non-sockets then we simply have no choice
+ about blocking on the poll() call. If we blocked on the
+ sigtimedwait() call as rtsig papers recommend we will not wake on
+ non-socket state transitions. As part of libevent, this module
+ must support non-socket polling.
+
+ Many applications, however, do not need to poll on non-sockets and
+ so this module should be able to optimize this case by using
+ sigtimedwait(). For this reason this module can actually trigger
+ events in each of three different ways:
+ - poll() returning ready events from descriptors in the pollset
+
+ - real-time signals dequeued via sigtimedwait()
+
+ - real-time signals that call an installed signal handler which in
+ turn writes the contents of siginfo to one end of a socketpair
+ DGRAM socket. The other end of the socket is always in the
+ pollset so poll will be guaranteed to return even if the signal is
+ received before entering poll().
+
+ non-socket descriptors force us to block on the poll() for the
+ duration of a dispatch. In this case we unblock (w/ sigprocmask)
+ the managed signals just before polling. Each managed signal is
+ handled by signal_handler() which send()'s the contents of siginfo
+ over the socketpair. Otherwise, we call poll() with a timeout of
+ 0ms so it checks the levels of the fd's in the pollset and returns
+ immediately. Any fd that is a socket and has no active state is
+ removed from the pollset for the next pass -- we will rely on
+ getting a signal for events on these fd's.
+
+ The receiving end of the siginfo socketpair is in the pollset
+ (permanently) so if we are polling on non-sockets, the delivery of
+ signals immediately following sigprocmask( SIG_UNBLOCK...) will
+ result in a readable op->signal_recv_fd which ensures the poll()
+ will return immediately. If the poll() call is blocking and a
+ signal arrives ( possibly a real-time signal from a socket not in
+ the pollset ) its handler will write the data to the socketpair
+ and interrupt the poll().
+
+ After every poll call we attempt a non-blocking recv from the
+ signal_recv_fd and continue to recv and dispatch the events until
+ recv indicates the socket buffer is empty.
+
+ One might raise concerns about receiving event activations from
+ both poll() and from the rtsig data in the signal_recv_fd.
+ Fortunately, libevent is already structured for event coalescing,
+ so this issue is mitigated ( though we do some work twice for the
+ same event making us less efficient ). I suspect that the cost of
+ turning off the O_ASYNC flag on fd's in the pollset is more
+ expensive than handling some events twice. Looking at the
+ kernel's source code for setting O_ASYNC, it looks like it takes a
+ global kernel lock...
+
+ After a poll and recv-loop for the signal_recv_fd, we finally do a
+ sigtimedwait(). sigtimedwait will only block if we haven't
+ blocked in poll() and we have not enqueued events from either the
+ poll or the recv-loop. Because sigtimedwait blocks all signals
+ that are not in the set of signals to be dequeued, we need to
+ dequeue almost all signals and make sure we dispatch them
+ correctly. We dequeue any signal that is not blocked as well as
+ all libevent-managed signals. If we get a signal that is not
+ managed by libevent we lookup the sigaction for the specific
+ signal and call that function ourselves.
+
+ Finally, I should mention that getting a SIGIO signal indicates
+ that the rtsig buffer has overflowed and we have lost events.
+ This forces us to add _every_ descriptor to the pollset to recover.
+
+*/
+
+
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
@@ -20,152 +169,465 @@
#include <string.h>
#include <sys/poll.h>
#include <sys/queue.h>
-#ifndef HAVE_WORKING_RTSIG
-#include <sys/stat.h>
-#endif
+#include <sys/tree.h>
#include <unistd.h>
+#include <sys/socket.h>

-#define EVLIST_X_NORT 0x1000 /* Skip RT signals (internal) */
-
#include "event.h"
+#include "event-internal.h"
#include "log.h"
extern struct event_list signalqueue;

+#include <linux/unistd.h>
+#ifndef __NR_gettid
+#define gettid() getpid()
+#else
+
+#if ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 3)))
+_syscall0(pid_t,gettid)
+#endif
+
+#endif
+
+#define EVLIST_NONSOCK 0x1000 /* event is for a non-socket file-descriptor */
+#define EVLIST_DONTDEL 0x2000 /* event should always be in the pollset */
+#define MAXBUFFERSIZE (1024 * 1024 * 2) /* max socketbuffer for signal-spair */
+#define INIT_MAX 16 /* init/min # of fd positions in our pollset */
+
+static int signal_send_fd[_NSIG]; /* the globalend of the signal socketpair */
+static int trouble[_NSIG]; /* 1 when signal-handler cant send to signal_send_fd */
+
+struct rtdata;
+TAILQ_HEAD(rtdata_list, rtdata);
+
struct rtsigop {
- sigset_t sigs;
- struct pollfd *poll;
- struct event **toev;
- int cur, max, total;
-#ifndef HAVE_WORKING_RTSIG
- int pollmode;
-#endif
+ sigset_t sigs; /* signal mask for all _managed_ signals */
+ struct pollfd *poll; /* poll structures */
+ struct rtdata **ptodat; /* map poll_position to rtdata */
+ int cur; /* cur # fd's in a poll set */
+ int max; /* max # fd's in a poll set, start at 16 and grow as needed */
+ int total; /* count of fd's we are watching now */
+ int signo; /* the signo we use for ASYNC fd notifications */
+ int nonsock; /* number of non-socket fd's we are watching */
+ int highestfd; /* highest fd accomodated by fdtodat */
+ struct rtdata_list **fdtodat; /* map fd to rtdata ( and thus to event ) */
+ int signal_recv_fd; /* recv side of the signal_send_fd */
+ int signal_send_fd; /* recv side of the signal_send_fd */
+ struct event sigfdev; /* our own event structure for the signal fd */
};

-#define INIT_MAX 16
+struct rtdata {
+ /* rtdata holds rtsig-private state on each event */
+ TAILQ_ENTRY (rtdata) next;
+ struct event *ev;
+ int poll_position;
+};

+void *rtsig_init(void);
+int rtsig_add(void *, struct event *);
+int rtsig_del(void *, struct event *);
+int rtsig_recalc(struct event_base *, void *, int);
+int rtsig_dispatch(struct event_base *, void *, struct timeval *);
+
+struct eventop rtsigops = {
+ "rtsig",
+ rtsig_init,
+ rtsig_add,
+ rtsig_del,
+ rtsig_recalc,
+ rtsig_dispatch
+};
+
+static void
+signal_handler(int sig, siginfo_t *info, void *ctx)
+{
+ /*
+ * the signal handler for all libevent-managed signals only
+ * used if we need to do a blocking poll() call due to
+ * non-socket fd's in the pollset.
+ */
+
+ siginfo_t *i = info;
+ siginfo_t i_local;
+
+ if (trouble[sig - 1]) {
+ i_local.si_signo = SIGIO;
+ i_local.si_errno = 0;
+ i_local.si_code = 0;
+ i = &i_local;
+ trouble[sig - 1] = 0;
+ }
+
+ if (send(signal_send_fd[sig - 1], i, sizeof(*i),
+ MSG_DONTWAIT|MSG_NOSIGNAL) == -1)
+ trouble[sig - 1] = 1;
+}
+
+static void
+donothing(int fd, short event, void *arg)
+{
+ /*
+ * callback for our signal_recv_fd event structure
+ * we don't want to act on these events, we just want to wake the poll()
+ */
+};
+
+static void
+signotset(sigset_t *set)
+{
+ int i, l;
+ l = sizeof(*set) / 4;
+ for (i = 0; i < l; i++) {
+ ((unsigned *)set)[i] = ~((unsigned *)set)[i];
+ }
+}
+
+/* The next three functions manage our private data about each event struct */
+
static int
-poll_add(struct rtsigop *op, struct event *ev)
+grow_fdset(struct rtsigop *op, int newhigh)
{
- struct pollfd *pfd;
+ /*
+ * grow the fd -> rtdata array because we have encountered a
+ * new fd too high to fit in the existing array
+ */

- if (op->poll == NULL) return 0;
+ struct rtdata_list **p;
+ struct rtdata_list *datset;
+ int i,x;
+ int newcnt = (newhigh + 1) << 1;

- if (op->cur == op->max) {
- void *p;
+ if (newhigh <= op->highestfd)
+ return (0);

- p = realloc(op->poll, sizeof(*op->poll) * (op->max << 1));
- if (!p) {
- errno = ENOMEM;
- return -1;
- }
- op->poll = p;
- p = realloc(op->toev, sizeof(*op->toev) * (op->max << 1));
- if (!p) {
- op->poll = realloc(op->poll, sizeof(*op->poll) * op->max);
- errno = ENOMEM;
- return -1;
- }
- op->toev = p;
- op->max <<= 1;
- }
+ p = op->fdtodat;
+ p = realloc(op->fdtodat, sizeof(struct rtdata_list *) * newcnt);
+ if (p == NULL)
+ return (-1);
+ op->fdtodat = p;

- pfd = &op->poll[op->cur];
- pfd->fd = ev->ev_fd;
- pfd->events = 0;
- if (ev->ev_events & EV_READ) pfd->events |= POLLIN;
- if (ev->ev_events & EV_WRITE) pfd->events |= POLLOUT;
- pfd->revents = 0;
+ datset = calloc(newcnt - (op->highestfd + 1),
+ sizeof(struct rtdata_list));
+ if (datset == NULL)
+ return (-1);

- op->toev[op->cur] = ev;
- op->cur++;
+ for (i = op->highestfd + 1, x = 0; i < newcnt; i++, x++) {
+ op->fdtodat[i] = &(datset[x]);
+ TAILQ_INIT(op->fdtodat[i]);
+ }

- return 0;
+ op->highestfd = newcnt - 1;
+ return (0);
}

+static struct rtdata *
+ev2dat(struct rtsigop *op, struct event *ev, int create)
+{
+ /*
+ * given an event struct, find the dat structure that
+ * corresponds to it if create is non-zero and the rtdata
+ * structure does not exist, create it return NULL if not
+ * found
+ */
+
+ int found = 0;
+ int fd = ev->ev_fd;
+ struct rtdata *ret = NULL;
+
+ if (op->highestfd < fd && create)
+ if (grow_fdset(op, fd) == -1)
+ return (NULL);
+
+ TAILQ_FOREACH(ret, op->fdtodat[fd], next) {
+ if (ret->ev == ev) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ if (!create)
+ return (NULL);
+
+ ret = calloc(1, sizeof(struct rtdata));
+ if (ret == NULL)
+ return (NULL);
+ ret->ev = ev;
+ ret->poll_position = -1;
+ TAILQ_INSERT_TAIL(op->fdtodat[fd], ret, next);
+ }
+
+ return (ret);
+}
+
static void
+dat_del(struct rtsigop *op, struct rtdata *dat)
+{
+ /*
+ * delete our private notes about a given event struct
+ * called from rtsig_del() only
+ */
+ int fd;
+ if (dat == NULL)
+ return;
+ fd = dat->ev->ev_fd;
+
+ TAILQ_REMOVE(op->fdtodat[fd], dat, next);
+ memset(dat, 0, sizeof(*dat));
+ free(dat);
+}
+
+
+static void
+set_sigaction(int sig)
+{
+ /*
+ * set the standard handler for any libevent-managed signal,
+ * including the rtsig used for O_ASYNC notifications
+ */
+ struct sigaction act;
+
+ act.sa_flags = SA_RESTART | SA_SIGINFO;
+ sigfillset(&(act.sa_mask));
+ act.sa_sigaction = &signal_handler;
+ sigaction(sig, &act, NULL);
+}
+
+static int
+find_rt_signal()
+{
+ /* find an unused rtsignal */
+ struct sigaction act;
+ int sig = SIGRTMIN;
+
+ while (sig <= SIGRTMAX) {
+ if (sigaction(sig, NULL, &act) != 0) {
+ if (errno == EINTR)
+ continue;
+ } else {
+ if (act.sa_flags & SA_SIGINFO) {
+ if (act.sa_sigaction == NULL)
+ return (sig);
+ } else {
+ if (act.sa_handler == SIG_DFL)
+ return (sig);
+ }
+ }
+ sig++;
+ }
+ return (0);
+}
+
+/*
+ * the next three functions manage our pollset and the memory management for
+ * fd -> rtdata -> event -> poll_position maps
+ */
+
+static int
+poll_add(struct rtsigop *op, struct event *ev, struct rtdata *dat)
+{
+ struct pollfd *pfd;
+ int newmax = op->max << 1;
+ int pp;
+
+ if (op->poll == NULL)
+ return (0);
+
+ if (dat == NULL)
+ dat = ev2dat(op, ev, 0);
+
+ if (dat == NULL)
+ return (0);
+
+ pp = dat->poll_position;
+
+ if (pp != -1) {
+ pfd = &op->poll[pp];
+ if (ev->ev_events & EV_READ)
+ pfd->events |= POLLIN;
+
+ if (ev->ev_events & EV_WRITE)
+ pfd->events |= POLLOUT;
+
+ return (0);
+ }
+
+ if (op->cur == op->max) {
+ void *p = realloc(op->poll, sizeof(*op->poll) * newmax);
+ if (p == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+ op->poll = p;
+
+ p = realloc(op->ptodat, sizeof(*op->ptodat) * newmax);
+ if (p == NULL) {
+ /* shrink the pollset back down */
+ op->poll = realloc(op->poll,
+ sizeof(*op->poll) * op->max);
+ errno = ENOMEM;
+ return (-1);
+ }
+ op->ptodat = p;
+ op->max = newmax;
+ }
+
+ pfd = &op->poll[op->cur];
+ pfd->fd = ev->ev_fd;
+ pfd->revents = 0;
+ pfd->events = 0;
+
+ if (ev->ev_events & EV_READ)
+ pfd->events |= POLLIN;
+
+ if (ev->ev_events & EV_WRITE)
+ pfd->events |= POLLOUT;
+
+ op->ptodat[op->cur] = dat;
+ dat->poll_position = op->cur;
+ op->cur++;
+
+ return (0);
+}
+
+static void
poll_free(struct rtsigop *op, int n)
{
- if (op->poll == NULL) return;
+ if (op->poll == NULL)
+ return;

- op->cur--;
- if (n < op->cur) {
- memcpy(&op->poll[n], &op->poll[op->cur], sizeof(*op->poll));
- op->toev[n] = op->toev[op->cur];
- }
- if (op->max > INIT_MAX && op->cur < op->max >> 1) {
- op->max >>= 1;
- op->poll = realloc(op->poll, sizeof(*op->poll) * op->max);
- op->toev = realloc(op->toev, sizeof(*op->toev) * op->max);
- }
+ op->cur--;
+
+ if (n < op->cur) {
+ memcpy(&op->poll[n], &op->poll[op->cur], sizeof(*op->poll));
+ op->ptodat[n] = op->ptodat[op->cur];
+ op->ptodat[n]->poll_position = n;
+ }
+
+
+ /* less then half the max in use causes us to shrink */
+ if (op->max > INIT_MAX && op->cur < op->max >> 1) {
+ op->max >>= 1;
+ op->poll = realloc(op->poll, sizeof(*op->poll) * op->max);
+ op->ptodat = realloc(op->ptodat, sizeof(*op->ptodat) * op->max);
+ }
}

static void
-poll_remove(struct rtsigop *op, struct event *ev)
+poll_remove(struct rtsigop *op, struct event *ev, struct rtdata *dat)
{
- int i;
+ int pp;
+ if (dat == NULL)
+ dat = ev2dat(op, ev, 0);

- for (i = 0; i < op->cur; i++) {
- if (op->toev[i] == ev) {
- poll_free(op, i);
- break;
- }
- }
+ if (dat == NULL) return;
+
+ pp = dat->poll_position;
+ if (pp != -1) {
+ poll_free(op, pp);
+ dat->poll_position = -1;
+ }
}

static void
activate(struct event *ev, int flags)
{
- if (!(ev->ev_events & EV_PERSIST)) event_del(ev);
- event_active(ev, flags, 1);
+ /* activate an event, possibly removing one-shot events */
+ if (!(ev->ev_events & EV_PERSIST))
+ event_del(ev);
+ event_active(ev, flags, 1);
}

-void *rtsig_init(void);
-int rtsig_add(void *, struct event *);
-int rtsig_del(void *, struct event *);
-int rtsig_recalc(struct event_base *, void *, int);
-int rtsig_dispatch(struct event_base *, void *, struct timeval *);
+#define FD_CLOSEONEXEC(x) do { \
+ if (fcntl(x, F_SETFD, 1) == -1) \
+ event_warn("fcntl(%d, F_SETFD)", x); \
+} while (0)

-struct eventop rtsigops = {
- "rtsig",
- rtsig_init,
- rtsig_add,
- rtsig_del,
- rtsig_recalc,
- rtsig_dispatch
-};
-
void *
rtsig_init(void)
{
struct rtsigop *op;
+ int sockets[2];
+ int optarg;
+ struct rtdata *dat;
+ int flags;

if (getenv("EVENT_NORTSIG"))
- return (NULL);
+ goto err;

- op = malloc(sizeof(*op));
- if (op == NULL) return (NULL);
+ op = calloc(1, sizeof(*op));
+ if (op == NULL)
+ goto err;

- memset(op, 0, sizeof(*op));
-
op->max = INIT_MAX;
op->poll = malloc(sizeof(*op->poll) * op->max);
- if (op->poll == NULL) {
- free(op);
- return (NULL);
- }
- op->toev = malloc(sizeof(*op->toev) * op->max);
- if (op->toev == NULL) {
- free(op->poll);
- free(op);
- return (NULL);
- }
+ if (op->poll == NULL)
+ goto err_free_op;

+ op->signo = find_rt_signal();
+ if (op->signo == 0)
+ goto err_free_poll;
+
+ op->nonsock = 0;
+
+ if (socketpair(PF_UNIX, SOCK_DGRAM, 0, sockets) != 0)
+ goto err_free_poll;
+
+ FD_CLOSEONEXEC(sockets[0]);
+ FD_CLOSEONEXEC(sockets[1]);
+
+ signal_send_fd[op->signo - 1] = sockets[0];
+ trouble[op->signo - 1] = 0;
+ op->signal_send_fd = sockets[0];
+ op->signal_recv_fd = sockets[1];
+ flags = fcntl(op->signal_recv_fd, F_GETFL);
+ fcntl(op->signal_recv_fd, F_SETFL, flags | O_NONBLOCK);
+
+ optarg = MAXBUFFERSIZE;
+ setsockopt(signal_send_fd[op->signo - 1],
+ SOL_SOCKET, SO_SNDBUF,
+ &optarg, sizeof(optarg));
+
+ optarg = MAXBUFFERSIZE;
+ setsockopt(op->signal_recv_fd,
+ SOL_SOCKET, SO_RCVBUF,
+ &optarg, sizeof(optarg));
+
+ op->highestfd = -1;
+ op->fdtodat = NULL;
+ if (grow_fdset(op, 1) == -1)
+ goto err_close_pair;
+
+ op->ptodat = malloc(sizeof(*op->ptodat) * op->max);
+ if (op->ptodat == NULL)
+ goto err_close_pair;
+
sigemptyset(&op->sigs);
sigaddset(&op->sigs, SIGIO);
- sigaddset(&op->sigs, SIGRTMIN);
+ sigaddset(&op->sigs, op->signo);
sigprocmask(SIG_BLOCK, &op->sigs, NULL);
+ set_sigaction(SIGIO);
+ set_sigaction(op->signo);

+ event_set(&(op->sigfdev), op->signal_recv_fd, EV_READ|EV_PERSIST,
+ donothing, NULL);
+ op->sigfdev.ev_flags |= EVLIST_DONTDEL;
+ dat = ev2dat(op, &(op->sigfdev), 1);
+ poll_add(op, &(op->sigfdev), dat);
+
return (op);
+
+ err_close_pair:
+ close(op->signal_recv_fd);
+ close(signal_send_fd[op->signo - 1]);
+
+ err_free_poll:
+ free(op->poll);
+
+ err_free_op:
+ free(op);
+ err:
+ return (NULL);
}

int
@@ -173,79 +635,102 @@
{
struct rtsigop *op = (struct rtsigop *) arg;
int flags, i;
-#ifndef HAVE_WORKING_RTSIG
- struct stat st;
-#endif
+ struct stat statbuf;
+ struct rtdata *dat;

if (ev->ev_events & EV_SIGNAL) {
+ int signo = EVENT_SIGNAL(ev);
+
sigaddset(&op->sigs, EVENT_SIGNAL(ev));
- return sigprocmask(SIG_BLOCK, &op->sigs, NULL);
+ if (sigprocmask(SIG_BLOCK, &op->sigs, NULL) == -1)
+ return (-1);
+
+ set_sigaction(signo);
+
+ signal_send_fd[signo - 1] = op->signal_send_fd;
+ trouble[signo - 1] = 0;
+
+ return (0);
}

- if (!(ev->ev_events & (EV_READ | EV_WRITE))) return 0;
+ if (!(ev->ev_events & (EV_READ|EV_WRITE)))
+ return (0);

-#ifndef HAVE_WORKING_RTSIG
- if (fstat(ev->ev_fd, &st) == -1) return -1;
- if (S_ISFIFO(st.st_mode)) {
- ev->ev_flags |= EVLIST_X_NORT;
- op->pollmode++;
- }
-#endif
+ if (-1 == fstat(ev->ev_fd, &statbuf))
+ return (-1);

+ if (!S_ISSOCK(statbuf.st_mode))
+ ev->ev_flags |= EVLIST_NONSOCK;
+
flags = fcntl(ev->ev_fd, F_GETFL);
if (flags == -1)
return (-1);

if (!(flags & O_ASYNC)) {
- if (fcntl(ev->ev_fd, F_SETSIG, SIGRTMIN) == -1
- || fcntl(ev->ev_fd, F_SETOWN, (int) getpid()) == -1)
+ if (fcntl(ev->ev_fd, F_SETSIG, op->signo) == -1 ||
+ fcntl(ev->ev_fd, F_SETOWN, (int) gettid()) == -1)
return (-1);
-
- if (fcntl(ev->ev_fd, F_SETFL, flags | O_ASYNC))
+
+ /*
+ * the overhead of always handling writeable edges
+ * isn't going to be that bad...
+ */
+ if (fcntl(ev->ev_fd, F_SETFL, flags | O_ASYNC|O_RDWR))
return (-1);
}

#ifdef O_ONESIGFD
+ /*
+ * F_SETAUXFL and O_ONESIGFD are defined in a non-standard
+ * linux kernel patch to coalesce events for fds
+ */
fcntl(ev->ev_fd, F_SETAUXFL, O_ONESIGFD);
#endif

+ dat = ev2dat(op, ev, 1);
+ if (dat == NULL)
+ return (-1);
+
op->total++;
- if (poll_add(op, ev) == -1)
- goto err;
+ if (ev->ev_flags & EVLIST_NONSOCK)
+ op->nonsock++;

+ if (poll_add(op, ev, dat) == -1) {
+ /* must check the level of new fd's */
+ i = errno;
+ fcntl(ev->ev_fd, F_SETFL, flags);
+ errno = i;
+ return (-1);
+ }
+
return (0);
-
- err:
- i = errno;
- fcntl(ev->ev_fd, F_SETFL, flags);
- errno = i;
- return (-1);
}

int
rtsig_del(void *arg, struct event *ev)
{
+ struct rtdata *dat;
struct rtsigop *op = (struct rtsigop *) arg;

if (ev->ev_events & EV_SIGNAL) {
sigset_t sigs;

sigdelset(&op->sigs, EVENT_SIGNAL(ev));
-
+
sigemptyset(&sigs);
sigaddset(&sigs, EVENT_SIGNAL(ev));
return (sigprocmask(SIG_UNBLOCK, &sigs, NULL));
}

- if (!(ev->ev_events & (EV_READ | EV_WRITE)))
+ if (!(ev->ev_events & (EV_READ|EV_WRITE)))
return (0);

-#ifndef HAVE_WORKING_RTSIG
- if (ev->ev_flags & EVLIST_X_NORT)
- op->pollmode--;
-#endif
- poll_remove(op, ev);
+ dat = ev2dat(op, ev, 0);
+ poll_remove(op, ev, dat);
+ dat_del(op, dat);
op->total--;
+ if (ev->ev_flags & EVLIST_NONSOCK)
+ op->nonsock--;

return (0);
}
@@ -253,183 +738,248 @@
int
rtsig_recalc(struct event_base *base, void *arg, int max)
{
- return (0);
+ return (0);
}

-int
-rtsig_dispatch(struct event_base *base, void *arg, struct timeval *tv)
+/*
+ * the following do_X functions implement the different stages of a single
+ * eventloop pass: poll(), recv(sigsock), sigtimedwait()
+ *
+ * do_siginfo_dispatch() is a common factor to both do_sigwait() and
+ * do_signals_from_socket().
+ */
+
+static inline int
+do_poll(struct rtsigop *op, struct timespec *ts)
{
- struct rtsigop *op = (struct rtsigop *) arg;
- struct timespec ts;
- int res, i;
+ int res = 0;
+ int i = 0;
+
+ if (op->cur > 1) {
+ /* non-empty poll set (modulo the signalfd) */
+ if (op->nonsock) {
+ int timeout = ts->tv_nsec / 1000000 + ts->tv_sec * 1000;
+
+ sigprocmask(SIG_UNBLOCK, &(op->sigs), NULL);

- if (op->poll == NULL)
- goto retry_poll;
-#ifndef HAVE_WORKING_RTSIG
- if (op->pollmode)
- goto poll_all;
-#endif
+ res = poll(op->poll, op->cur, timeout);
+
+ sigprocmask(SIG_BLOCK, &(op->sigs), NULL);
+
+ ts->tv_sec = 0;
+ ts->tv_nsec = 0;
+ } else {
+ res = poll(op->poll, op->cur, 0);
+ }

- if (op->cur) {
- ts.tv_sec = ts.tv_nsec = 0;
- } else {
- ts.tv_sec = tv->tv_sec;
- ts.tv_nsec = tv->tv_usec * 1000;
- }
-
- for (;;) {
- siginfo_t info;
- struct event *ev;
- int signum;
-
- signum = sigtimedwait(&op->sigs, &info, &ts);
-
- if (signum == -1) {
- if (errno == EAGAIN)
- break;
+ if (res < 0) {
return (errno == EINTR ? 0 : -1);
+ } else if (res) {
+ ts->tv_sec = 0;
+ ts->tv_nsec = 0;
}

- ts.tv_sec = ts.tv_nsec = 0;
+ i = 0;
+ while (i < op->cur) {
+ struct rtdata *dat = op->ptodat[i];
+ struct event *ev = dat->ev;

- if (signum == SIGIO) {
-#ifndef HAVE_WORKING_RTSIG
- poll_all:
-#endif
- free(op->poll);
- free(op->toev);
- retry_poll:
- op->cur = 0;
- op->max = op->total;
- op->poll = malloc(sizeof(*op->poll) * op->total);
- if (op->poll == NULL)
- return (-1);
- op->toev = malloc(sizeof(*op->toev) * op->total);
- if (op->toev == NULL) {
- free(op->poll);
- op->poll = NULL;
- return (-1);
+ if (op->poll[i].revents) {
+ int flags = 0;
+
+ if (op->poll[i].revents & (POLLIN | POLLERR))
+ flags |= EV_READ;
+
+ if (op->poll[i].revents & POLLOUT)
+ flags |= EV_WRITE;
+
+ if (!(ev->ev_events & EV_PERSIST)) {
+ poll_remove(op, ev, op->ptodat[i]);
+ event_del(ev);
+ } else {
+ i++;
+ }
+
+ event_active(ev, flags, 1);
+ } else {
+ if (ev->ev_flags & (EVLIST_NONSOCK|EVLIST_DONTDEL)) {
+ i++;
+ } else {
+ poll_remove(op, ev, op->ptodat[i]);
+ }
}
+ }
+ }
+ return (res);
+}

- TAILQ_FOREACH(ev, &base->eventqueue, ev_next)
- if (!(ev->ev_flags & EVLIST_X_NORT))
- poll_add(op, ev);
+static inline int
+do_siginfo_dispatch(struct event_base *base, struct rtsigop *op,
+ siginfo_t *info)
+{
+ int signum;
+ struct rtdata *dat, *next_dat;
+ struct event *ev, *next_ev;

- break;
- }
+ if (info == NULL)
+ return (-1);

- if (signum == SIGRTMIN) {
- int flags, i, sigok = 0;
+ signum = info->si_signo;
+ if (signum == op->signo) {
+ int flags, sigok = 0;
+ flags = 0;

- if (info.si_band <= 0) { /* SI_SIGIO */
- flags = EV_READ | EV_WRITE;
- } else {
- flags = 0;
- if (info.si_band & POLLIN) flags |= EV_READ;
- if (info.si_band & POLLOUT) flags |= EV_WRITE;
- if (!flags) continue;
- }
+ if (info->si_band & (POLLIN|POLLERR))
+ flags |= EV_READ;
+ if (info->si_band & POLLOUT)
+ flags |= EV_WRITE;

- for (i = 0; flags && i < op->cur; i++) {
- ev = op->toev[i];
+ if (!flags)
+ return (0);

- if (ev->ev_fd == info.si_fd) {
- flags &= ~ev->ev_events;
- sigok = 1;
- }
+ if (info->si_fd > op->highestfd)
+ return (-1);
+
+ dat = TAILQ_FIRST(op->fdtodat[info->si_fd]);
+ while (dat != TAILQ_END(op->fdtodat[info->si_fd])) {
+ next_dat = TAILQ_NEXT(dat, next);
+ if (flags & dat->ev->ev_events) {
+ ev = dat->ev;
+ poll_add(op, ev, dat);
+ activate(ev, flags & ev->ev_events);
+ sigok = 1;
}
-
- for (ev = TAILQ_FIRST(&base->eventqueue);
- flags && ev != TAILQ_END(&base->eventqueue);
- ev = TAILQ_NEXT(ev, ev_next)) {
- if (ev->ev_fd == info.si_fd) {
- if (flags & ev->ev_events) {
- i = poll_add(op, ev);
- if (i == -1) return -1;
- flags &= ~ev->ev_events;
- }
- sigok = 1;
+ dat = next_dat;
+ }
+ } else if (signum == SIGIO) {
+ TAILQ_FOREACH(ev, &base->eventqueue, ev_next) {
+ if (ev->ev_events & (EV_READ|EV_WRITE))
+ poll_add(op, ev, NULL);
+ }
+ return (1); /* 1 means the caller should poll() again */
+
+ } else if (sigismember(&op->sigs, signum)) {
+ /* managed signals are queued */
+ ev = TAILQ_FIRST(&signalqueue);
+ while (ev != TAILQ_END(&signalqueue)) {
+ next_ev = TAILQ_NEXT(ev, ev_signal_next);
+ if (EVENT_SIGNAL(ev) == signum)
+ activate(ev, EV_SIGNAL);
+ ev = next_ev;
+ }
+ } else {
+ /* dispatch unmanaged signals immediately */
+ struct sigaction sa;
+ if (sigaction(signum, NULL, &sa) == 0) {
+ if ((sa.sa_flags & SA_SIGINFO) && sa.sa_sigaction) {
+ (*sa.sa_sigaction)(signum, info, NULL);
+ } else if (sa.sa_handler) {
+ if ((int)sa.sa_handler != 1)
+ (*sa.sa_handler)(signum);
+ } else {
+ if (signum != SIGCHLD) {
+ /* non-blocked SIG_DFL */
+ kill(gettid(), signum);
}
}
-
- if (!sigok) {
- flags = fcntl(info.si_fd, F_GETFL);
- if (flags == -1) return -1;
- fcntl(info.si_fd, F_SETFL, flags & ~O_ASYNC);
- }
- } else {
- TAILQ_FOREACH(ev, &signalqueue, ev_signal_next) {
- if (EVENT_SIGNAL(ev) == signum)
- activate(ev, EV_SIGNAL);
- }
}
}

- if (!op->cur)
- return (0);
+ return (0);
+}

- res = poll(op->poll, op->cur, tv->tv_sec * 1000 +
- (tv->tv_usec + 999) / 1000);
- if (res < 0)
- return (-1);
+/*
+ * return 1 if we should poll again
+ * return 0 if we are all set
+ * return -1 on error
+ */
+static inline int
+do_sigwait(struct event_base *base, struct rtsigop *op, struct timespec *ts,
+ sigset_t *sigs)
+{
+ for (;;) {
+ siginfo_t info;
+ int signum;

- i = 0;
-#ifdef HAVE_WORKING_RTSIG
- while (i < res) {
-#else
- while (i < op->cur) {
-#endif
- if (op->poll[i].revents) {
- int flags = 0;
- struct event *ev = op->toev[i];
+ signum = sigtimedwait(sigs, &info, ts);

- if (op->poll[i].revents & POLLIN)
- flags |= EV_READ;
- if (op->poll[i].revents & POLLOUT)
- flags |= EV_WRITE;
+ ts->tv_sec = 0;
+ ts->tv_nsec = 0;

- if (!(ev->ev_events & EV_PERSIST)) {
- event_del(ev);
- res--;
- } else {
- i++;
- }
- event_active(ev, flags, 1);
- } else {
-#ifndef HAVE_WORKING_RTSIG
- if (op->toev[i]->ev_flags & EVLIST_X_NORT) {
- i++;
- res++;
- continue;
- }
-#endif
- for (;;) {
- op->cur--;
- if (i == op->cur)
- break;
- if (op->poll[op->cur].revents) {
- memcpy(&op->poll[i], &op->poll[op->cur], sizeof(*op->poll));
- op->toev[i] = op->toev[op->cur];
- break;
- }
- }
+ if (signum == -1) {
+ if (errno == EAGAIN || errno == EINTR)
+ return (0);
+ return (-1);
+ } else if (1 == do_siginfo_dispatch(base, op, &info)) {
+ return (1);
}
}
-#ifdef HAVE_WORKING_RTSIG
- op->cur = res;
-#endif

- if (!op->cur) {
- op->max = INIT_MAX;
- free(op->poll);
- free(op->toev);
- /* We just freed it, we shouldn't have a problem getting it back. */
- op->poll = malloc(sizeof(*op->poll) * op->max);
- op->toev = malloc(sizeof(*op->toev) * op->max);
+ /* NOTREACHED */
+}

- if (op->poll == NULL || op->toev == NULL)
- event_err(1, "%s: malloc");
+static inline int
+do_signals_from_socket(struct event_base *base, struct rtsigop *op,
+ struct timespec *ts)
+{
+ int fd = op->signal_recv_fd;
+ siginfo_t info;
+ int res;
+
+ for (;;) {
+ res = recv(fd, &info, sizeof(info), MSG_NOSIGNAL);
+ if (res == -1) {
+ if (errno == EAGAIN)
+ return (0);
+ if (errno == EINTR)
+ continue;
+ return (-1);
+ } else {
+ ts->tv_sec = 0;
+ ts->tv_nsec = 0;
+ if (1 == do_siginfo_dispatch(base, op, &info))
+ return (1);
+ }
}
+ /* NOTREACHED */
+}

+int
+rtsig_dispatch(struct event_base *base, void *arg, struct timeval *tv)
+{
+ struct rtsigop *op = (struct rtsigop *) arg;
+ struct timespec ts;
+ int res;
+ sigset_t sigs;
+
+ ts.tv_sec = tv->tv_sec;
+ ts.tv_nsec = tv->tv_usec * 1000;
+
+ poll_for_level:
+ res = do_poll(op, &ts); /* ts can be modified in do_XXX() */
+
+ res = do_signals_from_socket(base, op, &ts);
+ if (res == 1)
+ goto poll_for_level;
+ else if (res == -1)
+ return (-1);
+
+ /*
+ * the mask = managed_signals | unblocked-signals
+ * MM - if this is not blocking do we need to cast the net this wide?
+ */
+ sigemptyset(&sigs);
+ sigprocmask(SIG_BLOCK, &sigs, &sigs);
+ signotset(&sigs);
+ sigorset(&sigs, &sigs, &op->sigs);
+
+ res = do_sigwait(base, op, &ts, &sigs);
+
+ if (res == 1)
+ goto poll_for_level;
+ else if (res == -1)
+ return (-1);
+
return (0);
}
+

Modified: trunk/varnish-cache/contrib/libevent/select.c
===================================================================
--- trunk/varnish-cache/contrib/libevent/select.c 2006-04-03 11:05:53 UTC (rev 97)
+++ trunk/varnish-cache/contrib/libevent/select.c 2006-04-03 11:59:08 UTC (rev 98)
@@ -76,6 +76,7 @@
int select_del (void *, struct event *);
int select_recalc (struct event_base *, void *, int);
int select_dispatch (struct event_base *, void *, struct timeval *);
+void select_dealloc (void *);

const struct eventop selectops = {
"select",
@@ -83,7 +84,8 @@
select_add,
select_del,
select_recalc,
- select_dispatch
+ select_dispatch,
+ select_dealloc
};

static int select_resize(struct selectop *sop, int fdsz);
@@ -93,7 +95,7 @@
{
struct selectop *sop;

- /* Disable kqueue when this environment variable is set */
+ /* Disable select when this environment variable is set */
if (getenv("EVENT_NOSELECT"))
return (NULL);

@@ -131,7 +133,7 @@

}
#else
-#define check_selectop(sop)
+#define check_selectop(sop) do {;} while (0)
#endif

/*
@@ -350,3 +352,25 @@
check_selectop(sop);
return (0);
}
+
+void
+select_dealloc(void *arg)
+{
+ struct selectop *sop = arg;
+
+ if (sop->event_readset_in)
+ free(sop->event_readset_in);
+ if (sop->event_writeset_in)
+ free(sop->event_writeset_in);
+ if (sop->event_readset_out)
+ free(sop->event_readset_out);
+ if (sop->event_writeset_out)
+ free(sop->event_writeset_out);
+ if (sop->event_r_by_fd)
+ free(sop->event_r_by_fd);
+ if (sop->event_w_by_fd)
+ free(sop->event_w_by_fd);
+
+ memset(sop, 0, sizeof(struct selectop));
+ free(sop);
+}

Modified: trunk/varnish-cache/contrib/libevent/signal.c
===================================================================
--- trunk/varnish-cache/contrib/libevent/signal.c 2006-04-03 11:05:53 UTC (rev 97)
+++ trunk/varnish-cache/contrib/libevent/signal.c 2006-04-03 11:59:08 UTC (rev 98)
@@ -26,7 +26,9 @@
* (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 HAVE_CONFIG_H
#include "config.h"
+#endif

#include <sys/types.h>
#ifdef HAVE_SYS_TIME_H
@@ -52,7 +54,7 @@

extern struct event_list signalqueue;

-static short evsigcaught[NSIG];
+static sig_atomic_t evsigcaught[NSIG];
static int needrecalc;
volatile sig_atomic_t evsignal_caught = 0;

@@ -61,11 +63,12 @@
static int ev_signal_added;

/* Callback for when the signal handler write a byte to our signaling socket */
-static void evsignal_cb(int fd, short what, void *arg)
+static void
+evsignal_cb(int fd, short what, void *arg)
{
static char signals[100];
struct event *ev = arg;
- int n;
+ ssize_t n;

n = read(fd, signals, sizeof(signals));
if (n == -1)
@@ -98,6 +101,8 @@
FD_CLOSEONEXEC(ev_signal_pair[0]);
FD_CLOSEONEXEC(ev_signal_pair[1]);

+ fcntl(ev_signal_pair[0], F_SETFL, O_NONBLOCK);
+
event_set(&ev_signal, ev_signal_pair[1], EV_READ,
evsignal_cb, &ev_signal);
ev_signal.ev_flags |= EVLIST_INTERNAL;
@@ -107,12 +112,12 @@
evsignal_add(sigset_t *evsigmask, struct event *ev)
{
int evsignal;
-
+
if (ev->ev_events & (EV_READ|EV_WRITE))
event_errx(1, "%s: EV_SIGNAL incompatible use", __func__);
evsignal = EVENT_SIGNAL(ev);
sigaddset(evsigmask, evsignal);
-
+
return (0);
}

@@ -135,11 +140,14 @@
static void
evsignal_handler(int sig)
{
+ int save_errno = errno;
+
evsigcaught[sig]++;
evsignal_caught = 1;

/* Wake up our notification mechanism */
write(ev_signal_pair[0], "a", 1);
+ errno = save_errno;
}

int
@@ -147,7 +155,7 @@
{
struct sigaction sa;
struct event *ev;
-
+
if (!ev_signal_added) {
ev_signal_added = 1;
event_add(&ev_signal, NULL);
@@ -159,13 +167,13 @@

if (sigprocmask(SIG_BLOCK, evsigmask, NULL) == -1)
return (-1);
-
+
/* Reinstall our signal handler. */
memset(&sa, 0, sizeof(sa));
sa.sa_handler = evsignal_handler;
sa.sa_mask = *evsigmask;
sa.sa_flags |= SA_RESTART;
-
+
TAILQ_FOREACH(ev, &signalqueue, ev_signal_next) {
if (sigaction(EVENT_SIGNAL(ev), &sa, NULL) == -1)
return (-1);
@@ -187,7 +195,7 @@
evsignal_process(void)
{
struct event *ev;
- short ncalls;
+ sig_atomic_t ncalls;

TAILQ_FOREACH(ev, &signalqueue, ev_signal_next) {
ncalls = evsigcaught[EVENT_SIGNAL(ev)];

Added: trunk/varnish-cache/contrib/libevent/strlcpy.c
===================================================================
--- trunk/varnish-cache/contrib/libevent/strlcpy.c 2006-04-03 11:05:53 UTC (rev 97)
+++ trunk/varnish-cache/contrib/libevent/strlcpy.c 2006-04-03 11:59:08 UTC (rev 98)
@@ -0,0 +1,74 @@
+/* $OpenBSD: strlcpy.c,v 1.5 2001/05/13 15:40:16 deraadt Exp $ */
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller at courtesan.com>
+ * 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 ``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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: strlcpy.c,v 1.5 2001/05/13 15:40:16 deraadt Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+
+/*
+ * Copy src to string dst of size siz. At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t
+strlcpy(dst, src, siz)
+ char *dst;
+ const char *src;
+ size_t siz;
+{
+ register char *d = dst;
+ register const char *s = src;
+ register size_t n = siz;
+
+ /* Copy as many bytes as will fit */
+ if (n != 0 && --n != 0) {
+ do {
+ if ((*d++ = *s++) == 0)
+ break;
+ } while (--n != 0);
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0) {
+ if (siz != 0)
+ *d = '\0'; /* NUL-terminate dst */
+ while (*s++)
+ ;
+ }
+
+ return(s - src - 1); /* count does not include NUL */
+}

Modified: trunk/varnish-cache/contrib/libevent/test/Makefile.am
===================================================================
--- trunk/varnish-cache/contrib/libevent/test/Makefile.am 2006-04-03 11:05:53 UTC (rev 97)
+++ trunk/varnish-cache/contrib/libevent/test/Makefile.am 2006-04-03 11:59:08 UTC (rev 98)
@@ -4,20 +4,28 @@
CPPFPLAGS = -I..
CFLAGS = -I../compat @CFLAGS@

+EXTRA_DIST = regress.rpc
+
noinst_PROGRAMS = test-init test-eof test-weof test-time regress bench

-test_init_sources = test-init.c
-test_eof_sources = test-eof.c
-test_weof_sources = test-weof.c
-test_time_sources = test-time.c
-regress_sources = regress.c
-bench_sources = bench.c
+BUILT_SOURCES = regress.gen.c regress.gen.h
+test_init_SOURCES = test-init.c
+test_eof_SOURCES = test-eof.c
+test_weof_SOURCES = test-weof.c
+test_time_SOURCES = test-time.c
+regress_SOURCES = regress.c regress.h regress_http.c \
+ regress.gen.c regress.gen.h
+bench_SOURCES = bench.c

+regress.gen.c regress.gen.h: regress.rpc
+ ../event_rpcgen.py regress.rpc
+
DISTCLEANFILES = *~
+CLEANFILES = regress.gen.h regress.gen.c

test: test-init test-eof test-weof test-time regress

verify: test
@./test.sh

-bench test-init test-eof test-weof test-time regress: ../libevent.la
+bench test-init test-eof test-weof test-time: ../libevent.la

Modified: trunk/varnish-cache/contrib/libevent/test/regress.c
===================================================================
--- trunk/varnish-cache/contrib/libevent/test/regress.c 2006-04-03 11:05:53 UTC (rev 97)
+++ trunk/varnish-cache/contrib/libevent/test/regress.c 2006-04-03 11:59:08 UTC (rev 98)
@@ -39,21 +39,28 @@
#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>
#endif
+#include <netdb.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

-#include <event.h>
+#include "event.h"
+#include "log.h"
+#include "http.h"

-static int pair[2];
-static int test_ok;
+#include "regress.h"
+#include "regress.gen.h"
+
+int pair[2];
+int test_ok;
static int called;
static char wbuf[4096];
static char rbuf[4096];
@@ -276,7 +283,7 @@
}

void
-test1(void)
+test_simpleread(void)
{
struct event ev;

@@ -295,7 +302,7 @@
}

void
-test2(void)
+test_simplewrite(void)
{
struct event ev;

@@ -311,7 +318,7 @@
}

void
-test3(void)
+test_multiple(void)
{
struct event ev, ev2;
int i;
@@ -340,7 +347,7 @@
}

void
-test4(void)
+test_persistent(void)
{
struct event ev, ev2;
int i;
@@ -369,7 +376,7 @@
}

void
-test5(void)
+test_combined(void)
{
struct both r1, r2, w1, w2;

@@ -404,7 +411,7 @@
}

void
-test6(void)
+test_simpletimeout(void)
{
struct timeval tv;
struct event ev;
@@ -424,7 +431,7 @@

#ifndef WIN32
void
-test7(void)
+test_simplesignal(void)
{
struct event ev;
struct itimerval itv;
@@ -447,7 +454,7 @@
#endif

void
-test8(void)
+test_loopexit(void)
{
struct timeval tv, tv_start, tv_end;
struct event ev;
@@ -477,6 +484,21 @@
}

void
+test_evbuffer(void) {
+ setup_test("Evbuffer: ");
+
+ struct evbuffer *evb = evbuffer_new();
+
+ evbuffer_add_printf(evb, "%s/%d", "hello", 1);
+
+ if (EVBUFFER_LENGTH(evb) == 7 &&
+ strcmp(EVBUFFER_DATA(evb), "hello/1") == 0)
+ test_ok = 1;
+
+ cleanup_test();
+}
+
+void
readcb(struct bufferevent *bev, void *arg)
{
if (EVBUFFER_LENGTH(bev->input) == 8333) {
@@ -499,7 +521,7 @@
}

void
-test9(void)
+test_bufferevent(void)
{
struct bufferevent *bev1, *bev2;
char buffer[8333];
@@ -637,7 +659,211 @@
cleanup_test();
}

+int decode_int(u_int32_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 */
+ 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();
+ u_int32_t integers[TEST_MAX_INT] = {
+ 0xaf0, 0x1000, 0x1, 0xdeadbeef, 0x00, 0xbef000
+ };
+ u_int32_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()
+{
+ 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);
+ 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 *kill;
+ struct run *run;
+ struct evbuffer *tmp = evbuffer_new();
+ 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, kill, &kill) == -1) {
+ fprintf(stderr, "Failed to set kill message.\n");
+ exit(1);
+ }
+
+ EVTAG_ASSIGN(kill, weapon, "feather");
+ EVTAG_ASSIGN(kill, action, "tickle");
+
+ for (i = 0; i < 3; ++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");
+ }
+
+ 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);
+ }
+
+ if (!EVTAG_HAS(msg2, from_name) ||
+ !EVTAG_HAS(msg2, to_name) ||
+ !EVTAG_HAS(msg2, kill)) {
+ fprintf(stderr, "Missing data structures.\n");
+ exit(1);
+ }
+
+ if (EVTAG_LEN(msg2, run) != 3) {
+ fprintf(stderr, "Wrong number of run messages.\n");
+ exit(1);
+ }
+
+ msg_free(msg);
+ msg_free(msg2);
+
+ fprintf(stdout, "OK\n");
+}
+
int
main (int argc, char **argv)
{
@@ -656,23 +882,27 @@
/* Initalize the event library */
event_base = event_init();

- test1();
+ http_suite();
+
+ test_simpleread();

- test2();
+ test_simplewrite();

- test3();
+ test_multiple();

- test4();
+ test_persistent();

- test5();
+ test_combined();

- test6();
+ test_simpletimeout();
#ifndef WIN32
- test7();
+ test_simplesignal();
#endif
- test8();
+ test_loopexit();

- test9();
+ test_evbuffer();
+
+ test_bufferevent();

test_priorities(1);
test_priorities(2);
@@ -680,6 +910,12 @@

test_multiple_events_for_same_fd();

+ test_want_only_once();
+
+ evtag_test();
+
+ rpc_test();
+
return (0);
}


Added: trunk/varnish-cache/contrib/libevent/test/regress.h
===================================================================
--- trunk/varnish-cache/contrib/libevent/test/regress.h 2006-04-03 11:05:53 UTC (rev 97)
+++ trunk/varnish-cache/contrib/libevent/test/regress.h 2006-04-03 11:59:08 UTC (rev 98)
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2000-2004 Niels Provos <provos at 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.
+ */
+#ifndef _REGRESS_H_
+#define _REGRESS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void http_suite(void);
+void http_basic_test(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _REGRESS_H_ */

Added: trunk/varnish-cache/contrib/libevent/test/regress.rpc
===================================================================
--- trunk/varnish-cache/contrib/libevent/test/regress.rpc 2006-04-03 11:05:53 UTC (rev 97)
+++ trunk/varnish-cache/contrib/libevent/test/regress.rpc 2006-04-03 11:59:08 UTC (rev 98)
@@ -0,0 +1,17 @@
+/* tests data packing and unpacking */
+
+struct msg {
+ string from_name = 1;
+ string to_name = 2;
+ optional struct[kill] kill = 3;
+ array struct[run] run = 4;
+}
+
+struct kill {
+ string weapon = 1;
+ string action = 2;
+}
+
+struct run {
+ string how = 1;
+}

Added: trunk/varnish-cache/contrib/libevent/test/regress_http.c
===================================================================
--- trunk/varnish-cache/contrib/libevent/test/regress_http.c 2006-04-03 11:05:53 UTC (rev 97)
+++ trunk/varnish-cache/contrib/libevent/test/regress_http.c 2006-04-03 11:59:08 UTC (rev 98)
@@ -0,0 +1,439 @@
+/*
+ * Copyright (c) 2003-2006 Niels Provos <provos at 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>
+#endif
+#include <netdb.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "event.h"
+#include "log.h"
+#include "http.h"
+
+extern int pair[];
+extern int test_ok;
+
+static struct evhttp *http;
+
+void http_basic_cb(struct evhttp_request *req, void *arg);
+void http_post_cb(struct evhttp_request *req, void *arg);
+
+struct evhttp *
+http_setup(short *pport)
+{
+ int i;
+ struct evhttp *myhttp;
+ short port = -1;
+
+ /* Try a few different ports */
+ for (i = 0; i < 50; ++i) {
+ myhttp = evhttp_start("127.0.0.1", 8080 + i);
+ if (myhttp != NULL) {
+ port = 8080 + i;
+ break;
+ }
+ }
+
+ if (port == -1)
+ event_errx(1, "Could not start web server");
+
+ /* Register a callback for certain types of requests */
+ evhttp_set_cb(myhttp, "/test", http_basic_cb, NULL);
+ evhttp_set_cb(myhttp, "/postit", http_post_cb, NULL);
+
+ *pport = port;
+ return (myhttp);
+}
+
+int
+http_connect(const char *address, u_short port)
+{
+ /* Stupid code for connecting */
+ struct addrinfo ai, *aitop;
+ char strport[NI_MAXSERV];
+ int fd;
+
+ memset(&ai, 0, sizeof (ai));
+ ai.ai_family = AF_INET;
+ ai.ai_socktype = SOCK_STREAM;
+ snprintf(strport, sizeof (strport), "%d", port);
+ if (getaddrinfo(address, strport, &ai, &aitop) != 0) {
+ event_warn("getaddrinfo");
+ return (-1);
+ }
+
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd == -1)
+ event_err(1, "socket failed");
+
+ if (connect(fd, aitop->ai_addr, aitop->ai_addrlen) == -1)
+ event_err(1, "connect failed");
+
+ freeaddrinfo(aitop);
+
+ return (fd);
+}
+
+void
+http_readcb(struct bufferevent *bev, void *arg)
+{
+ const char *what = "This is funny";
+
+ event_debug(("%s: %s\n", __func__, EVBUFFER_DATA(bev->input)));
+
+ if (evbuffer_find(bev->input, what, strlen(what)) != NULL) {
+ struct evhttp_request *req = evhttp_request_new(NULL, NULL);
+ req->kind = EVHTTP_RESPONSE;
+ int done = evhttp_parse_lines(req, bev->input);
+
+ if (done == 1 &&
+ evhttp_find_header(req->input_headers,
+ "Content-Type") != NULL)
+ test_ok++;
+ evhttp_request_free(req);
+ bufferevent_disable(bev, EV_READ);
+ event_loopexit(NULL);
+ }
+}
+
+void
+http_writecb(struct bufferevent *bev, void *arg)
+{
+ if (EVBUFFER_LENGTH(bev->output) == 0) {
+ /* enable reading of the reply */
+ bufferevent_enable(bev, EV_READ);
+ test_ok++;
+ }
+}
+
+void
+http_errorcb(struct bufferevent *bev, short what, void *arg)
+{
+ test_ok = -2;
+ event_loopexit(NULL);
+}
+
+void
+http_basic_cb(struct evhttp_request *req, void *arg)
+{
+ event_debug((stderr, "%s: called\n", __func__));
+
+ struct evbuffer *evb = evbuffer_new();
+ evbuffer_add_printf(evb, "This is funny");
+
+ evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb);
+
+ evbuffer_free(evb);
+}
+
+void
+http_basic_test(void)
+{
+ struct bufferevent *bev;
+ int fd;
+ char *http_request;
+ short port = -1;
+
+ test_ok = 0;
+ fprintf(stdout, "Testing Basic HTTP Server: ");
+
+ http = http_setup(&port);
+
+ fd = http_connect("127.0.0.1", port);
+
+ /* Stupid thing to send a request */
+ bev = bufferevent_new(fd, http_readcb, http_writecb,
+ http_errorcb, NULL);
+
+ http_request =
+ "GET /test HTTP/1.1\r\n"
+ "Host: somehost \r\n"
+ "\r\n";
+
+ bufferevent_write(bev, http_request, strlen(http_request));
+
+ event_dispatch();
+
+ bufferevent_free(bev);
+ close(fd);
+
+ evhttp_free(http);
+
+ if (test_ok != 2) {
+ fprintf(stdout, "FAILED\n");
+ exit(1);
+ }
+
+ fprintf(stdout, "OK\n");
+}
+
+void http_connectcb(struct evhttp_connection *evcon, void *arg);
+
+void
+http_connection_test(void)
+{
+ short port = -1;
+ struct evhttp_connection *evcon = NULL;
+
+ test_ok = 0;
+ fprintf(stdout, "Testing Basic HTTP Connection: ");
+
+ http = http_setup(&port);
+
+ evcon = evhttp_connect("127.0.0.1", port, http_connectcb, NULL);
+ if (evcon == NULL) {
+ fprintf(stdout, "FAILED\n");
+ exit(1);
+ }
+
+ event_dispatch();
+
+ evhttp_connection_free(evcon);
+ evhttp_free(http);
+
+ if (test_ok != 1) {
+ fprintf(stdout, "FAILED\n");
+ exit(1);
+ }
+
+ fprintf(stdout, "OK\n");
+}
+
+void http_request_done(struct evhttp_request *, void *);
+
+void
+http_connectcb(struct evhttp_connection *evcon, void *arg)
+{
+ struct evhttp_request *req = NULL;
+
+ if (evcon == NULL) {
+ fprintf(stdout, "FAILED\n");
+ exit (1);
+ }
+
+ /*
+ * At this point, we want to schedule a request to the HTTP
+ * server using our make request method.
+ */
+
+ req = evhttp_request_new(http_request_done, NULL);
+
+ /* Add the information that we care about */
+ evhttp_add_header(req->output_headers, "Host", "somehost");
+
+ if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
+ fprintf(stdout, "FAILED\n");
+ exit(1);
+ }
+}
+
+void
+http_request_done(struct evhttp_request *req, void *arg)
+{
+ const char *what = "This is funny";
+
+ if (req->response_code != HTTP_OK) {
+
+ fprintf(stderr, "FAILED\n");
+ exit(1);
+ }
+
+ if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) {
+ fprintf(stderr, "FAILED\n");
+ exit(1);
+ }
+
+ if (EVBUFFER_LENGTH(req->buffer) != strlen(what)) {
+ fprintf(stderr, "FAILED\n");
+ exit(1);
+ }
+
+ if (memcmp(EVBUFFER_DATA(req->buffer), what, strlen(what)) != 0) {
+ fprintf(stderr, "FAILED\n");
+ exit(1);
+ }
+
+ test_ok = 1;
+ event_loopexit(NULL);
+}
+
+/*
+ * HTTP POST test.
+ */
+
+void http_connect_forpostcb(struct evhttp_connection *evcon, void *arg);
+
+void
+http_post_test(void)
+{
+ short port = -1;
+ struct evhttp_connection *evcon = NULL;
+
+ test_ok = 0;
+ fprintf(stdout, "Testing HTTP POST Request: ");
+
+ http = http_setup(&port);
+
+ evcon = evhttp_connect("127.0.0.1", port, http_connect_forpostcb, NULL);
+ if (evcon == NULL) {
+ fprintf(stdout, "FAILED\n");
+ exit(1);
+ }
+
+ event_dispatch();
+
+ evhttp_connection_free(evcon);
+ evhttp_free(http);
+
+ if (test_ok != 1) {
+ fprintf(stdout, "FAILED\n");
+ exit(1);
+ }
+
+ fprintf(stdout, "OK\n");
+}
+
+void http_postrequest_done(struct evhttp_request *, void *);
+
+#define POST_DATA "Okay. Not really printf"
+
+void
+http_connect_forpostcb(struct evhttp_connection *evcon, void *arg)
+{
+ struct evhttp_request *req = NULL;
+
+ if (evcon == NULL) {
+ fprintf(stdout, "FAILED\n");
+ exit (1);
+ }
+
+ /*
+ * At this point, we want to schedule an HTTP POST request
+ * server using our make request method.
+ */
+
+ req = evhttp_request_new(http_postrequest_done, NULL);
+
+ /* Add the information that we care about */
+ evhttp_add_header(req->output_headers, "Host", "somehost");
+ evbuffer_add_printf(req->buffer, POST_DATA);
+
+ if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/postit") == -1) {
+ fprintf(stdout, "FAILED\n");
+ exit(1);
+ }
+}
+
+void
+http_post_cb(struct evhttp_request *req, void *arg)
+{
+ event_debug((stderr, "%s: called\n", __func__));
+
+ /* Yes, we are expecting a post request */
+ if (req->type != EVHTTP_REQ_POST) {
+ fprintf(stdout, "FAILED\n");
+ exit(1);
+ }
+
+ if (EVBUFFER_LENGTH(req->buffer) != strlen(POST_DATA)) {
+ fprintf(stdout, "FAILED\n");
+ exit(1);
+ }
+
+ if (strcmp(EVBUFFER_DATA(req->buffer), POST_DATA)) {
+ fprintf(stdout, "FAILED\n");
+ exit(1);
+ }
+
+ struct evbuffer *evb = evbuffer_new();
+ evbuffer_add_printf(evb, "This is funny");
+
+ evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb);
+
+ evbuffer_free(evb);
+}
+
+void
+http_postrequest_done(struct evhttp_request *req, void *arg)
+{
+ const char *what = "This is funny";
+
+ if (req->response_code != HTTP_OK) {
+
+ fprintf(stderr, "FAILED\n");
+ exit(1);
+ }
+
+ if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) {
+ fprintf(stderr, "FAILED\n");
+ exit(1);
+ }
+
+ if (EVBUFFER_LENGTH(req->buffer) != strlen(what)) {
+ fprintf(stderr, "FAILED\n");
+ exit(1);
+ }
+
+ if (memcmp(EVBUFFER_DATA(req->buffer), what, strlen(what)) != 0) {
+ fprintf(stderr, "FAILED\n");
+ exit(1);
+ }
+
+ test_ok = 1;
+ event_loopexit(NULL);
+}
+
+
+void
+http_suite(void)
+{
+ http_basic_test();
+ http_connection_test();
+ http_post_test();
+}

Modified: trunk/varnish-cache/contrib/libevent/test/test-weof.c
===================================================================
--- trunk/varnish-cache/contrib/libevent/test/test-weof.c 2006-04-03 11:05:53 UTC (rev 97)
+++ trunk/varnish-cache/contrib/libevent/test/test-weof.c 2006-04-03 11:59:08 UTC (rev 98)
@@ -47,7 +47,7 @@
{
struct event ev;

- if (signal(SIGPIPE, SIG_IGN) == SIG_IGN)
+ if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
return (1);

if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1)

Modified: trunk/varnish-cache/contrib/libevent/test/test.sh
===================================================================
--- trunk/varnish-cache/contrib/libevent/test/test.sh 2006-04-03 11:05:53 UTC (rev 97)
+++ trunk/varnish-cache/contrib/libevent/test/test.sh 2006-04-03 11:59:08 UTC (rev 98)
@@ -1,17 +1,19 @@
#!/bin/sh

setup () {
- export EVENT_NOKQUEUE=yes
- export EVENT_NODEVPOLL=yes
- export EVENT_NOPOLL=yes
- export EVENT_NOSELECT=yes
- export EVENT_NOEPOLL=yes
- export EVENT_NORTSIG=yes
+ EVENT_NOKQUEUE=yes; export EVENT_NOKQUEUE
+ EVENT_NODEVPOLL=yes; export EVENT_NODEVPOLL
+ EVENT_NOPOLL=yes; export EVENT_NOPOLL
+ EVENT_NOSELECT=yes; export EVENT_NOSELECT
+ EVENT_NOEPOLL=yes; export EVENT_NOEPOLL
+ EVENT_NORTSIG=yes; export EVENT_NORTSIG
}

test () {
- if ! ./test-init 2>/dev/null ;
+ if ./test-init 2>/dev/null ;
then
+ true
+ else
echo Skipping test
return
fi
@@ -51,31 +53,37 @@
# Need to do this by hand?
setup
unset EVENT_NOKQUEUE
+export EVENT_NOKQUEUE
echo "KQUEUE"
test

setup
unset EVENT_NODEVPOLL
+export EVENT_NODEVPOLL
echo "DEVPOLL"
test

setup
unset EVENT_NOPOLL
+export EVENT_NOPOLL
echo "POLL"
test

setup
unset EVENT_NOSELECT
+export EVENT_NOSELECT
echo "SELECT"
test

setup
unset EVENT_NORTSIG
+export EVENT_NORTSIG
echo "RTSIG"
test

setup
unset EVENT_NOEPOLL
+export EVENT_NOEPOLL
echo "EPOLL"
test