]> err.no Git - varnish/commitdiff
Update from libevent CVS trunk.
authordes <des@d4fa192b-c00b-0410-8231-f00ffab90ce4>
Mon, 3 Apr 2006 11:59:08 +0000 (11:59 +0000)
committerdes <des@d4fa192b-c00b-0410-8231-f00ffab90ce4>
Mon, 3 Apr 2006 11:59:08 +0000 (11:59 +0000)
git-svn-id: svn+ssh://projects.linpro.no/svn/varnish/trunk@98 d4fa192b-c00b-0410-8231-f00ffab90ce4

30 files changed:
varnish-cache/contrib/libevent/Makefile.am
varnish-cache/contrib/libevent/README
varnish-cache/contrib/libevent/WIN32-Code/win32.c
varnish-cache/contrib/libevent/WIN32-Prj/libevent.dsp
varnish-cache/contrib/libevent/buffer.c
varnish-cache/contrib/libevent/configure.in
varnish-cache/contrib/libevent/devpoll.c
varnish-cache/contrib/libevent/epoll.c
varnish-cache/contrib/libevent/evbuffer.c
varnish-cache/contrib/libevent/event.3
varnish-cache/contrib/libevent/event.c
varnish-cache/contrib/libevent/event.h
varnish-cache/contrib/libevent/event_rpcgen.py [new file with mode: 0755]
varnish-cache/contrib/libevent/event_tagging.c [new file with mode: 0644]
varnish-cache/contrib/libevent/http.c [new file with mode: 0644]
varnish-cache/contrib/libevent/http.h [new file with mode: 0644]
varnish-cache/contrib/libevent/kqueue.c
varnish-cache/contrib/libevent/log.h
varnish-cache/contrib/libevent/poll.c
varnish-cache/contrib/libevent/rtsig.c
varnish-cache/contrib/libevent/select.c
varnish-cache/contrib/libevent/signal.c
varnish-cache/contrib/libevent/strlcpy.c [new file with mode: 0644]
varnish-cache/contrib/libevent/test/Makefile.am
varnish-cache/contrib/libevent/test/regress.c
varnish-cache/contrib/libevent/test/regress.h [new file with mode: 0644]
varnish-cache/contrib/libevent/test/regress.rpc [new file with mode: 0644]
varnish-cache/contrib/libevent/test/regress_http.c [new file with mode: 0644]
varnish-cache/contrib/libevent/test/test-weof.c
varnish-cache/contrib/libevent/test/test.sh

index e08bc03f8df128b4e82f5fe3a8fbeeed75e6fe0c..c9ec048135e293c7a580bb14fdc6cbeabbef8c64 100644 (file)
@@ -2,9 +2,11 @@ AUTOMAKE_OPTIONS = foreign no-dependencies
 
 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 @@ EXTRA_DIST = acconfig.h event.h event-internal.h log.h evsignal.h event.3 \
 
 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
 
index b978fe98ebf568f3fe324ac53d28a4d0d24c1319..bd4a84ca325b7f7a12aa0d42a1d4db3d81c98ae2 100644 (file)
@@ -12,6 +12,10 @@ $ make verify
 
 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 @@ fixing bugs:
   Mike Davis
   William Ahern
   Alexander von Gernler
+  Artur Grabowski
+  Stas Bekman
+  Tassilo von Parseval
 
 If I have forgotten your name, please contact me.
index f4cc2d8aeffb7ef71713cf44cd56aaadadc0eb76..1978bcae888037c584bebefd04001a14674fe055 100644 (file)
@@ -81,6 +81,7 @@ int win32_insert      (void *, struct event *);
 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 @@ struct eventop win32ops = {
        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 @@ do_fd_set(struct win32op *op, SOCKET s, int read)
        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,6 +367,27 @@ win32_dispatch(struct event_base *base, struct win32op *win32op,
        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)
index c7bfd60e4fa4cd3a242bd55e29daebccbabe11d7..e9848f7fc01e55e90c47682c8ea62c02e61000c8 100644 (file)
@@ -85,7 +85,7 @@ LIB32=link.exe -lib
 # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"\r
 # Begin Source File\r
 \r
-SOURCE=..\err.c\r
+SOURCE=..\log.c\r
 # End Source File\r
 # Begin Source File\r
 \r
index 1dc16ae394361f136999e9934d6fd6ee7f7a4a9b..e641e91fe8c6832553b297a355674f93bd646fee 100644 (file)
@@ -117,45 +117,55 @@ evbuffer_add_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf)
        }
 
        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 @@ evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen)
 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 @@ evbuffer_read(struct evbuffer *buf, int fd, int howmuch)
 #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 @@ evbuffer_write(struct evbuffer *buffer, int fd)
 }
 
 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;
