1c43e99fdSEd Maste /*
2c43e99fdSEd Maste  * Copyright 2009-2012 Niels Provos and Nick Mathewson
3c43e99fdSEd Maste  *
4c43e99fdSEd Maste  * Redistribution and use in source and binary forms, with or without
5c43e99fdSEd Maste  * modification, are permitted provided that the following conditions
6c43e99fdSEd Maste  * are met:
7c43e99fdSEd Maste  * 1. Redistributions of source code must retain the above copyright
8c43e99fdSEd Maste  *    notice, this list of conditions and the following disclaimer.
9c43e99fdSEd Maste  * 2. Redistributions in binary form must reproduce the above copyright
10c43e99fdSEd Maste  *    notice, this list of conditions and the following disclaimer in the
11c43e99fdSEd Maste  *    documentation and/or other materials provided with the distribution.
12c43e99fdSEd Maste  * 4. The name of the author may not be used to endorse or promote products
13c43e99fdSEd Maste  *    derived from this software without specific prior written permission.
14c43e99fdSEd Maste  *
15c43e99fdSEd Maste  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16c43e99fdSEd Maste  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17c43e99fdSEd Maste  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18c43e99fdSEd Maste  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19c43e99fdSEd Maste  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20c43e99fdSEd Maste  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21c43e99fdSEd Maste  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22c43e99fdSEd Maste  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23c43e99fdSEd Maste  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24c43e99fdSEd Maste  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25c43e99fdSEd Maste  *
26c43e99fdSEd Maste  */
27c43e99fdSEd Maste 
28c43e99fdSEd Maste /* for EVUTIL_ERR_CONNECT_RETRIABLE macro */
29c43e99fdSEd Maste #include "util-internal.h"
30c43e99fdSEd Maste 
31c43e99fdSEd Maste #include <sys/types.h>
32c43e99fdSEd Maste #ifdef _WIN32
33c43e99fdSEd Maste #include <winsock2.h>
34c43e99fdSEd Maste #else
35c43e99fdSEd Maste #include <sys/socket.h>
36c43e99fdSEd Maste #include <netinet/in.h>
37c43e99fdSEd Maste # ifdef _XOPEN_SOURCE_EXTENDED
38c43e99fdSEd Maste #  include <arpa/inet.h>
39c43e99fdSEd Maste # endif
40c43e99fdSEd Maste #endif
41c43e99fdSEd Maste #include <stdlib.h>
42c43e99fdSEd Maste #include <string.h>
43c43e99fdSEd Maste #include <errno.h>
44c43e99fdSEd Maste 
45c43e99fdSEd Maste #include "event2/event.h"
46c43e99fdSEd Maste #include "event2/bufferevent.h"
47c43e99fdSEd Maste #include "event2/buffer.h"
48c43e99fdSEd Maste #include "event2/util.h"
49c43e99fdSEd Maste 
50c43e99fdSEd Maste const char *resource = NULL;
51c43e99fdSEd Maste struct event_base *base = NULL;
52c43e99fdSEd Maste 
53c43e99fdSEd Maste int total_n_handled = 0;
54c43e99fdSEd Maste int total_n_errors = 0;
55c43e99fdSEd Maste int total_n_launched = 0;
56c43e99fdSEd Maste size_t total_n_bytes = 0;
57c43e99fdSEd Maste struct timeval total_time = {0,0};
58c43e99fdSEd Maste int n_errors = 0;
59c43e99fdSEd Maste 
60c43e99fdSEd Maste const int PARALLELISM = 200;
61c43e99fdSEd Maste const int N_REQUESTS = 20000;
62c43e99fdSEd Maste 
63c43e99fdSEd Maste struct request_info {
64c43e99fdSEd Maste 	size_t n_read;
65c43e99fdSEd Maste 	struct timeval started;
66c43e99fdSEd Maste };
67c43e99fdSEd Maste 
68c43e99fdSEd Maste static int launch_request(void);
69c43e99fdSEd Maste static void readcb(struct bufferevent *b, void *arg);
70c43e99fdSEd Maste static void errorcb(struct bufferevent *b, short what, void *arg);
71c43e99fdSEd Maste 
72c43e99fdSEd Maste static void
readcb(struct bufferevent * b,void * arg)73c43e99fdSEd Maste readcb(struct bufferevent *b, void *arg)
74c43e99fdSEd Maste {
75c43e99fdSEd Maste 	struct request_info *ri = arg;
76c43e99fdSEd Maste 	struct evbuffer *input = bufferevent_get_input(b);
77c43e99fdSEd Maste 	size_t n = evbuffer_get_length(input);
78c43e99fdSEd Maste 
79c43e99fdSEd Maste 	ri->n_read += n;
80c43e99fdSEd Maste 	evbuffer_drain(input, n);
81c43e99fdSEd Maste }
82c43e99fdSEd Maste 
83c43e99fdSEd Maste static void
errorcb(struct bufferevent * b,short what,void * arg)84c43e99fdSEd Maste errorcb(struct bufferevent *b, short what, void *arg)
85c43e99fdSEd Maste {
86c43e99fdSEd Maste 	struct request_info *ri = arg;
87c43e99fdSEd Maste 	struct timeval now, diff;
88c43e99fdSEd Maste 	if (what & BEV_EVENT_EOF) {
89c43e99fdSEd Maste 		++total_n_handled;
90c43e99fdSEd Maste 		total_n_bytes += ri->n_read;
91c43e99fdSEd Maste 		evutil_gettimeofday(&now, NULL);
92c43e99fdSEd Maste 		evutil_timersub(&now, &ri->started, &diff);
93c43e99fdSEd Maste 		evutil_timeradd(&diff, &total_time, &total_time);
94c43e99fdSEd Maste 
95c43e99fdSEd Maste 		if (total_n_handled && (total_n_handled%1000)==0)
96c43e99fdSEd Maste 			printf("%d requests done\n",total_n_handled);
97c43e99fdSEd Maste 
98c43e99fdSEd Maste 		if (total_n_launched < N_REQUESTS) {
99c43e99fdSEd Maste 			if (launch_request() < 0)
100c43e99fdSEd Maste 				perror("Can't launch");
101c43e99fdSEd Maste 		}
102c43e99fdSEd Maste 	} else {
103c43e99fdSEd Maste 		++total_n_errors;
104c43e99fdSEd Maste 		perror("Unexpected error");
105c43e99fdSEd Maste 	}
106c43e99fdSEd Maste 
107c43e99fdSEd Maste 	bufferevent_setcb(b, NULL, NULL, NULL, NULL);
108c43e99fdSEd Maste 	free(ri);
109c43e99fdSEd Maste 	bufferevent_disable(b, EV_READ|EV_WRITE);
110c43e99fdSEd Maste 	bufferevent_free(b);
111c43e99fdSEd Maste }
112c43e99fdSEd Maste 
113c43e99fdSEd Maste static void
frob_socket(evutil_socket_t sock)114c43e99fdSEd Maste frob_socket(evutil_socket_t sock)
115c43e99fdSEd Maste {
116*b50261e2SCy Schubert #ifdef EVENT__HAVE_STRUCT_LINGER
117c43e99fdSEd Maste 	struct linger l;
118c43e99fdSEd Maste #endif
119c43e99fdSEd Maste 	int one = 1;
120c43e99fdSEd Maste 	if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*)&one, sizeof(one))<0)
121c43e99fdSEd Maste 		perror("setsockopt(SO_REUSEADDR)");
122*b50261e2SCy Schubert #ifdef EVENT__HAVE_STRUCT_LINGER
123c43e99fdSEd Maste 	l.l_onoff = 1;
124c43e99fdSEd Maste 	l.l_linger = 0;
125c43e99fdSEd Maste 	if (setsockopt(sock, SOL_SOCKET, SO_LINGER, (void*)&l, sizeof(l))<0)
126c43e99fdSEd Maste 		perror("setsockopt(SO_LINGER)");
127c43e99fdSEd Maste #endif
128c43e99fdSEd Maste }
129c43e99fdSEd Maste 
130c43e99fdSEd Maste static int
launch_request(void)131c43e99fdSEd Maste launch_request(void)
132c43e99fdSEd Maste {
133c43e99fdSEd Maste 	evutil_socket_t sock;
134c43e99fdSEd Maste 	struct sockaddr_in sin;
135c43e99fdSEd Maste 	struct bufferevent *b;
136c43e99fdSEd Maste 
137c43e99fdSEd Maste 	struct request_info *ri;
138c43e99fdSEd Maste 
139c43e99fdSEd Maste 	memset(&sin, 0, sizeof(sin));
140c43e99fdSEd Maste 
141c43e99fdSEd Maste 	++total_n_launched;
142c43e99fdSEd Maste 
143c43e99fdSEd Maste 	sin.sin_family = AF_INET;
144c43e99fdSEd Maste 	sin.sin_addr.s_addr = htonl(0x7f000001);
145c43e99fdSEd Maste 	sin.sin_port = htons(8080);
146c43e99fdSEd Maste 	if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
147c43e99fdSEd Maste 		return -1;
148c43e99fdSEd Maste 	if (evutil_make_socket_nonblocking(sock) < 0) {
149c43e99fdSEd Maste 		evutil_closesocket(sock);
150c43e99fdSEd Maste 		return -1;
151c43e99fdSEd Maste 	}
152c43e99fdSEd Maste 	frob_socket(sock);
153c43e99fdSEd Maste 	if (connect(sock, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
154c43e99fdSEd Maste 		int e = evutil_socket_geterror(sock);
155c43e99fdSEd Maste 		if (! EVUTIL_ERR_CONNECT_RETRIABLE(e)) {
156c43e99fdSEd Maste 			evutil_closesocket(sock);
157c43e99fdSEd Maste 			return -1;
158c43e99fdSEd Maste 		}
159c43e99fdSEd Maste 	}
160c43e99fdSEd Maste 
161c43e99fdSEd Maste 	ri = malloc(sizeof(*ri));
162c43e99fdSEd Maste 	ri->n_read = 0;
163c43e99fdSEd Maste 	evutil_gettimeofday(&ri->started, NULL);
164c43e99fdSEd Maste 
165c43e99fdSEd Maste 	b = bufferevent_socket_new(base, sock, BEV_OPT_CLOSE_ON_FREE);
166c43e99fdSEd Maste 
167c43e99fdSEd Maste 	bufferevent_setcb(b, readcb, NULL, errorcb, ri);
168c43e99fdSEd Maste 	bufferevent_enable(b, EV_READ|EV_WRITE);
169c43e99fdSEd Maste 
170c43e99fdSEd Maste 	evbuffer_add_printf(bufferevent_get_output(b),
171c43e99fdSEd Maste 	    "GET %s HTTP/1.0\r\n\r\n", resource);
172c43e99fdSEd Maste 
173c43e99fdSEd Maste 	return 0;
174c43e99fdSEd Maste }
175c43e99fdSEd Maste 
176c43e99fdSEd Maste 
177c43e99fdSEd Maste int
main(int argc,char ** argv)178c43e99fdSEd Maste main(int argc, char **argv)
179c43e99fdSEd Maste {
180c43e99fdSEd Maste 	int i;
181c43e99fdSEd Maste 	struct timeval start, end, total;
182c43e99fdSEd Maste 	long long usec;
183c43e99fdSEd Maste 	double throughput;
184c43e99fdSEd Maste 
185c43e99fdSEd Maste #ifdef _WIN32
186c43e99fdSEd Maste 	WSADATA WSAData;
187c43e99fdSEd Maste 	WSAStartup(0x101, &WSAData);
188c43e99fdSEd Maste #endif
189c43e99fdSEd Maste 
190c43e99fdSEd Maste 	resource = "/ref";
191c43e99fdSEd Maste 
192c43e99fdSEd Maste 	setvbuf(stdout, NULL, _IONBF, 0);
193c43e99fdSEd Maste 
194c43e99fdSEd Maste 	base = event_base_new();
195c43e99fdSEd Maste 
196c43e99fdSEd Maste 	for (i=0; i < PARALLELISM; ++i) {
197c43e99fdSEd Maste 		if (launch_request() < 0)
198c43e99fdSEd Maste 			perror("launch");
199c43e99fdSEd Maste 	}
200c43e99fdSEd Maste 
201c43e99fdSEd Maste 	evutil_gettimeofday(&start, NULL);
202c43e99fdSEd Maste 
203c43e99fdSEd Maste 	event_base_dispatch(base);
204c43e99fdSEd Maste 
205c43e99fdSEd Maste 	evutil_gettimeofday(&end, NULL);
206c43e99fdSEd Maste 	evutil_timersub(&end, &start, &total);
207c43e99fdSEd Maste 	usec = total_time.tv_sec * (long long)1000000 + total_time.tv_usec;
208c43e99fdSEd Maste 
209c43e99fdSEd Maste 	if (!total_n_handled) {
210c43e99fdSEd Maste 		puts("Nothing worked.  You probably did something dumb.");
211c43e99fdSEd Maste 		return 0;
212c43e99fdSEd Maste 	}
213c43e99fdSEd Maste 
214c43e99fdSEd Maste 
215c43e99fdSEd Maste 	throughput = total_n_handled /
216c43e99fdSEd Maste 	    (total.tv_sec+ ((double)total.tv_usec)/1000000.0);
217c43e99fdSEd Maste 
218c43e99fdSEd Maste #ifdef _WIN32
219c43e99fdSEd Maste #define I64_FMT "%I64d"
220c43e99fdSEd Maste #define I64_TYP __int64
221c43e99fdSEd Maste #else
222c43e99fdSEd Maste #define I64_FMT "%lld"
223c43e99fdSEd Maste #define I64_TYP long long int
224c43e99fdSEd Maste #endif
225c43e99fdSEd Maste 
226c43e99fdSEd Maste 	printf("\n%d requests in %d.%06d sec. (%.2f throughput)\n"
227c43e99fdSEd Maste 	    "Each took about %.02f msec latency\n"
228c43e99fdSEd Maste 	    I64_FMT "bytes read. %d errors.\n",
229c43e99fdSEd Maste 	    total_n_handled,
230c43e99fdSEd Maste 	    (int)total.tv_sec, (int)total.tv_usec,
231c43e99fdSEd Maste 	    throughput,
232c43e99fdSEd Maste 	    (double)(usec/1000) / total_n_handled,
233c43e99fdSEd Maste 	    (I64_TYP)total_n_bytes, n_errors);
234c43e99fdSEd Maste 
235c43e99fdSEd Maste #ifdef _WIN32
236c43e99fdSEd Maste 	WSACleanup();
237c43e99fdSEd Maste #endif
238c43e99fdSEd Maste 
239c43e99fdSEd Maste 	return 0;
240c43e99fdSEd Maste }
241