1 /* ====================================================================
2  * Copyright (c) 1998-1999 The Apache Group.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in
13  *    the documentation and/or other materials provided with the
14  *    distribution.
15  *
16  * 3. All advertising materials mentioning features or use of this
17  *    software must display the following acknowledgment:
18  *    "This product includes software developed by the Apache Group
19  *    for use in the Apache HTTP server project (http://www.apache.org/)."
20  *
21  * 4. The names "Apache Server" and "Apache Group" must not be used to
22  *    endorse or promote products derived from this software without
23  *    prior written permission. For written permission, please contact
24  *    apache@apache.org.
25  *
26  * 5. Products derived from this software may not be called "Apache"
27  *    nor may "Apache" appear in their names without prior written
28  *    permission of the Apache Group.
29  *
30  * 6. Redistributions of any form whatsoever must retain the following
31  *    acknowledgment:
32  *    "This product includes software developed by the Apache Group
33  *    for use in the Apache HTTP server project (http://www.apache.org/)."
34  *
35  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
36  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
37  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
38  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
41  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
42  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
43  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
44  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
45  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
46  * OF THE POSSIBILITY OF SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Apache Group and was originally based
51  * on public domain software written at the National Center for
52  * Supercomputing Applications, University of Illinois, Urbana-Champaign.
53  * For more information on the Apache Group and the Apache HTTP server
54  * project, please see <http://www.apache.org/>.
55  *
56  */
57 
58 /*
59    ** This program is based on ZeusBench V1.0 written by Adam Twiss
60    ** which is Copyright (c) 1996 by Zeus Technology Ltd. http://www.zeustech.net/
61    **
62    ** This software is provided "as is" and any express or implied waranties,
63    ** including but not limited to, the implied warranties of merchantability and
64    ** fitness for a particular purpose are disclaimed.  In no event shall
65    ** Zeus Technology Ltd. be liable for any direct, indirect, incidental, special,
66    ** exemplary, or consequential damaged (including, but not limited to,
67    ** procurement of substitute good or services; loss of use, data, or profits;
68    ** or business interruption) however caused and on theory of liability.  Whether
69    ** in contract, strict liability or tort (including negligence or otherwise)
70    ** arising in any way out of the use of this software, even if advised of the
71    ** possibility of such damage.
72    **
73  */
74 
75 /*
76    ** HISTORY:
77    **    - Originally written by Adam Twiss <adam@zeus.co.uk>, March 1996
78    **      with input from Mike Belshe <mbelshe@netscape.com> and
79    **      Michael Campanella <campanella@stevms.enet.dec.com>
80    **    - Enhanced by Dean Gaudet <dgaudet@apache.org>, November 1997
81    **    - Cleaned up by Ralf S. Engelschall <rse@apache.org>, March 1998
82    **    - POST and verbosity by Kurt Sussman <kls@merlot.com>, August 1998
83    **    - HTML table output added by David N. Welton <davidw@prosa.it>, January 1999
84    **    - Added Cookie, Arbitrary header and auth support. <dirkx@webweaving.org>, April 1999
85    **
86    **    - CODE FORK: added Perl XS interface and is now released on CPAN as
87    **      HTTPD::Bench::ApacheBench, September 2000
88    **    - merged code from Apache 1.3.22 ab, October-November 2001
89    **    - various refactors, rewrites, and improvements; see Changes
90    **
91  */
92 
93 /*
94  * BUGS:
95  *
96  * - uses strcpy/etc.
97  * - has various other poor buffer attacks related to the lazy parsing of
98  *   response headers from the server
99  * - doesn't implement much of HTTP/1.x, only accepts certain forms of
100  *   responses
101  * - (performance problem) heavy use of strstr shows up top in profile
102  *   only an issue for loopback usage
103  */
104 
105 /*  ------------------ DEBUGGING --------------------------------------- */
106 
107 // uncomment to turn on debugging messages
108 //#define AB_DEBUG 1
109 
110 /*  -------------------------------------------------------------------- */
111 
112 #ifdef AB_DEBUG
113 #define AB_DEBUG_XS 1
114 #else
115 #define AB_DEBUG_XS 0
116 #endif
117 
118 /* XS library */
119 #include "EXTERN.h"
120 #include "perl.h"
121 #include "XSUB.h"
122 
123 /* ApacheBench source code */
124 #include "apachebench/util.c"
125 #include "apachebench/xs_util.c"
126 #include "apachebench/http_util.c"
127 #include "apachebench/socket_io.c"
128 #include "apachebench/execute.c"
129 #include "apachebench/regression_data.c"
130 
131 /* ------------------- MACROS -------------------------- */
132 #define ap_min(a,b) ((a)<(b))?(a):(b)
133 #define ap_max(a,b) ((a)>(b))?(a):(b)
134 
135 
136 MODULE = HTTPD::Bench::ApacheBench	PACKAGE = HTTPD::Bench::ApacheBench
137 PROTOTYPES: ENABLE
138 
139 HV *
140 ab(input_hash)
141     SV * input_hash;
142 
143     PREINIT:
144     char *pt,**url_keys;
145     int i,j,k,arrlen,arrlen2;
146     int def_buffersize; /* default buffersize for all runs */
147     int def_repeat; /* default number of repeats if unspecified in runs */
148     int def_memory; /* default memory setting if unspecified in runs */
149     bool def_keepalive = 0; /* default keepalive setting */
150     struct global *registry = calloc(1, sizeof(struct global));
151     int total_started = 0, total_good = 0, total_failed = 0;
152 
153     CODE:
154     SV * runs;
155     SV * urls = 0;
156     SV * post_data = 0;
157     SV * head_requests = 0;
158     SV * cookies = 0;
159     SV * ctypes = 0;
160     SV * req_headers = 0;
161     SV * keepalive = 0;
162     SV * url_tlimits = 0;
163     AV * run_group, *tmpav, *tmpav2;
164     SV * tmpsv, *tmpsv2 = 0, *tmpsv3;
165     HV * tmphv;
166     STRLEN len;
167 
168     if (AB_DEBUG_XS) printf("AB_DEBUG: start of ab()\n");
169 
170     registry->concurrency = 1;
171     registry->requests = 0;
172     registry->tlimit = 0;
173     registry->min_tlimit.tv_sec = 30;
174     registry->min_tlimit.tv_usec = 0;
175     registry->tail = 0;
176     registry->done = 0;
177     registry->need_to_be_done = 0;
178     strcpy(registry->version, VERSION);
179     strcpy(registry->warn_and_error, "\nWarning messages from ab():");
180     registry->total_bytes_received = 0;
181     registry->number_of_urls = 0;
182 
183 
184     /*Get necessary initial information and initialize*/
185     tmphv = (HV *)SvRV(input_hash);
186 
187     tmpsv = *(hv_fetch(tmphv, "concurrency", 11, 0));
188     registry->concurrency = SvIV(tmpsv);
189 
190     tmpsv = *(hv_fetch(tmphv, "timelimit", 9, 0));
191     if (SvOK(tmpsv)) {
192         registry->tlimit = SvNV(tmpsv);
193         registry->min_tlimit =
194             double2timeval(ap_min(timeval2double(registry->min_tlimit),
195                                   registry->tlimit));
196     }
197 
198     tmpsv = *(hv_fetch(tmphv, "buffersize", 10, 0));
199     def_buffersize = SvIV(tmpsv);
200 
201     tmpsv = *(hv_fetch(tmphv, "repeat", 6, 0));
202     def_repeat = SvIV(tmpsv);
203 
204     tmpsv = *(hv_fetch(tmphv, "memory", 6, 0));
205     def_memory = SvIV(tmpsv);
206 
207     if (AB_DEBUG_XS) printf("AB_DEBUG: ab() init - stage 1\n");
208 
209     tmpsv = *(hv_fetch(tmphv, "keepalive", 9, 0));
210     if (SvTRUE(tmpsv))
211         def_keepalive = 1;
212 
213     if (AB_DEBUG_XS) printf("AB_DEBUG: ab() init - stage 2\n");
214 
215     tmpsv = *(hv_fetch(tmphv, "priority", 8, 0));
216     pt = SvPV(tmpsv, len);
217     if (strcmp(pt, "run_priority") == 0)
218         registry->priority = RUN_PRIORITY;
219     else {
220         registry->priority = EQUAL_OPPORTUNITY;
221         if (strcmp(pt, "equal_opportunity") != 0)
222             myerr(registry->warn_and_error, "Unknown priority value (the only possible priorities are run_priority and equal_opportunity), using default: equal_opportunity");
223     }
224 
225     if (AB_DEBUG_XS) printf("AB_DEBUG: ab() init - stage 3\n");
226 
227     runs = *(hv_fetch(tmphv, "runs", 4, 0));
228     run_group = (AV *)SvRV(runs);
229     registry->number_of_runs = av_len(run_group) + 1;
230 
231     registry->order = malloc(registry->number_of_runs * sizeof(int));
232     registry->repeats = malloc(registry->number_of_runs * sizeof(int));
233     registry->position = malloc((registry->number_of_runs+1) * sizeof(int));
234     registry->memory = malloc(registry->number_of_runs * sizeof(int));
235     registry->use_auto_cookies = malloc(registry->number_of_runs * sizeof(bool));
236 
237     if (AB_DEBUG_XS) printf("AB_DEBUG: done with ab() initialization\n");
238 
239     for (i = 0,j = 0; i < registry->number_of_runs; i++) {
240         if (AB_DEBUG_XS) printf("AB_DEBUG: starting run %d setup\n", i);
241 
242         tmpsv = *(av_fetch(run_group, i, 0));
243 
244         if (SvROK(tmpsv))
245             tmphv = (HV *)SvRV(tmpsv);
246 
247         registry->memory[i] = def_memory;
248         if (hv_exists(tmphv, "memory", 6)) {
249             tmpsv = *(hv_fetch(tmphv, "memory", 6, 0));
250             registry->memory[i] = SvIV(tmpsv);
251         }
252 
253         registry->repeats[i] = def_repeat;
254         if (hv_exists(tmphv, "repeat", 6)) {
255             /* Number of requests to make */
256             tmpsv = *(hv_fetch(tmphv, "repeat", 6, 0));
257             registry->repeats[i] = SvIV(tmpsv);
258         }
259 
260         registry->use_auto_cookies[i] = 1;
261         if (hv_exists(tmphv, "use_auto_cookies", 16)) {
262             tmpsv = *(hv_fetch(tmphv, "use_auto_cookies", 16, 0));
263             registry->use_auto_cookies[i] = SvTRUE(tmpsv) ? 1 : 0;
264         }
265 
266         registry->requests = ap_max(registry->requests, registry->repeats[i]);
267 
268         urls = *(hv_fetch(tmphv, "urls", 4, 0));
269         tmpav = (AV *) SvRV(urls);
270         registry->position[i] = registry->number_of_urls;
271         registry->number_of_urls += av_len(tmpav) + 1;
272 
273         if (AB_DEBUG_XS) printf("AB_DEBUG: run %d: position[%d] == %d\n", i, i, registry->position[i]);
274 
275         if (hv_exists(tmphv, "order", 5)) {
276             tmpsv = *(hv_fetch(tmphv, "order", 5, 0));
277             pt = SvPV(tmpsv, len);
278             if (strcmp(pt, "depth_first") == 0) {
279                 registry->order[i] = DEPTH_FIRST;
280                 j += 1;
281             } else if (strcmp(pt, "breadth_first") == 0) {
282                 registry->order[i] = BREADTH_FIRST;
283                 j += registry->repeats[i];
284             } else {
285                 myerr(registry->warn_and_error, "invalid order: order can only be depth_first or breadth_first");
286                 registry->order[i] = BREADTH_FIRST;
287                 j += registry->repeats[i];
288             }
289         } else {
290             registry->order[i] = BREADTH_FIRST;
291             j += registry->repeats[i];
292         }
293     }
294     if (registry->number_of_urls <= 0) {
295         myerr(registry->warn_and_error, "No urls.");
296         return;
297     }
298     registry->position[registry->number_of_runs] = registry->number_of_urls;
299     registry->concurrency = ap_min(registry->concurrency, j);
300 
301     if (AB_DEBUG_XS) printf("AB_DEBUG: set all run info, ready to call initialize()\n");
302 
303     initialize(registry);
304 
305     url_keys = malloc(registry->number_of_urls * sizeof(char *));
306 
307     for (k = 0; k < registry->number_of_runs; k++) {
308         if (AB_DEBUG_XS) printf("AB_DEBUG: starting run %d setup2 - postdata + cookie\n", k);
309 
310         registry->buffersize[k] = def_buffersize;
311         tmpsv = *(av_fetch(run_group, k, 0));
312         if (SvROK(tmpsv)) {
313             tmphv = (HV *)SvRV(tmpsv);
314             if (hv_exists(tmphv, "buffersize", 10)) {
315                 tmpsv = *(hv_fetch(tmphv, "buffersize", 10, 0));
316                 registry->buffersize[k] = SvIV(tmpsv);
317             }
318         }
319 
320         if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 1\n", k);
321 
322         /* error checking: make sure all of the run specific hashkeys exist */
323         if (hv_exists(tmphv, "urls", 4))
324             urls = *(hv_fetch(tmphv, "urls", 4, 0));
325         if (hv_exists(tmphv, "postdata", 8))
326             post_data = *(hv_fetch(tmphv, "postdata", 8, 0));
327         if (hv_exists(tmphv, "head_requests", 13))
328             head_requests = *(hv_fetch(tmphv, "head_requests", 13, 0));
329         if (hv_exists(tmphv, "cookies", 7))
330             cookies = *(hv_fetch(tmphv, "cookies", 7, 0));
331         if (hv_exists(tmphv, "content_types", 13))
332             ctypes = *(hv_fetch(tmphv, "content_types", 13, 0));
333         if (hv_exists(tmphv, "request_headers", 15))
334             req_headers = *(hv_fetch(tmphv, "request_headers", 15, 0));
335         if (hv_exists(tmphv, "keepalive", 9))
336             keepalive = *(hv_fetch(tmphv, "keepalive", 9, 0));
337         if (hv_exists(tmphv, "timelimits", 10))
338             url_tlimits = *(hv_fetch(tmphv, "timelimits", 10, 0));
339 
340         /* configure urls */
341         for (i = registry->position[k]; i < registry->position[k+1]; i++) {
342             tmpav =(AV *) SvRV(urls);
343             tmpsv = *(av_fetch(tmpav, i - registry->position[k], 0));
344             if (SvPOK(tmpsv)) {
345                 pt = SvPV(tmpsv, len);
346                 url_keys[i] = pt;
347 
348                 if (parse_url(registry, pt, i)) {
349                     char *warn = malloc(CBUFFSIZE * sizeof(char));
350                     sprintf(warn, "Invalid url: %s, the information for this url may be wrong", pt);
351                     myerr(registry->warn_and_error, warn);
352                     free(warn);
353                 }
354             } else {
355                 myerr(registry->warn_and_error, "Undefined url in urls list");
356             }
357         }
358 
359 
360         if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 2\n", k);
361 
362         tmpav = (AV *) SvRV(post_data);
363         tmpav2 = (AV *) SvRV(head_requests);
364 
365         /* find smaller of post_data array length and urls array length */
366         arrlen = av_len(tmpav);
367         i = ap_min(registry->position[k+1],
368                    registry->position[k] + arrlen + 1);
369 
370         if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 2.1\n", k);
371 
372         /* find larger of head_requests and post_data (to get the most data) */
373         arrlen2 = av_len(tmpav2);
374         if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 2.1.1\n", k);
375         i = ap_max(i, registry->position[k] + arrlen2 + 1);
376 
377         /* configure post_data or head_requests */
378         for (j = registry->position[k]; j < i; j++) {
379             if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 2.2, j=%d\n", k, j);
380             if (j - registry->position[k] <= arrlen)
381                 tmpsv = *(av_fetch(tmpav, j - registry->position[k], 0));
382             if (j - registry->position[k] <= arrlen2)
383                 tmpsv2 = *(av_fetch(tmpav2, j - registry->position[k], 0));
384             if (j - registry->position[k] <= arrlen && (SvROK(tmpsv) || SvPOK(tmpsv))) {
385                 /* this url is a POST request */
386                 if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 2.3, j=%d\n", k, j);
387                 if (SvROK(tmpsv)) {
388                     if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 2.4a, j=%d\n", k, j);
389                     tmpsv3 = SvRV(tmpsv);
390                     if (SvTYPE(tmpsv3) == SVt_PVCV) {
391                         if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 2.4a.i, j=%d\n", k, j);
392                         registry->postsubs[j] = tmpsv3;
393                         registry->posting[j] = 2;
394                     }
395                 } else if (SvPOK(tmpsv)) {
396                     if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 2.4b, j=%d\n", k, j);
397                     pt = SvPV(tmpsv, len);
398                     registry->postdata[j] = pt;
399                     registry->postlen[j] = len;
400                     registry->posting[j] = 1;
401                 }
402             } else if (j - registry->position[k] <= arrlen2 && SvTRUE(tmpsv2))
403                 /* this url is a HEAD request */
404                 registry->posting[j] = -1;
405             else
406                 registry->posting[j] = 0;
407             if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 2.5, j=%d\n", k, j);
408         }
409 
410         if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 2.6\n", k);
411 
412         /*If the number of postdata strings is less than
413           that of urls, then assign empty strings to force GET requests*/
414         for (j = i; j < registry->position[k+1]; j++)
415             registry->posting[j] = 0;
416 
417 
418         if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 3\n", k);
419 
420         /* configure cookies */
421         registry->cookie[k] = NULL;
422         tmpav = (AV *) SvRV(cookies);
423         if (av_len(tmpav) >= 0) {
424             tmpsv = *(av_fetch(tmpav, 0, 0));
425             if (SvPOK(tmpsv)) {
426                 pt = SvPV(tmpsv, len);
427                 if (len != 0) {
428                     registry->cookie[k] = malloc((len+1) * sizeof(char));
429                     strcpy(registry->cookie[k], pt);
430                     if (AB_DEBUG_XS) printf("AB_DEBUG: cookie[%d] == '%s'\n", k, registry->cookie[k]);
431                 }
432             }
433         }
434 
435 
436         if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 4\n", k);
437 
438         /* find smaller of req_headers array length and urls array length */
439         tmpav = (AV *) SvRV(req_headers);
440         if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 4.1\n", k);
441         i = ap_min(registry->position[k+1],
442                    registry->position[k] + av_len(tmpav) + 1);
443 
444         if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 4.2\n", k);
445         /* configure arbitrary request headers */
446         for (j = registry->position[k]; j < i; j++) {
447             if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 4.3, j=%d\n", k, j);
448             tmpsv = *(av_fetch(tmpav, j - registry->position[k], 0));
449             if (SvPOK(tmpsv)) {
450                 if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 4.4, j=%d\n", k, j);
451                 pt = SvPV(tmpsv, len);
452                 if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 4.5, j=%d\n", k, j);
453                 registry->req_headers[j] = pt;
454             } else
455                 registry->req_headers[j] = 0;
456             if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 4.6, j=%d\n", k, j);
457         }
458 
459         /*If the number of req_headers strings is less than
460           that of urls, then assign NULL (undef) */
461         for (j = i; j < registry->position[k+1]; j++)
462             registry->req_headers[j] = 0;
463 
464 
465         if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 5\n", k);
466 
467         /* find smaller of ctypes array length and urls array length */
468         tmpav = (AV *) SvRV(ctypes);
469         i = ap_min(registry->position[k+1],
470                    registry->position[k] + av_len(tmpav) + 1);
471 
472         if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 5.1\n", k);
473 
474         /* configure ctypes */
475         for (j = registry->position[k]; j < i; j++) {
476             if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 5.2, j=%d\n", k, j);
477             tmpsv = *(av_fetch(tmpav, j - registry->position[k], 0));
478             if (SvPOK(tmpsv)) {
479                 if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 5.3, j=%d\n", k, j);
480                 pt = SvPV(tmpsv, len);
481                 if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 5.4, j=%d\n", k, j);
482                 registry->ctypes[j] = pt;
483             } else
484                 registry->ctypes[j] = 0;
485             if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 5.5, j=%d\n", k, j);
486         }
487 
488         /*If the number of ctypes strings is less than
489           that of urls, then assign NULL (undef) */
490         for (j = i; j < registry->position[k+1]; j++)
491             registry->ctypes[j] = 0;
492 
493 
494         if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 6\n", k);
495 
496         /* find smaller of keepalive array length and urls array length */
497         tmpav = (AV *) SvRV(keepalive);
498         i = ap_min(registry->position[k+1],
499                    registry->position[k] + av_len(tmpav) + 1);
500 
501         if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 6.1\n", k);
502 
503         /* configure keepalive */
504         for (j = registry->position[k]; j < i; j++) {
505             if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 6.2, j=%d\n", k, j);
506             tmpsv = *(av_fetch(tmpav, j - registry->position[k], 0));
507             if (SvOK(tmpsv))
508                 if (SvTRUE(tmpsv))
509                     registry->keepalive[j] = 1;
510                 else
511                     registry->keepalive[j] = 0;
512             else
513                 registry->keepalive[j] = def_keepalive;
514             if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 6.3, j=%d\n", k, j);
515         }
516 
517         /*If the number of keepalive strings is less than
518           that of urls, then assign object's default keepalive value */
519         for (j = i; j < registry->position[k+1]; j++)
520             registry->keepalive[j] = def_keepalive;
521 
522 
523         if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 7\n", k);
524 
525         /* find smaller of url_tlimits array length and urls array length */
526         tmpav = (AV *) SvRV(url_tlimits);
527         i = ap_min(registry->position[k+1],
528                    registry->position[k] + av_len(tmpav) + 1);
529 
530         if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 7.1\n", k);
531 
532         /* configure url_tlimits */
533         for (j = registry->position[k]; j < i; j++) {
534             if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 7.2, j=%d\n", k, j);
535             tmpsv = *(av_fetch(tmpav, j - registry->position[k], 0));
536             if (SvOK(tmpsv)) {
537                 if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 7.3, j=%d\n", k, j);
538                 registry->url_tlimit[j] = SvNV(tmpsv);
539                 registry->min_tlimit =
540                     double2timeval(ap_min(timeval2double(registry->min_tlimit),
541                                           registry->url_tlimit[j]));
542                 if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 7.3.1, j=%d, url_tlimit=%.3f sec, min tlimit so far = %.3f sec\n", k, j, registry->url_tlimit[j], timeval2double(registry->min_tlimit));
543             } else
544                 registry->url_tlimit[j] = 0;
545             if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 7.4, j=%d\n", k, j);
546         }
547 
548         /*If the number of url_tlimits is less than
549           that of urls, then assign 0 for no time limit */
550         for (j = i; j < registry->position[k+1]; j++)
551             registry->url_tlimit[j] = 0;
552 
553     }
554 
555     if (AB_DEBUG_XS) printf("AB_DEBUG: ready to test()\n");
556 
557     test(registry);
558 
559     if (AB_DEBUG_XS) printf("AB_DEBUG: done with test()\n");
560 
561     RETVAL = newHV();/* ready to get information stored in global variables */
562 
563     for (k = 0; k < registry->number_of_runs; k++) {
564         if (registry->memory[k] >= 1) {
565             AV *started = newAV();/* number of started requests for each url */
566             AV *good = newAV();   /* number of good responses for each url */
567             AV *failed = newAV(); /* number of bad responses for each url */
568             tmpav = newAV();      /* array to keep the thread information */
569 
570             if (AB_DEBUG_XS) printf("AB_DEBUG: getting regression info for run %d\n", k);
571 
572             for (i = 0; i < registry->repeats[k]; i++) {
573                 AV *th_t = newAV();  /* times for processing and connecting */
574                 AV *th_r = newAV();  /* times for http request */
575                 AV *th_c = newAV();  /* connecting times */
576                 AV *page_contents = newAV(); /* pages read from servers */
577                 AV *request_headers = newAV(); /* HTTP requests sent to servers */
578                 AV *request_body = newAV(); /* HTTP requests sent to servers */
579                 AV *headers = newAV();
580                 AV *bytes_posted = newAV();
581                 AV *doc_length = newAV();
582                 AV *bytes_read = newAV();
583 
584                 /* variables for calculating min/max/avg times*/
585                 int totalcon = 0, totalreq = 0, total = 0;
586                 int mincon = 999999, minreq = 999999, mintot = 999999;
587                 int maxcon = 0, maxreq = 0, maxtot = 0;
588 
589                 /* byte counters */
590                 int total_bytes_posted = 0, total_bytes_read = 0;
591 
592                 for (j = registry->position[k]; j < registry->position[k+1]; j++) {
593                     struct data s = registry->stats[j][i];
594                     mincon = ap_min(mincon, s.ctime);
595                     minreq = ap_min(minreq, s.rtime);
596                     mintot = ap_min(mintot, s.time);
597                     maxcon = ap_max(maxcon, s.ctime);
598                     maxreq = ap_max(maxreq, s.rtime);
599                     maxtot = ap_max(maxtot, s.time);
600                     totalcon += s.ctime;
601                     totalreq += s.rtime;
602                     total += s.time;
603                     if (AB_DEBUG_XS) printf("AB_DEBUG: getting regression - stage 1 - i,j=%d,%d: mintot=%d maxtot=%d total=%d\n", i, j, mintot, maxtot, total);
604 
605                     total_bytes_posted += registry->totalposted[j];
606                     total_bytes_read += registry->stats[j][i].read;
607 
608                     av_push(th_c, newSVnv(registry->stats[j][i].ctime));
609                     av_push(th_r, newSVnv(registry->stats[j][i].rtime));
610                     av_push(th_t, newSVnv(registry->stats[j][i].time));
611                     if (AB_DEBUG_XS) printf("AB_DEBUG: getting regression - stage 1.1 - i,j=%d,%d\n", i, j);
612                     if (i == 0) {
613                         av_push(started, newSViv(registry->started[j]));
614                         av_push(good, newSViv(registry->good[j]));
615                         av_push(failed, newSViv(registry->failed[j]));
616                         total_started += registry->started[j];
617                         total_good += registry->good[j];
618                         total_failed += registry->failed[j];
619                     }
620 
621                     if (registry->memory[k] >= 2) {
622                         if (AB_DEBUG_XS) printf("AB_DEBUG: getting regression - stage 1.1.0 - i,j=%d,%d  header='%s'\n", i, j, registry->stats[j][i].response_headers);
623                         if (registry->stats[j][i].response_headers &&
624                             strlen(registry->stats[j][i].response_headers) > 0)
625                             av_push(headers, newSVpv(registry->stats[j][i].response_headers, 0));
626                         else
627                             av_push(headers, &PL_sv_undef);
628                         if (registry->stats[j][i].request_headers &&
629                             strlen(registry->stats[j][i].request_headers) > 0)
630                             av_push(request_headers, newSVpv(registry->stats[j][i].request_headers, 0));
631                         else
632                             av_push(request_headers, &PL_sv_undef);
633                         if (AB_DEBUG_XS) printf("AB_DEBUG: getting regression - stage 1.1.1 - i,j=%d,%d\n", i, j);
634                         av_push(doc_length, newSVnv(registry->stats[j][i].bread));
635                         if (AB_DEBUG_XS) printf("AB_DEBUG: getting regression - stage 1.1.2 - i,j=%d,%d\n", i, j);
636                         av_push(bytes_read, newSVnv(registry->stats[j][i].read));
637                         if (AB_DEBUG_XS) printf("AB_DEBUG: getting regression - stage 1.1.3 - i,j=%d,%d\n", i, j);
638                         /*if (registry->posting[j] > 0)*/
639                         av_push(bytes_posted, newSVnv(registry->totalposted[j]));
640                     }
641                     if (AB_DEBUG_XS) printf("AB_DEBUG: getting regression - stage 1.2 - i,j=%d,%d\n", i, j);
642                     if (registry->memory[k] >= 3) {
643                         if (registry->stats[j][i].response &&
644                             strlen(registry->stats[j][i].response) > 0)
645                             av_push(page_contents, newSVpv(registry->stats[j][i].response, 0));
646                         else
647                             av_push(page_contents, &PL_sv_undef);
648                         if (AB_DEBUG_XS) printf("AB_DEBUG: getting regression - stage 1.2.5 - i,j=%d,%d\n", i, j);
649                         if (registry->stats[j][i].request &&
650                             strlen(registry->stats[j][i].request) > 0)
651                             av_push(request_body, newSVpv(registry->stats[j][i].request, 0));
652                         else
653                             av_push(request_body, &PL_sv_undef);
654                     }
655                     if (AB_DEBUG_XS) printf("AB_DEBUG: getting regression - stage 1.3 - i,j=%d,%d\n", i, j);
656                 }
657 
658                 if (AB_DEBUG_XS) printf("AB_DEBUG: getting regression - stage 3 - i=%d\n", i);
659                 tmphv = newHV();
660                 hv_store(tmphv, "max_connect_time", 16, newSVnv(maxcon), 0);
661                 hv_store(tmphv, "max_request_time", 16, newSVnv(maxreq), 0);
662                 hv_store(tmphv, "max_response_time", 17, newSVnv(maxtot), 0);
663                 hv_store(tmphv, "min_connect_time", 16, newSVnv(mincon), 0);
664                 hv_store(tmphv, "min_request_time", 16, newSVnv(minreq), 0);
665                 hv_store(tmphv, "min_response_time", 17, newSVnv(mintot), 0);
666                 if (AB_DEBUG_XS) printf("AB_DEBUG: getting regression - stage 4 - i=%d\n", i);
667                 hv_store(tmphv, "total_connect_time", 18, newSVnv(totalcon), 0);
668                 hv_store(tmphv, "total_request_time", 18, newSVnv(totalreq), 0);
669                 hv_store(tmphv, "total_response_time", 19, newSVnv(total), 0);
670                 hv_store(tmphv, "average_connect_time", 20, newSVnv((float)totalcon/(j-registry->position[k])), 0);
671                 hv_store(tmphv, "average_request_time", 20, newSVnv((float)totalreq/(j-registry->position[k])), 0);
672                 hv_store(tmphv, "average_response_time", 21, newSVnv((double)total/(j-registry->position[k])), 0);
673                 hv_store(tmphv, "total_bytes_read", 16, newSVnv(total_bytes_read), 0);
674                 hv_store(tmphv, "total_bytes_posted", 18, newSVnv(total_bytes_posted), 0);
675                 if (AB_DEBUG_XS) printf("AB_DEBUG: getting regression - stage 5 - i=%d\n", i);
676 
677                 /* started/good/failed are url-specific, store only 1st time */
678                 if (i == 0) {
679                     hv_store(tmphv, "started", 7, newRV_inc((SV *)started), 0);
680                     hv_store(tmphv, "good", 4, newRV_inc((SV *)good), 0);
681                     hv_store(tmphv, "failed", 6, newRV_inc((SV *)failed), 0);
682                 }
683                 hv_store(tmphv, "connect_time", 12, newRV_inc((SV *)th_c), 0);
684                 hv_store(tmphv, "request_time", 12, newRV_inc((SV *)th_r), 0);
685                 hv_store(tmphv, "response_time", 13, newRV_inc((SV *)th_t), 0);
686                 if (registry->memory[k] >= 2) {
687                     hv_store(tmphv, "headers", 7, newRV_inc((SV *)headers), 0);
688                     hv_store(tmphv, "doc_length", 10, newRV_inc((SV *)doc_length), 0);
689                     hv_store(tmphv, "bytes_read", 10, newRV_inc((SV *)bytes_read), 0);
690                     hv_store(tmphv, "bytes_posted", 12, newRV_inc((SV *)bytes_posted), 0);
691                     hv_store(tmphv, "request_headers", 15, newRV_inc((SV *)request_headers), 0);
692                 }
693                 if (registry->memory[k] >= 3) {
694                     hv_store(tmphv, "page_content", 12, newRV_inc((SV *)page_contents), 0);
695                     hv_store(tmphv, "request_body", 12, newRV_inc((SV *)request_body), 0);
696                 }
697                 if (AB_DEBUG_XS) printf("AB_DEBUG: getting regression - stage 6 - i=%d\n", i);
698                 av_push(tmpav, newRV_inc((SV*)tmphv));
699             }
700             {
701                 char key[10];
702                 sprintf(key, "run%d", k);
703                 hv_store(RETVAL, key, strlen(key), newRV_inc((SV *)tmpav), 0);
704             }
705         }
706     }
707 
708     hv_store(RETVAL, "warnings", 8, newSVpv(registry->warn_and_error, 0), 0);
709     hv_store(RETVAL, "total_time", 10,
710              newSVnv(timedif(registry->endtime, registry->starttime)), 0);
711     hv_store(RETVAL, "bytes_received", 14,
712              newSVnv(registry->total_bytes_received), 0);
713     hv_store(RETVAL, "started", 7, newSViv(total_started), 0);
714     hv_store(RETVAL, "good", 4, newSViv(total_good), 0);
715     hv_store(RETVAL, "failed", 6, newSViv(total_failed), 0);
716 
717     OUTPUT:
718     RETVAL
719