index b75d2d66b89f331e0b53e611b056e4d4ac690c1b..42e17a731ee815ed23fc5a7c4b72dce758936c7a 100644 (file)
@@ -2,7 +2,7 @@ dnl configure.in for libevent
 dnl Dug Song <dugsong@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_C_INLINE
 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,
index 68def058eb946d3f86876a9ed9441bc6dc878b32..b4cdae27236298c30df5cf1869d283c670e5348f 100644 (file)
@@ -76,6 +76,7 @@ int devpoll_add       (void *, struct event *);
 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 @@ struct eventop devpollops = {
        devpoll_add,
        devpoll_del,
        devpoll_recalc,
-       devpoll_dispatch
+       devpoll_dispatch,
+       devpoll_dealloc
 };
 
 #define NEVENT 32000
@@ -401,3 +403,21 @@ devpoll_del(void *arg, struct event *ev)
 
        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);
+}
index 9f1066d8618eff1d63c5d62e968d1a89c75489da..19a88a1f72dc4fe6e0080cab82e541a9cc9cb6dc 100644 (file)
@@ -76,6 +76,7 @@ int epoll_add (void *, struct event *);
 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 @@ struct eventop epollops = {
        epoll_add,
        epoll_del,
        epoll_recalc,
-       epoll_dispatch
+       epoll_dispatch,
+       epoll_dealloc
 };
 
 #ifdef HAVE_SETFD
@@ -109,8 +111,14 @@ epoll_init(void)
                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 @@ epoll_del(void *arg, struct event *ev)
 
        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);
