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