]> granicus.if.org Git - apache/blob - support/ab.c
ignore the intermediate build files.
[apache] / support / ab.c
1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000 The Apache Software Foundation.  All rights
5  * reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
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
17  *    distribution.
18  *
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.
25  *
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.
30  *
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.
34  *
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
46  * SUCH DAMAGE.
47  * ====================================================================
48  *
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/>.
53  *
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.
57  */
58
59 /*
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/
62    **
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.
73    **
74  */
75
76 /*
77    ** HISTORY:
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
86    **
87  */
88
89 /*
90  * BUGS:
91  *
92  * - uses strcpy/etc.
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
96  *   responses
97  * - (performance problem) heavy use of strstr shows up top in profile
98  *   only an issue for loopback usage
99  */
100
101 #define VERSION "1.3c"
102
103 /*  -------------------------------------------------------------------- */
104
105 #if 'A' != 0x41
106 /* Hmmm... This source code isn't being compiled in ASCII.
107  * In order for data that flows over the network to make
108  * sense, we need to translate to/from ASCII.
109  */
110 #define NOT_ASCII
111 #endif
112
113 /* affects include files on Solaris */
114 #define BSD_COMP
115
116 #include "apr_network_io.h"
117 #include "apr_file_io.h"
118 #include "apr_time.h"
119 #include "apr_getopt.h"
120 #ifdef NOT_ASCII
121 #include "apr_xlate.h"
122 #endif
123 #include <string.h>
124 #include <stdio.h>
125 #include <stdlib.h>
126
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     ap_socket_t *aprsock;
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
149                                  * cbuff */
150     ap_time_t start, connect, done;
151     int socknum;
152 };
153
154 struct data {
155     int read;                   /* number of bytes read */
156     int ctime;                  /* time in ms to connect */
157     int time;                   /* time in ms for connection */
158 };
159
160 #define ap_min(a,b) ((a)<(b))?(a):(b)
161 #define ap_max(a,b) ((a)>(b))?(a):(b)
162
163 /* --------------------- GLOBALS ---------------------------- */
164 API_VAR_IMPORT char *ap_optarg; /* argument associated with option */
165 API_VAR_IMPORT int  ap_optind;
166
167 int verbosity = 0;              /* no verbosity by default */
168 int posting = 0;                /* GET by default */
169 int requests = 1;               /* Number of requests to make */
170 int concurrency = 1;            /* Number of multiple requests to make */
171 int tlimit = 0;                 /* time limit in cs */
172 int keepalive = 0;              /* try and do keepalive connections */
173 char servername[1024];          /* name that server reports */
174 char hostname[1024];            /* host name */
175 char path[1024];                /* path name */
176 char postfile[1024];            /* name of file containing post data */
177 char *postdata;                 /* *buffer containing data from postfile */
178 ap_ssize_t postlen = 0;         /* length of data to be POSTed */
179 char content_type[1024];        /* content type to put in POST header */
180 char cookie[1024],              /* optional cookie line */
181      auth[1024],                /* optional (basic/uuencoded)
182                                  * authentification */
183      hdrs[4096];                /* optional arbitrary headers */
184 int port = 80;                  /* port number */
185 time_t aprtimeout = 30 * AP_USEC_PER_SEC; /* timeout value */
186
187 int use_html = 0;               /* use html in the report */
188 char *tablestring;
189 char *trstring;
190 char *tdstring;
191
192 int doclen = 0;                 /* the length the document should be */
193 int totalread = 0;              /* total number of bytes read */
194 int totalbread = 0;             /* totoal amount of entity body read */
195 int totalposted = 0;            /* total number of bytes posted, inc. headers */
196 int done = 0;                   /* number of requests we have done */
197 int doneka = 0;                 /* number of keep alive connections done */
198 int good = 0, bad = 0;          /* number of good and bad requests */
199
200 /* store error cases */
201 int err_length = 0, err_conn = 0, err_except = 0;
202 int err_response = 0;
203
204 ap_time_t start, endtime;
205
206 /* global request (and its length) */
207 char request[512];
208 ap_ssize_t reqlen;
209
210 /* one global throw-away buffer to read stuff into */
211 char buffer[8192];
212
213 struct connection *con;         /* connection array */
214 struct data *stats;             /* date for each request */
215 ap_pool_t *cntxt;
216
217 ap_pollfd_t *readbits;
218 #ifdef NOT_ASCII
219 ap_xlate_t *fromascii, *toascii;
220 #endif
221
222 /* --------------------------------------------------------- */
223
224 /* simple little function to perror and exit */
225
226 static void err(char *s)
227 {
228     if (errno) {
229         perror(s);
230     }
231     else {
232         printf("%s", s);
233     }
234     exit(errno);
235 }
236
237 /* --------------------------------------------------------- */
238
239 /* write out request to a connection - assumes we can write
240    (small) request out in one go into our new socket buffer  */
241
242 static void write_request(struct connection *c)
243 {
244     ap_ssize_t len = reqlen;
245     c->connect = ap_now();
246     ap_setsocketopt(c->aprsock, APR_SO_TIMEOUT, 30 * AP_USEC_PER_SEC);
247     if (ap_send(c->aprsock, request, &reqlen) != APR_SUCCESS ||
248         reqlen != len) {
249         printf("Send request failed!\n");
250     }
251     if (posting) {
252         ap_send(c->aprsock, postdata, &postlen);
253         totalposted += (reqlen + postlen);
254     }
255
256     c->state = STATE_READ;
257     ap_add_poll_socket(readbits, c->aprsock, APR_POLLIN);
258     ap_remove_poll_socket(readbits, c->aprsock);
259 }
260
261 /* --------------------------------------------------------- */
262
263 /* calculate and output results */
264
265 static void output_results(void)
266 {
267     int timetaken;
268
269     endtime = ap_now();
270     timetaken = (endtime - start) / 1000;
271
272     printf("\r                                                                           \r");
273     printf("Server Software:        %s\n", servername);
274     printf("Server Hostname:        %s\n", hostname);
275     printf("Server Port:            %d\n", port);
276     printf("\n");
277     printf("Document Path:          %s\n", path);
278     printf("Document Length:        %d bytes\n", doclen);
279     printf("\n");
280     printf("Concurrency Level:      %d\n", concurrency);
281     printf("Time taken for tests:   %d.%03d seconds\n",
282            timetaken / 1000, timetaken % 1000);
283     printf("Complete requests:      %d\n", done);
284     printf("Failed requests:        %d\n", bad);
285     if (bad)
286         printf("   (Connect: %d, Length: %d, Exceptions: %d)\n",
287                err_conn, err_length, err_except);
288     if (err_response)
289         printf("Non-2xx responses:      %d\n", err_response);
290     if (keepalive)
291         printf("Keep-Alive requests:    %d\n", doneka);
292     printf("Total transferred:      %d bytes\n", totalread);
293     if (posting)
294         printf("Total POSTed:           %d\n", totalposted);
295     printf("HTML transferred:       %d bytes\n", totalbread);
296
297     /* avoid divide by zero */
298     if (timetaken) {
299         printf("Requests per second:    %.2f\n", 1000 * (float) (done) / timetaken);
300         printf("Transfer rate:          %.2f kb/s received\n",
301                (float) (totalread) / timetaken);
302         if (posting>0) {
303             printf("                        %.2f kb/s sent\n",
304                   (float) (totalposted) / timetaken);
305             printf("                        %.2f kb/s total\n",
306                   (float) (totalread + totalposted) / timetaken);
307         }
308     }
309
310     {
311         /* work out connection times */
312         int i;
313         int totalcon = 0, total = 0;
314         int mincon = 9999999, mintot = 999999;
315         int maxcon = 0, maxtot = 0;
316
317         for (i = 0; i < requests; i++) {
318             struct data s = stats[i];
319             mincon = ap_min(mincon, s.ctime);
320             mintot = ap_min(mintot, s.time);
321             maxcon = ap_max(maxcon, s.ctime);
322             maxtot = ap_max(maxtot, s.time);
323             totalcon += s.ctime;
324             total += s.time;
325         }
326         if (requests > 0) { /* avoid division by zero (if 0 requests) */
327             printf("\nConnnection Times (ms)\n");
328             printf("              min   avg   max\n");
329             printf("Connect:    %5d %5d %5d\n", mincon, totalcon / requests, maxcon);
330             printf("Processing: %5d %5d %5d\n",
331                    mintot - mincon, (total / requests) - (totalcon / requests),
332                    maxtot - maxcon);
333             printf("Total:      %5d %5d %5d\n", mintot, total / requests, maxtot);
334         }
335     }
336 }
337
338 /* --------------------------------------------------------- */
339
340 /* calculate and output results in HTML  */
341
342 static void output_html_results(void)
343 {
344     int timetaken;
345
346     endtime = ap_now();
347     timetaken = (endtime - start) / 1000;
348
349     printf("\n\n<table %s>\n", tablestring);
350     printf("<tr %s><th colspan=2 %s>Server Software:</th>"
351            "<td colspan=2 %s>%s</td></tr>\n",
352            trstring, tdstring, tdstring, servername);
353     printf("<tr %s><th colspan=2 %s>Server Hostname:</th>"
354            "<td colspan=2 %s>%s</td></tr>\n",
355            trstring, tdstring, tdstring, hostname);
356     printf("<tr %s><th colspan=2 %s>Server Port:</th>"
357            "<td colspan=2 %s>%d</td></tr>\n",
358            trstring, tdstring, tdstring, port);
359     printf("<tr %s><th colspan=2 %s>Document Path:</th>"
360            "<td colspan=2 %s>%s</td></tr>\n",
361            trstring, tdstring, tdstring, path);
362     printf("<tr %s><th colspan=2 %s>Document Length:</th>"
363            "<td colspan=2 %s>%d bytes</td></tr>\n",
364            trstring, tdstring, tdstring, doclen);
365     printf("<tr %s><th colspan=2 %s>Concurrency Level:</th>"
366            "<td colspan=2 %s>%d</td></tr>\n",
367            trstring, tdstring, tdstring, concurrency);
368     printf("<tr %s><th colspan=2 %s>Time taken for tests:</th>"
369            "<td colspan=2 %s>%d.%03d seconds</td></tr>\n",
370            trstring, tdstring, tdstring, timetaken / 1000, timetaken % 1000);
371     printf("<tr %s><th colspan=2 %s>Complete requests:</th>"
372            "<td colspan=2 %s>%d</td></tr>\n",
373            trstring, tdstring, tdstring, done);
374     printf("<tr %s><th colspan=2 %s>Failed requests:</th>"
375            "<td colspan=2 %s>%d</td></tr>\n",
376            trstring, tdstring, tdstring, bad);
377     if (bad)
378         printf("<tr %s><td colspan=4 %s >   (Connect: %d, Length: %d, Exceptions: %d)</td></tr>\n",
379                trstring, tdstring, err_conn, err_length, err_except);
380     if (err_response)
381         printf("<tr %s><th colspan=2 %s>Non-2xx responses:</th>"
382                "<td colspan=2 %s>%d</td></tr>\n",
383                trstring, tdstring, tdstring, err_response);
384     if (keepalive)
385         printf("<tr %s><th colspan=2 %s>Keep-Alive requests:</th>"
386                "<td colspan=2 %s>%d</td></tr>\n",
387                trstring, tdstring, tdstring, doneka);
388     printf("<tr %s><th colspan=2 %s>Total transferred:</th>"
389            "<td colspan=2 %s>%d bytes</td></tr>\n",
390            trstring, tdstring, tdstring, totalread);
391     if (posting>0)
392         printf("<tr %s><th colspan=2 %s>Total POSTed:</th>"
393                "<td colspan=2 %s>%d</td></tr>\n",
394                trstring, tdstring, tdstring, totalposted);
395     printf("<tr %s><th colspan=2 %s>HTML transferred:</th>"
396            "<td colspan=2 %s>%d bytes</td></tr>\n",
397            trstring, tdstring, tdstring, totalbread);
398
399     /* avoid divide by zero */
400     if (timetaken) {
401         printf("<tr %s><th colspan=2 %s>Requests per second:</th>"
402                "<td colspan=2 %s>%.2f</td></tr>\n",
403            trstring, tdstring, tdstring, 1000 * (float) (done) / timetaken);
404         printf("<tr %s><th colspan=2 %s>Transfer rate:</th>"
405                "<td colspan=2 %s>%.2f kb/s received</td></tr>\n",
406              trstring, tdstring, tdstring, (float) (totalread) / timetaken);
407         if (posting>0) {
408             printf("<tr %s><td colspan=2 %s>&nbsp;</td>"
409                    "<td colspan=2 %s>%.2f kb/s sent</td></tr>\n",
410                    trstring, tdstring, tdstring,
411                    (float) (totalposted) / timetaken);
412             printf("<tr %s><td colspan=2 %s>&nbsp;</td>"
413                    "<td colspan=2 %s>%.2f kb/s total</td></tr>\n",
414                    trstring, tdstring, tdstring,
415                    (float) (totalread + totalposted) / timetaken);
416         }
417     }
418
419     {
420         /* work out connection times */
421         int i;
422         int totalcon = 0, total = 0;
423         int mincon = 9999999, mintot = 999999;
424         int maxcon = 0, maxtot = 0;
425
426         for (i = 0; i < requests; i++) {
427             struct data s = stats[i];
428             mincon = ap_min(mincon, s.ctime);
429             mintot = ap_min(mintot, s.time);
430             maxcon = ap_max(maxcon, s.ctime);
431             maxtot = ap_max(maxtot, s.time);
432             totalcon += s.ctime;
433             total += s.time;
434         }
435
436         if (requests > 0) { /* avoid division by zero (if 0 requests) */
437             printf("<tr %s><th %s colspan=4>Connnection Times (ms)</th></tr>\n",
438                    trstring, tdstring);
439             printf("<tr %s><th %s>&nbsp;</th> <th %s>min</th>   <th %s>avg</th>   <th %s>max</th></tr>\n",
440                    trstring, tdstring, tdstring, tdstring, tdstring);
441             printf("<tr %s><th %s>Connect:</th>"
442                    "<td %s>%5d</td>"
443                    "<td %s>%5d</td>"
444                    "<td %s>%5d</td></tr>\n",
445                    trstring, tdstring, tdstring, mincon, tdstring, totalcon / requests, tdstring, maxcon);
446             printf("<tr %s><th %s>Processing:</th>"
447                    "<td %s>%5d</td>"
448                    "<td %s>%5d</td>"
449                    "<td %s>%5d</td></tr>\n",
450                    trstring, tdstring, tdstring, mintot - mincon, tdstring,
451                    (total / requests) - (totalcon / requests), tdstring, maxtot - maxcon);
452             printf("<tr %s><th %s>Total:</th>"
453                    "<td %s>%5d</td>"
454                    "<td %s>%5d</td>"
455                    "<td %s>%5d</td></tr>\n",
456                    trstring, tdstring, tdstring, mintot, tdstring, total / requests, tdstring, maxtot);
457         }
458         printf("</table>\n");
459     }
460 }
461
462 /* --------------------------------------------------------- */
463
464 /* start asnchronous non-blocking connection */
465
466 static void start_connect(struct connection *c)
467 {
468     c->read = 0;
469     c->bread = 0;
470     c->keepalive = 0;
471     c->cbx = 0;
472     c->gotheader = 0;
473
474     if (ap_create_tcp_socket(&c->aprsock, cntxt) != APR_SUCCESS) {
475         err("Socket:");
476     }
477     c->start = ap_now();
478     if (ap_connect(c->aprsock, hostname) != APR_SUCCESS) {
479         if (errno == APR_EINPROGRESS) {
480             c->state = STATE_CONNECTING;
481             ap_add_poll_socket(readbits, c->aprsock, APR_POLLOUT);
482             return;
483         }
484         else {
485             /* we done't have to close the socket.  If we have an error this
486              * bad, ap_connect will destroy it for us.
487              */
488             err_conn++;
489             if (bad++ > 10) {
490                 err("\nTest aborted after 10 failures\n\n");
491             }
492             start_connect(c);
493         }
494     }
495
496     /* connected first time */
497     write_request(c);
498 }
499
500 /* --------------------------------------------------------- */
501
502 /* close down connection and save stats */
503
504 static void close_connection(struct connection *c)
505 {
506     if (c->read == 0 && c->keepalive) {
507         /* server has legitimately shut down an idle keep alive request */
508         good--;                 /* connection never happend */
509     }
510     else {
511         if (good == 1) {
512             /* first time here */
513             doclen = c->bread;
514         }
515         else if (c->bread != doclen) {
516             bad ++;
517             err_length++;
518         }
519         /* save out time */
520         if (done < requests) {
521             struct data s;
522             c->done = ap_now();
523             s.read  = c->read;
524             s.ctime = (c->connect - c->start) / 1000;
525             s.time  = (c->done - c->start) / 1000;
526             stats[done++] = s;
527         }
528     }
529
530     ap_remove_poll_socket(readbits, c->aprsock);
531     ap_close_socket(c->aprsock);
532
533     /* connect again */
534     start_connect(c);
535     return;
536 }
537
538 /* --------------------------------------------------------- */
539
540 /* read data from connection */
541
542 static void read_connection(struct connection *c)
543 {
544     ap_ssize_t r;
545     ap_status_t status;
546     char *part;
547     char respcode[4];           /* 3 digits and null */
548
549     r = sizeof(buffer);
550     ap_setsocketopt(c->aprsock, APR_SO_TIMEOUT, aprtimeout);
551     status = ap_recv(c->aprsock, buffer, &r);
552     if (r == 0 || (status != 0 && ap_canonical_error(status) != APR_EAGAIN)) {
553         good++;
554         close_connection(c);
555         return;
556     }
557
558     if (ap_canonical_error(status) == EAGAIN)
559         return;
560
561     c->read += r;
562     totalread += r;
563
564     if (!c->gotheader) {
565         char *s;
566         int l = 4;
567         int space = CBUFFSIZE - c->cbx - 1;  /* -1 to allow for 0 terminator */
568         int tocopy = (space < r) ? space : r;
569 #ifdef NOT_ASCII
570         ap_size_t inbytes_left = space, outbytes_left = space;
571
572         status = ap_xlate_conv_buffer(fromascii, buffer, &inbytes_left,
573                                       c->cbuff + c->cbx, &outbytes_left);
574         if (status || inbytes_left || outbytes_left) {
575             fprintf(stderr, "only simple translation is supported (%d/%u/%u)\n",
576                     status, inbytes_left, outbytes_left);
577             exit(1);
578         }
579 #else
580         memcpy(c->cbuff + c->cbx, buffer, space);
581 #endif /*NOT_ASCII */
582         c->cbx += tocopy;
583         space -= tocopy;
584         c->cbuff[c->cbx] = 0;  /* terminate for benefit of strstr */
585         if (verbosity >= 4) {
586             printf("LOG: header received:\n%s\n", c->cbuff);
587         }
588         s = strstr(c->cbuff, "\r\n\r\n");
589             /* this next line is so that we talk to NCSA 1.5 which blatantly 
590              * breaks the http specifaction 
591              */
592         if (!s) {
593             s = strstr(c->cbuff, "\n\n");
594             l = 2;
595         }
596
597         if (!s) {
598             /* read rest next time */
599             if (space) {
600                return;
601             }
602             else {
603                 /* header is in invalid or too big - close connection */
604                 ap_remove_poll_socket(readbits, c->aprsock);
605                 ap_close_socket(c->aprsock);
606                 if (bad++ > 10) {
607                     err("\nTest aborted after 10 failures\n\n");
608                 }
609                 start_connect(c);
610             }
611         }
612         else {
613             /* have full header */
614             if (!good) {
615                /* this is first time, extract some interesting info */
616                char *p, *q;
617                p = strstr(c->cbuff, "Server:");
618                q = servername;
619                if (p) {
620                    p += 8;
621                    while (*p > 32)
622                        *q++ = *p++;
623                }
624                *q = 0;
625             }
626
627             /* XXX: this parsing isn't even remotely HTTP compliant...
628              * but in the interest of speed it doesn't totally have to be,
629              * it just needs to be extended to handle whatever servers
630              * folks want to test against. -djg */
631
632             /* check response code */
633             part = strstr(c->cbuff, "HTTP");   /* really HTTP/1.x_ */
634             strncpy(respcode, (part + strlen("HTTP/1.x_")), 3);
635             respcode[3] = '\0';
636             if (respcode[0] != '2') {
637                err_response++;
638                if (verbosity >= 2)
639                    printf("WARNING: Response code not 2xx (%s)\n", respcode);
640             }
641             else if (verbosity >= 3) {
642                printf("LOG: Response code = %s\n", respcode);
643             }
644
645             c->gotheader = 1;
646             *s = 0;            /* terminate at end of header */
647             if (keepalive &&
648                (strstr(c->cbuff, "Keep-Alive")
649                || strstr(c->cbuff, "keep-alive"))) {  /* for benefit of MSIIS */
650                char *cl;
651                cl = strstr(c->cbuff, "Content-Length:");
652                /* handle NCSA, which sends Content-length: */
653                if (!cl)
654                    cl = strstr(c->cbuff, "Content-length:");
655                if (cl) {
656                    c->keepalive = 1;
657                    c->length = atoi(cl + 16);
658                }
659             }
660             c->bread += c->cbx - (s + l - c->cbuff) + r - tocopy;
661             totalbread += c->bread;
662         }
663     }
664     else {
665         /* outside header, everything we have read is entity body */
666         c->bread += r;
667         totalbread += r;
668     }
669
670     if (c->keepalive && (c->bread >= c->length)) {
671         /* finished a keep-alive connection */
672         good++;
673         doneka++;
674         /* save out time */
675         if (good == 1) {
676             /* first time here */
677             doclen = c->bread;
678         }
679         else if (c->bread != doclen) {
680             bad++;
681             err_length++;
682         }
683         if (done < requests) {
684             struct data s;
685            c->done = ap_now();
686             s.read = c->read;
687            s.ctime = (c->connect - c->start) / 1000;
688            s.time = (c->done - c->start) / 1000;
689             stats[done++] = s;
690         }
691         c->keepalive = 0;
692         c->length = 0;
693         c->gotheader = 0;
694         c->cbx = 0;
695         c->read = c->bread = 0;
696         write_request(c);
697         c->start = c->connect; /* zero connect time with keep-alive */
698     }
699 }
700
701 /* --------------------------------------------------------- */
702
703 /* run the tests */
704
705 static void test(void)
706 {
707     ap_time_t now;
708     ap_interval_time_t timeout;
709     ap_int16_t rv;
710     int i;
711 #ifdef NOT_ASCII
712     ap_status_t status;
713     ap_size_t inbytes_left, outbytes_left;
714 #endif
715
716     if (!use_html) {
717         printf("Benchmarking %s (be patient)...", hostname);
718         fflush(stdout);
719     }
720
721     now = ap_now();
722
723     con = malloc(concurrency * sizeof(struct connection));
724     memset(con, 0, concurrency * sizeof(struct connection));
725
726     stats = malloc(requests * sizeof(struct data));
727     ap_setup_poll(&readbits, concurrency, cntxt);
728
729     /* setup request */
730     if (!posting) {
731         sprintf(request, "%s %s HTTP/1.0\r\n"
732                 "User-Agent: ApacheBench/%s\r\n"
733                 "%s" "%s" "%s"
734                 "Host: %s\r\n"
735                 "Accept: */*\r\n"
736                 "%s" "\r\n",
737                 (posting == 0) ? "GET" : "HEAD",
738                 path,
739                 VERSION,
740                 keepalive ? "Connection: Keep-Alive\r\n" : "",
741                 cookie, auth, hostname, hdrs);
742     }
743     else {
744         sprintf(request, "POST %s HTTP/1.0\r\n"
745                 "User-Agent: ApacheBench/%s\r\n"
746                 "%s" "%s" "%s"
747                 "Host: %s\r\n"
748                 "Accept: */*\r\n"
749                 "Content-length: %d\r\n"
750                 "Content-type: %s\r\n"
751                 "%s"
752                 "\r\n",
753                 path,
754                 VERSION,
755                 keepalive ? "Connection: Keep-Alive\r\n" : "",
756                 cookie, auth,
757                 hostname, postlen,
758                 (content_type[0]) ? content_type : "text/plain", hdrs);
759     }
760
761     if (verbosity >= 2)
762         printf("INFO: POST header == \n---\n%s\n---\n", request);
763
764     reqlen = strlen(request);
765
766 #ifdef NOT_ASCII
767     inbytes_left = outbytes_left = reqlen;
768     status = ap_xlate_conv_buffer(toascii, request, &inbytes_left,
769                                   request, &outbytes_left);
770     if (status || inbytes_left || outbytes_left) {
771         fprintf(stderr, "only simple translation is supported (%d/%u/%u)\n",
772                 status, inbytes_left, outbytes_left);
773         exit(1);
774     }
775 #endif /*NOT_ASCII*/
776
777     /* ok - lets start */
778     start = ap_now();
779
780     /* initialise lots of requests */
781     for (i = 0; i < concurrency; i++) {
782         con[i].socknum = i;
783         start_connect(&con[i]);
784     }
785
786     while (done < requests) {
787         ap_int32_t n;
788         ap_int32_t timed;
789
790         /* check for time limit expiry */
791         now = ap_now();
792         timed = (now - start) / AP_USEC_PER_SEC;
793         if (tlimit && timed > (tlimit * 1000)) {
794             requests = done;   /* so stats are correct */
795         }
796         /* Timeout of 30 seconds. */
797         timeout = 30 * AP_USEC_PER_SEC;
798
799         n = concurrency;
800         ap_poll(readbits, &n, timeout);
801
802         if (!n) {
803             err("\nServer timed out\n\n");
804         }
805         if (n < 1)
806             err("select");
807
808         for (i = 0; i < concurrency; i++) {
809             ap_get_revents(&rv, con[i].aprsock, readbits);
810
811             /* Note: APR_POLLHUP is set after FIN is received on some
812              * systems, so treat that like APR_POLLIN so that we try
813              * to read again.
814              */
815             if ((rv & APR_POLLERR) || (rv & APR_POLLNVAL)) {
816                bad++;
817                err_except++;
818                start_connect(&con[i]);
819                continue;
820             }
821             if ((rv & APR_POLLIN) || (rv & APR_POLLPRI) || (rv & APR_POLLHUP))
822                read_connection(&con[i]);
823             if (rv & APR_POLLOUT)
824                write_request(&con[i]);
825         }
826     }
827     if (use_html)
828         output_html_results();
829     else
830         output_results();
831 }
832
833 /* ------------------------------------------------------- */
834
835 /* display copyright information */
836 static void copyright(void)
837 {
838     if (!use_html) {
839         printf("This is ApacheBench, Version %s\n", VERSION " <$Revision: 1.8 $> apache-2.0");
840         printf("Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/\n");
841         printf("Copyright (c) 1998-2000 The Apache Software Foundation, http://www.apache.org/\n");
842         printf("\n");
843     }
844     else {
845         printf("<p>\n");
846         printf(" This is ApacheBench, Version %s <i>&lt;%s&gt;</i> apache-2.0<br>\n", VERSION, "$Revision: 1.8 $");
847         printf(" Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/<br>\n");
848         printf(" Copyright (c) 1998-2000 The Apache Software Foundation, http://www.apache.org/<br>\n");
849         printf("</p>\n<p>\n");
850     }
851 }
852
853 /* display usage information */
854 static void usage(char *progname)
855 {
856     fprintf(stderr, "Usage: %s [options] [http://]hostname[:port]/path\n", progname);
857     fprintf(stderr, "Options are:\n");
858     fprintf(stderr, "    -n requests     Number of requests to perform\n");
859     fprintf(stderr, "    -c concurrency  Number of multiple requests to make\n");
860     fprintf(stderr, "    -t timelimit    Seconds to max. wait for responses\n");
861     fprintf(stderr, "    -p postfile     File containg data to POST\n");
862     fprintf(stderr, "    -T content-type Content-type header for POSTing\n");
863     fprintf(stderr, "    -v verbosity    How much troubleshooting info to print\n");
864     fprintf(stderr, "    -w              Print out results in HTML tables\n");
865     fprintf(stderr, "    -i              Use HEAD instead of GET\n");
866     fprintf(stderr, "    -x attributes   String to insert as table attributes\n");
867     fprintf(stderr, "    -y attributes   String to insert as tr attributes\n");
868     fprintf(stderr, "    -z attributes   String to insert as td or th attributes\n");
869     fprintf(stderr, "    -C attribute    Add cookie, eg. 'Apache=1234. (repeatable)\n");
870     fprintf(stderr, "    -H attribute    Add Arbitrary header line, eg. 'Accept-Encoding: zop'\n");
871     fprintf(stderr, "                    Inserted after all normal header lines. (repeatable)\n");
872     fprintf(stderr, "    -A attribute    Add Basic WWW Authentication, the attributes\n");
873     fprintf(stderr, "                    are a colon separated username and password.\n");
874     fprintf(stderr, "    -p attribute    Add Basic Proxy Authentication, the attributes\n");
875     fprintf(stderr, "                    are a colon separated username and password.\n");
876     fprintf(stderr, "    -V              Print version number and exit\n");
877     fprintf(stderr, "    -k              Use HTTP KeepAlive feature\n");
878     fprintf(stderr, "    -h              Display usage information (this message)\n");
879     exit(EINVAL);
880 }
881
882 /* ------------------------------------------------------- */
883
884 /* split URL into parts */
885
886 static int parse_url(char *url)
887 {
888     char *cp;
889     char *h;
890     char *p/* = NULL*/;
891
892     if (strlen(url) > 7 && strncmp(url, "http://", 7) == 0)
893         url += 7;
894     h = url;
895     if ((cp = strchr(url, ':')) != NULL) {
896         *cp++ = '\0';
897         p = cp;
898         url = cp;
899     }
900     if ((cp = strchr(url, '/')) == NULL)
901         return 1;
902     strcpy(path, cp);
903     *cp = '\0';
904     strcpy(hostname, h);
905     if (p != NULL)
906         port = atoi(p);
907     return 0;
908 }
909
910 /* ------------------------------------------------------- */
911
912 /* read data to POST from file, save contents and length */
913
914 static int open_postfile(char *pfile)
915 {
916     ap_file_t *postfd = NULL;
917     ap_finfo_t finfo;
918     ap_fileperms_t mode = APR_OS_DEFAULT;
919     ap_ssize_t length;
920
921     if (ap_open(&postfd, pfile, APR_READ, mode, cntxt) != APR_SUCCESS) {
922         printf("Invalid postfile name (%s)\n", pfile);
923         return errno;
924     }
925
926     ap_getfileinfo(&finfo, postfd);
927     postlen = finfo.size;
928     postdata = (char *)malloc(postlen);
929     if (!postdata) {
930         printf("Can\'t alloc postfile buffer\n");
931         return ENOMEM;
932     }
933     length = postlen;
934     if (ap_read(postfd, postdata, &length) != APR_SUCCESS &&
935         length != postlen) {
936         printf("error reading postfilen");
937         return EIO;
938     }
939     return 0;
940 }
941
942 /* ------------------------------------------------------- */
943
944 /* sort out command-line args and call test */
945 int main(int argc, char **argv)
946 {
947     int c, r, l;
948     char tmp[1024];
949 #ifdef NOT_ASCII
950     ap_status_t status;
951 #endif
952
953     /* table defaults  */
954     tablestring = "";
955     trstring = "";
956     tdstring = "bgcolor=white";
957     cookie[0] = '\0';
958     auth[0] = '\0';
959     hdrs[0] = '\0';
960
961     ap_initialize();
962     atexit(ap_terminate);
963     ap_create_pool(&cntxt, NULL);
964
965 #ifdef NOT_ASCII
966     status = ap_xlate_open(&toascii, "ISO8859-1", APR_DEFAULT_CHARSET, cntxt);
967     if (status) {
968         fprintf(stderr, "ap_xlate_open(to ASCII)->%d\n", status);
969         exit(1);
970     }
971     status = ap_xlate_open(&fromascii, APR_DEFAULT_CHARSET, "ISO8859-1", cntxt)
972
973     if (status) {
974         fprintf(stderr, "ap_xlate_open(from ASCII)->%d\n", status);
975         exit(1);
976     }
977 #endif
978
979     ap_optind = 1;
980     while (ap_getopt(argc, argv, "n:c:t:T:p:v:kVhwx:y:z:", &c, cntxt) == APR_SUCCESS) {
981         switch (c) {
982         case 'n':
983             requests = atoi(ap_optarg);
984             if (!requests) {
985                err("Invalid number of requests\n");
986             }
987             break;
988         case 'k':
989             keepalive = 1;
990             break;
991         case 'c':
992             concurrency = atoi(ap_optarg);
993             break;
994         case 'i':
995             if (posting == 1)
996                 err("Cannot mix POST and HEAD");
997             posting = -1;
998         case 'p':
999             if (posting != 0)
1000                 err("Cannot mix POST and HEAD");
1001
1002             if (0 == (r = open_postfile(ap_optarg))) {
1003                posting = 1;
1004             }
1005             else if (postdata) {
1006                exit(r);
1007             }
1008             break;
1009         case 'v':
1010             verbosity = atoi(ap_optarg);
1011             break;
1012         case 't':
1013             tlimit = atoi(ap_optarg);
1014             requests = MAX_REQUESTS;  /* need to size data array on something */
1015             break;
1016         case 'T':
1017             strcpy(content_type, ap_optarg);
1018             break;
1019         case 'C':
1020             strncat(cookie, "Cookie: ", sizeof(cookie));
1021             strncat(cookie, ap_optarg, sizeof(cookie));
1022             strncat(cookie, "\r\n", sizeof(cookie));
1023             break;
1024         case 'A':
1025             /* assume username passwd already to be in colon separated form. 
1026              * Ready to be uu-encoded.
1027              */
1028             while(isspace(*ap_optarg))
1029                 ap_optarg++;
1030             l=ap_base64encode(tmp, ap_optarg, strlen(ap_optarg));
1031             tmp[l]='\0';
1032  
1033             strncat(auth, "Authorization: basic ", sizeof(auth));
1034             strncat(auth, tmp, sizeof(auth));
1035             strncat(auth, "\r\n", sizeof(auth));
1036             break;
1037         case 'P':
1038             /*
1039              * assume username passwd already to be in colon separated form.
1040              */
1041             while(isspace(*ap_optarg))
1042                 ap_optarg++;
1043             l=ap_base64encode(tmp, ap_optarg, strlen(ap_optarg));
1044             tmp[l]='\0';
1045  
1046             strncat(auth, "Proxy-Authorization: basic ", sizeof(auth));
1047             strncat(auth, tmp, sizeof(auth));
1048             strncat(auth, "\r\n", sizeof(auth));
1049             break;
1050         case 'H':
1051             strncat(hdrs, ap_optarg, sizeof(hdrs));
1052             strncat(hdrs, "\r\n", sizeof(hdrs));
1053             strcpy(content_type, ap_optarg);
1054         }
1055     }
1056
1057     if (ap_optind != argc - 1) {
1058         fprintf(stderr, "%s: wrong number of arguments\n", argv[0]);
1059         usage(argv[0]);
1060     }
1061
1062     if (parse_url(argv[ap_optind++])) {
1063         fprintf(stderr, "%s: invalid URL\n", argv[0]);
1064         usage(argv[0]);
1065     }
1066
1067     copyright();
1068     test();
1069
1070     exit(0);
1071 }