+}
index e9f7e43ab1a2359479bf799a2940cec62b7e6c56..7bd8d49a3da661d3be3a02dda3d6461f5bdc3e00 100644 (file)
@@ -74,7 +74,7 @@ bufferevent_read_pressure_cb(struct evbuffer *buf, size_t old, size_t now,
     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 @@ bufferevent_readcb(int fd, short event, void *arg)
        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 @@ bufferevent_new(int fd, evbuffercb readcb, evbuffercb writecb,
 
        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_setwatermark(struct bufferevent *bufev, short events,
        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);
+}
index eb674b42c19783889b355cfeaea37b233e67c047..2e594c4a444423907fe0381882091028f0376c39 100644 (file)
 .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 ,
 .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 ,
 .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>
 .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"
 .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
 .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
 .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 @@ sets
 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 indicating the type of event, and a
 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 @@ and
 .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 @@ The function
 .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 @@ require the caller to prepare an
 structure.
 This function supports
 .Fa EV_TIMEOUT ,
-.Fa EV_READ
+.Fa EV_READ ,
 and
 .Fa EV_WRITE .
 .Pp
@@ -346,10 +368,10 @@ function can be used to check if the event specified by
 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 @@ The functions
 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 @@ adds
 .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 @@ By default,
 .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 @@ When initializing the library via
 .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 @@ but instead is reading from input and writing to output buffers.
 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" .
+The error callback has the following form:
 .Ft void
-.Fn "(*cb)" "struct bufferevent *bufev" "void *arg"
+.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 @@ The
 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 @@ Support for real-time signals is due to Taral.
 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
index 2a2150ac587e0e482b55f4d165f49267ee57c6a0..8621f6c27d29684aaa98601bb426681dd5278467 100644 (file)
@@ -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_list signalqueue;
 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 @@ compare(struct event *a, struct event *b)
        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_init(void)
 
        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 @@ event_init(void)
        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 @@ event_base_loop(struct event_base *base, int flags)
        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 @@ event_base_loop(struct event_base *base, int flags)
                }
 
                /* 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 @@ event_base_loop(struct event_base *base, int flags)
                                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 @@ event_priority_set(struct event *ev, int pri)
 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_pending(struct event *ev, short event, struct timeval *tv)
        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_add(struct event *ev, struct timeval *tv)
                        event_queue_remove(base, ev, EVLIST_ACTIVE);
                }
 
-               gettimeofday(&now, NULL);
+               gettime(&now);
                timeradd(&now, tv, &ev->ev_timeout);
 
                event_debug((
@@ -654,7 +704,7 @@ timeout_next(struct event_base *base, struct timeval *tv)
                return (0);
        }
 
-       if (gettimeofday(&now, NULL) == -1)
+       if (gettime(&now) == -1)
                return (-1);
 
        if (timercmp(&ev->ev_timeout, &now, <=)) {
@@ -690,7 +740,7 @@ timeout_process(struct event_base *base)
        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, >))
index 857dd376e42d4148f9ff3648470366fb9634c3fd..4c1b38f0795099172f6b078803840ac58e20645a 100644 (file)
@@ -31,6 +31,8 @@
 extern "C" {
 #endif
 
+#include <stdarg.h>
+
 #ifdef WIN32
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
@@ -101,11 +103,25 @@ struct event {
 #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 @@ struct eventop {
        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 @@ struct eventop {
 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_add(struct evbuffer *, void *, size_t);
 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
diff --git a/varnish-cache/contrib/libevent/event_rpcgen.py b/varnish-cache/contrib/libevent/event_rpcgen.py
new file mode 100755 (executable)
index 0000000..d6a579d
--- /dev/null
@@ -0,0 +1,1378 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2005 Niels Provos <provos@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)
diff --git a/varnish-cache/contrib/libevent/event_tagging.c b/varnish-cache/contrib/libevent/event_tagging.c
new file mode 100644 (file)
index 0000000..b923ad3
--- /dev/null
@@ -0,0 +1,360 @@
+/*
+ * Copyright (c) 2003, 2004 Niels Provos <provos@citi.umich.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#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);
+}
diff --git a/varnish-cache/contrib/libevent/http.c b/varnish-cache/contrib/libevent/http.c
new file mode 100644 (file)
index 0000000..9ff4dc4
--- /dev/null
@@ -0,0 +1,1492 @@
+/*
+ * Copyright (c) 2002-2006 Niels Provos <provos@citi.umich.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#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);
+}
diff --git a/varnish-cache/contrib/libevent/http.h b/varnish-cache/contrib/libevent/http.h
new file mode 100644 (file)
index 0000000..29e87b2
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2001 Niels Provos <provos@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 */
index 3bc6c2db501c3f36bc3d2c4c85cf52b1fbf40042..a92315fe674411946c90a89e9876580065080abd 100644 (file)
@@ -75,6 +75,7 @@ int kq_del    (void *, struct event *);
 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 @@ const struct eventop kqops = {
        kq_add,
        kq_del,
        kq_recalc,
-       kq_dispatch
+       kq_dispatch,
+       kq_dealloc
 };
 
 void *
@@ -266,10 +268,8 @@ kq_dispatch(struct event_base *base, void *arg, struct timeval *tv)
                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 @@ kq_del(void *arg, struct event *ev)
 
        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);
+}
index e19d8a06ea5e7b7f03a1e4e3d55e8a75fbfac72b..1f843cf984e008d2a653fb09c68711afef594cec 100644 (file)
@@ -33,11 +33,11 @@ void event_errx(int eval, const char *fmt, ...);
 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
index da35b20afab6d5c96c464697dfec450eaadfc9e9..f05819e0ccd59a79e01dc6fc5fc254cd64d7ad10 100644 (file)
@@ -74,14 +74,16 @@ int poll_add                (void *, struct event *);
 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 @@ poll_init(void)
 {
        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 @@ poll_dispatch(struct event_base *base, void *arg, struct timeval *tv)
                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_del(void *arg, struct event *ev)
        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);
+}
index 32a782cdee9845b696bdf2e1b110f6df40fec243..29aade6948dcdc23c2927e9c3dc7aa6453ff74b1 100644 (file)
@@ -1,3 +1,152 @@
+/*
+ * Copyright (c) 2006 Mathew Mills <mathewmills@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
 #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>
-
-#define EVLIST_X_NORT  0x1000  /* Skip RT signals (internal) */
+#include <sys/socket.h>
 
 #include "event.h"
+#include "event-internal.h"
 #include "log.h"
 extern struct event_list signalqueue;
 
-struct rtsigop {
-    sigset_t sigs;
-    struct pollfd *poll;
-    struct event **toev;
-    int cur, max, total;
-#ifndef HAVE_WORKING_RTSIG
-    int pollmode;
+#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;        /* 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 */
+};
+
+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()
+        */
 };
 
