Compare commits
13 Commits
Author | SHA1 | Date |
---|---|---|
Stefan Bühler | 09ef98e7ae | |
Stefan Bühler | 6a008f7148 | |
Stefan Bühler | d1913cac26 | |
Stefan Bühler | e7dbbd757f | |
Thomas Porzelt | 1bdbe4003c | |
Thomas Porzelt | 0fdf9d4229 | |
Thomas Porzelt | 0e287d6736 | |
Stefan | d86a398452 | |
Thomas Porzelt | cb720d0bf1 | |
Thomas Porzelt | 5c686391cb | |
Thomas Porzelt | bdfdd127c1 | |
Thomas Porzelt | 9df53d5efc | |
Thomas Porzelt | b9a679e2f5 |
2
COPYING
2
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
|
||||
|
|
2
TODO
2
TODO
|
@ -1,6 +1,6 @@
|
|||
- timing statistics
|
||||
- generally better statistics
|
||||
- chunked encoding support
|
||||
- ssl support
|
||||
- better error reporting
|
||||
- pipelining
|
||||
- fuzzer
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
weighttp (0.3-3) unstable; urgency=low
|
||||
|
||||
* remove Makefile from debian branch, use quilt. fix Makefile.
|
||||
|
||||
-- Stefan Bühler <stbuehler@web.de> Fri, 10 May 2013 13:54:24 +0200
|
||||
|
||||
weighttp (0.3-2) unstable; urgency=low
|
||||
|
||||
[ Jyri J. Virkki ]
|
||||
* Fix header parsing to compare HTTP header field names case insensitively. This fixes the problem where responses get incorrectly marked as errors if the response header field name case doesn't happen to match to what is in the code.
|
||||
|
||||
[ Ben Brown ]
|
||||
* Corrected Host header handling (fixes #2477)
|
||||
* User-Agent header no longer needs a leading space
|
||||
|
||||
-- Stefan Bühler <stbuehler@web.de> Thu, 09 May 2013 19:17:14 +0200
|
||||
|
||||
weighttp (0.3-1) unstable; urgency=low
|
||||
|
||||
* New upstream release
|
||||
|
||||
-- Stefan Bühler <stbuehler@web.de> Thu, 09 May 2013 19:07:56 +0200
|
||||
|
||||
weighttp (0.2-2) unstable; urgency=low
|
||||
|
||||
* Use old debhelper for backports
|
||||
|
||||
-- Stefan Bühler <source@stbuehler.de> Sun, 24 Oct 2010 18:49:44 +0000
|
||||
|
||||
weighttp (0.2-1) unstable; urgency=low
|
||||
|
||||
* Initial release
|
||||
|
||||
-- Stefan Bühler <source@stbuehler.de> Mon, 23 Aug 2010 12:58:47 +0000
|
||||
|
||||
weighttp (0.2-0) unstable; urgency=low
|
||||
|
||||
* Initial release
|
||||
|
||||
-- Stefan Bühler <source@stbuehler.de> Mon, 23 Aug 2010 12:58:46 +0000
|
|
@ -0,0 +1 @@
|
|||
9
|
|
@ -0,0 +1,26 @@
|
|||
Source: weighttp
|
||||
Section: net
|
||||
Priority: extra
|
||||
Maintainer: Stefan Bühler <stbuehler@web.de>
|
||||
Build-Depends: debhelper (>= 9), libev-dev
|
||||
Standards-Version: 3.9.4
|
||||
Homepage: http://weighttp.lighttpd.net/
|
||||
Vcs-Git: git://git.lighttpd.net/weighttp
|
||||
Vcs-Browser: http://cgit.lighttpd.net/weighttp/
|
||||
|
||||
Package: weighttp
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}
|
||||
Description: lightweight and simple webserver benchmarking tool
|
||||
weighttp was designed to be very fast and easy to use and only supports a
|
||||
tiny fraction of the HTTP protocol in order to be lean and simple.
|
||||
.
|
||||
weighttp supports multithreading to make good use of modern CPUs with
|
||||
multiple cores as well as asynchronous i/o for concurrent requests
|
||||
within a single thread.
|
||||
.
|
||||
For event handling, weighty relies on libev which fits the design perfectly,
|
||||
being lightweight and fast itself.
|
||||
.
|
||||
Thanks to that, weighty supports all modern high-performance event
|
||||
interfaces like epoll or kqueue, that the major OSs provide.
|
|
@ -0,0 +1,44 @@
|
|||
This work was packaged for Debian by:
|
||||
|
||||
Stefan Bühler <source@stbuehler.de> on Mon, 23 Aug 2010 12:58:46 +0000
|
||||
|
||||
It was downloaded from:
|
||||
|
||||
http://weighttp.lighttpd.net/
|
||||
|
||||
Upstream Author:
|
||||
|
||||
Thomas Porzelt <tp@cryosphere.de>
|
||||
|
||||
Copyright:
|
||||
|
||||
Copyright (c) 2009 Thomas Porzelt
|
||||
|
||||
License:
|
||||
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2009 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
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
The Debian packaging is:
|
||||
|
||||
Copyright (C) 2010 Stefan Bühler <source@stbuehler.de>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
README
|
||||
TODO
|
|
@ -0,0 +1,37 @@
|
|||
From: =?UTF-8?q?Stefan=20B=C3=BChler?= <stbuehler@web.de>
|
||||
Date: Fri, 10 May 2013 13:48:29 +0200
|
||||
Subject: Makefile to replace waf handling
|
||||
|
||||
---
|
||||
Makefile | 22 ++++++++++++++++++++++
|
||||
1 file changed, 22 insertions(+)
|
||||
create mode 100644 Makefile
|
||||
|
||||
diff --git a/Makefile b/Makefile
|
||||
new file mode 100644
|
||||
index 0000000..741b8a1
|
||||
--- /dev/null
|
||||
+++ b/Makefile
|
||||
@@ -0,0 +1,22 @@
|
||||
+
|
||||
+all: weighttp
|
||||
+
|
||||
+override SOURCES := $(wildcard src/*.c)
|
||||
+override OBJECTS := $(patsubst %.c,%.o,$(SOURCES))
|
||||
+
|
||||
+# VERSION must be set in env
|
||||
+
|
||||
+LIBS += -lev -lpthread
|
||||
+override CFLAGS += '-DVERSION="$(VERSION)"'
|
||||
+
|
||||
+weighttp: $(OBJECTS)
|
||||
+ $(CC) $(LDFLAGS) -o weighttp $^ $(LIBS)
|
||||
+
|
||||
+install: weighttp
|
||||
+ mkdir -p $(DESTDIR)/usr/bin
|
||||
+ cp weighttp $(DESTDIR)/usr/bin/
|
||||
+
|
||||
+clean:
|
||||
+ rm -f weighttp $(OBJECTS)
|
||||
+
|
||||
+.PHONY: all install clean
|
57
debian/patches/0002-Fix-header-parsing-to-compare-HTTP-header-field-name.patch
vendored
Normal file
57
debian/patches/0002-Fix-header-parsing-to-compare-HTTP-header-field-name.patch
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
From: "Jyri J. Virkki" <jyri@virkki.com>
|
||||
Date: Sun, 6 Jan 2013 02:21:09 -0800
|
||||
Subject: =?UTF-8?q?Fix=20header=20parsing=20to=20compare=20HTTP=20header=20f?=
|
||||
=?UTF-8?q?ield=20names=20case=0Ainsensitively.=20This=20fixes=20the=20probl?=
|
||||
=?UTF-8?q?em=20where=20responses=20get=20incorrectly=0Amarked=20as=20errors?=
|
||||
=?UTF-8?q?=20if=20the=20response=20header=20field=20name=20case=20doesn't=20?=
|
||||
=?UTF-8?q?happen=0Ato=20match=20to=20what=20is=20in=20the=20code.?=
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Also compare connection and transfer-encoding tokens case insensitively.
|
||||
|
||||
(Reference: RFC 2616: header field names are not case sensitive.)
|
||||
|
||||
Co-authored-by: Stefan Bühler <stbuehler@web.de>
|
||||
---
|
||||
src/client.c | 14 ++++++--------
|
||||
1 file changed, 6 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/src/client.c b/src/client.c
|
||||
index f8875e4..dd48265 100644
|
||||
--- a/src/client.c
|
||||
+++ b/src/client.c
|
||||
@@ -379,26 +379,24 @@ static uint8_t client_parse(Client *client, int size) {
|
||||
str = &client->buffer[client->parser_offset];
|
||||
//printf("checking header: '%s'\n", str);
|
||||
|
||||
- if (strncmp(str, "Content-Length: ", sizeof("Content-Length: ")-1) == 0) {
|
||||
+ if (strncasecmp(str, "Content-Length: ", sizeof("Content-Length: ")-1) == 0) {
|
||||
/* content length header */
|
||||
client->content_length = str_to_uint64(str + sizeof("Content-Length: ") - 1);
|
||||
- } else if (strncmp(str, "Connection: ", sizeof("Connection: ")-1) == 0) {
|
||||
+ } else if (strncasecmp(str, "Connection: ", sizeof("Connection: ")-1) == 0) {
|
||||
/* connection header */
|
||||
str += sizeof("Connection: ") - 1;
|
||||
|
||||
- if (strncmp(str, "close", sizeof("close")-1) == 0)
|
||||
+ if (strncasecmp(str, "close", sizeof("close")-1) == 0)
|
||||
client->keepalive = 0;
|
||||
- else if (strncmp(str, "Keep-Alive", sizeof("Keep-Alive")-1) == 0)
|
||||
- client->keepalive = client->worker->config->keep_alive;
|
||||
- else if (strncmp(str, "keep-alive", sizeof("keep-alive")-1) == 0)
|
||||
+ else if (strncasecmp(str, "keep-alive", sizeof("keep-alive")-1) == 0)
|
||||
client->keepalive = client->worker->config->keep_alive;
|
||||
else
|
||||
return 0;
|
||||
- } else if (strncmp(str, "Transfer-Encoding: ", sizeof("Transfer-Encoding: ")-1) == 0) {
|
||||
+ } else if (strncasecmp(str, "Transfer-Encoding: ", sizeof("Transfer-Encoding: ")-1) == 0) {
|
||||
/* transfer encoding header */
|
||||
str += sizeof("Transfer-Encoding: ") - 1;
|
||||
|
||||
- if (strncmp(str, "chunked", sizeof("chunked")-1) == 0)
|
||||
+ if (strncasecmp(str, "chunked", sizeof("chunked")-1) == 0)
|
||||
client->chunked = 1;
|
||||
else
|
||||
return 0;
|
|
@ -0,0 +1,76 @@
|
|||
From: Ben Brown <ben@427.org.uk>
|
||||
Date: Tue, 19 Feb 2013 20:03:57 +0000
|
||||
Subject: Corrected Host header handling (fixes #2477)
|
||||
|
||||
---
|
||||
src/weighttp.c | 33 ++++++++++++++++++++++++++++++---
|
||||
1 file changed, 30 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/src/weighttp.c b/src/weighttp.c
|
||||
index 77504f1..a7744b5 100644
|
||||
--- a/src/weighttp.c
|
||||
+++ b/src/weighttp.c
|
||||
@@ -76,9 +76,11 @@ static char *forge_request(char *url, char keep_alive, char **host, uint16_t *po
|
||||
uint32_t len;
|
||||
uint8_t i;
|
||||
uint8_t have_user_agent;
|
||||
+ char *header_host;
|
||||
|
||||
*host = NULL;
|
||||
*port = 0;
|
||||
+ header_host = NULL;
|
||||
|
||||
if (strncmp(url, "http://", 7) == 0)
|
||||
url += 7;
|
||||
@@ -137,6 +139,25 @@ static char *forge_request(char *url, char keep_alive, char **host, uint16_t *po
|
||||
|
||||
have_user_agent = 0;
|
||||
for (i = 0; i < headers_num; i++) {
|
||||
+ if (strncmp(headers[i], "Host:", sizeof("Host:")-1) == 0) {
|
||||
+ if (header_host) {
|
||||
+ W_ERROR("%s", "Duplicate Host header");
|
||||
+ free(*host);
|
||||
+ return NULL;
|
||||
+ }
|
||||
+ header_host = headers[i] + 5;
|
||||
+ if (*header_host == ' ')
|
||||
+ header_host++;
|
||||
+
|
||||
+ if (strlen(header_host) == 0) {
|
||||
+ W_ERROR("%s", "Invalid Host header");
|
||||
+ free(*host);
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
+ len += strlen(header_host);
|
||||
+ continue;
|
||||
+ }
|
||||
len += strlen(headers[i]) + strlen("\r\n");
|
||||
if (strncmp(headers[i], "User-Agent: ", sizeof("User-Agent: ")-1) == 0)
|
||||
have_user_agent = 1;
|
||||
@@ -150,9 +171,13 @@ static char *forge_request(char *url, char keep_alive, char **host, uint16_t *po
|
||||
strcpy(req, "GET ");
|
||||
strcat(req, url);
|
||||
strcat(req, " HTTP/1.1\r\nHost: ");
|
||||
- strcat(req, *host);
|
||||
- if (*port != 80)
|
||||
- sprintf(req + strlen(req), ":%"PRIu16, *port);
|
||||
+ if (header_host) {
|
||||
+ strcat(req, header_host);
|
||||
+ } else {
|
||||
+ strcat(req, *host);
|
||||
+ if (*port != 80)
|
||||
+ sprintf(req + strlen(req), ":%"PRIu16, *port);
|
||||
+ }
|
||||
|
||||
strcat(req, "\r\n");
|
||||
|
||||
@@ -160,6 +185,8 @@ static char *forge_request(char *url, char keep_alive, char **host, uint16_t *po
|
||||
sprintf(req + strlen(req), "User-Agent: weighttp/" VERSION "\r\n");
|
||||
|
||||
for (i = 0; i < headers_num; i++) {
|
||||
+ if (strncmp(headers[i], "Host:", sizeof("Host:")-1) == 0)
|
||||
+ continue;
|
||||
strcat(req, headers[i]);
|
||||
strcat(req, "\r\n");
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
From: Ben Brown <ben@427.org.uk>
|
||||
Date: Tue, 19 Feb 2013 20:08:13 +0000
|
||||
Subject: User-Agent header no longer needs a leading space
|
||||
|
||||
---
|
||||
src/weighttp.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/weighttp.c b/src/weighttp.c
|
||||
index a7744b5..fa6af29 100644
|
||||
--- a/src/weighttp.c
|
||||
+++ b/src/weighttp.c
|
||||
@@ -159,7 +159,7 @@ static char *forge_request(char *url, char keep_alive, char **host, uint16_t *po
|
||||
continue;
|
||||
}
|
||||
len += strlen(headers[i]) + strlen("\r\n");
|
||||
- if (strncmp(headers[i], "User-Agent: ", sizeof("User-Agent: ")-1) == 0)
|
||||
+ if (strncmp(headers[i], "User-Agent:", sizeof("User-Agent:")-1) == 0)
|
||||
have_user_agent = 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
0001-Makefile-to-replace-waf-handling.patch
|
||||
0002-Fix-header-parsing-to-compare-HTTP-header-field-name.patch
|
||||
0003-Corrected-Host-header-handling-fixes-2477.patch
|
||||
0004-User-Agent-header-no-longer-needs-a-leading-space.patch
|
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/make -f
|
||||
|
||||
VERSION := $(shell dpkg-parsechangelog | awk '/Version:/ { print $2 }' | sed -e 's/-[^-]*$$//')
|
||||
export VERSION
|
||||
|
||||
%:
|
||||
dh $@
|
|
@ -0,0 +1 @@
|
|||
3.0 (quilt)
|
180
src/client.c
180
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
|
||||
|
@ -10,7 +10,7 @@
|
|||
|
||||
#include "weighttp.h"
|
||||
|
||||
static uint8_t client_parse(Client *client);
|
||||
static uint8_t client_parse(Client *client, int size);
|
||||
static void client_io_cb(struct ev_loop *loop, ev_io *w, int revents);
|
||||
static void client_set_events(Client *client, int events);
|
||||
/*
|
||||
|
@ -66,6 +66,9 @@ Client *client_new(Worker *worker) {
|
|||
client->buffer_offset = 0;
|
||||
client->request_offset = 0;
|
||||
client->keepalive = client->worker->config->keep_alive;
|
||||
client->chunked = 0;
|
||||
client->chunk_size = -1;
|
||||
client->chunk_received = 0;
|
||||
|
||||
return client;
|
||||
}
|
||||
|
@ -103,12 +106,15 @@ 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;
|
||||
client->header_size = 0;
|
||||
client->keepalive = client->worker->config->keep_alive;
|
||||
client->chunked = 0;
|
||||
client->chunk_size = -1;
|
||||
client->chunk_received = 0;
|
||||
}
|
||||
|
||||
static uint8_t client_connect(Client *client) {
|
||||
|
@ -220,7 +226,7 @@ void client_state_machine(Client *client) {
|
|||
case CLIENT_READING:
|
||||
while (1) {
|
||||
r = read(client->sock_watcher.fd, &client->buffer[client->buffer_offset], sizeof(client->buffer) - client->buffer_offset - 1);
|
||||
//printf("read(): %d\n", r);
|
||||
//printf("read(): %d, offset was: %d\n", r, client->buffer_offset);
|
||||
if (r == -1) {
|
||||
/* error */
|
||||
if (errno == EINTR)
|
||||
|
@ -241,7 +247,7 @@ void client_state_machine(Client *client) {
|
|||
}
|
||||
client->buffer[client->buffer_offset] = '\0';
|
||||
//printf("buffer:\n==========\n%s\n==========\n", client->buffer);
|
||||
if (!client_parse(client)) {
|
||||
if (!client_parse(client, r)) {
|
||||
client->state = CLIENT_ERROR;
|
||||
//printf("parser failed\n");
|
||||
break;
|
||||
|
@ -253,8 +259,15 @@ void client_state_machine(Client *client) {
|
|||
}
|
||||
} else {
|
||||
/* disconnect */
|
||||
client->state = CLIENT_ERROR;
|
||||
break;
|
||||
if (client->parser_state == PARSER_BODY && !client->keepalive && client->status_success
|
||||
&& !client->chunked && client->content_length == -1) {
|
||||
client->success = 1;
|
||||
client->state = CLIENT_END;
|
||||
} else {
|
||||
client->state = CLIENT_ERROR;
|
||||
}
|
||||
|
||||
goto start;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -299,29 +312,52 @@ void client_state_machine(Client *client) {
|
|||
}
|
||||
|
||||
|
||||
static uint8_t client_parse(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");
|
||||
|
@ -336,7 +372,7 @@ static uint8_t client_parse(Client *client) {
|
|||
client->header_size = end + 2 - client->buffer;
|
||||
//printf("body reached\n");
|
||||
|
||||
return client_parse(client);
|
||||
return client_parse(client, size - client->header_size);
|
||||
}
|
||||
|
||||
*end = '\0';
|
||||
|
@ -358,15 +394,25 @@ static uint8_t client_parse(Client *client) {
|
|||
client->keepalive = client->worker->config->keep_alive;
|
||||
else
|
||||
return 0;
|
||||
} else if (strncmp(str, "Transfer-Encoding: ", sizeof("Transfer-Encoding: ")-1) == 0) {
|
||||
/* transfer encoding header */
|
||||
str += sizeof("Transfer-Encoding: ") - 1;
|
||||
|
||||
if (strncmp(str, "chunked", sizeof("chunked")-1) == 0)
|
||||
client->chunked = 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if (*(end+2) == '\r' && *(end+3) == '\n') {
|
||||
/* body reached */
|
||||
client->parser_state = PARSER_BODY;
|
||||
client->header_size = end + 4 - client->buffer;
|
||||
client->parser_offset = client->header_size;
|
||||
//printf("body reached\n");
|
||||
|
||||
return client_parse(client);
|
||||
return client_parse(client, size - client->header_size);
|
||||
}
|
||||
|
||||
client->parser_offset = end - client->buffer + 2;
|
||||
|
@ -378,15 +424,97 @@ static uint8_t client_parse(Client *client) {
|
|||
/* do nothing, just consume the data */
|
||||
/*printf("content-l: %"PRIu64", header: %d, recevied: %"PRIu64"\n",
|
||||
client->content_length, client->header_size, client->bytes_received);*/
|
||||
client->buffer_offset = 0;
|
||||
|
||||
if (client->content_length == -1)
|
||||
return 0;
|
||||
if (client->chunked) {
|
||||
int consume_max;
|
||||
|
||||
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;
|
||||
str = &client->buffer[client->parser_offset];
|
||||
/*printf("parsing chunk: '%s'\n(%"PRIi64" received, %"PRIi64" size, %d parser offset)\n",
|
||||
str, client->chunk_received, client->chunk_size, client->parser_offset
|
||||
);*/
|
||||
|
||||
if (client->chunk_size == -1) {
|
||||
/* read chunk size */
|
||||
client->chunk_size = 0;
|
||||
client->chunk_received = 0;
|
||||
end = str + size;
|
||||
|
||||
for (; str < end; str++) {
|
||||
if (*str == ';' || *str == '\r')
|
||||
break;
|
||||
|
||||
client->chunk_size *= 16;
|
||||
if (*str >= '0' && *str <= '9')
|
||||
client->chunk_size += *str - '0';
|
||||
else if (*str >= 'A' && *str <= 'Z')
|
||||
client->chunk_size += 10 + *str - 'A';
|
||||
else if (*str >= 'a' && *str <= 'z')
|
||||
client->chunk_size += 10 + *str - 'a';
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
str = strstr(str, "\r\n");
|
||||
if (!str)
|
||||
return 0;
|
||||
str += 2;
|
||||
|
||||
//printf("---------- chunk size: %"PRIi64", %d read, %d offset, data: '%s'\n", client->chunk_size, size, client->parser_offset, str);
|
||||
|
||||
if (client->chunk_size == 0) {
|
||||
/* chunk of size 0 marks end of content body */
|
||||
client->state = CLIENT_END;
|
||||
client->success = client->status_success ? 1 : 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
size -= str - &client->buffer[client->parser_offset];
|
||||
client->parser_offset = str - client->buffer;
|
||||
}
|
||||
|
||||
/* consume chunk till chunk_size is reached */
|
||||
consume_max = client->chunk_size - client->chunk_received;
|
||||
|
||||
if (size < consume_max)
|
||||
consume_max = size;
|
||||
|
||||
client->chunk_received += consume_max;
|
||||
client->parser_offset += consume_max;
|
||||
|
||||
//printf("---------- chunk consuming: %d, received: %"PRIi64" of %"PRIi64", offset: %d\n", consume_max, client->chunk_received, client->chunk_size, client->parser_offset);
|
||||
|
||||
if (client->chunk_received == client->chunk_size) {
|
||||
if (client->buffer[client->parser_offset] != '\r' || client->buffer[client->parser_offset+1] != '\n')
|
||||
return 0;
|
||||
|
||||
/* got whole chunk, next! */
|
||||
//printf("---------- got whole chunk!!\n");
|
||||
client->chunk_size = -1;
|
||||
client->chunk_received = 0;
|
||||
client->parser_offset += 2;
|
||||
consume_max += 2;
|
||||
|
||||
/* there is stuff left to parse */
|
||||
if (size - consume_max > 0)
|
||||
return client_parse(client, size - consume_max);
|
||||
}
|
||||
|
||||
client->parser_offset = 0;
|
||||
client->buffer_offset = 0;
|
||||
|
||||
return 1;
|
||||
} else {
|
||||
/* not chunked, just consume all data till content-length is reached */
|
||||
client->buffer_offset = 0;
|
||||
|
||||
if (client->content_length == -1)
|
||||
return 0;
|
||||
|
||||
if (client->bytes_received == (uint64_t) (client->header_size + client->content_length)) {
|
||||
/* full response received */
|
||||
client->state = CLIENT_END;
|
||||
client->success = client->status_success ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
|
|
@ -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
|
||||
|
@ -33,7 +33,10 @@ 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;
|
||||
int64_t content_length;
|
||||
uint64_t bytes_received; /* including http header */
|
||||
uint16_t header_size;
|
||||
|
|
|
@ -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
|
||||
|
@ -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\nConnection: keep-alive\r\n\r\n") + 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);
|
||||
|
@ -134,10 +153,21 @@ static char *forge_request(char *url, char keep_alive, char **host, uint16_t *po
|
|||
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, "\r\nConnection: keep-alive\r\n\r\n");
|
||||
strcat(req, "Connection: keep-alive\r\n\r\n");
|
||||
else
|
||||
strcat(req, "\r\nConnection: close\r\n\r\n");
|
||||
strcat(req, "Connection: close\r\n\r\n");
|
||||
|
||||
return req;
|
||||
}
|
||||
|
@ -175,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;
|
||||
|
@ -186,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();
|
||||
|
@ -210,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();
|
||||
|
@ -256,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;
|
||||
}
|
||||
|
||||
|
@ -330,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);
|
||||
}
|
||||
|
@ -348,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
|
||||
);
|
||||
|
@ -358,6 +404,7 @@ int main(int argc, char *argv[]) {
|
|||
free(workers);
|
||||
free(config.request);
|
||||
free(host);
|
||||
free(headers);
|
||||
freeaddrinfo(config.saddr);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -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
|
||||
|
@ -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) )
|
||||
|
||||
|
|
|
@ -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
|
||||
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
@ -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 {
|
||||
|
|
7
wscript
7
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.1'
|
||||
VERSION='0.3'
|
||||
APPNAME='weighttp'
|
||||
|
||||
# these variables are mandatory ('/' are converted automatically)
|
||||
|
@ -34,8 +34,7 @@ def configure(conf):
|
|||
'-std=gnu99', '-Wall', '-Wshadow', '-W', '-pedantic', '-g', '-g2', '-O2', '-Wmissing-declarations',
|
||||
'-Wdeclaration-after-statement', '-Wno-pointer-sign', '-Wcast-align', '-Winline', '-Wsign-compare',
|
||||
'-Wnested-externs', '-Wpointer-arith', '-Wbad-function-cast', '-Wmissing-prototypes',
|
||||
'-fPIC', '-D_GNU_SOURCE', '-D_FILE_OFFSET_BITS=64', '-D_LARGEFILE_SOURCE',
|
||||
'-D_LARGE_FILES', '-fno-strict-aliasing',
|
||||
'-fPIC', '-fno-strict-aliasing',
|
||||
]
|
||||
|
||||
conf.check_tool('compiler_cc')
|
||||
|
|
Loading…
Reference in New Issue