ab.c revision c49c786dc58938f915f20356d954ecec1de2d205
904N/A/* ==================================================================== * The Apache Software License, Version 1.1 * Copyright (c) 2000-2001 The Apache Software Foundation. All rights * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * 4. The names "Apache" and "Apache Software Foundation" must * not be used to endorse or promote products derived from this * software without prior written permission. For written * permission, please contact apache@apache.org. * 5. Products derived from this software may not be called "Apache", * nor may "Apache" appear in their name, without prior written * permission of the Apache Software Foundation. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * ==================================================================== * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * Portions of this software are based upon public domain software * originally written at the National Center for Supercomputing Applications, * University of Illinois, Urbana-Champaign. ** This program is based on ZeusBench V1.0 written by Adam Twiss ** This software is provided "as is" and any express or implied waranties, ** including but not limited to, the implied warranties of merchantability and ** fitness for a particular purpose are disclaimed. In no event shall ** Zeus Technology Ltd. be liable for any direct, indirect, incidental, special, ** exemplary, or consequential damaged (including, but not limited to, ** procurement of substitute good or services; loss of use, data, or profits; ** or business interruption) however caused and on theory of liability. Whether ** in contract, strict liability or tort (including negligence or otherwise) ** arising in any way out of the use of this software, even if advised of the ** possibility of such damage. ** - Originally written by Adam Twiss <adam@zeus.co.uk>, March 1996 ** with input from Mike Belshe <mbelshe@netscape.com> and ** Michael Campanella <campanella@stevms.enet.dec.com> ** - Enhanced by Dean Gaudet <dgaudet@apache.org>, November 1997 ** - Cleaned up by Ralf S. Engelschall <rse@apache.org>, March 1998 ** - POST and verbosity by Kurt Sussman <kls@merlot.com>, August 1998 ** - HTML table output added by David N. Welton <davidw@prosa.it>, January 1999 ** - Added Cookie, Arbitrary header and auth support. <dirkx@webweaving.org>, April 1999 ** - Increased version number - as some of the socket/error handling has ** fundamentally changed - and will give fundamentally different results ** in situations where a server is dropping requests. Therefore you can ** no longer compare results of AB as easily. Hence the inc of the version. ** They should be closer to the truth though. Sander & <dirkx@covalent.net>, End 2000. ** - Fixed proxy functionality, added median/mean statistics, added gnuplot ** confidence guestimators and warnings. Sander & <dirkx@covalent.net>, End 2000 ** - Fixed serious int overflow issues which would cause realistic (longer ** than a few minutes) run's to have wrong (but believable) results. Added ** trapping of connection errors which influenced measurements. ** Contributed by Sander Temme - <sctemme@covalent.net>, Early 2001 ** - Changed timeout behavour during write to work whilst the sockets ** are filling up and apr_write() does writes a few - but not all. ** This will potentially change results. <dirkx@webweaving.org>, April 2001 * - has various other poor buffer attacks related to the lazy parsing of * response headers from the server * - doesn't implement much of HTTP/1.x, only accepts certain forms of * - (performance problem) heavy use of strstr shows up top in profile * only an issue for loopback usage /* -------------------------------------------------------------------- */ /* Hmmm... This source code isn't being compiled in ASCII. * In order for data that flows over the network to make * sense, we need to translate to/from ASCII. /* affects include files on Solaris */ /* Libraries on most systems.. */ /* Libraries for RSAref and SYSSSL */ /* ------------------- DEFINITIONS -------------------------- */ /* maximum number of requests on a time limited test */ /* good old state hostname */ int read;
/* amount of bytes read */ int bread;
/* amount of body read */ int rwrite,
rwrote;
/* keep pointers in what we write - across int length;
/* Content-Length value used for keep-alive */ int cbx;
/* offset in cbuffer */ int keepalive;
/* non-zero if a keep-alive request */ int gotheader;
/* non-zero if we have the entire header in connect,
/* Connected, start writing */ done;
/* Connection closed */ /* XXXX insert SSL timings */ int read;
/* number of bytes read */ #
define ap_min(a,b) ((a)<(b))?(a):(b)
#
define ap_max(a,b) ((a)>(b))?(a):(b)
/* --------------------- GLOBALS ---------------------------- */ int verbosity = 0;
/* no verbosity by default */ int posting = 0;
/* GET by default */ int requests =
1;
/* Number of requests to make */ int heartbeatres =
100;
/* How often do we say we're alive */ int concurrency =
1;
/* Number of multiple requests to make */ int confidence =
1;
/* Show confidence estimator and warnings */ int tlimit = 0;
/* time limit in cs */ int keepalive = 0;
/* try and do keepalive connections */ char servername[
1024];
/* name that server reports */ char *
hostname;
/* host name from URL */ char *
host_field;
/* value of "Host:" header field */ char path[
1024];
/* path name */ char postfile[
1024];
/* name of file containing post data */ char *
postdata;
/* *buffer containing data from postfile */ char content_type[
1024];
/* content type to put in POST header */ char cookie[
1024],
/* optional cookie line */ hdrs[
4096];
/* optional arbitrary headers */ char *
csvperc;
/* CSV Percentile file */ * XXX - this is now a per read/write transact type of value int use_html = 0;
/* use html in the report */ int doclen = 0;
/* the length the document should be */ long started = 0;
/* number of requests started, so no excess */ long totalread = 0;
/* total number of bytes read */ long totalbread = 0;
/* totoal amount of entity body read */ long totalposted = 0;
/* total number of bytes posted, inc. headers */ long done = 0;
/* number of requests we have done */ long doneka = 0;
/* number of keep alive connections done */ long good = 0,
bad = 0;
/* number of good and bad requests */ long epipe = 0;
/* number of broken pipe writes */ /* global request (and its length) */ /* one global throw-away buffer to read stuff into */ /* interesting percentiles */ int percs[] = {
50,
66,
75,
80,
90,
95,
98,
99,
100};
struct data *
stats;
/* date for each request */ /* --------------------------------------------------------- */ /* simple little function to write an error string and exit */ /* simple little function to write an APR error string and exit */ /* --------------------------------------------------------- */ /* write out request to a connection - assumes we can write * (small) request out in one go into our new socket buffer printf(
"Send request timed out!\n");
* Bail early on the most common case * Let's hope this traps EWOULDBLOCK too ! printf(
"Send request failed!\n");
/* --------------------------------------------------------- */ /* calculate and output results */ printf(
" (Connect: %d, Length: %d, Exceptions: %d)\n",
/* avoid divide by zero */ printf(
"Time per request: %.3f [ms] (mean, across all concurrent requests)\n",
/* work out connection times */ perror(
"Cannot open gnuplot output file");
fprintf(
out,
"starttime\tseconds\tctime\tdtime\tttime\twait\n");
* XXX: what is better; this hideous cast of the copare function; or * the four warnings during compile ? dirkx just does not know and (
int (*) (
const void *,
const void *))
compradre);
(
int (*) (
const void *,
const void *))
compri);
(
int (*) (
const void *,
const void *))
compwait);
(
int (*) (
const void *,
const void *))
comprando);
printf(
"\nConnection Times (ms)\n");
printf(
" min mean[+/-sd] median max\n");
printf(
"ERROR: The median and mean for " what " are more than twice the standard\n" \
" deviation apart. These results are NOT reliable.\n"); \
printf(
"WARING: The median and mean for " what " are not within a normal deviation\n" \
" These results are propably not that reliable.\n"); \
/* Sorted on total connect times */ printf(
"\nPercentage of the requests served within a certain time (ms)\n");
for (i = 0; i <
sizeof(
percs) /
sizeof(
int); i++)
else if (
percs[i] >=
100)
perror(
"Cannot open CSV output file");
fprintf(
out,
"" "Percentage served" "," "Time in ms" "\n");
for (i = 0; i <
100; i++) {
/* --------------------------------------------------------- */ /* calculate and output results in HTML */ printf(
"<tr %s><th colspan=2 %s>Server Software:</th>" "<td colspan=2 %s>%s</td></tr>\n",
printf(
"<tr %s><th colspan=2 %s>Server Hostname:</th>" "<td colspan=2 %s>%s</td></tr>\n",
printf(
"<tr %s><th colspan=2 %s>Server Port:</th>" "<td colspan=2 %s>%hd</td></tr>\n",
printf(
"<tr %s><th colspan=2 %s>Document Path:</th>" "<td colspan=2 %s>%s</td></tr>\n",
printf(
"<tr %s><th colspan=2 %s>Document Length:</th>" "<td colspan=2 %s>%d bytes</td></tr>\n",
printf(
"<tr %s><th colspan=2 %s>Concurrency Level:</th>" "<td colspan=2 %s>%d</td></tr>\n",
printf(
"<tr %s><th colspan=2 %s>Time taken for tests:</th>" "<td colspan=2 %s>%qd.%03qd seconds</td></tr>\n",
printf(
"<tr %s><th colspan=2 %s>Complete requests:</th>" "<td colspan=2 %s>%ld</td></tr>\n",
printf(
"<tr %s><th colspan=2 %s>Failed requests:</th>" "<td colspan=2 %s>%ld</td></tr>\n",
printf(
"<tr %s><td colspan=4 %s > (Connect: %d, Length: %d, Exceptions: %d)</td></tr>\n",
printf(
"<tr %s><th colspan=2 %s>Non-2xx responses:</th>" "<td colspan=2 %s>%d</td></tr>\n",
printf(
"<tr %s><th colspan=2 %s>Keep-Alive requests:</th>" "<td colspan=2 %s>%ld</td></tr>\n",
printf(
"<tr %s><th colspan=2 %s>Total transferred:</th>" "<td colspan=2 %s>%ld bytes</td></tr>\n",
printf(
"<tr %s><th colspan=2 %s>Total POSTed:</th>" "<td colspan=2 %s>%ld</td></tr>\n",
printf(
"<tr %s><th colspan=2 %s>HTML transferred:</th>" "<td colspan=2 %s>%ld bytes</td></tr>\n",
/* avoid divide by zero */ printf(
"<tr %s><th colspan=2 %s>Requests per second:</th>" "<td colspan=2 %s>%.2f</td></tr>\n",
printf(
"<tr %s><th colspan=2 %s>Transfer rate:</th>" "<td colspan=2 %s>%.2f kb/s received</td></tr>\n",
printf(
"<tr %s><td colspan=2 %s> </td>" "<td colspan=2 %s>%.2f kb/s sent</td></tr>\n",
printf(
"<tr %s><td colspan=2 %s> </td>" "<td colspan=2 %s>%.2f kb/s total</td></tr>\n",
/* work out connection times */ if (
requests > 0) {
/* avoid division by zero (if 0 requests) */ printf(
"<tr %s><th %s colspan=4>Connnection Times (ms)</th></tr>\n",
printf(
"<tr %s><th %s> </th> <th %s>min</th> <th %s>avg</th> <th %s>max</th></tr>\n",
printf(
"<tr %s><th %s>Connect:</th>" printf(
"<tr %s><th %s>Processing:</th>" printf(
"<tr %s><th %s>Total:</th>" /* --------------------------------------------------------- */ /* start asnchronous non-blocking connection */ "\nTest aborted after 10 failures\n\n");
/* connected first time */ /* --------------------------------------------------------- */ /* close down connection and save stats */ * server has legitimately shut down an idle keep alive request good--;
/* connection never happened */ /* --------------------------------------------------------- */ /* read data from connection */ char respcode[
4];
/* 3 digits and null */ fprintf(
stderr,
"only simple translation is supported (%d/%u/%u)\n",
c->
cbuff[c->
cbx] = 0;
/* terminate for benefit of strstr */ * this next line is so that we talk to NCSA 1.5 which blatantly * breaks the http specifaction /* read rest next time */ /* header is in invalid or too big - close connection */ err(
"\nTest aborted after 10 failures\n\n");
* this is first time, extract some interesting info * XXX: 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 /* check response code */ *s = 0;
/* terminate at end of header */ ||
strstr(c->
cbuff,
"keep-alive"))) {
/* for benefit of MSIIS */ /* handle NCSA, which sends Content-length: */ /* outside header, everything we have read is entity body */ /* finished a keep-alive connection */ c->
start = c->
connect;
/* zero connect time with keep-alive *//* --------------------------------------------------------- */ "User-Agent: ApacheBench/%s\r\n" keepalive ?
"Connection: Keep-Alive\r\n" :
"",
"User-Agent: ApacheBench/%s\r\n" keepalive ?
"Connection: Keep-Alive\r\n" :
"",
* Combine headers and (optional) post file into one contineous buffer fprintf(
stderr,
"only simple translation is supported (%d/%u/%u)\n",
/* initialise lots of requests */ /* check for time limit expiry */ err(
"\nServer timed out\n\n");
* If the connection isn't connected how can we check it? * Notes: APR_POLLHUP is set after FIN is received on some * systems, so treat that like APR_POLLIN so that we try to read * Some systems return APR_POLLERR with APR_POLLHUP. We need to * call read_connection() for APR_POLLHUP, so check for * APR_POLLHUP first so that a closed connection isn't treated * like an I/O error. If it is, we never figure out that the * connection is done and we loop here endlessly calling * When using a select based poll every time we check the bits * are reset. In 1.3's ab we copied the FD_SET's each time * through, but here we're going to check the state and if the * connection is in STATE_READ or STATE_CONNECTING we'll add the * socket back in as APR_POLLIN. /* ------------------------------------------------------- */ /* display copyright information */ printf(
"This is ApacheBench, Version %s\n",
AB_VERSION " <$Revision: 1.74 $> apache-2.0");
printf(
" This is ApacheBench, Version %s <i><%s></i> apache-2.0<br>\n",
AB_VERSION,
"$Revision: 1.74 $");
/* display usage information */ "://]hostname[:port]/path\n",
progname);
fprintf(
stderr,
" -c concurrency Number of multiple requests to make\n");
fprintf(
stderr,
" -t timelimit Seconds to max. wait for responses\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,
" -x attributes String to insert as table attributes\n");
fprintf(
stderr,
" -y attributes String to insert as tr attributes\n");
fprintf(
stderr,
" -z attributes String to insert as td or th attributes\n");
fprintf(
stderr,
" -C attribute Add cookie, eg. 'Apache=1234. (repeatable)\n");
fprintf(
stderr,
" -H attribute Add Arbitrary header line, eg. 'Accept-Encoding: zop'\n");
fprintf(
stderr,
" Inserted after all normal header lines. (repeatable)\n");
fprintf(
stderr,
" -A attribute Add Basic WWW Authentication, the attributes\n");
fprintf(
stderr,
" are a colon separated username and password.\n");
fprintf(
stderr,
" -P attribute Add Basic Proxy Authentication, the attributes\n");
fprintf(
stderr,
" are a colon separated username and password.\n");
fprintf(
stderr,
" -X proxy:port Proxyserver and port number to use\n");
fprintf(
stderr,
" -S Do not show confidence estimators and warnings.\n");
fprintf(
stderr,
" -g filename Output collected data to gnuplot format file.\n");
fprintf(
stderr,
" -e filename Output CSV file with percentages served\n");
fprintf(
stderr,
" -h Display usage information (this message)\n");
/* ------------------------------------------------------- */ /* split URL into parts */ if (*
url ==
'[') {
/* IPv6 numeric address string */ if (
port == 0) {
/* no port specified */ /* ------------------------------------------------------- */ /* read data to POST from file, save contents and length */ printf(
"Can\'t alloc postfile buffer\n");
printf(
"error reading postfile: %s\n",
printf(
"error reading postfile: read only %" /* ------------------------------------------------------- */ /* sort out command-line args and call test */ err(
"Invalid number of requests\n");
err(
"Cannot mix POST and HEAD\n");
err(
"Cannot mix POST and HEAD\n");
* assume username passwd already to be in colon separated form. * Ready to be uu-encoded. * assume username passwd already to be in colon separated form. * if any of the following three are used, turn on html output * assume proxy-name[:port] heartbeatres =
100;
/* but never more often than once every 100 * have been closed at the other end. */