From afe5b670b00c8fb4a111f438bf52b52b4a77d54f Mon Sep 17 00:00:00 2001 From: Michael Mestnik Date: Tue, 10 Oct 2017 23:05:07 -0500 Subject: [PATCH 1/9] add Makefile --- .gitignore | 1 + Makefile | 11 +++++++++++ build.sh | 4 +++- 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100755 Makefile diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fb11266 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/htstress diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..998a225 --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +#!/usr/bin/make -f + +CFLAGS=-O3 +LDLIBS=-pthread + +all: htstress + +clean: + rm -f htstress + +.PHONY: all clean diff --git a/build.sh b/build.sh index c0cedf1..bd023c4 100755 --- a/build.sh +++ b/build.sh @@ -1 +1,3 @@ -gcc htstress.c -march=`uname -m|tr '_' '-'` -O3 -g -pthread -o htstress +#!/bin/sh +./Makefile || + gcc htstress.c -march=`uname -m|tr '_' '-'` -O3 -g -pthread -o htstress From 63e8e3b43abb413cc3e97c8ad90c1238fe32de17 Mon Sep 17 00:00:00 2001 From: Michael Mestnik Date: Fri, 13 Oct 2017 14:14:17 -0500 Subject: [PATCH 2/9] ignore eclips files --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index fb11266..e9be59c 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ /htstress +/.cproject +/.project From 0bb77082525dfd5d41af818ef875ceba6f1cb31b Mon Sep 17 00:00:00 2001 From: Michael Mestnik Date: Tue, 10 Oct 2017 23:04:48 -0500 Subject: [PATCH 3/9] add unistd.h header --- htstress.c | 1 + 1 file changed, 1 insertion(+) diff --git a/htstress.c b/htstress.c index 5fa11f8..6722741 100644 --- a/htstress.c +++ b/htstress.c @@ -39,6 +39,7 @@ OF SUCH DAMAGE. #include #include #include +#include #include #include #include From 2df65b59153d265c6f9697e23a7ba15e2697ab81 Mon Sep 17 00:00:00 2001 From: Michael Mestnik Date: Wed, 11 Oct 2017 15:50:54 -0500 Subject: [PATCH 4/9] properly terminate host --- htstress.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/htstress.c b/htstress.c index 6722741..c8f8edd 100644 --- a/htstress.c +++ b/htstress.c @@ -363,9 +363,11 @@ int main(int argc, char* argv[]) rq = "/"; else if (*rq == '/') { - host = malloc(rq - s); - memcpy(host, rq, rq - s); - + host = strndup(s, rq - s); + if(host == NULL) { + perror("host = strndup(s, rq - s)"); + exit(EXIT_FAILURE); + } } else if (*rq == ':') { *rq++ = 0; port = atoi(rq); From 0c2aeca101bca1d7581ae3ec1695c86b3c3886f2 Mon Sep 17 00:00:00 2001 From: Michael Mestnik Date: Wed, 11 Oct 2017 11:42:41 -0500 Subject: [PATCH 5/9] add unix socket support --- htstress.c | 44 ++++++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/htstress.c b/htstress.c index c8f8edd..e7bf0e8 100644 --- a/htstress.c +++ b/htstress.c @@ -33,6 +33,7 @@ OF SUCH DAMAGE. #include #include #include +#include #include #include #include @@ -70,11 +71,14 @@ struct econn { char *outbuf; size_t outbufsize; -struct sockaddr_in ssin; +struct sockaddr_storage sss; +socklen_t sssln = 0; int concurrency = 1; int num_threads = 1; +char *udaddr = ""; + volatile uint64_t num_requests = 0; volatile uint64_t max_requests = 0; volatile uint64_t good_requests = 0; @@ -88,12 +92,13 @@ int debug = 0; struct timeval tv, tve; -static const char short_options[] = "n:c:t:d"; +static const char short_options[] = "n:c:t:u:d"; static const struct option long_options[] = { { "number", 1, NULL, 'n' }, { "concurrency", 1, NULL, 'c' }, { "threads", 0, NULL, 't' }, + { "udaddr", 1, NULL, 'u' }, { "debug", 0, NULL, 'd' }, { "help", 0, NULL, '%' }, { NULL, 0, NULL, 0 } @@ -125,7 +130,7 @@ static void init_conn(int efd, struct econn* ec) { struct epoll_event evt; int ret; - ec->fd = socket(AF_INET, SOCK_STREAM, 0); + ec->fd = socket(sss.ss_family, SOCK_STREAM, 0); ec->offs = 0; ec->flags = 0; @@ -136,7 +141,7 @@ static void init_conn(int efd, struct econn* ec) { fcntl(ec->fd, F_SETFL, O_NONBLOCK); - ret = connect(ec->fd, (struct sockaddr*)&ssin, sizeof(ssin)); + ret = connect(ec->fd, (struct sockaddr*)&sss, sssln); if (ret && errno != EINPROGRESS) { perror("connect() failed"); @@ -291,6 +296,7 @@ static void print_usage() " -n, --number total number of requests (0 for inifinite, Ctrl-C to abort)\n" " -c, --concurrency number of concurrent connections\n" " -t, --threads number of threads (set this to the number of CPU cores)\n" + " -u, --udaddr path to unix domain socket\n" " -d, --debug debug HTTP response\n" " --help display this message\n" ); @@ -307,6 +313,8 @@ int main(int argc, char* argv[]) int port = 80; char *host = NULL; struct hostent *h; + struct sockaddr_in *ssin = (struct sockaddr_in *)&sss; + struct sockaddr_un *ssun = (struct sockaddr_un *)&sss; if (argc == 1) print_usage(); @@ -328,6 +336,10 @@ int main(int argc, char* argv[]) num_threads = atoi(optarg); break; + case 'u': + udaddr = optarg; + break; + case 'd': debug = 0x03; break; @@ -376,15 +388,23 @@ int main(int argc, char* argv[]) rq = "/"; } - h = gethostbyname(host); - if (!h || !h->h_length) { - printf("gethostbyname failed\n"); - return 1; - } + if(strnlen(udaddr, sizeof(ssun->sun_path)-1) == 0) { + h = gethostbyname(host); + if (!h || !h->h_length) { + printf("gethostbyname failed\n"); + return 1; + } - ssin.sin_addr.s_addr = *(u_int32_t*)h->h_addr; - ssin.sin_family = PF_INET; - ssin.sin_port = htons(port); + ssin->sin_addr.s_addr = *(u_int32_t*)h->h_addr; + ssin->sin_family = PF_INET; + ssin->sin_port = htons(port); + sssln = sizeof(struct sockaddr_in); + } else { + ssun->sun_family = PF_UNIX; + ssun->sun_path; + strncpy(ssun->sun_path, udaddr, sizeof(ssun->sun_path)-1); + sssln = sizeof(struct sockaddr_un); + } /* prepare request buffer */ outbuf = malloc(strlen(rq) + sizeof(HTTP_REQUEST_FMT) + strlen(host)); From c4211450e9954dcfacf7a933ef1c6b6c7ddb6ff1 Mon Sep 17 00:00:00 2001 From: Michael Mestnik Date: Wed, 11 Oct 2017 12:57:15 -0500 Subject: [PATCH 6/9] add ipv6 support --- htstress.c | 75 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 63 insertions(+), 12 deletions(-) diff --git a/htstress.c b/htstress.c index e7bf0e8..568c692 100644 --- a/htstress.c +++ b/htstress.c @@ -92,7 +92,7 @@ int debug = 0; struct timeval tv, tve; -static const char short_options[] = "n:c:t:u:d"; +static const char short_options[] = "n:c:t:u:d46"; static const struct option long_options[] = { { "number", 1, NULL, 'n' }, @@ -310,11 +310,22 @@ int main(int argc, char* argv[]) int next_option; int n; pthread_t useless_thread; - int port = 80; char *host = NULL; + char *port = "http"; struct hostent *h; struct sockaddr_in *ssin = (struct sockaddr_in *)&sss; + struct sockaddr_in6 *ssin6 = (struct sockaddr_in6 *)&sss; struct sockaddr_un *ssun = (struct sockaddr_un *)&sss; + struct addrinfo *result, *rp; + struct addrinfo hints; + int j, testfd; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG; + + memset(&sss, 0, sizeof(struct sockaddr_storage)); if (argc == 1) print_usage(); @@ -344,6 +355,14 @@ int main(int argc, char* argv[]) debug = 0x03; break; + case '4': + hints.ai_family = PF_INET; + break; + + case '6': + hints.ai_family = PF_INET6; + break; + case '%': print_usage(); @@ -382,23 +401,55 @@ int main(int argc, char* argv[]) } } else if (*rq == ':') { *rq++ = 0; - port = atoi(rq); + port = rq; rq = strchr(rq, '/'); - if (rq == NULL) + if (*rq == '/') { + port = strndup(port, rq - port); + if(port == NULL) { + perror("port = strndup(rq, rq - port)"); + exit(EXIT_FAILURE); + } + } + else rq = "/"; } if(strnlen(udaddr, sizeof(ssun->sun_path)-1) == 0) { - h = gethostbyname(host); - if (!h || !h->h_length) { - printf("gethostbyname failed\n"); - return 1; + j = getaddrinfo(host, port, &hints, &result); + if (j != 0) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(j)); + exit(EXIT_FAILURE); + } + + for (rp = result; rp != NULL; rp = rp->ai_next) { + testfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (testfd == -1) + continue; + + if (connect(testfd, rp->ai_addr, rp->ai_addrlen) == 0) { + close(testfd); + break; + } + + close(testfd); + } + + if (rp == NULL) { /* No address succeeded */ + fprintf(stderr, "getaddrinfo failed\n"); + exit(EXIT_FAILURE); + } + + if(rp->ai_addr->sa_family == PF_INET) { + *ssin = *(struct sockaddr_in*)rp->ai_addr; + } else if(rp->ai_addr->sa_family == PF_INET6) { + *ssin6 = *(struct sockaddr_in6*)rp->ai_addr; + } else { + fprintf(stderr, "invalid family %d from getaddrinfo\n", rp->ai_addr->sa_family); + exit(EXIT_FAILURE); } + sssln = rp->ai_addrlen; - ssin->sin_addr.s_addr = *(u_int32_t*)h->h_addr; - ssin->sin_family = PF_INET; - ssin->sin_port = htons(port); - sssln = sizeof(struct sockaddr_in); + freeaddrinfo(result); } else { ssun->sun_family = PF_UNIX; ssun->sun_path; From 57ba0e7b5a2c7caec51a00a92aebf38a9d96d0c7 Mon Sep 17 00:00:00 2001 From: Michael Mestnik Date: Thu, 12 Oct 2017 19:34:22 -0500 Subject: [PATCH 7/9] add host paramiter --- htstress.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/htstress.c b/htstress.c index 568c692..2afa6f2 100644 --- a/htstress.c +++ b/htstress.c @@ -92,13 +92,14 @@ int debug = 0; struct timeval tv, tve; -static const char short_options[] = "n:c:t:u:d46"; +static const char short_options[] = "n:c:t:u:h:d46"; static const struct option long_options[] = { { "number", 1, NULL, 'n' }, { "concurrency", 1, NULL, 'c' }, { "threads", 0, NULL, 't' }, { "udaddr", 1, NULL, 'u' }, + { "host", 1, NULL, 'h' }, { "debug", 0, NULL, 'd' }, { "help", 0, NULL, '%' }, { NULL, 0, NULL, 0 } @@ -297,6 +298,7 @@ static void print_usage() " -c, --concurrency number of concurrent connections\n" " -t, --threads number of threads (set this to the number of CPU cores)\n" " -u, --udaddr path to unix domain socket\n" + " -h, --host host to use for http request\n" " -d, --debug debug HTTP response\n" " --help display this message\n" ); @@ -311,6 +313,7 @@ int main(int argc, char* argv[]) int n; pthread_t useless_thread; char *host = NULL; + char *node = NULL; char *port = "http"; struct hostent *h; struct sockaddr_in *ssin = (struct sockaddr_in *)&sss; @@ -355,6 +358,10 @@ int main(int argc, char* argv[]) debug = 0x03; break; + case 'h': + host = optarg; + break; + case '4': hints.ai_family = PF_INET; break; @@ -386,7 +393,7 @@ int main(int argc, char* argv[]) if (!strncmp(s, HTTP_REQUEST_PREFIX, sizeof(HTTP_REQUEST_PREFIX) - 1)) s += (sizeof(HTTP_REQUEST_PREFIX) - 1); - host = s; + node = s; rq = strpbrk(s, ":/"); @@ -394,9 +401,9 @@ int main(int argc, char* argv[]) rq = "/"; else if (*rq == '/') { - host = strndup(s, rq - s); - if(host == NULL) { - perror("host = strndup(s, rq - s)"); + node = strndup(s, rq - s); + if(node == NULL) { + perror("node = strndup(s, rq - s)"); exit(EXIT_FAILURE); } } else if (*rq == ':') { @@ -415,7 +422,7 @@ int main(int argc, char* argv[]) } if(strnlen(udaddr, sizeof(ssun->sun_path)-1) == 0) { - j = getaddrinfo(host, port, &hints, &result); + j = getaddrinfo(node, port, &hints, &result); if (j != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(j)); exit(EXIT_FAILURE); @@ -458,6 +465,8 @@ int main(int argc, char* argv[]) } /* prepare request buffer */ + if(host == NULL) + host = node; outbuf = malloc(strlen(rq) + sizeof(HTTP_REQUEST_FMT) + strlen(host)); outbufsize = sprintf(outbuf, HTTP_REQUEST_FMT, rq, host); From f57a97ab10698d8bd1b1b6fdd6bc0f63c8f59d83 Mon Sep 17 00:00:00 2001 From: Michael Mestnik Date: Thu, 12 Oct 2017 12:15:56 -0500 Subject: [PATCH 8/9] more error handling --- htstress.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/htstress.c b/htstress.c index 2afa6f2..5c12de1 100644 --- a/htstress.c +++ b/htstress.c @@ -142,7 +142,9 @@ static void init_conn(int efd, struct econn* ec) { fcntl(ec->fd, F_SETFL, O_NONBLOCK); - ret = connect(ec->fd, (struct sockaddr*)&sss, sssln); + do { + ret = connect(ec->fd, (struct sockaddr*)&sss, sssln); + } while (ret && errno == EAGAIN); if (ret && errno != EINPROGRESS) { perror("connect() failed"); @@ -193,9 +195,19 @@ static void* worker(void* arg) exit(1); } - if (evts[n].events & (EPOLLHUP | EPOLLERR)) { + if (evts[n].events & EPOLLERR) { /* normally this should not happen */ - fprintf(stderr, "broken connection"); + int error = 0; + socklen_t errlen = sizeof(error); + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&error, &errlen) == 0) { + fprintf(stderr, "error = %s\n", strerror(error)); + } + exit(1); + } + + if (evts[n].events & EPOLLHUP) { + /* This can happen for HTTP/1.0 */ + fprintf(stderr, "EPOLLHUP\n"); exit(1); } From cf39f496df8a99a92fd296a507a45dd75bdd6882 Mon Sep 17 00:00:00 2001 From: Michael Mestnik Date: Sat, 14 Oct 2017 09:15:37 -0500 Subject: [PATCH 9/9] add signal handlers for exit and loop epoll_wait on signal --- htstress.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/htstress.c b/htstress.c index 5c12de1..7c7c68c 100644 --- a/htstress.c +++ b/htstress.c @@ -48,6 +48,8 @@ OF SUCH DAMAGE. #include #include #include +#include +typedef void (*sighandler_t)(int); #define HTTP_REQUEST_PREFIX "http://" @@ -89,6 +91,7 @@ volatile uint64_t out_bytes = 0; uint64_t ticks; int debug = 0; +int exit_i = 0; struct timeval tv, tve; @@ -179,7 +182,13 @@ static void* worker(void* arg) for(;;) { - nevts = epoll_wait(efd, evts, sizeof(evts) / sizeof(evts[0]), -1); + do { + nevts = epoll_wait(efd, evts, sizeof(evts) / sizeof(evts[0]), -1); + } while (!exit_i && nevts < 0 && errno == EINTR); + + if (exit_i != 0) { + exit(0); + } if (nevts == -1) { perror("epoll_wait"); @@ -302,6 +311,10 @@ static void* worker(void* arg) } } +void signal_exit(int signal) { + exit_i++; +} + static void print_usage() { printf("Usage: htstress [options] [http://]hostname[:port]/path\n" @@ -335,6 +348,21 @@ int main(int argc, char* argv[]) struct addrinfo hints; int j, testfd; + sighandler_t ret; + ret = signal(SIGINT, signal_exit); + + if (ret == SIG_ERR) { + perror("signal(SIGINT, handler)"); + exit(0); + } + + ret = signal(SIGTERM, signal_exit); + + if (ret == SIG_ERR) { + perror("signal(SIGTERM, handler)"); + exit(0); + } + memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM;