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