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