One Level Up
Top Level
src/weighttp.c - weighttp
Functions defined
Source code
- #include "weighttp.h"
- extern int optind, optopt;
- static void show_help(void) {
- printf("weighttp <options> <url>\n");
- printf(" -n num number of requests (mandatory)\n");
- printf(" -t num threadcount (default: 1)\n");
- printf(" -c num concurrent clients (default: 1)\n");
- printf(" -k keep alive (default: no)\n");
- printf(" -6 use ipv6 (default: no)\n");
- printf(" -H str add header to request\n");
- printf(" -h show help and exit\n");
- printf(" -v show version and exit\n\n");
- printf("example: weighttpd -n 10000 -c 10 -t 2 -k -H \"User-Agent: foo\" localhost/index.html\n\n");
- }
- static struct addrinfo *resolve_host(char *hostname, uint16_t port, uint8_t use_ipv6) {
- int err;
- char port_str[6];
- struct addrinfo hints, *res, *res_first, *res_last;
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = PF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
- sprintf(port_str, "%d", port);
- err = getaddrinfo(hostname, port_str, &hints, &res_first);
- if (err) {
- W_ERROR("could not resolve hostname: %s", hostname);
- return NULL;
- }
-
- res_last = NULL;
- for (res = res_first; res != NULL; res = res->ai_next) {
- if (res->ai_family == AF_INET && !use_ipv6)
- break;
- else if (res->ai_family == AF_INET6 && use_ipv6)
- break;
- res_last = res;
- }
- if (!res) {
- freeaddrinfo(res_first);
- W_ERROR("could not resolve hostname: %s", hostname);
- return NULL;
- }
- if (res != res_first) {
-
- res_last->ai_next = res->ai_next;
- freeaddrinfo(res_first);
- res->ai_next = NULL;
- }
- return res;
- }
- static char *forge_request(char *url, char keep_alive, char **host, uint16_t *port, char **headers, uint8_t headers_num) {
- char *c, *end;
- char *req;
- uint32_t len;
- uint8_t i;
- uint8_t have_user_agent, have_host;
- *host = NULL;
- *port = 0;
- if (strncmp(url, "http://", 7) == 0)
- url += 7;
- else if (strncmp(url, "https://", 8) == 0) {
- W_ERROR("%s", "no ssl support yet");
- url += 8;
- return NULL;
- }
- len = strlen(url);
- if ((c = strchr(url, ':'))) {
-
- *host = W_MALLOC(char, c - url + 1);
- memcpy(*host, url, c - url);
- (*host)[c - url] = '\0';
- if ((end = strchr(c+1, '/'))) {
- *end = '\0';
- *port = atoi(c+1);
- *end = '/';
- url = end;
- } else {
- *port = atoi(c+1);
- url += len;
- }
- } else {
- *port = 80;
- if ((c = strchr(url, '/'))) {
- *host = W_MALLOC(char, c - url + 1);
- memcpy(*host, url, c - url);
- (*host)[c - url] = '\0';
- url = c;
- } else {
- *host = W_MALLOC(char, len + 1);
- memcpy(*host, url, len);
- (*host)[len] = '\0';
- url += len;
- }
- }
- if (*port == 0) {
- W_ERROR("%s", "could not parse url");
- free(*host);
- return NULL;
- }
- if (*url == '\0')
- url = "/";
-
- len = strlen("GET HTTP/1.1\r\nConnection: keep-alive\r\n\r\n") + 1;
- len += strlen(url);
- have_user_agent = 0;
- have_host = 0;
- for (i = 0; i < headers_num; i++) {
- len += strlen(headers[i]) + strlen("\r\n");
- if (strncmp(headers[i], "User-Agent: ", sizeof("User-Agent: ")-1) == 0)
- have_user_agent = 1;
- if (strncasecmp(headers[i], "Host:", sizeof("Host:")-1) == 0)
- have_host = 1;
- }
- if (!have_user_agent)
- len += strlen("User-Agent: weighttp/" VERSION "\r\n");
- if (!have_host) {
- len += strlen("Host: :65536\r\n")-1;
- len += strlen(*host);
- }
- req = W_MALLOC(char, len);
- strcpy(req, "GET ");
- strcat(req, url);
- strcat(req, " HTTP/1.1\r\n");
- if (!have_host) {
- strcat(req, "Host: ");
- strcat(req, *host);
- if (*port != 80)
- sprintf(req + strlen(req), ":%"PRIu16, *port);
- strcat(req, "\r\n");
- }
- if (!have_user_agent)
- sprintf(req + strlen(req), "User-Agent: weighttp/" VERSION "\r\n");
- for (i = 0; i < headers_num; i++) {
- strcat(req, headers[i]);
- strcat(req, "\r\n");
- }
- if (keep_alive)
- strcat(req, "Connection: keep-alive\r\n\r\n");
- else
- strcat(req, "Connection: close\r\n\r\n");
- return req;
- }
- uint64_t str_to_uint64(char *str) {
- uint64_t i;
- for (i = 0; *str; str++) {
- if (*str < '0' || *str > '9')
- return UINT64_MAX;
- i *= 10;
- i += *str - '0';
- }
- return i;
- }
- int main(int argc, char *argv[]) {
- Worker **workers;
- pthread_t *threads;
- int i;
- char c;
- int err;
- struct ev_loop *loop;
- ev_tstamp ts_start, ts_end;
- Config config;
- Worker *worker;
- char *host;
- uint16_t port;
- uint8_t use_ipv6;
- uint16_t rest_concur, rest_req;
- Stats stats;
- ev_tstamp duration;
- int sec, millisec, microsec;
- uint64_t rps;
- uint64_t kbps;
- char **headers;
- uint8_t headers_num;
- printf("weighttp - a lightweight and simple webserver benchmarking tool\n\n");
- headers = NULL;
- headers_num = 0;
-
- use_ipv6 = 0;
- config.thread_count = 1;
- config.concur_count = 1;
- config.req_count = 0;
- config.keep_alive = 0;
- while ((c = getopt(argc, argv, ":hv6kn:t:c:H:")) != -1) {
- switch (c) {
- case 'h':
- show_help();
- return 0;
- case 'v':
- printf("version: " VERSION "\n");
- printf("build-date: " __DATE__ " " __TIME__ "\n\n");
- return 0;
- case '6':
- use_ipv6 = 1;
- break;
- case 'k':
- config.keep_alive = 1;
- break;
- case 'n':
- config.req_count = str_to_uint64(optarg);
- break;
- case 't':
- config.thread_count = atoi(optarg);
- break;
- case 'c':
- config.concur_count = atoi(optarg);
- break;
- case 'H':
- headers = W_REALLOC(headers, char*, headers_num+1);
- headers[headers_num] = optarg;
- headers_num++;
- break;
- case '?':
- W_ERROR("unkown option: -%c", optopt);
- show_help();
- return 1;
- }
- }
- if ((argc - optind) < 1) {
- W_ERROR("%s", "missing url argument\n");
- show_help();
- return 1;
- } else if ((argc - optind) > 1) {
- W_ERROR("%s", "too many arguments\n");
- show_help();
- return 1;
- }
-
- if (!config.thread_count) {
- W_ERROR("%s", "thread count has to be > 0\n");
- show_help();
- return 1;
- }
- if (!config.concur_count) {
- W_ERROR("%s", "number of concurrent clients has to be > 0\n");
- show_help();
- return 1;
- }
- if (!config.req_count) {
- W_ERROR("%s", "number of requests has to be > 0\n");
- show_help();
- return 1;
- }
- if (config.req_count == UINT64_MAX || config.thread_count > config.req_count || config.thread_count > config.concur_count || config.concur_count > config.req_count) {
- W_ERROR("%s", "insane arguments\n");
- show_help();
- return 1;
- }
- loop = ev_default_loop(0);
- if (!loop) {
- W_ERROR("%s", "could not initialize libev\n");
- return 2;
- }
- if (NULL == (config.request = forge_request(argv[optind], config.keep_alive, &host, &port, headers, headers_num))) {
- return 1;
- }
- config.request_size = strlen(config.request);
-
-
-
- if(!(config.saddr = resolve_host(host, port, use_ipv6))) {
- return 1;
- }
-
- threads = W_MALLOC(pthread_t, config.thread_count);
- workers = W_MALLOC(Worker*, config.thread_count);
- rest_concur = config.concur_count % config.thread_count;
- rest_req = config.req_count % config.thread_count;
- printf("starting benchmark...\n");
- memset(&stats, 0, sizeof(stats));
- ts_start = ev_time();
- for (i = 0; i < config.thread_count; i++) {
- uint64_t reqs = config.req_count / config.thread_count;
- uint16_t concur = config.concur_count / config.thread_count;
- uint64_t diff;
- if (rest_concur) {
- diff = (i == config.thread_count) ? rest_concur : (rest_concur / config.thread_count);
- diff = diff ? diff : 1;
- concur += diff;
- rest_concur -= diff;
- }
- if (rest_req) {
- diff = (i == config.thread_count) ? rest_req : (rest_req / config.thread_count);
- diff = diff ? diff : 1;
- reqs += diff;
- rest_req -= diff;
- }
- printf("spawning thread #%d: %"PRIu16" concurrent requests, %"PRIu64" total requests\n", i+1, concur, reqs);
- workers[i] = worker_new(i+1, &config, concur, reqs);
- if (!(workers[i])) {
- W_ERROR("%s", "failed to allocate worker or client");
- return 1;
- }
- err = pthread_create(&threads[i], NULL, worker_thread, (void*)workers[i]);
- if (err != 0) {
- W_ERROR("failed spawning thread (%d)", err);
- return 2;
- }
- }
- for (i = 0; i < config.thread_count; i++) {
- err = pthread_join(threads[i], NULL);
- worker = workers[i];
- if (err != 0) {
- W_ERROR("failed joining thread (%d)", err);
- return 3;
- }
- stats.req_started += worker->stats.req_started;
- stats.req_done += worker->stats.req_done;
- stats.req_success += worker->stats.req_success;
- stats.req_failed += worker->stats.req_failed;
- stats.bytes_total += worker->stats.bytes_total;
- stats.bytes_body += worker->stats.bytes_body;
- stats.req_2xx += worker->stats.req_2xx;
- stats.req_3xx += worker->stats.req_3xx;
- stats.req_4xx += worker->stats.req_4xx;
- stats.req_5xx += worker->stats.req_5xx;
- worker_free(worker);
- }
- ts_end = ev_time();
- duration = ts_end - ts_start;
- sec = duration;
- duration -= sec;
- duration = duration * 1000;
- millisec = duration;
- duration -= millisec;
- microsec = duration * 1000;
- rps = stats.req_done / (ts_end - ts_start);
- kbps = stats.bytes_total / (ts_end - ts_start) / 1024;
- printf("\nfinished in %d sec, %d millisec and %d microsec, %"PRIu64" req/s, %"PRIu64" kbyte/s\n", sec, millisec, microsec, rps, kbps);
- printf("requests: %"PRIu64" total, %"PRIu64" started, %"PRIu64" done, %"PRIu64" succeeded, %"PRIu64" failed, %"PRIu64" errored\n",
- config.req_count, stats.req_started, stats.req_done, stats.req_success, stats.req_failed, stats.req_error
- );
- printf("status codes: %"PRIu64" 2xx, %"PRIu64" 3xx, %"PRIu64" 4xx, %"PRIu64" 5xx\n",
- stats.req_2xx, stats.req_3xx, stats.req_4xx, stats.req_5xx
- );
- printf("traffic: %"PRIu64" bytes total, %"PRIu64" bytes http, %"PRIu64" bytes data\n",
- stats.bytes_total, stats.bytes_total - stats.bytes_body, stats.bytes_body
- );
- ev_default_destroy();
- free(threads);
- free(workers);
- free(config.request);
- free(host);
- free(headers);
- freeaddrinfo(config.saddr);
- return 0;
- }
One Level Up
Top Level