-#define INIT_MAX 16
+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;
-
-    if (op->poll == NULL) return 0;
-
-    if (op->cur == op->max) {
-        void *p;
-
-        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;
-    }
-
-    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;
-
-    op->toev[op->cur] = ev;
-    op->cur++;
-
-    return 0;
+       /*
+        * grow the fd -> rtdata array because we have encountered a
+        * new fd too high to fit in the existing array
+        */
+
+       struct rtdata_list **p;
+       struct rtdata_list *datset;
+       int i,x;
+       int newcnt = (newhigh + 1) << 1;
+
+       if (newhigh <= op->highestfd)
+               return (0);
+
+       p = op->fdtodat;
+       p = realloc(op->fdtodat, sizeof(struct rtdata_list *) * newcnt);
+       if (p == NULL)
+               return (-1);
+       op->fdtodat = p;
+
+       datset = calloc(newcnt - (op->highestfd + 1),
+           sizeof(struct rtdata_list));
+       if (datset == NULL)
+               return (-1);
+
+       for (i = op->highestfd + 1, x = 0; i < newcnt; i++, x++) {
+               op->fdtodat[i] = &(datset[x]);
+               TAILQ_INIT(op->fdtodat[i]);
+       }
+
+       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;
-
-    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);
-    }
+  if (op->poll == NULL)
+         return;
+
+  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;
-
-    for (i = 0; i < op->cur; i++) {
-        if (op->toev[i] == ev) {
-            poll_free(op, i);
-            break;
-        }
-    }
+  int pp;
+  if (dat == NULL)
+    dat = ev2dat(op, ev, 0);
+
+  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 *);
-
-struct eventop rtsigops = {
-    "rtsig",
-    rtsig_init,
-    rtsig_add,
-    rtsig_del,
-    rtsig_recalc,
-    rtsig_dispatch
-};
+#define FD_CLOSEONEXEC(x) do { \
+        if (fcntl(x, F_SETFD, 1) == -1) \
+                event_warn("fcntl(%d, F_SETFD)", x); \
+} while (0)
 
 void *
 rtsig_init(void)
 {
        struct rtsigop *op;
+       int sockets[2];
+       int optarg;
+       struct rtdata *dat;
+       int flags;
 
        if (getenv("EVENT_NORTSIG"))
-               return (NULL);
-
-       op = malloc(sizeof(*op));
-       if (op == NULL) return (NULL);
+               goto err;
 
-       memset(op, 0, sizeof(*op));
+       op = calloc(1, sizeof(*op));
+       if (op == NULL)
+               goto err;
 
        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 @@ rtsig_add(void *arg, struct event *ev)
 {
        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);
+
+       if (-1 == fstat(ev->ev_fd, &statbuf))
+               return (-1);
 
-#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 (!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 @@ rtsig_del(void *arg, struct event *ev)
 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);
+
+                       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->poll == NULL)
-               goto retry_poll;
-#ifndef HAVE_WORKING_RTSIG
-       if (op->pollmode)
-               goto poll_all;
-#endif
+               if (res < 0) {
+                       return (errno == EINTR ? 0 : -1);
+               } else if (res) {
+                       ts->tv_sec = 0;
+                       ts->tv_nsec = 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;
+               i = 0;
+               while (i < op->cur) {
+                       struct rtdata *dat = op->ptodat[i];
+                       struct event *ev = dat->ev;
+
+                       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);
+}
 
-       for (;;) {
-               siginfo_t info;
-               struct event *ev;
-               int signum;
+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;
 
-               signum = sigtimedwait(&op->sigs, &info, &ts);
+       if (info == NULL)
+               return (-1);
 
-               if (signum == -1) {
-                       if (errno == EAGAIN)
-                               break;
-                       return (errno == EINTR ? 0 : -1);
-               }
+       signum = info->si_signo;
+       if (signum == op->signo) {
+               int flags, sigok = 0;
+               flags = 0;
 
-               ts.tv_sec = ts.tv_nsec = 0;
+               if (info->si_band & (POLLIN|POLLERR))
+                       flags |= EV_READ;
+               if (info->si_band & POLLOUT)
+                       flags |= EV_WRITE;
 
-               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 (!flags)
+                       return (0);
 
-                       TAILQ_FOREACH(ev, &base->eventqueue, ev_next)
-                           if (!(ev->ev_flags & EVLIST_X_NORT))
-                                   poll_add(op, ev);
+               if (info->si_fd > op->highestfd)
+                       return (-1);
 
-                       break;
+               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;
+                       }
+                       dat = next_dat;
                }
