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 /* for EVUTIL_ERR_CONNECT_RETRIABLE macro */ 29 #include "util-internal.h" 30 31 #include <sys/types.h> 32 #ifdef _WIN32 33 #include <winsock2.h> 34 #else 35 #include <sys/socket.h> 36 #include <netinet/in.h> 37 # ifdef _XOPEN_SOURCE_EXTENDED 38 # include <arpa/inet.h> 39 # endif 40 #endif 41 #include <stdlib.h> 42 #include <string.h> 43 #include <errno.h> 44 45 #include "event2/event.h" 46 #include "event2/bufferevent.h" 47 #include "event2/buffer.h" 48 #include "event2/util.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 #ifdef HAVE_SO_LINGER 117 struct linger l; 118 #endif 119 int one = 1; 120 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*)&one, sizeof(one))<0) 121 perror("setsockopt(SO_REUSEADDR)"); 122 #ifdef HAVE_SO_LINGER 123 l.l_onoff = 1; 124 l.l_linger = 0; 125 if (setsockopt(sock, SOL_SOCKET, SO_LINGER, (void*)&l, sizeof(l))<0) 126 perror("setsockopt(SO_LINGER)"); 127 #endif 128 } 129 130 static int 131 launch_request(void) 132 { 133 evutil_socket_t sock; 134 struct sockaddr_in sin; 135 struct bufferevent *b; 136 137 struct request_info *ri; 138 139 memset(&sin, 0, sizeof(sin)); 140 141 ++total_n_launched; 142 143 sin.sin_family = AF_INET; 144 sin.sin_addr.s_addr = htonl(0x7f000001); 145 sin.sin_port = htons(8080); 146 if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) 147 return -1; 148 if (evutil_make_socket_nonblocking(sock) < 0) { 149 evutil_closesocket(sock); 150 return -1; 151 } 152 frob_socket(sock); 153 if (connect(sock, (struct sockaddr*)&sin, sizeof(sin)) < 0) { 154 int e = errno; 155 if (! EVUTIL_ERR_CONNECT_RETRIABLE(e)) { 156 evutil_closesocket(sock); 157 return -1; 158 } 159 } 160 161 ri = malloc(sizeof(*ri)); 162 if (ri == NULL) { 163 printf("Unable to allocate memory in launch_request()\n"); 164 return -1; 165 } 166 ri->n_read = 0; 167 evutil_gettimeofday(&ri->started, NULL); 168 169 b = bufferevent_socket_new(base, sock, BEV_OPT_CLOSE_ON_FREE); 170 171 bufferevent_setcb(b, readcb, NULL, errorcb, ri); 172 bufferevent_enable(b, EV_READ|EV_WRITE); 173 174 evbuffer_add_printf(bufferevent_get_output(b), 175 "GET %s HTTP/1.0\r\n\r\n", resource); 176 177 return 0; 178 } 179 180 181 int 182 main(int argc, char **argv) 183 { 184 int i; 185 struct timeval start, end, total; 186 long long usec; 187 double throughput; 188 resource = "/ref"; 189 190 setvbuf(stdout, NULL, _IONBF, 0); 191 192 base = event_base_new(); 193 194 for (i=0; i < PARALLELISM; ++i) { 195 if (launch_request() < 0) 196 perror("launch"); 197 } 198 199 evutil_gettimeofday(&start, NULL); 200 201 event_base_dispatch(base); 202 203 evutil_gettimeofday(&end, NULL); 204 evutil_timersub(&end, &start, &total); 205 usec = total_time.tv_sec * (long long)1000000 + total_time.tv_usec; 206 207 if (!total_n_handled) { 208 puts("Nothing worked. You probably did something dumb."); 209 return 0; 210 } 211 212 213 throughput = total_n_handled / 214 (total.tv_sec+ ((double)total.tv_usec)/1000000.0); 215 216 #ifdef _WIN32 217 #define I64_FMT "%I64d" 218 #define I64_TYP __int64 219 #else 220 #define I64_FMT "%lld" 221 #define I64_TYP long long int 222 #endif 223 224 printf("\n%d requests in %d.%06d sec. (%.2f throughput)\n" 225 "Each took about %.02f msec latency\n" 226 I64_FMT "bytes read. %d errors.\n", 227 total_n_handled, 228 (int)total.tv_sec, (int)total.tv_usec, 229 throughput, 230 (double)(usec/1000) / total_n_handled, 231 (I64_TYP)total_n_bytes, n_errors); 232 233 return 0; 234 } 235