From 0e287d6736c38fe357cbc65f59a11e8a7c11d459 Mon Sep 17 00:00:00 2001 From: Thomas Porzelt Date: Sun, 16 Oct 2011 10:24:51 +0200 Subject: [PATCH 1/3] add -H commandline parameter to specify custom headers --- src/weighttp.c | 49 +++++++++++++++++++++++++++++++++++++++++++------ src/weighttp.h | 1 + 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/src/weighttp.c b/src/weighttp.c index fbbe3ad..370e9ec 100644 --- a/src/weighttp.c +++ b/src/weighttp.c @@ -19,8 +19,10 @@ static void show_help(void) { 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) { @@ -68,10 +70,12 @@ static struct addrinfo *resolve_host(char *hostname, uint16_t port, uint8_t use_ return res; } -static char *forge_request(char *url, char keep_alive, char **host, uint16_t *port) { +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; *host = NULL; *port = 0; @@ -126,7 +130,22 @@ static char *forge_request(char *url, char keep_alive, char **host, uint16_t *po if (*url == '\0') url = "/"; - req = W_MALLOC(char, sizeof("GET HTTP/1.1\r\nHost: :65536\r\nUser-Agent: weighttp/\r\nConnection: keep-alive\r\n\r\n") + strlen(VERSION) + strlen(*host) + strlen(url)); + // total request size + len = strlen("GET HTTP/1.1\r\nHost: :65536\r\nConnection: keep-alive\r\n\r\n") + 1; + len += strlen(*host); + len += strlen(url); + + have_user_agent = 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 (!have_user_agent) + len += strlen("User-Agent: weighttp/" VERSION "\r\n"); + + req = W_MALLOC(char, len); strcpy(req, "GET "); strcat(req, url); @@ -135,7 +154,15 @@ static char *forge_request(char *url, char keep_alive, char **host, uint16_t *po if (*port != 80) sprintf(req + strlen(req), ":%"PRIu16, *port); - sprintf(req + strlen(req), "\r\nUser-Agent: weighttp/" VERSION "\r\n"); + 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"); @@ -178,10 +205,14 @@ int main(int argc, char *argv[]) { 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; + /* default settings */ use_ipv6 = 0; config.thread_count = 1; @@ -189,7 +220,7 @@ int main(int argc, char *argv[]) { config.req_count = 0; config.keep_alive = 0; - while ((c = getopt(argc, argv, ":hv6kn:t:c:")) != -1) { + while ((c = getopt(argc, argv, ":hv6kn:t:c:H:")) != -1) { switch (c) { case 'h': show_help(); @@ -213,6 +244,11 @@ int main(int argc, char *argv[]) { 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(); @@ -259,7 +295,7 @@ int main(int argc, char *argv[]) { return 2; } - if (NULL == (config.request = forge_request(argv[optind], config.keep_alive, &host, &port))) { + if (NULL == (config.request = forge_request(argv[optind], config.keep_alive, &host, &port, headers, headers_num))) { return 1; } @@ -361,6 +397,7 @@ int main(int argc, char *argv[]) { free(workers); free(config.request); free(host); + free(headers); freeaddrinfo(config.saddr); return 0; diff --git a/src/weighttp.h b/src/weighttp.h index 2ebbf12..584c975 100644 --- a/src/weighttp.h +++ b/src/weighttp.h @@ -30,6 +30,7 @@ #define CLIENT_BUFFER_SIZE 32 * 1024 #define W_MALLOC(t, n) ((t*) calloc((n), sizeof(t))) +#define W_REALLOC(p, t, n) ((t*) realloc(p, (n) * sizeof(t))) #define W_ERROR(f, ...) fprintf(stderr, "error: " f "\n", __VA_ARGS__) #define UNUSED(x) ( (void)(x) ) From 0fdf9d4229d56146023005461b953a864e5dc134 Mon Sep 17 00:00:00 2001 From: Thomas Porzelt Date: Sun, 16 Oct 2011 12:01:08 +0200 Subject: [PATCH 2/3] add stats about response status codes (2xx, 3xx, 4xx, 5xx) --- src/client.c | 49 ++++++++++++++++++++++++++++++++++++------------- src/client.h | 2 +- src/weighttp.c | 7 +++++++ src/worker.c | 3 ++- src/worker.h | 5 +++++ 5 files changed, 51 insertions(+), 15 deletions(-) diff --git a/src/client.c b/src/client.c index f6e31cc..87b0a1a 100644 --- a/src/client.c +++ b/src/client.c @@ -106,7 +106,7 @@ static void client_reset(Client *client) { client->request_offset = 0; client->ts_start = 0; client->ts_end = 0; - client->status_200 = 0; + client->status_success = 0; client->success = 0; client->content_length = -1; client->bytes_received = 0; @@ -259,7 +259,7 @@ void client_state_machine(Client *client) { } } else { /* disconnect */ - if (client->parser_state == PARSER_BODY && !client->keepalive && client->status_200 + if (client->parser_state == PARSER_BODY && !client->keepalive && client->status_success && !client->chunked && client->content_length == -1) { client->success = 1; client->state = CLIENT_END; @@ -314,27 +314,50 @@ void client_state_machine(Client *client) { static uint8_t client_parse(Client *client, int size) { char *end, *str; + uint16_t status_code; switch (client->parser_state) { case PARSER_START: //printf("parse (START):\n%s\n", &client->buffer[client->parser_offset]); /* look for HTTP/1.1 200 OK */ - if (client->buffer_offset < sizeof("HTTP/1.1 200 OK\r\n")) + if (client->buffer_offset < sizeof("HTTP/1.1 200\r\n")) return 1; - if (strncmp(client->buffer, "HTTP/1.1 200 OK\r\n", sizeof("HTTP/1.1 200 OK\r\n")-1) == 0) { - client->status_200 = 1; - client->parser_offset = sizeof("HTTP/1.1 200 ok\r\n") - 1; - } else { - client->status_200 = 0; - end = strchr(client->buffer, '\r'); + if (strncmp(client->buffer, "HTTP/1.1 ", sizeof("HTTP/1.1 ")-1) != 0) + return 0; - if (!end || *(end+1) != '\n') + // now the status code + status_code = 0; + str = client->buffer + sizeof("HTTP/1.1 ")-1; + for (end = str + 3; str != end; str++) { + if (*str < '0' || *str > '9') return 0; - client->parser_offset = end + 2 - client->buffer; + status_code *= 10; + status_code += *str - '0'; } + if (status_code >= 200 && status_code < 300) { + client->worker->stats.req_2xx++; + client->status_success = 1; + } else if (status_code < 400) { + client->worker->stats.req_3xx++; + client->status_success = 1; + } else if (status_code < 500) { + client->worker->stats.req_4xx++; + } else if (status_code < 600) { + client->worker->stats.req_5xx++; + } else { + // invalid status code + return 0; + } + + // look for next \r\n + end = strchr(end, '\r'); + if (!end || *(end+1) != '\n') + return 0; + + client->parser_offset = end + 2 - client->buffer; client->parser_state = PARSER_HEADER; case PARSER_HEADER: //printf("parse (HEADER)\n"); @@ -441,7 +464,7 @@ static uint8_t client_parse(Client *client, int size) { if (client->chunk_size == 0) { /* chunk of size 0 marks end of content body */ client->state = CLIENT_END; - client->success = client->status_200 ? 1 : 0; + client->success = client->status_success ? 1 : 0; return 1; } @@ -490,7 +513,7 @@ static uint8_t client_parse(Client *client, int size) { if (client->bytes_received == (uint64_t) (client->header_size + client->content_length)) { /* full response received */ client->state = CLIENT_END; - client->success = client->status_200 ? 1 : 0; + client->success = client->status_success ? 1 : 0; } } diff --git a/src/client.h b/src/client.h index 54ee5a7..0e387d1 100644 --- a/src/client.h +++ b/src/client.h @@ -33,7 +33,7 @@ struct Client { ev_tstamp ts_end; uint8_t keepalive; uint8_t success; - uint8_t status_200; + uint8_t status_success; uint8_t chunked; int64_t chunk_size; int64_t chunk_received; diff --git a/src/weighttp.c b/src/weighttp.c index 370e9ec..56862ef 100644 --- a/src/weighttp.c +++ b/src/weighttp.c @@ -369,6 +369,10 @@ int main(int argc, char *argv[]) { 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); } @@ -387,6 +391,9 @@ int main(int argc, char *argv[]) { 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 ); diff --git a/src/worker.c b/src/worker.c index 291981c..fea79a8 100644 --- a/src/worker.c +++ b/src/worker.c @@ -52,7 +52,8 @@ void *worker_thread(void* arg) { /* start all clients */ for (i = 0; i < worker->num_clients; i++) { - client_state_machine(worker->clients[i]); + if (worker->stats.req_started < worker->stats.req_todo) + client_state_machine(worker->clients[i]); } ev_loop(worker->loop, 0); diff --git a/src/worker.h b/src/worker.h index fd6f041..318d325 100644 --- a/src/worker.h +++ b/src/worker.h @@ -20,6 +20,11 @@ struct Stats { uint64_t req_error; /* total number of error'd requests */ uint64_t bytes_total; /* total number of bytes received (html+body) */ uint64_t bytes_body; /* total number of bytes received (body) */ + uint64_t req_1xx; + uint64_t req_2xx; + uint64_t req_3xx; + uint64_t req_4xx; + uint64_t req_5xx; }; struct Worker { From 1bdbe4003c8d7d61568ed45548fccf354ab19018 Mon Sep 17 00:00:00 2001 From: Thomas Porzelt Date: Sun, 16 Oct 2011 12:01:50 +0200 Subject: [PATCH 3/3] v0.3 --- COPYING | 2 +- src/client.c | 2 +- src/client.h | 2 +- src/weighttp.c | 2 +- src/weighttp.h | 2 +- src/worker.c | 2 +- src/worker.h | 2 +- wscript | 4 ++-- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/COPYING b/COPYING index 149ffdf..d58074d 100644 --- a/COPYING +++ b/COPYING @@ -1,7 +1,7 @@ The MIT License -Copyright (c) 2009 Thomas Porzelt +Copyright (c) 2009-2011 Thomas Porzelt Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/client.c b/src/client.c index 87b0a1a..f8875e4 100644 --- a/src/client.c +++ b/src/client.c @@ -2,7 +2,7 @@ * weighttp - a lightweight and simple webserver benchmarking tool * * Author: - * Copyright (c) 2009 Thomas Porzelt + * Copyright (c) 2009-2011 Thomas Porzelt * * License: * MIT, see COPYING file diff --git a/src/client.h b/src/client.h index 0e387d1..bb0a8b0 100644 --- a/src/client.h +++ b/src/client.h @@ -2,7 +2,7 @@ * weighttp - a lightweight and simple webserver benchmarking tool * * Author: - * Copyright (c) 2009 Thomas Porzelt + * Copyright (c) 2009-2011 Thomas Porzelt * * License: * MIT, see COPYING file diff --git a/src/weighttp.c b/src/weighttp.c index 56862ef..77504f1 100644 --- a/src/weighttp.c +++ b/src/weighttp.c @@ -2,7 +2,7 @@ * weighttp - a lightweight and simple webserver benchmarking tool * * Author: - * Copyright (c) 2009 Thomas Porzelt + * Copyright (c) 2009-2011 Thomas Porzelt * * License: * MIT, see COPYING file diff --git a/src/weighttp.h b/src/weighttp.h index 584c975..4d98fba 100644 --- a/src/weighttp.h +++ b/src/weighttp.h @@ -2,7 +2,7 @@ * weighttp - a lightweight and simple webserver benchmarking tool * * Author: - * Copyright (c) 2009 Thomas Porzelt + * Copyright (c) 2009-2011 Thomas Porzelt * * License: * MIT, see COPYING file diff --git a/src/worker.c b/src/worker.c index fea79a8..8cadc9b 100644 --- a/src/worker.c +++ b/src/worker.c @@ -2,7 +2,7 @@ * weighttp - a lightweight and simple webserver benchmarking tool * * Author: - * Copyright (c) 2009 Thomas Porzelt + * Copyright (c) 2009-2011 Thomas Porzelt * * License: * MIT, see COPYING file diff --git a/src/worker.h b/src/worker.h index 318d325..f87476b 100644 --- a/src/worker.h +++ b/src/worker.h @@ -2,7 +2,7 @@ * weighttp - a lightweight and simple webserver benchmarking tool * * Author: - * Copyright (c) 2009 Thomas Porzelt + * Copyright (c) 2009-2011 Thomas Porzelt * * License: * MIT, see COPYING file diff --git a/wscript b/wscript index 6d3cfc0..67fa9e8 100644 --- a/wscript +++ b/wscript @@ -5,7 +5,7 @@ * weighttp - a lightweight and simple webserver benchmarking tool * * Author: - * Copyright (c) 2009 Thomas Porzelt + * Copyright (c) 2009-2011 Thomas Porzelt * * License: * MIT, see COPYING file @@ -14,7 +14,7 @@ import Options # the following two variables are used by the target "waf dist" -VERSION='0.2' +VERSION='0.3' APPNAME='weighttp' # these variables are mandatory ('/' are converted automatically)