-
-               if (signum == SIGRTMIN) {
-                       int flags, i, sigok = 0;
-
-                       if (info.si_band <= 0) { /* SI_SIGIO */
-                               flags = EV_READ | EV_WRITE;
+       } 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 {
-                               flags = 0;
-                               if (info.si_band & POLLIN) flags |= EV_READ;
-                               if (info.si_band & POLLOUT) flags |= EV_WRITE;
-                               if (!flags) continue;
+                               if (signum != SIGCHLD) {
+                                       /* non-blocked SIG_DFL */
+                                       kill(gettid(), signum);
+                               }
                        }
+               }
+       }
 
-                       for (i = 0; flags && i < op->cur; i++) {
-                               ev = op->toev[i];
+       return (0);
+}
 
-                               if (ev->ev_fd == info.si_fd) {
-                                       flags &= ~ev->ev_events;
-                                       sigok = 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;
 
-                       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;
-                               }
-                       }
+               signum = sigtimedwait(sigs, &info, ts);
 
-                       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);
-                       }
+               ts->tv_sec = 0;
+               ts->tv_nsec = 0;
+
+               if (signum == -1) {
+                       if (errno == EAGAIN || errno == EINTR)
+                               return (0);
+                       return (-1);
+               } else if (1 == do_siginfo_dispatch(base, op, &info)) {
+                       return (1);
                }
        }
 
-       if (!op->cur)
-               return (0);
+       /* NOTREACHED */
+}
 
-       res = poll(op->poll, op->cur, tv->tv_sec * 1000 + 
-                                     (tv->tv_usec + 999) / 1000);
-       if (res < 0)
-               return (-1);
+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;
 
-       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];
-
-                       if (op->poll[i].revents & POLLIN)
-                               flags |= EV_READ;
-                       if (op->poll[i].revents & POLLOUT)
-                               flags |= EV_WRITE;
-
-                       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++;
+       for (;;) {
+               res = recv(fd, &info, sizeof(info), MSG_NOSIGNAL);
+               if (res == -1) {
+                       if (errno == EAGAIN)
+                               return (0);
+                       if (errno == EINTR)
                                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;
-                               }
-                       }
+                       return (-1);
+               } else {
+                       ts->tv_sec = 0;
+                       ts->tv_nsec = 0;
+                       if (1 == do_siginfo_dispatch(base, op, &info))
+                               return (1);
                }
        }
-#ifdef HAVE_WORKING_RTSIG
-       op->cur = res;
-#endif
+       /* NOTREACHED */
+}
 
-       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);
+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;
 
-               if (op->poll == NULL || op->toev == NULL)
-                       event_err(1, "%s: malloc");
-       }
+       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);
 }
+
index 00909b79051e53bbdfb40de143ad4ace295a72cc..1e039e1f002c0962684dd97ea1f7b847d9fccf9d 100644 (file)
@@ -76,6 +76,7 @@ int select_add                (void *, struct event *);
 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 @@ const struct eventop selectops = {
        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 @@ select_init(void)
 {
        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 @@ check_selectop(struct selectop *sop)
 
 }
 #else
-#define check_selectop(sop)
+#define check_selectop(sop) do {;} while (0)
 #endif
 
 /*
@@ -350,3 +352,25 @@ select_del(void *arg, struct event *ev)
        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);
+}
index 19a85f1477141a7e8f2ed1cd24096bd7289d99d3..8a6963b166a200928e279752f8a39b2fe39ad1af 100644 (file)
@@ -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_pair[2];
 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 @@ evsignal_init(sigset_t *evsigmask)
        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 @@ int
 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 @@ evsignal_del(sigset_t *evsigmask, struct event *ev)
 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 @@ evsignal_recalc(sigset_t *evsigmask)
 {
        struct sigaction sa;
        struct event *ev;
-       
+
        if (!ev_signal_added) {
                ev_signal_added = 1;
                event_add(&ev_signal, NULL);
@@ -159,13 +167,13 @@ evsignal_recalc(sigset_t *evsigmask)
 
        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 @@ void
 evsignal_process(void)
 {
        struct event *ev;
-       short ncalls;
+       sig_atomic_t ncalls;
 
        TAILQ_FOREACH(ev, &signalqueue, ev_signal_next) {
                ncalls = evsigcaught[EVENT_SIGNAL(ev)];
diff --git a/varnish-cache/contrib/libevent/strlcpy.c b/varnish-cache/contrib/libevent/strlcpy.c
new file mode 100644 (file)
index 0000000..163f425
--- /dev/null
@@ -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@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 */
+}
index 0efdd40c166abc479eabbf5addde9659c0398a46..157d261ecce6b7f16a2e4d62c60b04f6f90d1fbb 100644 (file)
@@ -4,20 +4,28 @@ LDADD = ../libevent.la
 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
