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