|
1 /* |
|
2 * Copyright 2009-2012 Niels Provos and Nick Mathewson |
|
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 * 4. 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 |
|
28 #include <sys/types.h> |
|
29 #ifdef WIN32 |
|
30 #include <winsock2.h> |
|
31 #else |
|
32 #include <sys/socket.h> |
|
33 #include <netinet/in.h> |
|
34 # ifdef _XOPEN_SOURCE_EXTENDED |
|
35 # include <arpa/inet.h> |
|
36 # endif |
|
37 #endif |
|
38 #include <stdlib.h> |
|
39 #include <string.h> |
|
40 #include <errno.h> |
|
41 |
|
42 #include "event2/event.h" |
|
43 #include "event2/bufferevent.h" |
|
44 #include "event2/buffer.h" |
|
45 #include "event2/util.h" |
|
46 |
|
47 /* for EVUTIL_ERR_CONNECT_RETRIABLE macro */ |
|
48 #include "util-internal.h" |
|
49 |
|
50 const char *resource = NULL; |
|
51 struct event_base *base = NULL; |
|
52 |
|
53 int total_n_handled = 0; |
|
54 int total_n_errors = 0; |
|
55 int total_n_launched = 0; |
|
56 size_t total_n_bytes = 0; |
|
57 struct timeval total_time = {0,0}; |
|
58 int n_errors = 0; |
|
59 |
|
60 const int PARALLELISM = 200; |
|
61 const int N_REQUESTS = 20000; |
|
62 |
|
63 struct request_info { |
|
64 size_t n_read; |
|
65 struct timeval started; |
|
66 }; |
|
67 |
|
68 static int launch_request(void); |
|
69 static void readcb(struct bufferevent *b, void *arg); |
|
70 static void errorcb(struct bufferevent *b, short what, void *arg); |
|
71 |
|
72 static void |
|
73 readcb(struct bufferevent *b, void *arg) |
|
74 { |
|
75 struct request_info *ri = arg; |
|
76 struct evbuffer *input = bufferevent_get_input(b); |
|
77 size_t n = evbuffer_get_length(input); |
|
78 |
|
79 ri->n_read += n; |
|
80 evbuffer_drain(input, n); |
|
81 } |
|
82 |
|
83 static void |
|
84 errorcb(struct bufferevent *b, short what, void *arg) |
|
85 { |
|
86 struct request_info *ri = arg; |
|
87 struct timeval now, diff; |
|
88 if (what & BEV_EVENT_EOF) { |
|
89 ++total_n_handled; |
|
90 total_n_bytes += ri->n_read; |
|
91 evutil_gettimeofday(&now, NULL); |
|
92 evutil_timersub(&now, &ri->started, &diff); |
|
93 evutil_timeradd(&diff, &total_time, &total_time); |
|
94 |
|
95 if (total_n_handled && (total_n_handled%1000)==0) |
|
96 printf("%d requests done\n",total_n_handled); |
|
97 |
|
98 if (total_n_launched < N_REQUESTS) { |
|
99 if (launch_request() < 0) |
|
100 perror("Can't launch"); |
|
101 } |
|
102 } else { |
|
103 ++total_n_errors; |
|
104 perror("Unexpected error"); |
|
105 } |
|
106 |
|
107 bufferevent_setcb(b, NULL, NULL, NULL, NULL); |
|
108 free(ri); |
|
109 bufferevent_disable(b, EV_READ|EV_WRITE); |
|
110 bufferevent_free(b); |
|
111 } |
|
112 |
|
113 static void |
|
114 frob_socket(evutil_socket_t sock) |
|
115 { |
|
116 struct linger l; |
|
117 int one = 1; |
|
118 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*)&one, sizeof(one))<0) |
|
119 perror("setsockopt(SO_REUSEADDR)"); |
|
120 l.l_onoff = 1; |
|
121 l.l_linger = 0; |
|
122 if (setsockopt(sock, SOL_SOCKET, SO_LINGER, (void*)&l, sizeof(l))<0) |
|
123 perror("setsockopt(SO_LINGER)"); |
|
124 } |
|
125 |
|
126 static int |
|
127 launch_request(void) |
|
128 { |
|
129 evutil_socket_t sock; |
|
130 struct sockaddr_in sin; |
|
131 struct bufferevent *b; |
|
132 |
|
133 struct request_info *ri; |
|
134 |
|
135 memset(&sin, 0, sizeof(sin)); |
|
136 |
|
137 ++total_n_launched; |
|
138 |
|
139 sin.sin_family = AF_INET; |
|
140 sin.sin_addr.s_addr = htonl(0x7f000001); |
|
141 sin.sin_port = htons(8080); |
|
142 if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) |
|
143 return -1; |
|
144 if (evutil_make_socket_nonblocking(sock) < 0) |
|
145 return -1; |
|
146 frob_socket(sock); |
|
147 if (connect(sock, (struct sockaddr*)&sin, sizeof(sin)) < 0) { |
|
148 int e = errno; |
|
149 if (! EVUTIL_ERR_CONNECT_RETRIABLE(e)) { |
|
150 return -1; |
|
151 } |
|
152 } |
|
153 |
|
154 ri = malloc(sizeof(*ri)); |
|
155 ri->n_read = 0; |
|
156 evutil_gettimeofday(&ri->started, NULL); |
|
157 |
|
158 b = bufferevent_socket_new(base, sock, BEV_OPT_CLOSE_ON_FREE); |
|
159 |
|
160 bufferevent_setcb(b, readcb, NULL, errorcb, ri); |
|
161 bufferevent_enable(b, EV_READ|EV_WRITE); |
|
162 |
|
163 evbuffer_add_printf(bufferevent_get_output(b), |
|
164 "GET %s HTTP/1.0\r\n\r\n", resource); |
|
165 |
|
166 return 0; |
|
167 } |
|
168 |
|
169 |
|
170 int |
|
171 main(int argc, char **argv) |
|
172 { |
|
173 int i; |
|
174 struct timeval start, end, total; |
|
175 long long usec; |
|
176 double throughput; |
|
177 resource = "/ref"; |
|
178 |
|
179 setvbuf(stdout, NULL, _IONBF, 0); |
|
180 |
|
181 base = event_base_new(); |
|
182 |
|
183 for (i=0; i < PARALLELISM; ++i) { |
|
184 if (launch_request() < 0) |
|
185 perror("launch"); |
|
186 } |
|
187 |
|
188 evutil_gettimeofday(&start, NULL); |
|
189 |
|
190 event_base_dispatch(base); |
|
191 |
|
192 evutil_gettimeofday(&end, NULL); |
|
193 evutil_timersub(&end, &start, &total); |
|
194 usec = total_time.tv_sec * 1000000 + total_time.tv_usec; |
|
195 |
|
196 if (!total_n_handled) { |
|
197 puts("Nothing worked. You probably did something dumb."); |
|
198 return 0; |
|
199 } |
|
200 |
|
201 |
|
202 throughput = total_n_handled / |
|
203 (total.tv_sec+ ((double)total.tv_usec)/1000000.0); |
|
204 |
|
205 #ifdef WIN32 |
|
206 #define I64_FMT "%I64d" |
|
207 #define I64_TYP __int64 |
|
208 #else |
|
209 #define I64_FMT "%lld" |
|
210 #define I64_TYP long long int |
|
211 #endif |
|
212 |
|
213 printf("\n%d requests in %d.%06d sec. (%.2f throughput)\n" |
|
214 "Each took about %.02f msec latency\n" |
|
215 I64_FMT "bytes read. %d errors.\n", |
|
216 total_n_handled, |
|
217 (int)total.tv_sec, (int)total.tv_usec, |
|
218 throughput, |
|
219 (double)(usec/1000) / total_n_handled, |
|
220 (I64_TYP)total_n_bytes, n_errors); |
|
221 |
|
222 return 0; |
|
223 } |