index ce47d22599b484deb599b85f4868edfbeaadfc71..3f5158385c31cdc8c8c57a910a7233ad83a03bb6 100644 (file)
 #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 @@ cleanup_test(void)
 }
 
 void
-test1(void)
+test_simpleread(void)
 {
        struct event ev;
 
@@ -295,7 +302,7 @@ test1(void)
 }
 
 void
-test2(void)
+test_simplewrite(void)
 {
        struct event ev;
 
@@ -311,7 +318,7 @@ test2(void)
 }
 
 void
-test3(void)
+test_multiple(void)
 {
        struct event ev, ev2;
        int i;
@@ -340,7 +347,7 @@ test3(void)
 }
 
 void
-test4(void)
+test_persistent(void)
 {
        struct event ev, ev2;
        int i;
@@ -369,7 +376,7 @@ test4(void)
 }
 
 void
-test5(void)
+test_combined(void)
 {
        struct both r1, r2, w1, w2;
 
@@ -404,7 +411,7 @@ test5(void)
 }
 
 void
-test6(void)
+test_simpletimeout(void)
 {
        struct timeval tv;
        struct event ev;
@@ -424,7 +431,7 @@ test6(void)
 
 #ifndef WIN32
 void
-test7(void)
+test_simplesignal(void)
 {
        struct event ev;
        struct itimerval itv;
@@ -447,7 +454,7 @@ test7(void)
 #endif
 
 void
-test8(void)
+test_loopexit(void)
 {
        struct timeval tv, tv_start, tv_end;
        struct event ev;
@@ -476,6 +483,21 @@ test8(void)
        cleanup_test();
 }
 
+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)
 {
@@ -499,7 +521,7 @@ errorcb(struct bufferevent *bev, short what, void *arg)
 }
 
 void
-test9(void)
+test_bufferevent(void)
 {
        struct bufferevent *bev1, *bev2;
        char buffer[8333];
@@ -637,6 +659,210 @@ test_multiple_events_for_same_fd(void)
    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 @@ main (int argc, char **argv)
        /* 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 @@ main (int argc, char **argv)
 
        test_multiple_events_for_same_fd();
 
+       test_want_only_once();
+       
+       evtag_test();
+
+       rpc_test();
+
        return (0);
 }
 
diff --git a/varnish-cache/contrib/libevent/test/regress.h b/varnish-cache/contrib/libevent/test/regress.h
new file mode 100644 (file)
index 0000000..3006830
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#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_ */
diff --git a/varnish-cache/contrib/libevent/test/regress.rpc b/varnish-cache/contrib/libevent/test/regress.rpc
new file mode 100644 (file)
index 0000000..7bc6b75
--- /dev/null
@@ -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;
+}
diff --git a/varnish-cache/contrib/libevent/test/regress_http.c b/varnish-cache/contrib/libevent/test/regress_http.c
new file mode 100644 (file)
index 0000000..f96fa69
--- /dev/null
@@ -0,0 +1,439 @@
+/*
+ * Copyright (c) 2003-2006 Niels Provos <provos@citi.umich.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef WIN32
+#include <winsock2.h>
+#include <windows.h>
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#include <sys/queue.h>
+#ifndef WIN32
+#include <sys/socket.h>
+#include <sys/signal.h>
+#include <unistd.h>
+#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();
+}
index 8862b4a291cc61708b673fd1f25f89fbe58e4183..5f3be277bcd2e34b06c26f4653ddc03b0ca3086f 100644 (file)
@@ -47,7 +47,7 @@ main (int argc, char **argv)
 {
        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)
index d23c1fac4c720cacb8db1eba8157fc97290c29bb..878d468df6a84e027a57e2c965dcd854777b43b9 100755 (executable)
@@ -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 @@ echo "Running tests:"
 # 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