1275970Scy/* 2275970Scy * Copyright 2009-2012 Niels Provos and Nick Mathewson 3275970Scy * 4275970Scy * Redistribution and use in source and binary forms, with or without 5275970Scy * modification, are permitted provided that the following conditions 6275970Scy * are met: 7275970Scy * 1. Redistributions of source code must retain the above copyright 8275970Scy * notice, this list of conditions and the following disclaimer. 9275970Scy * 2. Redistributions in binary form must reproduce the above copyright 10275970Scy * notice, this list of conditions and the following disclaimer in the 11275970Scy * documentation and/or other materials provided with the distribution. 12275970Scy * 4. The name of the author may not be used to endorse or promote products 13275970Scy * derived from this software without specific prior written permission. 14275970Scy * 15275970Scy * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16275970Scy * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17275970Scy * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18275970Scy * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19275970Scy * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20275970Scy * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21275970Scy * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22275970Scy * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23275970Scy * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24275970Scy * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25275970Scy * 26275970Scy */ 27275970Scy 28275970Scy/* for EVUTIL_ERR_CONNECT_RETRIABLE macro */ 29275970Scy#include "util-internal.h" 30275970Scy 31275970Scy#include <sys/types.h> 32275970Scy#ifdef _WIN32 33275970Scy#include <winsock2.h> 34275970Scy#else 35275970Scy#include <sys/socket.h> 36275970Scy#include <netinet/in.h> 37275970Scy# ifdef _XOPEN_SOURCE_EXTENDED 38275970Scy# include <arpa/inet.h> 39275970Scy# endif 40275970Scy#endif 41275970Scy#include <stdlib.h> 42275970Scy#include <string.h> 43275970Scy#include <errno.h> 44275970Scy 45275970Scy#include "event2/event.h" 46275970Scy#include "event2/bufferevent.h" 47275970Scy#include "event2/buffer.h" 48275970Scy#include "event2/util.h" 49275970Scy 50275970Scyconst char *resource = NULL; 51275970Scystruct event_base *base = NULL; 52275970Scy 53275970Scyint total_n_handled = 0; 54275970Scyint total_n_errors = 0; 55275970Scyint total_n_launched = 0; 56275970Scysize_t total_n_bytes = 0; 57275970Scystruct timeval total_time = {0,0}; 58275970Scyint n_errors = 0; 59275970Scy 60275970Scyconst int PARALLELISM = 200; 61275970Scyconst int N_REQUESTS = 20000; 62275970Scy 63275970Scystruct request_info { 64275970Scy size_t n_read; 65275970Scy struct timeval started; 66275970Scy}; 67275970Scy 68275970Scystatic int launch_request(void); 69275970Scystatic void readcb(struct bufferevent *b, void *arg); 70275970Scystatic void errorcb(struct bufferevent *b, short what, void *arg); 71275970Scy 72275970Scystatic void 73275970Scyreadcb(struct bufferevent *b, void *arg) 74275970Scy{ 75275970Scy struct request_info *ri = arg; 76275970Scy struct evbuffer *input = bufferevent_get_input(b); 77275970Scy size_t n = evbuffer_get_length(input); 78275970Scy 79275970Scy ri->n_read += n; 80275970Scy evbuffer_drain(input, n); 81275970Scy} 82275970Scy 83275970Scystatic void 84275970Scyerrorcb(struct bufferevent *b, short what, void *arg) 85275970Scy{ 86275970Scy struct request_info *ri = arg; 87275970Scy struct timeval now, diff; 88275970Scy if (what & BEV_EVENT_EOF) { 89275970Scy ++total_n_handled; 90275970Scy total_n_bytes += ri->n_read; 91275970Scy evutil_gettimeofday(&now, NULL); 92275970Scy evutil_timersub(&now, &ri->started, &diff); 93275970Scy evutil_timeradd(&diff, &total_time, &total_time); 94275970Scy 95275970Scy if (total_n_handled && (total_n_handled%1000)==0) 96275970Scy printf("%d requests done\n",total_n_handled); 97275970Scy 98275970Scy if (total_n_launched < N_REQUESTS) { 99275970Scy if (launch_request() < 0) 100275970Scy perror("Can't launch"); 101275970Scy } 102275970Scy } else { 103275970Scy ++total_n_errors; 104275970Scy perror("Unexpected error"); 105275970Scy } 106275970Scy 107275970Scy bufferevent_setcb(b, NULL, NULL, NULL, NULL); 108275970Scy free(ri); 109275970Scy bufferevent_disable(b, EV_READ|EV_WRITE); 110275970Scy bufferevent_free(b); 111275970Scy} 112275970Scy 113275970Scystatic void 114275970Scyfrob_socket(evutil_socket_t sock) 115275970Scy{ 116275970Scy#ifdef HAVE_SO_LINGER 117275970Scy struct linger l; 118275970Scy#endif 119275970Scy int one = 1; 120275970Scy if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*)&one, sizeof(one))<0) 121275970Scy perror("setsockopt(SO_REUSEADDR)"); 122275970Scy#ifdef HAVE_SO_LINGER 123275970Scy l.l_onoff = 1; 124275970Scy l.l_linger = 0; 125275970Scy if (setsockopt(sock, SOL_SOCKET, SO_LINGER, (void*)&l, sizeof(l))<0) 126275970Scy perror("setsockopt(SO_LINGER)"); 127275970Scy#endif 128275970Scy} 129275970Scy 130275970Scystatic int 131275970Scylaunch_request(void) 132275970Scy{ 133275970Scy evutil_socket_t sock; 134275970Scy struct sockaddr_in sin; 135275970Scy struct bufferevent *b; 136275970Scy 137275970Scy struct request_info *ri; 138275970Scy 139275970Scy memset(&sin, 0, sizeof(sin)); 140275970Scy 141275970Scy ++total_n_launched; 142275970Scy 143275970Scy sin.sin_family = AF_INET; 144275970Scy sin.sin_addr.s_addr = htonl(0x7f000001); 145275970Scy sin.sin_port = htons(8080); 146275970Scy if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) 147275970Scy return -1; 148275970Scy if (evutil_make_socket_nonblocking(sock) < 0) { 149275970Scy evutil_closesocket(sock); 150275970Scy return -1; 151275970Scy } 152275970Scy frob_socket(sock); 153275970Scy if (connect(sock, (struct sockaddr*)&sin, sizeof(sin)) < 0) { 154275970Scy int e = errno; 155275970Scy if (! EVUTIL_ERR_CONNECT_RETRIABLE(e)) { 156275970Scy evutil_closesocket(sock); 157275970Scy return -1; 158275970Scy } 159275970Scy } 160275970Scy 161275970Scy ri = malloc(sizeof(*ri)); 162289997Sglebius if (ri == NULL) { 163289997Sglebius printf("Unable to allocate memory in launch_request()\n"); 164289997Sglebius return -1; 165289997Sglebius } 166275970Scy ri->n_read = 0; 167275970Scy evutil_gettimeofday(&ri->started, NULL); 168275970Scy 169275970Scy b = bufferevent_socket_new(base, sock, BEV_OPT_CLOSE_ON_FREE); 170275970Scy 171275970Scy bufferevent_setcb(b, readcb, NULL, errorcb, ri); 172275970Scy bufferevent_enable(b, EV_READ|EV_WRITE); 173275970Scy 174275970Scy evbuffer_add_printf(bufferevent_get_output(b), 175275970Scy "GET %s HTTP/1.0\r\n\r\n", resource); 176275970Scy 177275970Scy return 0; 178275970Scy} 179275970Scy 180275970Scy 181275970Scyint 182275970Scymain(int argc, char **argv) 183275970Scy{ 184275970Scy int i; 185275970Scy struct timeval start, end, total; 186275970Scy long long usec; 187275970Scy double throughput; 188275970Scy resource = "/ref"; 189275970Scy 190275970Scy setvbuf(stdout, NULL, _IONBF, 0); 191275970Scy 192275970Scy base = event_base_new(); 193275970Scy 194275970Scy for (i=0; i < PARALLELISM; ++i) { 195275970Scy if (launch_request() < 0) 196275970Scy perror("launch"); 197275970Scy } 198275970Scy 199275970Scy evutil_gettimeofday(&start, NULL); 200275970Scy 201275970Scy event_base_dispatch(base); 202275970Scy 203275970Scy evutil_gettimeofday(&end, NULL); 204275970Scy evutil_timersub(&end, &start, &total); 205275970Scy usec = total_time.tv_sec * (long long)1000000 + total_time.tv_usec; 206275970Scy 207275970Scy if (!total_n_handled) { 208275970Scy puts("Nothing worked. You probably did something dumb."); 209275970Scy return 0; 210275970Scy } 211275970Scy 212275970Scy 213275970Scy throughput = total_n_handled / 214275970Scy (total.tv_sec+ ((double)total.tv_usec)/1000000.0); 215275970Scy 216275970Scy#ifdef _WIN32 217275970Scy#define I64_FMT "%I64d" 218275970Scy#define I64_TYP __int64 219275970Scy#else 220275970Scy#define I64_FMT "%lld" 221275970Scy#define I64_TYP long long int 222275970Scy#endif 223275970Scy 224275970Scy printf("\n%d requests in %d.%06d sec. (%.2f throughput)\n" 225275970Scy "Each took about %.02f msec latency\n" 226275970Scy I64_FMT "bytes read. %d errors.\n", 227275970Scy total_n_handled, 228275970Scy (int)total.tv_sec, (int)total.tv_usec, 229275970Scy throughput, 230275970Scy (double)(usec/1000) / total_n_handled, 231275970Scy (I64_TYP)total_n_bytes, n_errors); 232275970Scy 233275970Scy return 0; 234275970Scy} 235