home *** CD-ROM | disk | FTP | other *** search
/ PC Online 1999 April / PCO0499.ISO / filesbbs / os2 / apach134.arj / APACH134.ZIP / src / support / ab.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-01-03  |  26.2 KB  |  841 lines

  1. /* ====================================================================
  2.  * Copyright (c) 1998-1999 The Apache Group.  All rights reserved.
  3.  *
  4.  * Redistribution and use in source and binary forms, with or without
  5.  * modification, are permitted provided that the following conditions
  6.  * are met:
  7.  *
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer. 
  10.  *
  11.  * 2. Redistributions in binary form must reproduce the above copyright
  12.  *    notice, this list of conditions and the following disclaimer in
  13.  *    the documentation and/or other materials provided with the
  14.  *    distribution.
  15.  *
  16.  * 3. All advertising materials mentioning features or use of this
  17.  *    software must display the following acknowledgment:
  18.  *    "This product includes software developed by the Apache Group
  19.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  20.  *
  21.  * 4. The names "Apache Server" and "Apache Group" must not be used to
  22.  *    endorse or promote products derived from this software without
  23.  *    prior written permission. For written permission, please contact
  24.  *    apache@apache.org.
  25.  *
  26.  * 5. Products derived from this software may not be called "Apache"
  27.  *    nor may "Apache" appear in their names without prior written
  28.  *    permission of the Apache Group.
  29.  *
  30.  * 6. Redistributions of any form whatsoever must retain the following
  31.  *    acknowledgment:
  32.  *    "This product includes software developed by the Apache Group
  33.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  34.  *
  35.  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
  36.  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  37.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  38.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
  39.  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  41.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  42.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  43.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  44.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  45.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  46.  * OF THE POSSIBILITY OF SUCH DAMAGE.
  47.  * ====================================================================
  48.  *
  49.  * This software consists of voluntary contributions made by many
  50.  * individuals on behalf of the Apache Group and was originally based
  51.  * on public domain software written at the National Center for
  52.  * Supercomputing Applications, University of Illinois, Urbana-Champaign.
  53.  * For more information on the Apache Group and the Apache HTTP server
  54.  * project, please see <http://www.apache.org/>.
  55.  *
  56.  */
  57.  
  58. /* 
  59. ** This program is based on ZeusBench V1.0 written by Adam Twiss 
  60. ** which is Copyright (c) 1996 by Zeus Technology Ltd. http://www.zeustech.net/
  61. **
  62. ** This software is provided "as is" and any express or implied waranties, 
  63. ** including but not limited to, the implied warranties of merchantability and
  64. ** fitness for a particular purpose are disclaimed.  In no event shall 
  65. ** Zeus Technology Ltd. be liable for any direct, indirect, incidental, special, 
  66. ** exemplary, or consequential damaged (including, but not limited to, 
  67. ** procurement of substitute good or services; loss of use, data, or profits;
  68. ** or business interruption) however caused and on theory of liability.  Whether
  69. ** in contract, strict liability or tort (including negligence or otherwise) 
  70. ** arising in any way out of the use of this software, even if advised of the
  71. ** possibility of such damage.
  72. **
  73. */
  74.  
  75. /*
  76. ** HISTORY: 
  77. **    - Originally written by Adam Twiss <adam@zeus.co.uk>, March 1996
  78. **      with input from Mike Belshe <mbelshe@netscape.com> and 
  79. **      Michael Campanella <campanella@stevms.enet.dec.com>
  80. **    - Enhanced by Dean Gaudet <dgaudet@apache.org>, November 1997
  81. **    - Cleaned up by Ralf S. Engelschall <rse@apache.org>, March 1998 
  82. **    - POST and verbosity by Kurt Sussman <kls@merlot.com>, August 1998 
  83. **
  84. */
  85.  
  86. /*
  87.  * BUGS:
  88.  *
  89.  * - uses strcpy/etc.
  90.  * - has various other poor buffer attacks related to the lazy parsing of
  91.  *   response headers from the server
  92.  * - doesn't implement much of HTTP/1.x, only accepts certain forms of
  93.  *   responses
  94.  * - (performance problem) heavy use of strstr shows up top in profile
  95.  *   only an issue for loopback usage
  96.  */
  97.  
  98. #define VERSION "1.2"
  99.  
  100. /*  -------------------------------------------------------------------- */
  101.  
  102. /* affects include files on Solaris */
  103. #define BSD_COMP
  104.  
  105. /* allow compilation outside an Apache build tree */
  106. #ifdef NO_APACHE_INCLUDES
  107. #include <sys/time.h>
  108. #include <sys/ioctl.h>
  109. #include <sys/stat.h>
  110. #include <unistd.h>
  111. #include <stdlib.h>
  112. #include <stdio.h>
  113. #include <fcntl.h>
  114. #include <sys/socket.h>
  115. #include <netinet/in.h>
  116. #include <netdb.h>
  117. #include <errno.h>
  118. #include <sys/ioctl.h>
  119. #include <string.h>
  120.  
  121. #define ap_select    select
  122. #else /* (!)NO_APACHE_INCLUDES */
  123. #include "ap_config.h"
  124. #include <fcntl.h>
  125. #include <sys/time.h>
  126. #endif /* NO_APACHE_INCLUDES */
  127. /* ------------------- DEFINITIONS -------------------------- */
  128.  
  129. /* maximum number of requests on a time limited test */
  130. #define MAX_REQUESTS 50000
  131.  
  132. /* good old state hostname */
  133. #define STATE_UNCONNECTED 0
  134. #define STATE_CONNECTING  1
  135. #define STATE_READ        2
  136.  
  137. #define CBUFFSIZE       512
  138.  
  139. struct connection {
  140.     int fd;
  141.     int state;
  142.     int read;                   /* amount of bytes read */
  143.     int bread;                  /* amount of body read */
  144.     int length;                 /* Content-Length value used for keep-alive */
  145.     char cbuff[CBUFFSIZE];      /* a buffer to store server response header */
  146.     int cbx;                    /* offset in cbuffer */
  147.     int keepalive;              /* non-zero if a keep-alive request */
  148.     int gotheader;              /* non-zero if we have the entire header in cbuff */
  149.     struct timeval start, connect, done;
  150. };
  151.  
  152. struct data {
  153.     int read;                   /* number of bytes read */
  154.     int ctime;                  /* time in ms to connect */
  155.     int time;                   /* time in ms for connection */
  156. };
  157.  
  158. #define ap_min(a,b) ((a)<(b))?(a):(b)
  159. #define ap_max(a,b) ((a)>(b))?(a):(b)
  160.  
  161. /* --------------------- GLOBALS ---------------------------- */
  162.  
  163. int verbosity = 0;              /* no verbosity by default */
  164. int posting = 0;                /* GET by default */
  165. int requests = 1;               /* Number of requests to make */
  166. int concurrency = 1;            /* Number of multiple requests to make */
  167. int tlimit = 0;                 /* time limit in cs */
  168. int keepalive = 0;              /* try and do keepalive connections */
  169. char servername[1024];          /* name that server reports */
  170. char hostname[1024];            /* host name */
  171. char path[1024];                /* path name */
  172. char postfile[1024];            /* name of file containing post data */
  173. char* postdata;                 /* *buffer containing data from postfile */
  174. int postlen = 0;                /* length of data to be POSTed */
  175. char content_type[1024];        /* content type to put in POST header */
  176. int port = 80;                  /* port number */
  177.  
  178. int doclen = 0;                 /* the length the document should be */
  179. int totalread = 0;              /* total number of bytes read */
  180. int totalbread = 0;             /* totoal amount of entity body read */
  181. int totalposted = 0;            /* total number of bytes posted, inc. headers */
  182. int done = 0;                   /* number of requests we have done */
  183. int doneka = 0;                 /* number of keep alive connections done */
  184. int good = 0, bad = 0;          /* number of good and bad requests */
  185.  
  186. /* store error cases */
  187. int err_length = 0, err_conn = 0, err_except = 0;
  188. int err_response = 0;
  189.  
  190. struct timeval start, endtime;
  191.  
  192. /* global request (and its length) */
  193. char request[512];
  194. int reqlen;
  195.  
  196. /* one global throw-away buffer to read stuff into */
  197. char buffer[8192];
  198.  
  199. struct connection *con;         /* connection array */
  200. struct data *stats;             /* date for each request */
  201.  
  202. fd_set readbits, writebits;     /* bits for select */
  203. struct sockaddr_in server;      /* server addr structure */
  204.  
  205. /* --------------------------------------------------------- */
  206.  
  207. /* simple little function to perror and exit */
  208.  
  209. static void err(char *s)
  210. {
  211.     if (errno) {
  212.         perror(s);
  213.     }
  214.     else {
  215.     printf("%s", s);
  216.     }
  217.     exit(errno);
  218. }
  219.  
  220. /* --------------------------------------------------------- */
  221.  
  222. /* write out request to a connection - assumes we can write 
  223.    (small) request out in one go into our new socket buffer  */
  224.  
  225. static void write_request(struct connection *c)
  226. {
  227.     gettimeofday(&c->connect, 0);
  228.     /* XXX: this could use writev for posting -- more efficient -djg */
  229.     write(c->fd, request, reqlen);
  230.     if (posting) {
  231.         write(c->fd,postdata, postlen);
  232.         totalposted += (reqlen + postlen); 
  233.     }
  234.  
  235.     c->state = STATE_READ;
  236.     FD_SET(c->fd, &readbits);
  237.     FD_CLR(c->fd, &writebits);
  238. }
  239.  
  240. /* --------------------------------------------------------- */
  241.  
  242. /* make an fd non blocking */
  243.  
  244. static void nonblock(int fd)
  245. {
  246.     int i = 1;
  247.     ioctl(fd, FIONBIO, &i);
  248. }
  249.  
  250. /* --------------------------------------------------------- */
  251.  
  252. /* returns the time in ms between two timevals */
  253.  
  254. static int timedif(struct timeval a, struct timeval b)
  255. {
  256.     register int us, s;
  257.  
  258.     us = a.tv_usec - b.tv_usec;
  259.     us /= 1000;
  260.     s = a.tv_sec - b.tv_sec;
  261.     s *= 1000;
  262.     return s + us;
  263. }
  264.  
  265. /* --------------------------------------------------------- */
  266.  
  267. /* calculate and output results */
  268.  
  269. static void output_results(void)
  270. {
  271.     int timetaken;
  272.  
  273.     gettimeofday(&endtime, 0);
  274.     timetaken = timedif(endtime, start);
  275.  
  276.     printf("\r                                                                           \r");
  277.     printf("Server Software:        %s\n", servername);
  278.     printf("Server Hostname:        %s\n", hostname);
  279.     printf("Server Port:            %d\n", port);
  280.     printf("\n");
  281.     printf("Document Path:          %s\n", path);
  282.     printf("Document Length:        %d bytes\n", doclen);
  283.     printf("\n");
  284.     printf("Concurrency Level:      %d\n", concurrency);
  285.     printf("Time taken for tests:   %d.%03d seconds\n",
  286.            timetaken / 1000, timetaken % 1000);
  287.     printf("Complete requests:      %d\n", done);
  288.     printf("Failed requests:        %d\n", bad);
  289.     if (bad)
  290.         printf("   (Connect: %d, Length: %d, Exceptions: %d)\n",
  291.                err_conn, err_length, err_except);
  292.     if (err_response)
  293.         printf("Non-2xx responses:      %d\n", err_response);
  294.     if (keepalive)
  295.         printf("Keep-Alive requests:    %d\n", doneka);
  296.     printf("Total transferred:      %d bytes\n", totalread);
  297.     if (posting)
  298.         printf("Total POSTed:           %d\n", totalposted);
  299.     printf("HTML transferred:       %d bytes\n", totalbread);
  300.  
  301.     /* avoid divide by zero */
  302.     if (timetaken) {
  303.         printf("Requests per second:    %.2f\n", 1000 * (float) (done) / timetaken);
  304.         printf("Transfer rate:          %.2f kb/s received\n",
  305.                (float) (totalread) / timetaken);
  306.         if (posting) {
  307.             printf("                        %.2f kb/s sent\n", 
  308.                    (float)(totalposted)/timetaken);
  309.             printf("                        %.2f kb/s total\n", 
  310.                    (float)(totalread + totalposted)/timetaken);
  311.         }
  312.     }
  313.  
  314.     {
  315.         /* work out connection times */
  316.         int i;
  317.         int totalcon = 0, total = 0;
  318.         int mincon = 9999999, mintot = 999999;
  319.         int maxcon = 0, maxtot = 0;
  320.  
  321.         for (i = 0; i < requests; i++) {
  322.             struct data s = stats[i];
  323.             mincon = ap_min(mincon, s.ctime);
  324.             mintot = ap_min(mintot, s.time);
  325.             maxcon = ap_max(maxcon, s.ctime);
  326.             maxtot = ap_max(maxtot, s.time);
  327.             totalcon += s.ctime;
  328.             total += s.time;
  329.         }
  330.         printf("\nConnnection Times (ms)\n");
  331.         printf("              min   avg   max\n");
  332.         printf("Connect:    %5d %5d %5d\n", mincon, totalcon / requests, maxcon);
  333.         printf("Processing: %5d %5d %5d\n", 
  334.             mintot - mincon, (total/requests) - (totalcon/requests),
  335.             maxtot - maxcon);
  336.         printf("Total:      %5d %5d %5d\n", mintot, total / requests, maxtot);
  337.     }
  338. }
  339.  
  340. /* --------------------------------------------------------- */
  341.  
  342. /* start asynchronous non-blocking connection */
  343.  
  344. static void start_connect(struct connection *c)
  345. {
  346.     c->read = 0;
  347.     c->bread = 0;
  348.     c->keepalive = 0;
  349.     c->cbx = 0;
  350.     c->gotheader = 0;
  351.  
  352.     c->fd = socket(AF_INET, SOCK_STREAM, 0);
  353.     if (c->fd < 0)
  354.         err("socket");
  355.  
  356.     nonblock(c->fd);
  357.     gettimeofday(&c->start, 0);
  358.  
  359.     if (connect(c->fd, (struct sockaddr *) &server, sizeof(server)) < 0) {
  360.         if (errno == EINPROGRESS) {
  361.             c->state = STATE_CONNECTING;
  362.             FD_SET(c->fd, &writebits);
  363.             return;
  364.         }
  365.         else {
  366.             close(c->fd);
  367.             err_conn++;
  368.             if (bad++ > 10) {
  369.                 err("\nTest aborted after 10 failures\n\n");
  370.             }
  371.             start_connect(c);
  372.         }
  373.     }
  374.  
  375.     /* connected first time */
  376.     write_request(c);
  377. }
  378.  
  379. /* --------------------------------------------------------- */
  380.  
  381. /* close down connection and save stats */
  382.  
  383. static void close_connection(struct connection *c)
  384. {
  385.     if (c->read == 0 && c->keepalive) {
  386.         /* server has legitimately shut down an idle keep alive request */
  387.         good--;                 /* connection never happend */
  388.     }
  389.     else {
  390.         if (good == 1) {
  391.             /* first time here */
  392.             doclen = c->bread;
  393.         }
  394.         else if (c->bread != doclen) {
  395.             bad++;
  396.             err_length++;
  397.         }
  398.  
  399.         /* save out time */
  400.         if (done < requests) {
  401.             struct data s;
  402.             gettimeofday(&c->done, 0);
  403.             s.read = c->read;
  404.             s.ctime = timedif(c->connect, c->start);
  405.             s.time = timedif(c->done, c->start);
  406.             stats[done++] = s;
  407.         }
  408.     }
  409.  
  410.     close(c->fd);
  411.     FD_CLR(c->fd, &readbits);
  412.     FD_CLR(c->fd, &writebits);
  413.  
  414.     /* connect again */
  415.     start_connect(c);
  416.     return;
  417. }
  418.  
  419. /* --------------------------------------------------------- */
  420.  
  421. /* read data from connection */
  422.  
  423. static void read_connection(struct connection *c)
  424. {
  425.     int r;
  426.     char *part;
  427.     char respcode[4];  /* 3 digits and null */
  428.  
  429.     r = read(c->fd, buffer, sizeof(buffer));
  430.     if (r == 0 || (r < 0 && errno != EAGAIN)) {
  431.         good++;
  432.         close_connection(c);
  433.         return;
  434.     }
  435.  
  436.     if (r < 0 && errno == EAGAIN)
  437.         return;
  438.  
  439.     c->read += r;
  440.     totalread += r;
  441.  
  442.     if (!c->gotheader) {
  443.         char *s;
  444.         int l = 4;
  445.         int space = CBUFFSIZE - c->cbx - 1;     /* -1 to allow for 0 terminator */
  446.         int tocopy = (space < r) ? space : r;
  447. #ifndef CHARSET_EBCDIC
  448.         memcpy(c->cbuff + c->cbx, buffer, space);
  449. #else /*CHARSET_EBCDIC*/
  450.         ascii2ebcdic(c->cbuff + c->cbx, buffer, space);
  451. #endif /*CHARSET_EBCDIC*/
  452.         c->cbx += tocopy;
  453.         space -= tocopy;
  454.         c->cbuff[c->cbx] = 0;   /* terminate for benefit of strstr */
  455.     if (verbosity >= 4) {
  456.         printf("LOG: header received:\n%s\n", c->cbuff);
  457.     }
  458.         s = strstr(c->cbuff, "\r\n\r\n");
  459.         /* this next line is so that we talk to NCSA 1.5 which blatantly breaks 
  460.            the http specifaction */
  461.         if (!s) {
  462.             s = strstr(c->cbuff, "\n\n");
  463.             l = 2;
  464.         }
  465.  
  466.         if (!s) {
  467.             /* read rest next time */
  468.             if (space)
  469.                 return;
  470.             else {
  471.                 /* header is in invalid or too big - close connection */
  472.                 close(c->fd);
  473.                 if (bad++ > 10) {
  474.                     err("\nTest aborted after 10 failures\n\n");
  475.                 }
  476.                 FD_CLR(c->fd, &writebits);
  477.                 start_connect(c);
  478.             }
  479.         }
  480.         else {
  481.             /* have full header */
  482.             if (!good) {
  483.                 /* this is first time, extract some interesting info */
  484.                 char *p, *q;
  485.                 p = strstr(c->cbuff, "Server:");
  486.                 q = servername;
  487.                 if (p) {
  488.                     p += 8;
  489.                     while (*p > 32)
  490.                         *q++ = *p++;
  491.                 }
  492.                 *q = 0;
  493.             }
  494.  
  495.         /* XXX: this parsing isn't even remotely HTTP compliant...
  496.          * but in the interest of speed it doesn't totally have to be,
  497.          * it just needs to be extended to handle whatever servers
  498.          * folks want to test against. -djg */
  499.  
  500.             /* check response code */
  501.             part = strstr(c->cbuff, "HTTP");                /* really HTTP/1.x_ */
  502.             strncpy(respcode, (part+strlen("HTTP/1.x_")), 3);
  503.         respcode[3] = '\0';
  504.             if (respcode[0] != '2') {
  505.                 err_response++;
  506.                 if (verbosity >= 2) printf ("WARNING: Response code not 2xx (%s)\n", respcode);
  507.             }
  508.         else if (verbosity >= 3) {
  509.                 printf("LOG: Response code = %s\n", respcode);
  510.             }
  511.  
  512.             c->gotheader = 1;
  513.             *s = 0;             /* terminate at end of header */
  514.             if (keepalive &&
  515.                 (strstr(c->cbuff, "Keep-Alive")
  516.                  || strstr(c->cbuff, "keep-alive"))) {  /* for benefit of MSIIS */
  517.                 char *cl;
  518.                 cl = strstr(c->cbuff, "Content-Length:");
  519.                 /* handle NCSA, which sends Content-length: */
  520.                 if (!cl)
  521.                     cl = strstr(c->cbuff, "Content-length:");
  522.                 if (cl) {
  523.                     c->keepalive = 1;
  524.                     c->length = atoi(cl + 16);
  525.                 }
  526.             }
  527.             c->bread += c->cbx - (s + l - c->cbuff) + r - tocopy;
  528.             totalbread += c->bread;
  529.         }
  530.     }
  531.     else {
  532.         /* outside header, everything we have read is entity body */
  533.         c->bread += r;
  534.         totalbread += r;
  535.     }
  536.  
  537.     if (c->keepalive && (c->bread >= c->length)) {
  538.         /* finished a keep-alive connection */
  539.         good++;
  540.         doneka++;
  541.         /* save out time */
  542.         if (good == 1) {
  543.             /* first time here */
  544.             doclen = c->bread;
  545.         }
  546.         else if (c->bread != doclen) {
  547.             bad++;
  548.             err_length++;
  549.         }
  550.         if (done < requests) {
  551.             struct data s;
  552.             gettimeofday(&c->done, 0);
  553.             s.read = c->read;
  554.             s.ctime = timedif(c->connect, c->start);
  555.             s.time = timedif(c->done, c->start);
  556.             stats[done++] = s;
  557.         }
  558.         c->keepalive = 0;
  559.         c->length = 0;
  560.         c->gotheader = 0;
  561.         c->cbx = 0;
  562.         c->read = c->bread = 0;
  563.         write_request(c);
  564.         c->start = c->connect;  /* zero connect time with keep-alive */
  565.     }
  566. }
  567.  
  568. /* --------------------------------------------------------- */
  569.  
  570. /* run the tests */
  571.  
  572. static void test(void)
  573. {
  574.     struct timeval timeout, now;
  575.     fd_set sel_read, sel_except, sel_write;
  576.     int i;
  577.  
  578.     printf("Benchmarking %s (be patient)...", hostname);
  579.     fflush(stdout);
  580.  
  581.     {
  582.         /* get server information */
  583.         struct hostent *he;
  584.         he = gethostbyname(hostname);
  585.         if (!he)
  586.             err("bad hostname");
  587.         server.sin_family = he->h_addrtype;
  588.         server.sin_port = htons(port);
  589.         server.sin_addr.s_addr = ((unsigned long *) (he->h_addr_list[0]))[0];
  590.     }
  591.  
  592.     con = malloc(concurrency * sizeof(struct connection));
  593.     memset(con, 0, concurrency * sizeof(struct connection));
  594.  
  595.     stats = malloc(requests * sizeof(struct data));
  596.  
  597.     FD_ZERO(&readbits);
  598.     FD_ZERO(&writebits);
  599.  
  600.     /* setup request */
  601.     if (!posting) {
  602.     sprintf(request, "GET %s HTTP/1.0\r\n"
  603.                      "User-Agent: ApacheBench/%s\r\n"
  604.                      "%s"
  605.                      "Host: %s\r\n"
  606.                      "Accept: */*\r\n"
  607.                      "\r\n", 
  608.                      path, 
  609.                      VERSION,
  610.                      keepalive ? "Connection: Keep-Alive\r\n" : "", 
  611.                      hostname);
  612.     }
  613.     else {
  614.     sprintf(request, "POST %s HTTP/1.0\r\n"
  615.                      "User-Agent: ApacheBench/%s\r\n"
  616.                      "%s"
  617.                      "Host: %s\r\n"
  618.                      "Accept: */*\r\n"
  619.                      "Content-length: %d\r\n"
  620.                      "Content-type: %s\r\n"
  621.                      "\r\n", 
  622.                      path, 
  623.                      VERSION,
  624.                      keepalive ? "Connection: Keep-Alive\r\n" : "", 
  625.                      hostname, postlen, 
  626.                      (content_type[0]) ? content_type : "text/plain");
  627.     }
  628.  
  629.     if (verbosity >= 2) printf("INFO: POST header == \n---\n%s\n---\n", request);
  630.  
  631.     reqlen = strlen(request);
  632.  
  633. #ifdef CHARSET_EBCDIC
  634.     ebcdic2ascii(request, request, reqlen);
  635. #endif /*CHARSET_EBCDIC*/
  636.  
  637.     /* ok - lets start */
  638.     gettimeofday(&start, 0);
  639.  
  640.     /* initialise lots of requests */
  641.     for (i = 0; i < concurrency; i++)
  642.         start_connect(&con[i]);
  643.  
  644.     while (done < requests) {
  645.         int n;
  646.         /* setup bit arrays */
  647.         memcpy(&sel_except, &readbits, sizeof(readbits));
  648.         memcpy(&sel_read, &readbits, sizeof(readbits));
  649.         memcpy(&sel_write, &writebits, sizeof(readbits));
  650.  
  651.         /* check for time limit expiry */
  652.         gettimeofday(&now, 0);
  653.         if (tlimit && timedif(now, start) > (tlimit * 1000)) {
  654.             requests = done;    /* so stats are correct */
  655.         }
  656.  
  657.         /* Timeout of 30 seconds. */
  658.         timeout.tv_sec = 30;
  659.         timeout.tv_usec = 0;
  660.         n = ap_select(FD_SETSIZE, &sel_read, &sel_write, &sel_except, &timeout);
  661.         if (!n) {
  662.             err("\nServer timed out\n\n");
  663.         }
  664.         if (n < 1)
  665.             err("select");
  666.  
  667.         for (i = 0; i < concurrency; i++) {
  668.             int s = con[i].fd;
  669.             if (FD_ISSET(s, &sel_except)) {
  670.                 bad++;
  671.                 err_except++;
  672.                 start_connect(&con[i]);
  673.                 continue;
  674.             }
  675.             if (FD_ISSET(s, &sel_read))
  676.                 read_connection(&con[i]);
  677.             if (FD_ISSET(s, &sel_write))
  678.                 write_request(&con[i]);
  679.         }
  680.     }
  681.     output_results();
  682. }
  683.  
  684. /* ------------------------------------------------------- */
  685.  
  686. /* display copyright information */
  687. static void copyright(void) 
  688. {
  689.     printf("This is ApacheBench, Version %s\n", VERSION);
  690.     printf("Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/\n");
  691.     printf("Copyright (c) 1998-1999 The Apache Group, http://www.apache.org/\n");
  692.     printf("\n");
  693. }
  694.  
  695. /* display usage information */
  696. static void usage(char *progname)
  697. {
  698.     fprintf(stderr, "Usage: %s [options] [http://]hostname[:port]/path\n", progname);
  699.     fprintf(stderr, "Options are:\n");
  700.     fprintf(stderr, "    -n requests     Number of requests to perform\n");
  701.     fprintf(stderr, "    -c concurrency  Number of multiple requests to make\n");
  702.     fprintf(stderr, "    -t timelimit    Seconds to max. wait for responses\n");
  703.     fprintf(stderr, "    -p postfile     File containg data to POST\n");
  704.     fprintf(stderr, "    -T content-type Content-type header for POSTing\n");
  705.     fprintf(stderr, "    -v verbosity    How much troubleshooting info to print\n");
  706.     fprintf(stderr, "    -V              Print version number and exit\n");
  707.     fprintf(stderr, "    -k              Use HTTP KeepAlive feature\n");
  708.     fprintf(stderr, "    -h              Display usage information (this message)\n");
  709.     exit(EINVAL);
  710. }
  711.  
  712. /* ------------------------------------------------------- */
  713.  
  714. /* split URL into parts */
  715.  
  716. static int parse_url(char *url)
  717. {
  718.     char *cp;
  719.     char *h;
  720.     char *p = NULL;
  721.  
  722.     if (strlen(url) > 7 && strncmp(url, "http://", 7) == 0) 
  723.         url += 7;
  724.     h = url;
  725.     if ((cp = strchr(url, ':')) != NULL) {
  726.         *cp++ = '\0';
  727.         p = cp;
  728.         url = cp;
  729.     }
  730.     if ((cp = strchr(url, '/')) == NULL)
  731.         return 1;
  732.     strcpy(path, cp);
  733.     *cp = '\0';
  734.     strcpy(hostname, h);
  735.     if (p != NULL)
  736.         port = atoi(p);
  737.     return 0;
  738. }
  739.  
  740. /* ------------------------------------------------------- */
  741.  
  742. /* read data to POST from file, save contents and length */
  743.  
  744. static int open_postfile(char *pfile)
  745. {
  746.     int postfd, status;
  747.     struct stat postfilestat;
  748.  
  749.     if ((postfd = open(pfile, O_RDONLY)) == -1) {
  750.         printf("Invalid postfile name (%s)\n", pfile);
  751.         return errno;
  752.     }
  753.     if ((status = fstat(postfd, &postfilestat)) == -1) {
  754.         perror("Can\'t stat postfile\n");
  755.         return status;
  756.     }
  757.     postdata = malloc(postfilestat.st_size);
  758.     if (!postdata) {
  759.         printf("Can\'t alloc postfile buffer\n");
  760.         return ENOMEM;
  761.     }
  762.     if (read(postfd, postdata, postfilestat.st_size) != postfilestat.st_size) {
  763.         printf("error reading postfilen");
  764.         return EIO;
  765.     }
  766.     postlen = postfilestat.st_size;
  767.     return 0;
  768. }
  769.  
  770. /* ------------------------------------------------------- */
  771.  
  772. extern char *optarg;
  773. extern int optind, opterr, optopt;
  774.  
  775. /* sort out command-line args and call test */
  776. int main(int argc, char **argv)
  777. {
  778.     int c, r;
  779.     optind = 1;
  780.     while ((c = getopt(argc, argv, "n:c:t:T:p:v:kVh")) > 0) {
  781.         switch (c) {
  782.         case 'n':
  783.             requests = atoi(optarg);
  784.             if (!requests) {
  785.                 err("Invalid number of requests\n");
  786.             }
  787.             break;
  788.         case 'k':
  789.             keepalive = 1;
  790.             break;
  791.         case 'c':
  792.             concurrency = atoi(optarg);
  793.             break;
  794.         case 'p':
  795.             if (0 == (r = open_postfile(optarg))) {
  796.                 posting = 1;
  797.             }
  798.         else if (postdata) {
  799.                 exit(r);
  800.         }
  801.             break;
  802.         case 'v':
  803.             verbosity = atoi(optarg);
  804.             break;
  805.         case 't':
  806.             tlimit = atoi(optarg);
  807.             requests = MAX_REQUESTS;    /* need to size data array on something */
  808.             break;
  809.         case 'T':
  810.             strcpy(content_type, optarg);
  811.             break;
  812.         case 'V':
  813.             copyright();
  814.             exit(0);
  815.             break;
  816.         case 'h':
  817.             usage(argv[0]);
  818.             break;
  819.         default:
  820.             fprintf(stderr, "%s: invalid option `%c'\n", argv[0], c);
  821.             usage(argv[0]);
  822.             break;
  823.         }
  824.     }
  825.     if (optind != argc-1) {
  826.         fprintf(stderr, "%s: wrong number of arguments\n", argv[0]);
  827.         usage(argv[0]);
  828.     }
  829.  
  830.     if (parse_url(argv[optind++])) {
  831.         fprintf(stderr, "%s: invalid URL\n", argv[0]);
  832.         usage(argv[0]);
  833.     }
  834.  
  835.     copyright();
  836.     test();
  837.  
  838.     exit(0);
  839. }
  840.  
  841.