1 /* ====================================================================
2 * The Apache Software License, Version 1.1
4 * Copyright (c) 2000 The Apache Software Foundation. All rights
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
19 * 3. The end-user documentation included with the redistribution,
20 * if any, must include the following acknowledgment:
21 * "This product includes software developed by the
22 * Apache Software Foundation (http://www.apache.org/)."
23 * Alternately, this acknowledgment may appear in the software itself,
24 * if and wherever such third-party acknowledgments normally appear.
26 * 4. The names "Apache" and "Apache Software Foundation" must
27 * not be used to endorse or promote products derived from this
28 * software without prior written permission. For written
29 * permission, please contact apache@apache.org.
31 * 5. Products derived from this software may not be called "Apache",
32 * nor may "Apache" appear in their name, without prior written
33 * permission of the Apache Software Foundation.
35 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47 * ====================================================================
49 * This software consists of voluntary contributions made by many
50 * individuals on behalf of the Apache Software Foundation. For more
51 * information on the Apache Software Foundation, please see
52 * <http://www.apache.org/>.
54 * Portions of this software are based upon public domain software
55 * originally written at the National Center for Supercomputing Applications,
56 * University of Illinois, Urbana-Champaign.
60 ** This program is based on ZeusBench V1.0 written by Adam Twiss
61 ** which is Copyright (c) 1996 by Zeus Technology Ltd. http://www.zeustech.net/
63 ** This software is provided "as is" and any express or implied waranties,
64 ** including but not limited to, the implied warranties of merchantability and
65 ** fitness for a particular purpose are disclaimed. In no event shall
66 ** Zeus Technology Ltd. be liable for any direct, indirect, incidental, special,
67 ** exemplary, or consequential damaged (including, but not limited to,
68 ** procurement of substitute good or services; loss of use, data, or profits;
69 ** or business interruption) however caused and on theory of liability. Whether
70 ** in contract, strict liability or tort (including negligence or otherwise)
71 ** arising in any way out of the use of this software, even if advised of the
72 ** possibility of such damage.
78 ** - Originally written by Adam Twiss <adam@zeus.co.uk>, March 1996
79 ** with input from Mike Belshe <mbelshe@netscape.com> and
80 ** Michael Campanella <campanella@stevms.enet.dec.com>
81 ** - Enhanced by Dean Gaudet <dgaudet@apache.org>, November 1997
82 ** - Cleaned up by Ralf S. Engelschall <rse@apache.org>, March 1998
83 ** - POST and verbosity by Kurt Sussman <kls@merlot.com>, August 1998
84 ** - HTML table output added by David N. Welton <davidw@prosa.it>, January 1999
85 ** - Added Cookie, Arbitrary header and auth support. <dirkx@webweaving.org>, April 199
93 * - has various other poor buffer attacks related to the lazy parsing of
94 * response headers from the server
95 * - doesn't implement much of HTTP/1.x, only accepts certain forms of
97 * - (performance problem) heavy use of strstr shows up top in profile
98 * only an issue for loopback usage
101 #define VERSION "1.3c"
103 /* -------------------------------------------------------------------- */
105 /* affects include files on Solaris */
108 /* allow compilation outside an Apache build tree */
109 #ifdef NO_APACHE_INCLUDES
110 #include <sys/time.h>
111 #include <sys/ioctl.h>
112 #include <sys/stat.h>
117 #include <sys/socket.h>
118 #include <netinet/in.h>
121 #include <sys/ioctl.h>
124 #define ap_select select
125 #else /* (!)NO_APACHE_INCLUDES */
126 #include "ap_config.h"
128 #ifdef CHARSET_EBCDIC
132 #include <sys/time.h>
135 #include <sys/types.h>
139 #endif /* NO_APACHE_INCLUDES */
140 /* ------------------- DEFINITIONS -------------------------- */
142 /* maximum number of requests on a time limited test */
143 #define MAX_REQUESTS 50000
145 /* good old state hostname */
146 #define STATE_UNCONNECTED 0
147 #define STATE_CONNECTING 1
150 #define CBUFFSIZE 512
155 int read; /* amount of bytes read */
156 int bread; /* amount of body read */
157 int length; /* Content-Length value used for keep-alive */
158 char cbuff[CBUFFSIZE]; /* a buffer to store server response header */
159 int cbx; /* offset in cbuffer */
160 int keepalive; /* non-zero if a keep-alive request */
161 int gotheader; /* non-zero if we have the entire header in
163 struct timeval start, connect, done;
167 int read; /* number of bytes read */
168 int ctime; /* time in ms to connect */
169 int time; /* time in ms for connection */
172 #define ap_min(a,b) ((a)<(b))?(a):(b)
173 #define ap_max(a,b) ((a)>(b))?(a):(b)
175 /* --------------------- GLOBALS ---------------------------- */
177 int verbosity = 0; /* no verbosity by default */
178 int posting = 0; /* GET by default */
179 int requests = 1; /* Number of requests to make */
180 int concurrency = 1; /* Number of multiple requests to make */
181 int tlimit = 0; /* time limit in cs */
182 int keepalive = 0; /* try and do keepalive connections */
183 char servername[1024]; /* name that server reports */
184 char hostname[1024]; /* host name */
185 char path[1024]; /* path name */
186 char postfile[1024]; /* name of file containing post data */
187 char *postdata; /* *buffer containing data from postfile */
188 int postlen = 0; /* length of data to be POSTed */
189 char content_type[1024]; /* content type to put in POST header */
190 char cookie[1024], /* optional cookie line */
191 auth[1024], /* optional (basic/uuencoded)
192 * authentification */
193 hdrs[4096]; /* optional arbitrary headers */
194 int port = 80; /* port number */
196 int use_html = 0; /* use html in the report */
201 int doclen = 0; /* the length the document should be */
202 int totalread = 0; /* total number of bytes read */
203 int totalbread = 0; /* totoal amount of entity body read */
204 int totalposted = 0; /* total number of bytes posted, inc. headers */
205 int done = 0; /* number of requests we have done */
206 int doneka = 0; /* number of keep alive connections done */
207 int good = 0, bad = 0; /* number of good and bad requests */
209 /* store error cases */
210 int err_length = 0, err_conn = 0, err_except = 0;
211 int err_response = 0;
213 struct timeval start, endtime;
215 /* global request (and its length) */
219 /* one global throw-away buffer to read stuff into */
222 struct connection *con; /* connection array */
223 struct data *stats; /* date for each request */
225 fd_set readbits, writebits; /* bits for select */
226 struct sockaddr_in server; /* server addr structure */
229 #define ab_close(s) close(s)
230 #define ab_read(a,b,c) read(a,b,c)
231 #define ab_write(a,b,c) write(a,b,c)
233 #define ab_close(s) closesocket(s)
234 #define ab_read(a,b,c) recv(a,b,c,0)
235 #define ab_write(a,b,c) send(a,b,c,0)
238 /* --------------------------------------------------------- */
240 /* simple little function to perror and exit */
242 static void err(char *s)
253 /* --------------------------------------------------------- */
255 /* write out request to a connection - assumes we can write
256 (small) request out in one go into our new socket buffer */
258 static void write_request(struct connection * c)
261 struct iovec out[2]; int outcnt = 1;
263 gettimeofday(&c->connect, 0);
265 out[0].iov_base = request;
266 out[0].iov_len = reqlen;
269 out[1].iov_base = postdata;
270 out[1].iov_len = postlen;
272 totalposted += (reqlen + postlen);
274 writev(c->fd,out, outcnt);
276 ab_write(c->fd,request,reqlen);
278 ab_write(c->fd,postdata,postlen);
279 totalposted += (reqlen + postlen);
283 c->state = STATE_READ;
284 FD_SET(c->fd, &readbits);
285 FD_CLR(c->fd, &writebits);
288 /* --------------------------------------------------------- */
290 /* make an fd non blocking */
292 static void nonblock(int fd)
296 setsockopt(fd, SOL_SOCKET, SO_NONBLOCK, &i, sizeof(i));
298 ioctl(fd, FIONBIO, &i);
302 /* --------------------------------------------------------- */
304 /* returns the time in ms between two timevals */
306 static int timedif(struct timeval a, struct timeval b)
310 us = a.tv_usec - b.tv_usec;
312 s = a.tv_sec - b.tv_sec;
317 /* --------------------------------------------------------- */
319 /* calculate and output results */
321 static void output_results(void)
325 gettimeofday(&endtime, 0);
326 timetaken = timedif(endtime, start);
329 printf("Server Software: %s\n", servername);
330 printf("Server Hostname: %s\n", hostname);
331 printf("Server Port: %d\n", port);
333 printf("Document Path: %s\n", path);
334 printf("Document Length: %d bytes\n", doclen);
336 printf("Concurrency Level: %d\n", concurrency);
337 printf("Time taken for tests: %d.%03d seconds\n",
338 timetaken / 1000, timetaken % 1000);
339 printf("Complete requests: %d\n", done);
340 printf("Failed requests: %d\n", bad);
342 printf(" (Connect: %d, Length: %d, Exceptions: %d)\n",
343 err_conn, err_length, err_except);
345 printf("Non-2xx responses: %d\n", err_response);
347 printf("Keep-Alive requests: %d\n", doneka);
348 printf("Total transferred: %d bytes\n", totalread);
350 printf("Total POSTed: %d\n", totalposted);
351 printf("HTML transferred: %d bytes\n", totalbread);
353 /* avoid divide by zero */
355 printf("Requests per second: %.2f\n", 1000 * (float) (done) / timetaken);
356 printf("Transfer rate: %.2f kb/s received\n",
357 (float) (totalread) / timetaken);
359 printf(" %.2f kb/s sent\n",
360 (float) (totalposted) / timetaken);
361 printf(" %.2f kb/s total\n",
362 (float) (totalread + totalposted) / timetaken);
367 /* work out connection times */
369 int totalcon = 0, total = 0;
370 int mincon = 9999999, mintot = 999999;
371 int maxcon = 0, maxtot = 0;
373 for (i = 0; i < requests; i++) {
374 struct data s = stats[i];
375 mincon = ap_min(mincon, s.ctime);
376 mintot = ap_min(mintot, s.time);
377 maxcon = ap_max(maxcon, s.ctime);
378 maxtot = ap_max(maxtot, s.time);
382 if (requests > 0) { /* avoid division by zero (if 0 requests) */
383 printf("\nConnnection Times (ms)\n");
384 printf(" min avg max\n");
385 printf("Connect: %5d %5d %5d\n", mincon, totalcon / requests, maxcon);
386 printf("Processing: %5d %5d %5d\n",
387 mintot - mincon, (total / requests) - (totalcon / requests),
389 printf("Total: %5d %5d %5d\n", mintot, total / requests, maxtot);
394 /* --------------------------------------------------------- */
396 /* calculate and output results in HTML */
398 static void output_html_results(void)
402 gettimeofday(&endtime, 0);
403 timetaken = timedif(endtime, start);
405 printf("\n\n<table %s>\n", tablestring);
406 printf("<tr %s><th colspan=2 %s>Server Software:</th>"
407 "<td colspan=2 %s>%s</td></tr>\n",
408 trstring, tdstring, tdstring, servername);
409 printf("<tr %s><th colspan=2 %s>Server Hostname:</th>"
410 "<td colspan=2 %s>%s</td></tr>\n",
411 trstring, tdstring, tdstring, hostname);
412 printf("<tr %s><th colspan=2 %s>Server Port:</th>"
413 "<td colspan=2 %s>%d</td></tr>\n",
414 trstring, tdstring, tdstring, port);
415 printf("<tr %s><th colspan=2 %s>Document Path:</th>"
416 "<td colspan=2 %s>%s</td></tr>\n",
417 trstring, tdstring, tdstring, path);
418 printf("<tr %s><th colspan=2 %s>Document Length:</th>"
419 "<td colspan=2 %s>%d bytes</td></tr>\n",
420 trstring, tdstring, tdstring, doclen);
421 printf("<tr %s><th colspan=2 %s>Concurrency Level:</th>"
422 "<td colspan=2 %s>%d</td></tr>\n",
423 trstring, tdstring, tdstring, concurrency);
424 printf("<tr %s><th colspan=2 %s>Time taken for tests:</th>"
425 "<td colspan=2 %s>%d.%03d seconds</td></tr>\n",
426 trstring, tdstring, tdstring, timetaken / 1000, timetaken % 1000);
427 printf("<tr %s><th colspan=2 %s>Complete requests:</th>"
428 "<td colspan=2 %s>%d</td></tr>\n",
429 trstring, tdstring, tdstring, done);
430 printf("<tr %s><th colspan=2 %s>Failed requests:</th>"
431 "<td colspan=2 %s>%d</td></tr>\n",
432 trstring, tdstring, tdstring, bad);
434 printf("<tr %s><td colspan=4 %s > (Connect: %d, Length: %d, Exceptions: %d)</td></tr>\n",
435 trstring, tdstring, err_conn, err_length, err_except);
437 printf("<tr %s><th colspan=2 %s>Non-2xx responses:</th>"
438 "<td colspan=2 %s>%d</td></tr>\n",
439 trstring, tdstring, tdstring, err_response);
441 printf("<tr %s><th colspan=2 %s>Keep-Alive requests:</th>"
442 "<td colspan=2 %s>%d</td></tr>\n",
443 trstring, tdstring, tdstring, doneka);
444 printf("<tr %s><th colspan=2 %s>Total transferred:</th>"
445 "<td colspan=2 %s>%d bytes</td></tr>\n",
446 trstring, tdstring, tdstring, totalread);
448 printf("<tr %s><th colspan=2 %s>Total POSTed:</th>"
449 "<td colspan=2 %s>%d</td></tr>\n",
450 trstring, tdstring, tdstring, totalposted);
451 printf("<tr %s><th colspan=2 %s>HTML transferred:</th>"
452 "<td colspan=2 %s>%d bytes</td></tr>\n",
453 trstring, tdstring, tdstring, totalbread);
455 /* avoid divide by zero */
457 printf("<tr %s><th colspan=2 %s>Requests per second:</th>"
458 "<td colspan=2 %s>%.2f</td></tr>\n",
459 trstring, tdstring, tdstring, 1000 * (float) (done) / timetaken);
460 printf("<tr %s><th colspan=2 %s>Transfer rate:</th>"
461 "<td colspan=2 %s>%.2f kb/s received</td></tr>\n",
462 trstring, tdstring, tdstring, (float) (totalread) / timetaken);
464 printf("<tr %s><td colspan=2 %s> </td>"
465 "<td colspan=2 %s>%.2f kb/s sent</td></tr>\n",
466 trstring, tdstring, tdstring,
467 (float) (totalposted) / timetaken);
468 printf("<tr %s><td colspan=2 %s> </td>"
469 "<td colspan=2 %s>%.2f kb/s total</td></tr>\n",
470 trstring, tdstring, tdstring,
471 (float) (totalread + totalposted) / timetaken);
476 /* work out connection times */
478 int totalcon = 0, total = 0;
479 int mincon = 9999999, mintot = 999999;
480 int maxcon = 0, maxtot = 0;
482 for (i = 0; i < requests; i++) {
483 struct data s = stats[i];
484 mincon = ap_min(mincon, s.ctime);
485 mintot = ap_min(mintot, s.time);
486 maxcon = ap_max(maxcon, s.ctime);
487 maxtot = ap_max(maxtot, s.time);
492 if (requests > 0) { /* avoid division by zero (if 0 requests) */
493 printf("<tr %s><th %s colspan=4>Connnection Times (ms)</th></tr>\n",
495 printf("<tr %s><th %s> </th> <th %s>min</th> <th %s>avg</th> <th %s>max</th></tr>\n",
496 trstring, tdstring, tdstring, tdstring, tdstring);
497 printf("<tr %s><th %s>Connect:</th>"
500 "<td %s>%5d</td></tr>\n",
501 trstring, tdstring, tdstring, mincon, tdstring, totalcon / requests, tdstring, maxcon);
502 printf("<tr %s><th %s>Processing:</th>"
505 "<td %s>%5d</td></tr>\n",
506 trstring, tdstring, tdstring, mintot - mincon, tdstring,
507 (total / requests) - (totalcon / requests), tdstring, maxtot - maxcon);
508 printf("<tr %s><th %s>Total:</th>"
511 "<td %s>%5d</td></tr>\n",
512 trstring, tdstring, tdstring, mintot, tdstring, total / requests, tdstring, maxtot);
514 printf("</table>\n");
518 /* --------------------------------------------------------- */
520 /* start asnchronous non-blocking connection */
522 static void start_connect(struct connection * c)
530 c->fd = socket(AF_INET, SOCK_STREAM, 0);
535 gettimeofday(&c->start, 0);
537 if (connect(c->fd, (struct sockaddr *) & server, sizeof(server)) < 0) {
538 if (errno == EINPROGRESS) {
539 c->state = STATE_CONNECTING;
540 FD_SET(c->fd, &writebits);
547 err("\nTest aborted after 10 failures\n\n");
553 /* connected first time */
554 c->state = STATE_CONNECTING;
555 FD_SET(c->fd, &writebits);
558 /* --------------------------------------------------------- */
560 /* close down connection and save stats */
562 static void close_connection(struct connection * c)
564 if (c->read == 0 && c->keepalive) {
565 /* server has legitimately shut down an idle keep alive request */
566 good--; /* connection never happend */
570 /* first time here */
573 else if (c->bread != doclen) {
579 if (done < requests) {
581 gettimeofday(&c->done, 0);
583 s.ctime = timedif(c->connect, c->start);
584 s.time = timedif(c->done, c->start);
590 FD_CLR(c->fd, &readbits);
591 FD_CLR(c->fd, &writebits);
598 /* --------------------------------------------------------- */
600 /* read data from connection */
602 static void read_connection(struct connection * c)
606 char respcode[4]; /* 3 digits and null */
608 r = ab_read(c->fd, buffer, sizeof(buffer));
610 if (r == 0 || (r < 0 && errno != EAGAIN)) {
616 if (r < 0 && errno == EAGAIN)
625 int space = CBUFFSIZE - c->cbx - 1; /* -1 to allow for 0
627 int tocopy = (space < r) ? space : r;
628 #ifndef CHARSET_EBCDIC
629 memcpy(c->cbuff + c->cbx, buffer, tocopy);
630 #else /* CHARSET_EBCDIC */
631 ascii2ebcdic(c->cbuff + c->cbx, buffer, tocopy);
632 #endif /* CHARSET_EBCDIC */
635 c->cbuff[c->cbx] = 0; /* terminate for benefit of strstr */
636 if (verbosity >= 4) {
637 printf("LOG: header received:\n%s\n", c->cbuff);
639 s = strstr(c->cbuff, "\r\n\r\n");
641 * this next line is so that we talk to NCSA 1.5 which blatantly
642 * breaks the http specification
645 s = strstr(c->cbuff, "\n\n");
650 /* read rest next time */
654 /* header is in invalid or too big - close connection */
657 err("\nTest aborted after 10 failures\n\n");
659 FD_CLR(c->fd, &writebits);
664 /* have full header */
666 /* this is first time, extract some interesting info */
668 p = strstr(c->cbuff, "Server:");
679 * XXX: this parsing isn't even remotely HTTP compliant... but in
680 * the interest of speed it doesn't totally have to be, it just
681 * needs to be extended to handle whatever servers folks want to
685 /* check response code */
686 part = strstr(c->cbuff, "HTTP"); /* really HTTP/1.x_ */
687 strncpy(respcode, (part + strlen("HTTP/1.x_")), 3);
689 if (respcode[0] != '2') {
692 printf("WARNING: Response code not 2xx (%s)\n", respcode);
694 else if (verbosity >= 3) {
695 printf("LOG: Response code = %s\n", respcode);
699 *s = 0; /* terminate at end of header */
701 (strstr(c->cbuff, "Keep-Alive")
702 || strstr(c->cbuff, "keep-alive"))) { /* for benefit of MSIIS */
704 cl = strstr(c->cbuff, "Content-Length:");
705 /* handle NCSA, which sends Content-length: */
707 cl = strstr(c->cbuff, "Content-length:");
710 c->length = atoi(cl + 16);
713 c->bread += c->cbx - (s + l - c->cbuff) + r - tocopy;
714 totalbread += c->bread;
718 /* outside header, everything we have read is entity body */
723 /* cater for the case where we're using keepalives and doing HEAD requests */
724 if (c->keepalive && ((c->bread >= c->length) || (posting < 0))) {
725 /* finished a keep-alive connection */
730 /* first time here */
733 else if (c->bread != doclen) {
737 if (done < requests) {
739 gettimeofday(&c->done, 0);
741 s.ctime = timedif(c->connect, c->start);
742 s.time = timedif(c->done, c->start);
749 c->read = c->bread = 0;
751 c->start = c->connect; /* zero connect time with keep-alive */
755 /* --------------------------------------------------------- */
759 static void test(void)
761 struct timeval timeout, now;
762 fd_set sel_read, sel_except, sel_write;
766 printf("Benchmarking %s (be patient)...", hostname);
771 /* get server information */
773 he = gethostbyname(hostname);
776 server.sin_family = he->h_addrtype;
777 server.sin_port = htons(port);
778 server.sin_addr.s_addr = ((unsigned long *) (he->h_addr_list[0]))[0];
781 con = malloc(concurrency * sizeof(struct connection));
782 memset(con, 0, concurrency * sizeof(struct connection));
784 stats = malloc(requests * sizeof(struct data));
791 sprintf(request, "%s %s HTTP/1.0\r\n"
792 "User-Agent: ApacheBench/%s\r\n"
797 (posting == 0) ? "GET" : "HEAD",
800 keepalive ? "Connection: Keep-Alive\r\n" : "",
801 cookie, auth, hostname, hdrs);
804 sprintf(request, "POST %s HTTP/1.0\r\n"
805 "User-Agent: ApacheBench/%s\r\n"
809 "Content-length: %d\r\n"
810 "Content-type: %s\r\n"
815 keepalive ? "Connection: Keep-Alive\r\n" : "",
818 (content_type[0]) ? content_type : "text/plain", hdrs);
822 printf("INFO: POST header == \n---\n%s\n---\n", request);
824 reqlen = strlen(request);
826 #ifdef CHARSET_EBCDIC
827 ebcdic2ascii(request, request, reqlen);
828 #endif /* CHARSET_EBCDIC */
830 /* ok - lets start */
831 gettimeofday(&start, 0);
833 /* initialise lots of requests */
834 for (i = 0; i < concurrency; i++)
835 start_connect(&con[i]);
837 while (done < requests) {
839 /* setup bit arrays */
840 memcpy(&sel_except, &readbits, sizeof(readbits));
841 memcpy(&sel_read, &readbits, sizeof(readbits));
842 memcpy(&sel_write, &writebits, sizeof(readbits));
844 /* check for time limit expiry */
845 gettimeofday(&now, 0);
846 if (tlimit && timedif(now, start) > (tlimit * 1000)) {
847 requests = done; /* so stats are correct */
850 /* Timeout of 30 seconds. */
853 n = ap_select(FD_SETSIZE, &sel_read, &sel_write, &sel_except, &timeout);
855 err("\nServer timed out\n\n");
860 for (i = 0; i < concurrency; i++) {
862 if (FD_ISSET(s, &sel_except)) {
865 start_connect(&con[i]);
868 if (FD_ISSET(s, &sel_read))
869 read_connection(&con[i]);
870 if (FD_ISSET(s, &sel_write))
871 write_request(&con[i]);
875 output_html_results();
880 /* ------------------------------------------------------- */
882 /* display copyright information */
883 static void copyright(void)
886 printf("This is ApacheBench, Version %s\n", VERSION " <$Revision: 1.7 $> apache-2.0");
887 printf("Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/\n");
888 printf("Copyright (c) 1998-2000 The Apache Software Foundation, http://www.apache.org/\n");
893 printf(" This is ApacheBench, Version %s <i><%s></i> apache-2.0<br>\n", VERSION, "$Revision: 1.7 $");
894 printf(" Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/<br>\n");
895 printf(" Copyright (c) 1998-2000 The Apache Software Foundation, http://www.apache.org/<br>\n");
896 printf("</p>\n<p>\n");
900 /* display usage information */
901 static void usage(char *progname)
903 fprintf(stderr, "Usage: %s [options] [http://]hostname[:port]/path\n", progname);
904 fprintf(stderr, "Options are:\n");
905 fprintf(stderr, " -n requests Number of requests to perform\n");
906 fprintf(stderr, " -c concurrency Number of multiple requests to make\n");
907 fprintf(stderr, " -t timelimit Seconds to max. wait for responses\n");
908 fprintf(stderr, " -p postfile File containg data to POST\n");
909 fprintf(stderr, " -T content-type Content-type header for POSTing\n");
910 fprintf(stderr, " -v verbosity How much troubleshooting info to print\n");
911 fprintf(stderr, " -w Print out results in HTML tables\n");
912 fprintf(stderr, " -i Use HEAD instead of GET\n");
913 fprintf(stderr, " -x attributes String to insert as table attributes\n");
914 fprintf(stderr, " -y attributes String to insert as tr attributes\n");
915 fprintf(stderr, " -z attributes String to insert as td or th attributes\n");
916 fprintf(stderr, " -C attribute Add cookie, eg. 'Apache=1234. (repeatable)\n");
917 fprintf(stderr, " -H attribute Add Arbitrary header line, eg. 'Accept-Encoding: zop'\n");
918 fprintf(stderr, " Inserted after all normal header lines. (repeatable)\n");
919 fprintf(stderr, " -A attribute Add Basic WWW Authentication, the attributes\n");
920 fprintf(stderr, " are a colon separated username and password.\n");
921 fprintf(stderr, " -p attribute Add Basic Proxy Authentication, the attributes\n");
922 fprintf(stderr, " are a colon separated username and password.\n");
923 fprintf(stderr, " -V Print version number and exit\n");
924 fprintf(stderr, " -k Use HTTP KeepAlive feature\n");
925 fprintf(stderr, " -h Display usage information (this message)\n");
929 /* ------------------------------------------------------- */
931 /* split URL into parts */
933 static int parse_url(char *url)
939 if (strlen(url) > 7 && strncmp(url, "http://", 7) == 0)
942 if ((cp = strchr(url, ':')) != NULL) {
947 if ((cp = strchr(url, '/')) == NULL)
957 /* ------------------------------------------------------- */
959 /* read data to POST from file, save contents and length */
961 static int open_postfile(char *pfile)
964 struct stat postfilestat;
966 if ((postfd = open(pfile, O_RDONLY)) == -1) {
967 printf("Invalid postfile name (%s)\n", pfile);
970 if ((status = fstat(postfd, &postfilestat)) == -1) {
971 perror("Can\'t stat postfile\n");
974 postdata = malloc(postfilestat.st_size);
976 printf("Can\'t alloc postfile buffer\n");
979 if (read(postfd, postdata, postfilestat.st_size) != postfilestat.st_size) {
980 printf("error reading postfilen");
983 postlen = postfilestat.st_size;
987 /* ------------------------------------------------------- */
990 extern int optind, opterr, optopt;
992 /* sort out command-line args and call test */
993 int main(int argc, char **argv)
1001 tdstring = "bgcolor=white";
1006 while ((c = getopt(argc, argv, "n:c:t:T:p:v:kVhwix:y:z:C:H:P:A:")) > 0) {
1009 requests = atoi(optarg);
1011 err("Invalid number of requests\n");
1018 concurrency = atoi(optarg);
1022 err("Cannot mix POST and HEAD");
1028 err("Cannot mix POST and HEAD");
1030 if (0 == (r = open_postfile(optarg))) {
1033 else if (postdata) {
1038 verbosity = atoi(optarg);
1041 tlimit = atoi(optarg);
1042 requests = MAX_REQUESTS; /* need to size data array on
1046 strcpy(content_type, optarg);
1049 strncat(cookie, "Cookie: ", sizeof(cookie));
1050 strncat(cookie, optarg, sizeof(cookie));
1051 strncat(cookie, "\r\n", sizeof(cookie));
1054 /* assume username passwd already to be in colon separated form. Ready
1057 while(isspace(*optarg))
1059 l=ap_base64encode(tmp,optarg,strlen(optarg));
1062 strncat(auth, "Authorization: basic ", sizeof(auth));
1063 strncat(auth, tmp, sizeof(auth));
1064 strncat(auth, "\r\n", sizeof(auth));
1068 * assume username passwd already to be in colon separated form.
1070 while(isspace(*optarg))
1072 l=ap_base64encode(tmp,optarg,strlen(optarg));
1075 strncat(auth, "Proxy-Authorization: basic ", sizeof(auth));
1076 strncat(auth, tmp, sizeof(auth));
1077 strncat(auth, "\r\n", sizeof(auth));
1080 strncat(hdrs, optarg, sizeof(hdrs));
1081 strncat(hdrs, "\r\n", sizeof(hdrs));
1091 * if any of the following three are used, turn on html output
1096 tablestring = optarg;
1110 fprintf(stderr, "%s: invalid option `%c'\n", argv[0], c);
1115 if (optind != argc - 1) {
1116 fprintf(stderr, "%s: wrong number of arguments\n", argv[0]);
1120 if (parse_url(argv[optind++])) {
1121 fprintf(stderr, "%s: invalid URL\n", argv[0]);