test-fdleak.c revision 290001
1/* 2 * Copyright (c) 2012 Ross Lagerwall <rosslagerwall@gmail.com> 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 3. The name of the author may not be used to endorse or promote products 13 * derived from this software without specific prior written permission. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include "event2/event-config.h" 28 29#ifdef _WIN32 30#define WIN32_LEAN_AND_MEAN 31#include <windows.h> 32#endif 33#include <string.h> 34#include <stdlib.h> 35#include <errno.h> 36#ifdef EVENT__HAVE_SYS_TIME_H 37#include <sys/time.h> 38#endif 39#ifdef EVENT__HAVE_SYS_RESOURCE_H 40#include <sys/resource.h> 41#endif 42#ifdef EVENT__HAVE_NETINET_IN_H 43#include <netinet/in.h> 44#endif 45 46#include "event2/event.h" 47#include "event2/bufferevent.h" 48#include "event2/buffer.h" 49#include "event2/listener.h" 50 51/* Number of requests to make. Setting this too high might result in the machine 52 running out of ephemeral ports */ 53#ifdef _WIN32 54#define MAX_REQUESTS 1000 55#else 56#define MAX_REQUESTS 4000 57#endif 58 59/* Provide storage for the address, both for the server & the clients */ 60static struct sockaddr_in saddr; 61 62/* Number of sucessful requests so far */ 63static int num_requests; 64 65static void start_client(struct event_base *base); 66 67static void 68my_perror(const char *s) 69{ 70 fprintf(stderr, "%s: %s", 71 s, evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR())); 72} 73 74/* 75=============================================== 76Server functions 77=============================================== 78*/ 79 80/* Read a byte from the client and write it back */ 81static void 82server_read_cb(struct bufferevent *bev, void *ctx) 83{ 84 while (evbuffer_get_length(bufferevent_get_input(bev))) { 85 unsigned char tmp; 86 bufferevent_read(bev, &tmp, 1); 87 bufferevent_write(bev, &tmp, 1); 88 } 89} 90 91/* Wait for an EOF and then free the bufferevent */ 92static void 93server_event_cb(struct bufferevent *bev, short events, void *ctx) 94{ 95 if (events & BEV_EVENT_ERROR) { 96 my_perror("Error from bufferevent"); 97 exit(1); 98 } else if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) { 99 bufferevent_free(bev); 100 } 101} 102 103/* Accept a client socket and set it up to for reading & writing */ 104static void 105listener_accept_cb(struct evconnlistener *listener, evutil_socket_t sock, 106 struct sockaddr *addr, int len, void *ptr) 107{ 108 struct event_base *base = evconnlistener_get_base(listener); 109 struct bufferevent *bev = bufferevent_socket_new(base, sock, 110 BEV_OPT_CLOSE_ON_FREE); 111 112 bufferevent_setcb(bev, server_read_cb, NULL, server_event_cb, NULL); 113 bufferevent_enable(bev, EV_READ|EV_WRITE); 114} 115 116/* Start the server listening on a random port and start the first client. */ 117static void 118start_loop(void) 119{ 120 struct event_base *base; 121 struct evconnlistener *listener; 122 struct sockaddr_storage ss; 123 ev_socklen_t socklen = sizeof(ss); 124 evutil_socket_t fd; 125 126 base = event_base_new(); 127 if (base == NULL) { 128 puts("Could not open event base!"); 129 exit(1); 130 } 131 132 listener = evconnlistener_new_bind(base, listener_accept_cb, NULL, 133 LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, 134 -1, (struct sockaddr *)&saddr, sizeof(saddr)); 135 if (listener == NULL) { 136 my_perror("Could not create listener!"); 137 exit(1); 138 } 139 fd = evconnlistener_get_fd(listener); 140 if (fd < 0) { 141 puts("Couldn't get fd from listener"); 142 exit(1); 143 } 144 if (getsockname(fd, (struct sockaddr *)&ss, &socklen) < 0) { 145 my_perror("getsockname()"); 146 exit(1); 147 } 148 memcpy(&saddr, &ss, sizeof(saddr)); 149 if (saddr.sin_family != AF_INET) { 150 puts("AF mismatch from getsockname()."); 151 exit(1); 152 } 153 154 start_client(base); 155 156 event_base_dispatch(base); 157} 158 159/* 160=============================================== 161Client functions 162=============================================== 163*/ 164 165/* Check that the server sends back the same byte that the client sent. 166 If MAX_REQUESTS have been reached, exit. Otherwise, start another client. */ 167static void 168client_read_cb(struct bufferevent *bev, void *ctx) 169{ 170 unsigned char tmp; 171 struct event_base *base = bufferevent_get_base(bev); 172 173 bufferevent_read(bev, &tmp, 1); 174 if (tmp != 'A') { 175 puts("Incorrect data received!"); 176 exit(2); 177 } 178 bufferevent_free(bev); 179 180 num_requests++; 181 if (num_requests == MAX_REQUESTS) { 182 event_base_loopbreak(base); 183 } else { 184 start_client(base); 185 } 186} 187 188/* Send a byte to the server. */ 189static void 190client_event_cb(struct bufferevent *bev, short events, void *ctx) 191{ 192 if (events & BEV_EVENT_CONNECTED) { 193 unsigned char tmp = 'A'; 194 bufferevent_write(bev, &tmp, 1); 195 } else if (events & BEV_EVENT_ERROR) { 196 puts("Client socket got error!"); 197 exit(2); 198 } 199 200 bufferevent_enable(bev, EV_READ); 201} 202 203/* Open a client socket to connect to localhost on sin */ 204static void 205start_client(struct event_base *base) 206{ 207 struct bufferevent *bev = bufferevent_socket_new(base, -1, 208 BEV_OPT_CLOSE_ON_FREE); 209 bufferevent_setcb(bev, client_read_cb, NULL, client_event_cb, NULL); 210 211 if (bufferevent_socket_connect(bev, (struct sockaddr *)&saddr, 212 sizeof(saddr)) < 0) { 213 my_perror("Could not connect!"); 214 bufferevent_free(bev); 215 exit(2); 216 } 217} 218 219int 220main(int argc, char **argv) 221{ 222#ifdef EVENT__HAVE_SETRLIMIT 223 /* Set the fd limit to a low value so that any fd leak is caught without 224 making many requests. */ 225 struct rlimit rl; 226 rl.rlim_cur = rl.rlim_max = 20; 227 if (setrlimit(RLIMIT_NOFILE, &rl) == -1) { 228 my_perror("setrlimit"); 229 exit(3); 230 } 231#endif 232 233#ifdef _WIN32 234 WSADATA WSAData; 235 WSAStartup(0x101, &WSAData); 236#endif 237 238 /* Set up an address, used by both client & server. */ 239 memset(&saddr, 0, sizeof(saddr)); 240 saddr.sin_family = AF_INET; 241 saddr.sin_addr.s_addr = htonl(0x7f000001); 242 saddr.sin_port = 0; /* Tell the implementation to pick a port. */ 243 244 start_loop(); 245 246 return 0; 247} 248 249/* XXX why does this test cause so much latency sometimes (OSX 10.5)? */ 250