Module Name: src Committed By: snj Date: Wed Aug 5 18:33:20 UTC 2009
Modified Files: src/regress/lib/libevent [netbsd-5]: regress.c regress.gen.c regress.gen.h regress_dns.c regress_http.c regress_rpc.c Log Message: Pull up following revision(s) (requested by tls in ticket #854): regress/lib/libevent/regress.c: revision 1.2 regress/lib/libevent/regress.gen.c: revision 1.2 regress/lib/libevent/regress.gen.h: revision 1.2 regress/lib/libevent/regress_dns.c: revision 1.2 regress/lib/libevent/regress_http.c: revision 1.2 regress/lib/libevent/regress_rpc.c: revision 1.2 Update regression tests for libevent-1.4.11-stable. To generate a diff of this commit: cvs rdiff -u -r1.1 -r1.1.8.1 src/regress/lib/libevent/regress.c \ src/regress/lib/libevent/regress.gen.c \ src/regress/lib/libevent/regress.gen.h \ src/regress/lib/libevent/regress_dns.c \ src/regress/lib/libevent/regress_http.c \ src/regress/lib/libevent/regress_rpc.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/regress/lib/libevent/regress.c diff -u src/regress/lib/libevent/regress.c:1.1 src/regress/lib/libevent/regress.c:1.1.8.1 --- src/regress/lib/libevent/regress.c:1.1 Fri May 16 20:24:57 2008 +++ src/regress/lib/libevent/regress.c Wed Aug 5 18:33:20 2009 @@ -1,4 +1,4 @@ -/* $NetBSD: regress.c,v 1.1 2008/05/16 20:24:57 peter Exp $ */ +/* $NetBSD: regress.c,v 1.1.8.1 2009/08/05 18:33:20 snj Exp $ */ /* * Copyright (c) 2003, 2004 Niels Provos <pro...@citi.umich.edu> * All rights reserved. @@ -83,6 +83,9 @@ char buf[256]; int len; + if (arg == NULL) + return; + len = read(fd, buf, sizeof(buf)); if (len) { @@ -101,6 +104,9 @@ { int len; + if (arg == NULL) + return; + len = write(fd, TEST1, strlen(TEST1) + 1); if (len == -1) test_ok = 0; @@ -169,7 +175,7 @@ struct timeval tv; int diff; - gettimeofday(&tcalled, NULL); + evutil_gettimeofday(&tcalled, NULL); if (evutil_timercmp(&tcalled, &tset, >)) evutil_timersub(&tcalled, &tset, &tv); else @@ -288,6 +294,52 @@ } static void +test_registerfds(void) +{ + int i, j; + int pair[2]; + struct event read_evs[512]; + struct event write_evs[512]; + + struct event_base *base = event_base_new(); + + fprintf(stdout, "Testing register fds: "); + + for (i = 0; i < 512; ++i) { + if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) { + /* run up to the limit of file descriptors */ + break; + } + event_set(&read_evs[i], pair[0], + EV_READ|EV_PERSIST, simple_read_cb, NULL); + event_base_set(base, &read_evs[i]); + event_add(&read_evs[i], NULL); + event_set(&write_evs[i], pair[1], + EV_WRITE|EV_PERSIST, simple_write_cb, NULL); + event_base_set(base, &write_evs[i]); + event_add(&write_evs[i], NULL); + + /* just loop once */ + event_base_loop(base, EVLOOP_ONCE); + } + + /* now delete everything */ + for (j = 0; j < i; ++j) { + event_del(&read_evs[j]); + event_del(&write_evs[j]); + close(read_evs[j].ev_fd); + close(write_evs[j].ev_fd); + + /* just loop once */ + event_base_loop(base, EVLOOP_ONCE); + } + + event_base_free(base); + + fprintf(stdout, "OK\n"); +} + +static void test_simpleread(void) { struct event ev; @@ -428,18 +480,32 @@ evtimer_set(&ev, timeout_cb, NULL); evtimer_add(&ev, &tv); - gettimeofday(&tset, NULL); + evutil_gettimeofday(&tset, NULL); event_dispatch(); cleanup_test(); } extern struct event_base *current_base; + +static void +child_signal_cb(int fd, short event, void *arg) +{ + struct timeval tv; + int *pint = arg; + + *pint = 1; + + tv.tv_usec = 500000; + tv.tv_sec = 0; + event_loopexit(&tv); +} + static void test_fork(void) { - int status; - struct event ev; + int status, got_sigchld = 0; + struct event ev, sig_ev; pid_t pid; setup_test("After fork: "); @@ -450,6 +516,9 @@ if (event_add(&ev, NULL) == -1) exit(1); + signal_set(&sig_ev, SIGCHLD, child_signal_cb, &got_sigchld); + signal_add(&sig_ev, NULL); + if ((pid = fork()) == 0) { /* in the child */ if (event_reinit(current_base) == -1) { @@ -457,6 +526,8 @@ exit(1); } + signal_del(&sig_ev); + called = 0; event_dispatch(); @@ -488,6 +559,13 @@ event_dispatch(); + if (!got_sigchld) { + fprintf(stdout, "FAILED (sigchld)\n"); + exit(1); + } + + signal_del(&sig_ev); + cleanup_test(); } @@ -500,6 +578,9 @@ setup_test("Simple signal: "); signal_set(&ev, SIGALRM, signal_cb, &ev); signal_add(&ev, NULL); + /* find bugs in which operations are re-ordered */ + signal_del(&ev); + signal_add(&ev, NULL); memset(&itv, 0, sizeof(itv)); itv.it_value.tv_sec = 1; @@ -515,6 +596,36 @@ } static void +test_multiplesignal(void) +{ + struct event ev_one, ev_two; + struct itimerval itv; + + setup_test("Multiple signal: "); + + signal_set(&ev_one, SIGALRM, signal_cb, &ev_one); + signal_add(&ev_one, NULL); + + signal_set(&ev_two, SIGALRM, signal_cb, &ev_two); + signal_add(&ev_two, NULL); + + memset(&itv, 0, sizeof(itv)); + itv.it_value.tv_sec = 1; + if (setitimer(ITIMER_REAL, &itv, NULL) == -1) + goto skip_simplesignal; + + event_dispatch(); + + skip_simplesignal: + if (signal_del(&ev_one) == -1) + test_ok = 0; + if (signal_del(&ev_two) == -1) + test_ok = 0; + + cleanup_test(); +} + +static void test_immediatesignal(void) { struct event ev; @@ -690,6 +801,52 @@ } static void +signal_cb_swp(int sig, short event, void *arg) +{ + called++; + if (called < 5) + raise(sig); + else + event_loopexit(NULL); +} +static void +timeout_cb_swp(int fd, short event, void *arg) +{ + if (called == -1) { + struct timeval tv = {5, 0}; + + called = 0; + evtimer_add((struct event *)arg, &tv); + raise(SIGUSR1); + return; + } + test_ok = 0; + event_loopexit(NULL); +} + +static void +test_signal_while_processing(void) +{ + struct event_base *base = event_init(); + struct event ev, ev_timer; + struct timeval tv = {0, 0}; + + setup_test("Receiving a signal while processing other signal: "); + + called = -1; + test_ok = 1; + signal_set(&ev, SIGUSR1, signal_cb_swp, NULL); + signal_add(&ev, NULL); + evtimer_set(&ev_timer, timeout_cb_swp, &ev_timer); + evtimer_add(&ev_timer, &tv); + event_dispatch(); + + event_base_free(base); + cleanup_test(); + return; +} + +static void test_free_active_base(void) { struct event_base *base1; @@ -744,9 +901,9 @@ tv.tv_sec = 1; event_loopexit(&tv); - gettimeofday(&tv_start, NULL); + evutil_gettimeofday(&tv_start, NULL); event_dispatch(); - gettimeofday(&tv_end, NULL); + evutil_gettimeofday(&tv_end, NULL); evutil_timersub(&tv_end, &tv_start, &tv_end); evtimer_del(&ev); @@ -1046,7 +1203,7 @@ struct test_pri_event one, two; struct timeval tv; - snprintf(buf, sizeof(buf), "Testing Priorities %d: ", npriorities); + evutil_snprintf(buf, sizeof(buf), "Testing Priorities %d: ", npriorities); setup_test(buf); event_base_priority_init(global_base, npriorities); @@ -1336,7 +1493,7 @@ EVTAG_ASSIGN(attack, weapon, "feather"); EVTAG_ASSIGN(attack, action, "tickle"); - gettimeofday(&tv_start, NULL); + evutil_gettimeofday(&tv_start, NULL); for (i = 0; i < 1000; ++i) { run = EVTAG_ADD(msg, run); if (run == NULL) { @@ -1344,6 +1501,8 @@ exit(1); } EVTAG_ASSIGN(run, how, "very fast but with some data in it"); + EVTAG_ASSIGN(run, fixed_bytes, + (unsigned char*)"012345678901234567890123"); } if (msg_complete(msg) == -1) { @@ -1369,7 +1528,7 @@ exit(1); } - gettimeofday(&tv_end, NULL); + evutil_gettimeofday(&tv_end, NULL); evutil_timersub(&tv_end, &tv_start, &tv_end); fprintf(stderr, "(%.1f us/add) ", (float)tv_end.tv_sec/(float)i * 1000000.0 + @@ -1425,11 +1584,15 @@ main (int argc, char **argv) { + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) + return (1); setvbuf(stdout, NULL, _IONBF, 0); /* Initalize the event library */ global_base = event_init(); + test_registerfds(); + test_evutil_strtoll(); /* use the global event base and need to be called first */ @@ -1467,6 +1630,7 @@ test_simpletimeout(); test_simplesignal(); + test_multiplesignal(); test_immediatesignal(); test_loopexit(); @@ -1487,6 +1651,7 @@ test_signal_switchbase(); test_signal_restore(); test_signal_assert(); + test_signal_while_processing(); return (0); } Index: src/regress/lib/libevent/regress.gen.c diff -u src/regress/lib/libevent/regress.gen.c:1.1 src/regress/lib/libevent/regress.gen.c:1.1.8.1 --- src/regress/lib/libevent/regress.gen.c:1.1 Fri May 16 20:24:57 2008 +++ src/regress/lib/libevent/regress.gen.c Wed Aug 5 18:33:20 2009 @@ -1,6 +1,5 @@ -/* $NetBSD: regress.gen.c,v 1.1 2008/05/16 20:24:57 peter Exp $ */ /* - * Automatically generated from ./regress.rpc + * Automatically generated from regress.rpc * by event_rpcgen.py/0.1. DO NOT EDIT THIS FILE. */ @@ -12,7 +11,7 @@ #include <event.h> -#include "./regress.gen.h" +#include "regress.gen.h" void event_err(int eval, const char *fmt, ...); void event_warn(const char *fmt, ...); @@ -640,6 +639,10 @@ static struct run_access_ __run_base = { run_how_assign, run_how_get, + run_some_bytes_assign, + run_some_bytes_get, + run_fixed_bytes_assign, + run_fixed_bytes_get, }; struct run * @@ -655,10 +658,19 @@ tmp->how_data = NULL; tmp->how_set = 0; + tmp->some_bytes_data = NULL; + tmp->some_bytes_length = 0; + tmp->some_bytes_set = 0; + + memset(tmp->fixed_bytes_data, 0, sizeof(tmp->fixed_bytes_data)); + tmp->fixed_bytes_set = 0; + return (tmp); } + + int run_how_assign(struct run *msg, const char * value) @@ -672,6 +684,28 @@ } int +run_some_bytes_assign(struct run *msg, const uint8_t * value, uint32_t len) +{ + if (msg->some_bytes_data != NULL) + free (msg->some_bytes_data); + msg->some_bytes_data = malloc(len); + if (msg->some_bytes_data == NULL) + return (-1); + msg->some_bytes_set = 1; + msg->some_bytes_length = len; + memcpy(msg->some_bytes_data, value, len); + return (0); +} + +int +run_fixed_bytes_assign(struct run *msg, const uint8_t *value) +{ + msg->fixed_bytes_set = 1; + memcpy(msg->fixed_bytes_data, value, 24); + return (0); +} + +int run_how_get(struct run *msg, char * *value) { if (msg->how_set != 1) @@ -680,6 +714,25 @@ return (0); } +int +run_some_bytes_get(struct run *msg, uint8_t * *value, uint32_t *plen) +{ + if (msg->some_bytes_set != 1) + return (-1); + *value = msg->some_bytes_data; + *plen = msg->some_bytes_length; + return (0); +} + +int +run_fixed_bytes_get(struct run *msg, uint8_t **value) +{ + if (msg->fixed_bytes_set != 1) + return (-1); + *value = msg->fixed_bytes_data; + return (0); +} + void run_clear(struct run *tmp) { @@ -688,6 +741,14 @@ tmp->how_data = NULL; tmp->how_set = 0; } + if (tmp->some_bytes_set == 1) { + free (tmp->some_bytes_data); + tmp->some_bytes_data = NULL; + tmp->some_bytes_length = 0; + tmp->some_bytes_set = 0; + } + tmp->fixed_bytes_set = 0; + memset(tmp->fixed_bytes_data, 0, sizeof(tmp->fixed_bytes_data)); } void @@ -695,12 +756,18 @@ { if (tmp->how_data != NULL) free (tmp->how_data); + if (tmp->some_bytes_data != NULL) + free (tmp->some_bytes_data); free(tmp); } void run_marshal(struct evbuffer *evbuf, const struct run *tmp){ evtag_marshal_string(evbuf, RUN_HOW, tmp->how_data); + if (tmp->some_bytes_set) { + evtag_marshal(evbuf, RUN_SOME_BYTES, tmp->some_bytes_data, tmp->some_bytes_length); + } + evtag_marshal(evbuf, RUN_FIXED_BYTES, tmp->fixed_bytes_data, sizeof(tmp->fixed_bytes_data)); } int @@ -723,6 +790,34 @@ tmp->how_set = 1; break; + case RUN_SOME_BYTES: + + if (tmp->some_bytes_set) + return (-1); + if (evtag_payload_length(evbuf, &tmp->some_bytes_length) == -1) + return (-1); + if (tmp->some_bytes_length > EVBUFFER_LENGTH(evbuf)) + return (-1); + if ((tmp->some_bytes_data = malloc(tmp->some_bytes_length)) == NULL) + return (-1); + if (evtag_unmarshal_fixed(evbuf, RUN_SOME_BYTES, tmp->some_bytes_data, tmp->some_bytes_length) == -1) { + event_warnx("%s: failed to unmarshal some_bytes", __func__); + return (-1); + } + tmp->some_bytes_set = 1; + break; + + case RUN_FIXED_BYTES: + + if (tmp->fixed_bytes_set) + return (-1); + if (evtag_unmarshal_fixed(evbuf, RUN_FIXED_BYTES, tmp->fixed_bytes_data, sizeof(tmp->fixed_bytes_data)) == -1) { + event_warnx("%s: failed to unmarshal fixed_bytes", __func__); + return (-1); + } + tmp->fixed_bytes_set = 1; + break; + default: return -1; } @@ -738,6 +833,8 @@ { if (!msg->how_set) return (-1); + if (!msg->fixed_bytes_set) + return (-1); return (0); } Index: src/regress/lib/libevent/regress.gen.h diff -u src/regress/lib/libevent/regress.gen.h:1.1 src/regress/lib/libevent/regress.gen.h:1.1.8.1 --- src/regress/lib/libevent/regress.gen.h:1.1 Fri May 16 20:24:57 2008 +++ src/regress/lib/libevent/regress.gen.h Wed Aug 5 18:33:20 2009 @@ -1,12 +1,14 @@ -/* $NetBSD: regress.gen.h,v 1.1 2008/05/16 20:24:57 peter Exp $ */ /* - * Automatically generated from ./regress.rpc + * Automatically generated from regress.rpc */ -#ifndef ___REGRESS_RPC_ -#define ___REGRESS_RPC_ +#ifndef _REGRESS_RPC_ +#define _REGRESS_RPC_ +#include <event-config.h> +#ifdef _EVENT_HAVE_STDINT_H #include <stdint.h> +#endif #define EVTAG_HAS(msg, member) ((msg)->member##_set == 1) #define EVTAG_ASSIGN(msg, member, args...) (*(msg)->base->member##_assign)(msg, ## args) #define EVTAG_GET(msg, member, args...) (*(msg)->base->member##_get)(msg, ## args) @@ -127,6 +129,8 @@ /* Tag definition for run */ enum run_ { RUN_HOW=1, + RUN_SOME_BYTES=2, + RUN_FIXED_BYTES=3, RUN_MAX_TAGS }; @@ -134,14 +138,23 @@ struct run_access_ { int (*how_assign)(struct run *, const char *); int (*how_get)(struct run *, char * *); + int (*some_bytes_assign)(struct run *, const uint8_t *, uint32_t); + int (*some_bytes_get)(struct run *, uint8_t * *, uint32_t *); + int (*fixed_bytes_assign)(struct run *, const uint8_t *); + int (*fixed_bytes_get)(struct run *, uint8_t **); }; struct run { struct run_access_ *base; char *how_data; + uint8_t *some_bytes_data; + uint32_t some_bytes_length; + uint8_t fixed_bytes_data[24]; uint8_t how_set; + uint8_t some_bytes_set; + uint8_t fixed_bytes_set; }; struct run *run_new(void); @@ -156,6 +169,10 @@ struct run *); int run_how_assign(struct run *, const char *); int run_how_get(struct run *, char * *); +int run_some_bytes_assign(struct run *, const uint8_t *, uint32_t); +int run_some_bytes_get(struct run *, uint8_t * *, uint32_t *); +int run_fixed_bytes_assign(struct run *, const uint8_t *); +int run_fixed_bytes_get(struct run *, uint8_t **); /* --- run done --- */ -#endif /* ___REGRESS_RPC_ */ +#endif /* _REGRESS_RPC_ */ Index: src/regress/lib/libevent/regress_dns.c diff -u src/regress/lib/libevent/regress_dns.c:1.1 src/regress/lib/libevent/regress_dns.c:1.1.8.1 --- src/regress/lib/libevent/regress_dns.c:1.1 Fri May 16 20:24:57 2008 +++ src/regress/lib/libevent/regress_dns.c Wed Aug 5 18:33:20 2009 @@ -1,4 +1,4 @@ -/* $NetBSD: regress_dns.c,v 1.1 2008/05/16 20:24:57 peter Exp $ */ +/* $NetBSD: regress_dns.c,v 1.1.8.1 2009/08/05 18:33:20 snj Exp $ */ /* * Copyright (c) 2003-2006 Niels Provos <pro...@citi.umich.edu> * All rights reserved. @@ -37,7 +37,7 @@ #endif #include <sys/queue.h> #include <sys/socket.h> -#include <sys/signal.h> +#include <signal.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> @@ -83,7 +83,7 @@ switch (type) { case DNS_IPv6_AAAA: { -#if defined(HAVE_STRUCT_IN6_ADDR) && defined(HAVE_INET_NTOP) +#if defined(HAVE_STRUCT_IN6_ADDR) && defined(HAVE_INET_NTOP) && defined(INET6_ADDRSTRLEN) struct in6_addr *in6_addrs = addresses; char buf[INET6_ADDRSTRLEN+1]; int i; @@ -252,7 +252,7 @@ break; } case DNS_IPv6_AAAA: { -#if defined (HAVE_STRUCT_IN6_ADDR) && defined(HAVE_INET_NTOP) +#if defined (HAVE_STRUCT_IN6_ADDR) && defined(HAVE_INET_NTOP) && defined(INET6_ADDRSTRLEN) struct in6_addr *in6_addrs = addresses; char buf[INET6_ADDRSTRLEN+1]; if (memcmp(&in6_addrs[0].s6_addr, "abcdefghijklmnop", 16) Index: src/regress/lib/libevent/regress_http.c diff -u src/regress/lib/libevent/regress_http.c:1.1 src/regress/lib/libevent/regress_http.c:1.1.8.1 --- src/regress/lib/libevent/regress_http.c:1.1 Fri May 16 20:24:57 2008 +++ src/regress/lib/libevent/regress_http.c Wed Aug 5 18:33:20 2009 @@ -1,4 +1,4 @@ -/* $NetBSD: regress_http.c,v 1.1 2008/05/16 20:24:57 peter Exp $ */ +/* $NetBSD: regress_http.c,v 1.1.8.1 2009/08/05 18:33:20 snj Exp $ */ /* * Copyright (c) 2003-2006 Niels Provos <pro...@citi.umich.edu> * All rights reserved. @@ -37,7 +37,7 @@ #endif #include <sys/queue.h> #include <sys/socket.h> -#include <sys/signal.h> +#include <signal.h> #include <unistd.h> #include <netdb.h> #include <fcntl.h> @@ -61,8 +61,10 @@ void http_suite(void); void http_basic_cb(struct evhttp_request *req, void *arg); +static void http_chunked_cb(struct evhttp_request *req, void *arg); void http_post_cb(struct evhttp_request *req, void *arg); void http_dispatcher_cb(struct evhttp_request *req, void *arg); +static void http_large_delay_cb(struct evhttp_request *req, void *arg); static struct evhttp * http_setup(short *pport, struct event_base *base) @@ -85,7 +87,9 @@ /* Register a callback for certain types of requests */ evhttp_set_cb(myhttp, "/test", http_basic_cb, NULL); + evhttp_set_cb(myhttp, "/chunked", http_chunked_cb, NULL); evhttp_set_cb(myhttp, "/postit", http_post_cb, NULL); + evhttp_set_cb(myhttp, "/largedelay", http_large_delay_cb, NULL); evhttp_set_cb(myhttp, "/", http_dispatcher_cb, NULL); *pport = port; @@ -139,15 +143,23 @@ if (evbuffer_find(bev->input, (const unsigned char*) what, strlen(what)) != NULL) { struct evhttp_request *req = evhttp_request_new(NULL, NULL); - int done; + enum message_read_status done; req->kind = EVHTTP_RESPONSE; - done = evhttp_parse_lines(req, bev->input); + done = evhttp_parse_firstline(req, bev->input); + if (done != ALL_DATA_READ) + goto out; + + done = evhttp_parse_headers(req, bev->input); + if (done != ALL_DATA_READ) + goto out; if (done == 1 && evhttp_find_header(req->input_headers, "Content-Type") != NULL) test_ok++; + + out: evhttp_request_free(req); bufferevent_disable(bev, EV_READ); if (base) @@ -181,6 +193,23 @@ int empty = evhttp_find_header(req->input_headers, "Empty") != NULL; event_debug(("%s: called\n", __func__)); evbuffer_add_printf(evb, "This is funny"); + + /* For multi-line headers test */ + { + const char *multi = + evhttp_find_header(req->input_headers,"X-multi"); + if (multi) { + if (strcmp("END", multi + strlen(multi) - 3) == 0) + test_ok++; + if (evhttp_find_header(req->input_headers, "X-Last")) + test_ok++; + } + } + + /* injecting a bad content-length */ + if (evhttp_find_header(req->input_headers, "X-Negative")) + evhttp_add_header(req->output_headers, + "Content-Length", "-100"); /* allow sending of an empty reply */ evhttp_send_reply(req, HTTP_OK, "Everything is fine", @@ -189,9 +218,69 @@ evbuffer_free(evb); } +static char const* const CHUNKS[] = { + "This is funny", + "but not hilarious.", + "bwv 1052" +}; + +struct chunk_req_state { + struct evhttp_request *req; + int i; +}; + +static void +http_chunked_trickle_cb(int fd, short events, void *arg) +{ + struct evbuffer *evb = evbuffer_new(); + struct chunk_req_state *state = arg; + struct timeval when = { 0, 0 }; + + evbuffer_add_printf(evb, "%s", CHUNKS[state->i]); + evhttp_send_reply_chunk(state->req, evb); + evbuffer_free(evb); + + if (++state->i < sizeof(CHUNKS)/sizeof(CHUNKS[0])) { + event_once(-1, EV_TIMEOUT, + http_chunked_trickle_cb, state, &when); + } else { + evhttp_send_reply_end(state->req); + free(state); + } +} + +static void +http_chunked_cb(struct evhttp_request *req, void *arg) +{ + struct timeval when = { 0, 0 }; + struct chunk_req_state *state = malloc(sizeof(struct chunk_req_state)); + event_debug(("%s: called\n", __func__)); + + memset(state, 0, sizeof(struct chunk_req_state)); + state->req = req; + + /* generate a chunked reply */ + evhttp_send_reply_start(req, HTTP_OK, "Everything is fine"); + + /* but trickle it across several iterations to ensure we're not + * assuming it comes all at once */ + event_once(-1, EV_TIMEOUT, http_chunked_trickle_cb, state, &when); +} + +static void +http_complete_write(int fd, short what, void *arg) +{ + struct bufferevent *bev = arg; + const char *http_request = "host\r\n" + "Connection: close\r\n" + "\r\n"; + bufferevent_write(bev, http_request, strlen(http_request)); +} + static void http_basic_test(void) { + struct timeval tv; struct bufferevent *bev; int fd; const char *http_request; @@ -214,24 +303,26 @@ bev = bufferevent_new(fd, http_readcb, http_writecb, http_errorcb, NULL); + /* first half of the http request */ http_request = "GET /test HTTP/1.1\r\n" - "Host: somehost\r\n" - "Connection: close\r\n" - "\r\n"; + "Host: some"; bufferevent_write(bev, http_request, strlen(http_request)); + timerclear(&tv); + tv.tv_usec = 10000; + event_once(-1, EV_TIMEOUT, http_complete_write, bev, &tv); event_dispatch(); - if (test_ok != 2) { + if (test_ok != 3) { fprintf(stdout, "FAILED\n"); exit(1); } /* connect to the second port */ bufferevent_free(bev); - close(fd); + EVUTIL_CLOSESOCKET(fd); fd = http_connect("127.0.0.1", port + 1); @@ -250,11 +341,11 @@ event_dispatch(); bufferevent_free(bev); - close(fd); + EVUTIL_CLOSESOCKET(fd); evhttp_free(http); - if (test_ok != 4) { + if (test_ok != 5) { fprintf(stdout, "FAILED\n"); exit(1); } @@ -262,6 +353,31 @@ fprintf(stdout, "OK\n"); } +static struct evhttp_connection *delayed_client; + +static void +http_delay_reply(int fd, short what, void *arg) +{ + struct evhttp_request *req = arg; + + evhttp_send_reply(req, HTTP_OK, "Everything is fine", NULL); + + ++test_ok; +} + +static void +http_large_delay_cb(struct evhttp_request *req, void *arg) +{ + struct timeval tv; + timerclear(&tv); + tv.tv_sec = 3; + + event_once(-1, EV_TIMEOUT, http_delay_reply, req, &tv); + + /* here we close the client connection which will cause an EOF */ + evhttp_connection_fail(delayed_client, EVCON_HTTP_EOF); +} + void http_request_done(struct evhttp_request *, void *); void http_request_empty_done(struct evhttp_request *, void *); @@ -691,7 +807,7 @@ event_dispatch(); bufferevent_free(bev); - close(fd); + EVUTIL_CLOSESOCKET(fd); evhttp_free(http); @@ -706,6 +822,7 @@ static void close_detect_done(struct evhttp_request *req, void *arg) { + struct timeval tv; if (req == NULL || req->response_code != HTTP_OK) { fprintf(stderr, "FAILED\n"); @@ -713,7 +830,11 @@ } test_ok = 1; - event_loopexit(NULL); + + timerclear(&tv); + tv.tv_sec = 3; /* longer than the http time out */ + + event_loopexit(&tv); } static void @@ -740,7 +861,7 @@ struct evhttp_connection *evcon = arg; struct timeval tv; - if (req->response_code != HTTP_OK) { + if (req != NULL && req->response_code != HTTP_OK) { fprintf(stderr, "FAILED\n"); exit(1); @@ -755,14 +876,15 @@ static void -http_close_detection(void) +http_close_detection(int with_delay) { short port = -1; struct evhttp_connection *evcon = NULL; struct evhttp_request *req = NULL; test_ok = 0; - fprintf(stdout, "Testing Connection Close Detection: "); + fprintf(stdout, "Testing Connection Close Detection%s: ", + with_delay ? " (with delay)" : ""); http = http_setup(&port, NULL); @@ -775,6 +897,8 @@ exit(1); } + delayed_client = evcon; + /* * At this point, we want to schedule a request to the HTTP * server using our make request method. @@ -786,7 +910,8 @@ evhttp_add_header(req->output_headers, "Host", "somehost"); /* We give ownership of the request to the connection */ - if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { + if (evhttp_make_request(evcon, + req, EVHTTP_REQ_GET, with_delay ? "/largedelay" : "/test") == -1) { fprintf(stdout, "FAILED\n"); exit(1); } @@ -798,6 +923,12 @@ exit(1); } + /* at this point, the http server should have no connection */ + if (TAILQ_FIRST(&http->connections) != NULL) { + fprintf(stdout, "FAILED (left connections)\n"); + exit(1); + } + evhttp_connection_free(evcon); evhttp_free(http); @@ -840,13 +971,16 @@ if (evhttp_add_header(&headers, "One\r", "Two") != -1) goto fail; - + if (evhttp_add_header(&headers, "One", "Two") != 0) + goto fail; + if (evhttp_add_header(&headers, "One", "Two\r\n Three") != 0) + goto fail; + if (evhttp_add_header(&headers, "One\r", "Two") != -1) + goto fail; if (evhttp_add_header(&headers, "One\n", "Two") != -1) goto fail; - if (evhttp_add_header(&headers, "One", "Two\r") != -1) goto fail; - if (evhttp_add_header(&headers, "One", "Two\n") != -1) goto fail; @@ -859,6 +993,61 @@ exit(1); } +static int validate_header( + const struct evkeyvalq* headers, + const char *key, const char *value) +{ + const char *real_val = evhttp_find_header(headers, key); + if (real_val == NULL) + return (-1); + if (strcmp(real_val, value) != 0) + return (-1); + return (0); +} + +static void +http_parse_query_test(void) +{ + struct evkeyvalq headers; + + fprintf(stdout, "Testing HTTP query parsing: "); + + TAILQ_INIT(&headers); + + evhttp_parse_query("http://www.test.com/?q=test", &headers); + if (validate_header(&headers, "q", "test") != 0) + goto fail; + evhttp_clear_headers(&headers); + + evhttp_parse_query("http://www.test.com/?q=test&foo=bar", &headers); + if (validate_header(&headers, "q", "test") != 0) + goto fail; + if (validate_header(&headers, "foo", "bar") != 0) + goto fail; + evhttp_clear_headers(&headers); + + evhttp_parse_query("http://www.test.com/?q=test+foo", &headers); + if (validate_header(&headers, "q", "test foo") != 0) + goto fail; + evhttp_clear_headers(&headers); + + evhttp_parse_query("http://www.test.com/?q=test%0Afoo", &headers); + if (validate_header(&headers, "q", "test\nfoo") != 0) + goto fail; + evhttp_clear_headers(&headers); + + evhttp_parse_query("http://www.test.com/?q=test%0Dfoo", &headers); + if (validate_header(&headers, "q", "test\rfoo") != 0) + goto fail; + evhttp_clear_headers(&headers); + + fprintf(stdout, "OK\n"); + return; +fail: + fprintf(stdout, "FAILED\n"); + exit(1); +} + static void http_base_test(void) { @@ -898,7 +1087,7 @@ event_base_dispatch(base); bufferevent_free(bev); - close(fd); + EVUTIL_CLOSESOCKET(fd); evhttp_free(http); @@ -913,17 +1102,351 @@ fprintf(stdout, "OK\n"); } +/* + * the server is going to reply with chunked data. + */ + +static void +http_chunked_readcb(struct bufferevent *bev, void *arg) +{ + /* nothing here */ +} + +static void +http_chunked_errorcb(struct bufferevent *bev, short what, void *arg) +{ + if (!test_ok) + goto out; + + test_ok = -1; + + if ((what & EVBUFFER_EOF) != 0) { + struct evhttp_request *req = evhttp_request_new(NULL, NULL); + const char *header; + enum message_read_status done; + + req->kind = EVHTTP_RESPONSE; + done = evhttp_parse_firstline(req, EVBUFFER_INPUT(bev)); + if (done != ALL_DATA_READ) + goto out; + + done = evhttp_parse_headers(req, EVBUFFER_INPUT(bev)); + if (done != ALL_DATA_READ) + goto out; + + header = evhttp_find_header(req->input_headers, "Transfer-Encoding"); + if (header == NULL || strcmp(header, "chunked")) + goto out; + + header = evhttp_find_header(req->input_headers, "Connection"); + if (header == NULL || strcmp(header, "close")) + goto out; + + header = evbuffer_readline(EVBUFFER_INPUT(bev)); + if (header == NULL) + goto out; + /* 13 chars */ + if (strcmp(header, "d")) + goto out; + free((char*)header); + + if (strncmp((char *)EVBUFFER_DATA(EVBUFFER_INPUT(bev)), + "This is funny", 13)) + goto out; + + evbuffer_drain(EVBUFFER_INPUT(bev), 13 + 2); + + header = evbuffer_readline(EVBUFFER_INPUT(bev)); + if (header == NULL) + goto out; + /* 18 chars */ + if (strcmp(header, "12")) + goto out; + free((char *)header); + + if (strncmp((char *)EVBUFFER_DATA(EVBUFFER_INPUT(bev)), + "but not hilarious.", 18)) + goto out; + + evbuffer_drain(EVBUFFER_INPUT(bev), 18 + 2); + + header = evbuffer_readline(EVBUFFER_INPUT(bev)); + if (header == NULL) + goto out; + /* 8 chars */ + if (strcmp(header, "8")) + goto out; + free((char *)header); + + if (strncmp((char *)EVBUFFER_DATA(EVBUFFER_INPUT(bev)), + "bwv 1052.", 8)) + goto out; + + evbuffer_drain(EVBUFFER_INPUT(bev), 8 + 2); + + header = evbuffer_readline(EVBUFFER_INPUT(bev)); + if (header == NULL) + goto out; + /* 0 chars */ + if (strcmp(header, "0")) + goto out; + free((char *)header); + + test_ok = 2; + } + +out: + event_loopexit(NULL); +} + +static void +http_chunked_writecb(struct bufferevent *bev, void *arg) +{ + if (EVBUFFER_LENGTH(EVBUFFER_OUTPUT(bev)) == 0) { + /* enable reading of the reply */ + bufferevent_enable(bev, EV_READ); + test_ok++; + } +} + +static void +http_chunked_request_done(struct evhttp_request *req, void *arg) +{ + if (req->response_code != HTTP_OK) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + if (evhttp_find_header(req->input_headers, + "Transfer-Encoding") == NULL) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + if (EVBUFFER_LENGTH(req->input_buffer) != 13 + 18 + 8) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + if (strncmp((char *)EVBUFFER_DATA(req->input_buffer), + "This is funnybut not hilarious.bwv 1052", + 13 + 18 + 8)) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + test_ok = 1; + event_loopexit(NULL); +} + +static void +http_chunked_test(void) +{ + struct bufferevent *bev; + int fd; + const char *http_request; + short port = -1; + struct timeval tv_start, tv_end; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + int i; + + test_ok = 0; + fprintf(stdout, "Testing Chunked HTTP Reply: "); + + http = http_setup(&port, NULL); + + fd = http_connect("127.0.0.1", port); + + /* Stupid thing to send a request */ + bev = bufferevent_new(fd, + http_chunked_readcb, http_chunked_writecb, + http_chunked_errorcb, NULL); + + http_request = + "GET /chunked HTTP/1.1\r\n" + "Host: somehost\r\n" + "Connection: close\r\n" + "\r\n"; + + bufferevent_write(bev, http_request, strlen(http_request)); + + evutil_gettimeofday(&tv_start, NULL); + + event_dispatch(); + + evutil_gettimeofday(&tv_end, NULL); + evutil_timersub(&tv_end, &tv_start, &tv_end); + + if (tv_end.tv_sec >= 1) { + fprintf(stdout, "FAILED (time)\n"); + exit (1); + } + + + if (test_ok != 2) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + /* now try again with the regular connection object */ + evcon = evhttp_connection_new("127.0.0.1", port); + if (evcon == NULL) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + /* make two requests to check the keepalive behavior */ + for (i = 0; i < 2; i++) { + test_ok = 0; + req = evhttp_request_new(http_chunked_request_done, NULL); + + /* Add the information that we care about */ + evhttp_add_header(req->output_headers, "Host", "somehost"); + + /* We give ownership of the request to the connection */ + if (evhttp_make_request(evcon, req, + EVHTTP_REQ_GET, "/chunked") == -1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + event_dispatch(); + + if (test_ok != 1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + } + + evhttp_connection_free(evcon); + evhttp_free(http); + + fprintf(stdout, "OK\n"); +} + +static void +http_multi_line_header_test(void) +{ + struct bufferevent *bev; + int fd; + const char *http_start_request; + short port = -1; + + test_ok = 0; + fprintf(stdout, "Testing HTTP Server with multi line: "); + + http = http_setup(&port, NULL); + + 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_start_request = + "GET /test HTTP/1.1\r\n" + "Host: somehost\r\n" + "Connection: close\r\n" + "X-Multi: aaaaaaaa\r\n" + " a\r\n" + "\tEND\r\n" + "X-Last: last\r\n" + "\r\n"; + + bufferevent_write(bev, http_start_request, strlen(http_start_request)); + + event_dispatch(); + + bufferevent_free(bev); + EVUTIL_CLOSESOCKET(fd); + + evhttp_free(http); + + if (test_ok != 4) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + fprintf(stdout, "OK\n"); +} + +static void +http_request_bad(struct evhttp_request *req, void *arg) +{ + if (req != NULL) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + test_ok = 1; + event_loopexit(NULL); +} + +static void +http_negative_content_length_test(void) +{ + short port = -1; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + + test_ok = 0; + fprintf(stdout, "Testing HTTP Negative Content Length: "); + + http = http_setup(&port, NULL); + + evcon = evhttp_connection_new("127.0.0.1", port); + 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_bad, NULL); + + /* Cause the response to have a negative content-length */ + evhttp_add_header(req->output_headers, "X-Negative", "makeitso"); + + /* We give ownership of the request to the connection */ + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + event_dispatch(); + + evhttp_free(http); + + if (test_ok != 1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + fprintf(stdout, "OK\n"); +} + void http_suite(void) { http_base_test(); http_bad_header_test(); + http_parse_query_test(); http_basic_test(); http_connection_test(0 /* not-persistent */); http_connection_test(1 /* persistent */); - http_close_detection(); + http_close_detection(0 /* with delay */); + http_close_detection(1 /* with delay */); http_post_test(); http_failure_test(); http_highport_test(); http_dispatcher_test(); + + http_multi_line_header_test(); + http_negative_content_length_test(); + + http_chunked_test(); } Index: src/regress/lib/libevent/regress_rpc.c diff -u src/regress/lib/libevent/regress_rpc.c:1.1 src/regress/lib/libevent/regress_rpc.c:1.1.8.1 --- src/regress/lib/libevent/regress_rpc.c:1.1 Fri May 16 20:24:57 2008 +++ src/regress/lib/libevent/regress_rpc.c Wed Aug 5 18:33:20 2009 @@ -1,4 +1,4 @@ -/* $NetBSD: regress_rpc.c,v 1.1 2008/05/16 20:24:57 peter Exp $ */ +/* $NetBSD: regress_rpc.c,v 1.1.8.1 2009/08/05 18:33:20 snj Exp $ */ /* * Copyright (c) 2003-2006 Niels Provos <pro...@citi.umich.edu> * All rights reserved. @@ -37,7 +37,7 @@ #endif #include <sys/queue.h> #include <sys/socket.h> -#include <sys/signal.h> +#include <signal.h> #include <unistd.h> #include <netdb.h> #include <fcntl.h> @@ -444,14 +444,14 @@ need_input_hook = 1; need_output_hook = 1; - assert(evrpc_add_hook(base, INPUT, rpc_hook_add_header, (void*)"input") + assert(evrpc_add_hook(base, EVRPC_INPUT, rpc_hook_add_header, (void*)"input") != NULL); - assert(evrpc_add_hook(base, OUTPUT, rpc_hook_add_header, (void*)"output") + assert(evrpc_add_hook(base, EVRPC_OUTPUT, rpc_hook_add_header, (void*)"output") != NULL); pool = rpc_pool_with_connection(port); - assert(evrpc_add_hook(pool, INPUT, rpc_hook_remove_header, (void*)"output")); + assert(evrpc_add_hook(pool, EVRPC_INPUT, rpc_hook_remove_header, (void*)"output")); /* set up the basic message */ msg = msg_new();