You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by dg...@hyperreal.org on 1998/08/20 06:07:46 UTC
cvs commit: apache-1.3/src/support ab.c
dgaudet 98/08/19 21:07:45
Modified: src CHANGES
src/support ab.c
Log:
Add the ability to benchmark POST requests.
Note I tweaked Kurt's submission to fit our style guide... and I corrected
a few comments and crud that have been in ab.c since the zb.c days. And
for some reason ab was printing the results twice for me, I fixed that.
PR: 2871
Submitted by: Kurt Sussman <kl...@best.com> [modified by Dean]
Revision Changes Path
1.1030 +3 -0 apache-1.3/src/CHANGES
Index: CHANGES
===================================================================
RCS file: /export/home/cvs/apache-1.3/src/CHANGES,v
retrieving revision 1.1029
retrieving revision 1.1030
diff -u -r1.1029 -r1.1030
--- CHANGES 1998/08/17 18:36:16 1.1029
+++ CHANGES 1998/08/20 04:07:43 1.1030
@@ -1,5 +1,8 @@
Changes with Apache 1.3.2
+ *) Add the ability to do POST requests to the ab benchmarking tool.
+ [Kurt Sussman <kl...@best.com>] PR#2871
+
*) Bump up MAX_ENV_FLAGS in mod_rewrite.h from the too conservatice limit of
5 to 10 because there are some users out there who always have 5 to 8
variables in one RewriteRule and had to patch mod_rewrite.h for every
1.12 +162 -31 apache-1.3/src/support/ab.c
Index: ab.c
===================================================================
RCS file: /export/home/cvs/apache-1.3/src/support/ab.c,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -r1.11 -r1.12
--- ab.c 1998/07/29 10:14:40 1.11
+++ ab.c 1998/08/20 04:07:45 1.12
@@ -79,20 +79,39 @@
** Michael Campanella <ca...@stevms.enet.dec.com>
** - Enhanced by Dean Gaudet <dg...@apache.org>, November 1997
** - Cleaned up by Ralf S. Engelschall <rs...@apache.org>, March 1998
+** - POST and verbosity by Kurt Sussman <kl...@merlot.com>, August 1998
**
*/
-#define VERSION "1.1"
+#define VERSION "1.2"
/* -------------------------------------------------------------------- */
/* affects include files on Solaris */
#define BSD_COMP
+/* allow compilation outside an Apache build tree */
+#ifdef NO_APACHE_INCLUDES
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <string.h>
+
+#define ap_select select
+#else /* (!)NO_APACHE_INCLUDES */
#include "ap_config.h"
#include <fcntl.h>
#include <sys/time.h>
-
+#endif /* NO_APACHE_INCLUDES */
/* ------------------- DEFINITIONS -------------------------- */
/* maximum number of requests on a time limited test */
@@ -129,6 +148,8 @@
/* --------------------- GLOBALS ---------------------------- */
+int verbosity = 0; /* no verbosity by default */
+int posting = 0; /* GET by default */
int requests = 1; /* Number of requests to make */
int concurrency = 1; /* Number of multiple requests to make */
int tlimit = 0; /* time limit in cs */
@@ -136,17 +157,23 @@
char servername[1024]; /* name that server reports */
char hostname[1024]; /* host name */
char path[1024]; /* path name */
+char postfile[1024]; /* name of file containing post data */
+char* postdata; /* *buffer containing data from postfile */
+int postlen = 0; /* length of data to be POSTed */
+char content_type[1024]; /* content type to put in POST header */
int port = 80; /* port number */
int doclen = 0; /* the length the document should be */
int totalread = 0; /* total number of bytes read */
int totalbread = 0; /* totoal amount of entity body read */
+int totalposted = 0; /* total number of bytes posted, inc. headers */
int done = 0; /* number of requests we have done */
int doneka = 0; /* number of keep alive connections done */
int good = 0, bad = 0; /* number of good and bad requests */
/* store error cases */
int err_length = 0, err_conn = 0, err_except = 0;
+int err_response = 0;
struct timeval start, endtime;
@@ -155,7 +182,7 @@
int reqlen;
/* one global throw-away buffer to read stuff into */
-char buffer[4096];
+char buffer[8192];
struct connection *con; /* connection array */
struct data *stats; /* date for each request */
@@ -169,7 +196,12 @@
static void err(char *s)
{
- perror(s);
+ if (errno) {
+ perror(s);
+ }
+ else {
+ printf("%s", s);
+ }
exit(errno);
}
@@ -181,7 +213,13 @@
static void write_request(struct connection *c)
{
gettimeofday(&c->connect, 0);
+ /* XXX: this could use writev for posting -- more efficient -djg */
write(c->fd, request, reqlen);
+ if (posting) {
+ write(c->fd,postdata, postlen);
+ totalposted += (reqlen + postlen);
+ }
+
c->state = STATE_READ;
FD_SET(c->fd, &readbits);
FD_CLR(c->fd, &writebits);
@@ -214,7 +252,7 @@
/* --------------------------------------------------------- */
-/* calculate and output results and exit */
+/* calculate and output results */
static void output_results(void)
{
@@ -239,16 +277,26 @@
if (bad)
printf(" (Connect: %d, Length: %d, Exceptions: %d)\n",
err_conn, err_length, err_except);
+ if (err_response)
+ printf("Non-2xx responses: %d\n", err_response);
if (keepalive)
printf("Keep-Alive requests: %d\n", doneka);
printf("Total transferred: %d bytes\n", totalread);
+ if (posting)
+ printf("Total POSTed: %d\n", totalposted);
printf("HTML transferred: %d bytes\n", totalbread);
/* avoid divide by zero */
if (timetaken) {
printf("Requests per second: %.2f\n", 1000 * (float) (done) / timetaken);
- printf("Transfer rate: %.2f kb/s\n",
+ printf("Transfer rate: %.2f kb/s received\n",
(float) (totalread) / timetaken);
+ if (posting) {
+ printf(" %.2f kb/s sent\n",
+ (float)(totalposted)/timetaken);
+ printf(" %.2f kb/s total\n",
+ (float)(totalread + totalposted)/timetaken);
+ }
}
{
@@ -268,12 +316,13 @@
total += s.time;
}
printf("\nConnnection Times (ms)\n");
- printf(" min avg max\n");
- printf("Connect: %5d %5d %5d\n", mincon, totalcon / requests, maxcon);
- printf("Total: %5d %5d %5d\n", mintot, total / requests, maxtot);
+ printf(" min avg max\n");
+ printf("Connect: %5d %5d %5d\n", mincon, totalcon / requests, maxcon);
+ printf("Processing: %5d %5d %5d\n",
+ mintot - mincon, (total/requests) - (totalcon/requests),
+ maxtot - maxcon);
+ printf("Total: %5d %5d %5d\n", mintot, total / requests, maxtot);
}
-
- exit(0);
}
/* --------------------------------------------------------- */
@@ -305,8 +354,7 @@
close(c->fd);
err_conn++;
if (bad++ > 10) {
- printf("\nTest aborted after 10 failures\n\n");
- exit(1);
+ err("\nTest aborted after 10 failures\n\n");
}
start_connect(c);
}
@@ -323,7 +371,7 @@
static void close_connection(struct connection *c)
{
if (c->read == 0 && c->keepalive) {
- /* server has legitiamately shut down an idle keep alive request */
+ /* server has legitimately shut down an idle keep alive request */
good--; /* connection never happend */
}
else {
@@ -363,6 +411,8 @@
static void read_connection(struct connection *c)
{
int r;
+ char *part;
+ char respcode[4]; /* 3 digits and null */
r = read(c->fd, buffer, sizeof(buffer));
if (r == 0 || (r < 0 && errno != EAGAIN)) {
@@ -390,6 +440,9 @@
c->cbx += tocopy;
space -= tocopy;
c->cbuff[c->cbx] = 0; /* terminate for benefit of strstr */
+ if (verbosity >= 4) {
+ printf("LOG: header received:\n%s\n", c->cbuff);
+ }
s = strstr(c->cbuff, "\r\n\r\n");
/* this next line is so that we talk to NCSA 1.5 which blatantly breaks
the http specifaction */
@@ -406,8 +459,7 @@
/* header is in invalid or too big - close connection */
close(c->fd);
if (bad++ > 10) {
- printf("\nTest aborted after 10 failures\n\n");
- exit(1);
+ err("\nTest aborted after 10 failures\n\n");
}
FD_CLR(c->fd, &writebits);
start_connect(c);
@@ -428,6 +480,23 @@
*q = 0;
}
+ /* FIXME: this parsing isn't even remotely HTTP compliant...
+ * but in the interest of speed it doesn't totally have to be,
+ * it just needs to be extended to handle whatever servers
+ * folks want to test against. -djg */
+
+ /* check response code */
+ part = strstr(c->cbuff, "HTTP"); /* really HTTP/1.x_ */
+ strncpy(respcode, (part+strlen("HTTP/1.x_")), 3);
+ respcode[3] = '\0';
+ if (respcode[0] != '2') {
+ err_response++;
+ if (verbosity >= 2) printf ("WARNING: Response code not 2xx (%s)\n", respcode);
+ }
+ else if (verbosity >= 3) {
+ printf("LOG: Response code = %s\n", respcode);
+ }
+
c->gotheader = 1;
*s = 0; /* terminate at end of header */
if (keepalive &&
@@ -435,8 +504,7 @@
|| strstr(c->cbuff, "keep-alive"))) { /* for benefit of MSIIS */
char *cl;
cl = strstr(c->cbuff, "Content-Length:");
- /* for cacky servers like NCSA which break the spec and send a
- lower case 'l' */
+ /* handle NCSA, which sends Content-length: */
if (!cl)
cl = strstr(c->cbuff, "Content-length:");
if (cl) {
@@ -503,7 +571,7 @@
struct hostent *he;
he = gethostbyname(hostname);
if (!he)
- err("gethostbyname");
+ err("bad hostname");
server.sin_family = he->h_addrtype;
server.sin_port = htons(port);
server.sin_addr.s_addr = ((unsigned long *) (he->h_addr_list[0]))[0];
@@ -518,7 +586,8 @@
FD_ZERO(&writebits);
/* setup request */
- sprintf(request, "GET %s HTTP/1.0\r\n"
+ if (!posting) {
+ sprintf(request, "GET %s HTTP/1.0\r\n"
"User-Agent: ApacheBench/%s\r\n"
"%s"
"Host: %s\r\n"
@@ -528,6 +597,24 @@
VERSION,
keepalive ? "Connection: Keep-Alive\r\n" : "",
hostname);
+ }
+ else {
+ sprintf(request, "POST %s HTTP/1.0\r\n"
+ "User-Agent: ApacheBench/%s\r\n"
+ "%s"
+ "Host: %s\r\n"
+ "Accept: */*\r\n"
+ "Content-length: %d\r\n"
+ "Content-type: %s\r\n"
+ "\r\n",
+ path,
+ VERSION,
+ keepalive ? "Connection: Keep-Alive\r\n" : "",
+ hostname, postlen,
+ (content_type) ? content_type : "text/plain");
+ }
+
+ if (verbosity >= 2) printf("INFO: POST header == \n---\n%s\n---\n", request);
reqlen = strlen(request);
@@ -553,7 +640,6 @@
gettimeofday(&now, 0);
if (tlimit && timedif(now, start) > (tlimit * 1000)) {
requests = done; /* so stats are correct */
- output_results();
}
/* Timeout of 30 seconds. */
@@ -561,8 +647,7 @@
timeout.tv_usec = 0;
n = ap_select(FD_SETSIZE, &sel_read, &sel_write, &sel_except, &timeout);
if (!n) {
- printf("\nServer timed out\n\n");
- exit(1);
+ err("\nServer timed out\n\n");
}
if (n < 1)
err("select");
@@ -580,9 +665,8 @@
if (FD_ISSET(s, &sel_write))
write_request(&con[i]);
}
- if (done >= requests)
- output_results();
}
+ output_results();
}
/* ------------------------------------------------------- */
@@ -604,8 +688,11 @@
fprintf(stderr, " -n requests Number of requests to perform\n");
fprintf(stderr, " -c concurrency Number of multiple requests to make\n");
fprintf(stderr, " -t timelimit Seconds to max. wait for responses\n");
+ fprintf(stderr, " -p postfile File containg data to POST\n");
+ fprintf(stderr, " -T content-type Content-type header for POSTing\n");
+ fprintf(stderr, " -v verbosity How much troubleshooting info to print\n");
+ fprintf(stderr, " -V Print version number and exit\n");
fprintf(stderr, " -k Use HTTP KeepAlive feature\n");
- fprintf(stderr, " -v Display version and copyright information\n");
fprintf(stderr, " -h Display usage information (this message)\n");
exit(EINVAL);
}
@@ -640,21 +727,50 @@
/* ------------------------------------------------------- */
+/* read data to POST from file, save contents and length */
+
+int open_postfile(char *pfile)
+{
+ int postfd, status;
+ struct stat postfilestat;
+
+ if ((postfd = open(pfile, O_RDONLY)) == -1) {
+ printf("Invalid postfile name (%s)\n", pfile);
+ return errno;
+ }
+ if ((status = fstat(postfd, &postfilestat)) == -1) {
+ perror("Can\'t stat postfile\n");
+ return status;
+ }
+ postdata = malloc(postfilestat.st_size);
+ if (!postdata) {
+ printf("Can\'t alloc postfile buffer\n");
+ return ENOMEM;
+ }
+ if (read(postfd, postdata, postfilestat.st_size) != postfilestat.st_size) {
+ printf("error reading postfilen");
+ return EIO;
+ }
+ postlen = postfilestat.st_size;
+ return 0;
+}
+
+/* ------------------------------------------------------- */
+
extern char *optarg;
extern int optind, opterr, optopt;
/* sort out command-line args and call test */
int main(int argc, char **argv)
{
- int c;
+ int c, r;
optind = 1;
- while ((c = getopt(argc, argv, "n:c:t:kvh")) > 0) {
+ while ((c = getopt(argc, argv, "n:c:t:T:p:v:kVh")) > 0) {
switch (c) {
case 'n':
requests = atoi(optarg);
if (!requests) {
- printf("Invalid number of requests\n");
- exit(1);
+ err("Invalid number of requests\n");
}
break;
case 'k':
@@ -663,11 +779,25 @@
case 'c':
concurrency = atoi(optarg);
break;
+ case 'p':
+ if (0 == (r = open_postfile(optarg))) {
+ posting = 1;
+ }
+ else if (postdata) {
+ exit(r);
+ }
+ break;
+ case 'v':
+ verbosity = atoi(optarg);
+ break;
case 't':
tlimit = atoi(optarg);
requests = MAX_REQUESTS; /* need to size data array on something */
break;
- case 'v':
+ case 'T':
+ strcpy(content_type, optarg);
+ break;
+ case 'V':
copyright();
exit(0);
break;
@@ -692,6 +822,7 @@
copyright();
test();
+
exit(0);
}