1 /* $NetBSD: bench_httpclient.c,v 1.7 2024/08/18 20:47:23 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 EVENT__HAVE_STRUCT_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 EVENT__HAVE_STRUCT_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 = evutil_socket_geterror(sock); 157 if (! EVUTIL_ERR_CONNECT_RETRIABLE(e)) { 158 evutil_closesocket(sock); 159 return -1; 160 } 161 } 162 163 ri = malloc(sizeof(*ri)); 164 ri->n_read = 0; 165 evutil_gettimeofday(&ri->started, NULL); 166 167 b = bufferevent_socket_new(base, sock, BEV_OPT_CLOSE_ON_FREE); 168 169 bufferevent_setcb(b, readcb, NULL, errorcb, ri); 170 bufferevent_enable(b, EV_READ|EV_WRITE); 171 172 evbuffer_add_printf(bufferevent_get_output(b), 173 "GET %s HTTP/1.0\r\n\r\n", resource); 174 175 return 0; 176 } 177 178 179 int 180 main(int argc, char **argv) 181 { 182 int i; 183 struct timeval start, end, total; 184 long long usec; 185 double throughput; 186 187 #ifdef _WIN32 188 WSADATA WSAData; 189 WSAStartup(0x101, &WSAData); 190 #endif 191 192 resource = "/ref"; 193 194 setvbuf(stdout, NULL, _IONBF, 0); 195 196 base = event_base_new(); 197 198 for (i=0; i < PARALLELISM; ++i) { 199 if (launch_request() < 0) 200 perror("launch"); 201 } 202 203 evutil_gettimeofday(&start, NULL); 204 205 event_base_dispatch(base); 206 207 evutil_gettimeofday(&end, NULL); 208 evutil_timersub(&end, &start, &total); 209 usec = total_time.tv_sec * (long long)1000000 + total_time.tv_usec; 210 211 if (!total_n_handled) { 212 puts("Nothing worked. You probably did something dumb."); 213 return 0; 214 } 215 216 217 throughput = total_n_handled / 218 (total.tv_sec+ ((double)total.tv_usec)/1000000.0); 219 220 #ifdef _WIN32 221 #define I64_FMT "%I64d" 222 #define I64_TYP __int64 223 #else 224 #define I64_FMT "%lld" 225 #define I64_TYP long long int 226 #endif 227 228 printf("\n%d requests in %d.%06d sec. (%.2f throughput)\n" 229 "Each took about %.02f msec latency\n" 230 I64_FMT "bytes read. %d errors.\n", 231 total_n_handled, 232 (int)total.tv_sec, (int)total.tv_usec, 233 throughput, 234 (double)(usec/1000) / total_n_handled, 235 (I64_TYP)total_n_bytes, n_errors); 236 237 #ifdef _WIN32 238 WSACleanup(); 239 #endif 240 241 return 0; 242 } 243