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