1package HTTPD::Bench::ApacheBench;
2
3use strict;
4use vars qw($VERSION @ISA);
5
6use base qw(DynaLoader HTTPD::Bench::ApacheBench::Regression);
7
8use HTTPD::Bench::ApacheBench::Run;
9use Scalar::Util qw/blessed/;
10
11$HTTPD::Bench::ApacheBench::VERSION = '0.73';
12
13bootstrap HTTPD::Bench::ApacheBench $VERSION;
14
15##################################################
16## the constructor                              ##
17##################################################
18sub new {
19    my ($this, $self) = @_;
20    my $class = ref($this) || $this;
21    if (ref($self) ne 'HASH') { $self = {} }
22    bless $self, $class;
23    $self->initialize;
24    return $self;
25}
26
27##################################################
28## initialize defaults                          ##
29##################################################
30sub initialize {
31    my ($self) = @_;
32    $self->{runs} = [] if ref $self->{runs} ne 'ARRAY';
33    $self->{concurrency} ||= 1;
34    $self->{repeat} ||= 1;
35    $self->{priority} ||= "equal_opportunity";
36    $self->{buffersize} ||= 262144;
37    $self->{request_buffersize} ||= 8192;
38    $self->{timelimit} = undef if ! defined $self->{timelimit};
39    $self->{keepalive} = 0 if ! defined $self->{keepalive};
40    $self->{memory} = 1 if ! defined $self->{memory};
41}
42
43
44##################################################
45## configure the global parameters              ##
46##################################################
47sub config {
48    my ($self, $opt) = @_;
49    foreach (qw(concurrency priority buffersize repeat memory)) {
50	$self->{$_} = $opt->{$_} if defined $opt->{$_};
51    }
52}
53
54sub concurrency {
55    my ($self, $arg) = @_;
56    $self->{concurrency} = $arg if $arg;
57    return $self->{concurrency};
58}
59
60sub priority {
61    my ($self, $arg) = @_;
62    $self->{priority} = $arg if $arg;
63    return $self->{priority};
64}
65
66sub memory {
67    my ($self, $arg) = @_;
68    $self->{memory} = $arg if defined $arg;
69    return $self->{memory};
70}
71
72sub repeat {
73    my ($self, $arg) = @_;
74    $self->{repeat} = $arg if $arg;
75    return $self->{repeat};
76}
77
78sub keepalive {
79    my ($self, $arg) = @_;
80    $self->{keepalive} = $arg if $arg;
81    return $self->{keepalive};
82}
83
84sub timelimit {
85    my ($self, $arg) = @_;
86    $self->{timelimit} = $arg if $arg;
87    return $self->{timelimit};
88}
89
90sub buffersize {
91    my ($self, $arg) = @_;
92    $self->{buffersize} = $arg if $arg;
93    return $self->{buffersize};
94}
95
96sub request_buffersize {
97    my ($self, $arg) = @_;
98    $self->{request_buffersize} = $arg if $arg;
99    return $self->{request_buffersize};
100}
101
102sub total_requests {
103    my ($self) = @_;
104    return 0 if ref $self->{runs} ne 'ARRAY';
105    my $total = 0;
106    foreach my $run (@{$self->{runs}}) {
107	my $repeat = $run->repeat ? $run->repeat : $self->{repeat};
108	$total += ($#{$run->urls} + 1) * $repeat
109	  if ref $run->urls eq 'ARRAY';
110    }
111    return $total;
112}
113
114
115##################################################
116## verify configuration of runs and execute     ##
117##################################################
118sub execute {
119    my ($self) = @_;
120    # keep track of temporarily altered run object variables
121    my %altered;
122
123    # fail if they have not added any runs
124    return undef if ref $self->{runs} ne 'ARRAY';
125
126    # pre execute initialization of each run
127    foreach my $run_no (0..$#{$self->{runs}}) {
128	my $runobj = $self->{runs}->[$run_no];
129
130	$runobj->ready_to_execute or $runobj->prepare_for_execute or
131	  return undef;
132
133	# default to base ApacheBench object variables if not specified in run
134	if (! $runobj->repeat) {
135	    $runobj->repeat($self->{repeat});
136	    $altered{$run_no}->{repeat} = 1;
137	}
138	if (! defined $runobj->memory) {
139	    $runobj->memory($self->{memory});
140	    $altered{$run_no}->{memory} = 1;
141	}
142
143        $runobj->pre_execute_warnings;
144    }
145
146    # call the XS code and store regression data
147    $self->{'regression'} = $self->ab;
148
149    # post execute polishing of each run
150    foreach my $run_no (0..$#{$self->{runs}}) {
151	my $runobj = $self->{runs}->[$run_no];
152	$runobj->{'run_no'} = $run_no;
153	$runobj->{'regression'} = $self->{'regression'};
154	foreach my $param (qw(repeat memory)) {
155	    delete $runobj->{$param}
156	      if (ref $altered{$run_no} and $altered{$run_no}->{$param});
157	}
158    }
159    return HTTPD::Bench::ApacheBench::Regression->new
160      ({ 'regression' => $self->{'regression'} });
161}
162
163
164##################################################
165## run accessors                                ##
166##################################################
167sub run {
168    my ($self, $run_no, $run) = @_;
169    return undef if ! (ref $self->{runs} eq 'ARRAY' && blessed $self->{runs}->[$run_no]
170                         && $self->{runs}->[$run_no]->isa('HTTPD::Bench::ApacheBench::Run'));
171    if (blessed $run && $run->isa('HTTPD::Bench::ApacheBench::Run')) {
172	my $replaced_run = $self->{runs}->[$run_no];
173	$self->{runs}->[$run_no] = $run;
174	return $replaced_run;
175    }
176    return $self->{runs}->[$run_no];
177}
178
179sub add_run {
180    my ($self, $newrun) = @_;
181    return undef if ! (ref $self->{runs} eq 'ARRAY' && blessed $newrun
182                         && $newrun->isa('HTTPD::Bench::ApacheBench::Run'));
183    push(@{$self->{runs}}, $newrun);
184    return $#{$self->{runs}};
185}
186
187sub delete_run {
188    my ($self, $run_no) = @_;
189    return undef if ref $self->{runs} ne 'ARRAY';
190    my $deleted_run = $self->{runs}->[$run_no];
191    $self->{runs} = [ @{$self->{runs}}[0..$run_no-1],
192		      @{$self->{runs}}[$run_no+1..$#{$self->{runs}}] ];
193    return $deleted_run;
194}
195
196sub num_runs {
197    my ($self) = @_;
198    return scalar(@{$self->{runs} || []});
199}
200
201
2021;
203
204__END__
205
206=head1 NAME
207
208HTTPD::Bench::ApacheBench - Perl API for Apache benchmarking and regression testing.
209
210=head1 SYNOPSIS
211
212  use HTTPD::Bench::ApacheBench;
213
214  my $b = HTTPD::Bench::ApacheBench->new;
215
216  # global configuration
217  $b->concurrency(5);
218  $b->priority("run_priority");
219
220  # add HTTP request sequences (aka: runs)
221  my $run1 = HTTPD::Bench::ApacheBench::Run->new
222    ({ urls => ["http://localhost/one", "http://localhost/two"] });
223  $b->add_run($run1);
224
225  my $run2 = HTTPD::Bench::ApacheBench::Run->new
226    ({ urls    => ["http://localhost/three", "http://localhost/four"],
227       cookies => ["Login_Cookie=b3dcc9bac34b7e60;"],
228       # note: manual cookies no longer necessary due to use_auto_cookies (enabled by default)
229       order   => "depth_first",
230       repeat  => 10,
231       memory  => 2 });
232  $b->add_run($run2);
233
234  my $run3 = HTTPD::Bench::ApacheBench::Run->new
235    ({ urls     => ["http://localhost/five", "http://localhost/six"],
236       memory   => 3,
237       postdata => [
238           sub($) { sleep(int(rand(5)) + 1); return undef; },
239           sub($) {
240               my $prev_response = shift;
241               sleep(int(rand(5)) + 1);
242               my ($username) = ( $prev_response =~ m|<div id="userName">([^<>]+)</div>|i );
243               return $username ? 'cgi-username='.$username : undef;
244           },
245       ] });
246  $b->add_run($run3);
247
248  # send HTTP request sequences to server and time responses
249  my $ro = $b->execute;
250
251  # calculate hits/sec
252  print ((1000*$b->total_requests/$b->total_time)." req/sec\n");
253
254  # show request times (in ms) for $run1, 1st repetition
255  print join(', ', @{$run1->request_times}) . "\n";
256
257  # show response times (in ms) for $run2, 7th repetition
258  print join(', ', @{$run2->iteration(6)->response_times}) . "\n";
259
260  # dump the entire regression object (WARNING, this could be a LOT OF DATA)
261  use Data::Dumper;
262  my $d = Data::Dumper->new([$ro]);
263  print $d->Dumpxs;
264
265
266=head1 GOALS
267
268This project is meant to be the foundation of a complete benchmarking
269and regression testing suite for an advanced, transaction-based mod_perl
270site.  We need to be able to stress our server to its limit while also
271having a way to verify the HTTP responses for correctness.  Since our site
272is transaction-based (as opposed to content-based), we needed to extend
273the single-URL ab model to a multiple-URL sequence model.
274
275ApacheBench was originally based on the Apache 1.3.12 ab code
276(src/support/ab.c), but has since undergone major rewrites and now barely
277resembles ab.c.
278
279Note: although this tool was designed to be used on an Apache mod_perl
280site, it is generally applicable to any HTTP-compliant server.  Beware,
281however, that it sends a high volume of HTTP requests in a very short period
282of time, which may overwhelm some weaker HTTP server implementations
283like NT/IIS.
284
285=head1 DESCRIPTION
286
287ApacheBench sends sequences of HTTP requests to an HTTP server and keeps
288track of the time taken to receive a response, the data that was returned,
289the size of the data that was returned, and various other bits of information.
290
291Since it is implemented in C, it sends HTTP requests in a tight loop which
292can stress your server to 100% capacity, especially if invoked in multiple
293concurrent instances.  It gives accurate time measurements down to the
294millisecond for each HTTP request-response interval.
295
296Included is a simplified re-implementation of ab using the ApacheBench/Perl
297API.  This should help get you started with ApacheBench.
298
299=head1 CONFIGURATION METHODS
300
301You need to tell ApacheBench what the requests will be, in what order to
302send them, and how to prioritize sending them.  ApacheBench was designed
303to simulate many users logged in simultaneously, each of whom may be doing
304many different transactions at once.
305
306=head2 Global configuration methods
307
308Global configuration methods apply to all benchmarking runs associated
309with this ApacheBench object.
310
311=over 4
312
313=item $b = HTTPD::Bench::ApacheBench->new()
314
315Constructor.
316
317=item $b->concurrency( $concur_level )
318
319Number of requests to send simultaneously (default: B<1>)
320
321=item $b->priority( $priority )
322
323$priority can be either "B<equal_opportunity>" or "B<run_priority>".
324
325If set to "B<equal_opportunity>", all benchmark runs that are configured
326(see below) under this ApacheBench object are given equal access to
327the concurrency level.  This means requests are taken from each run
328and sent in parallel (the level of parallelism defined by concurrency()).
329
330If set to "B<run_priority>", the benchmark runs that are configured first
331get the highest priority.  This means all requests in $b->run(0) will
332be sent before any requests in $b->run(1) are sent, provided that
333$b->run(0)->order eq "B<breadth_first>" (see below).
334
335See L<"EXAMPLES"> near the bottom of this manual for a tutorial on the
336effects of "B<equal_opportunity>" vs. "B<run_priority>".
337
338(default: "B<equal_opportunity>")
339
340=item $b->repeat( $n )
341
342The number of times to repeat the request sequence in each run.
343This can be overridden on a per-run basis (see below).
344
345(default: B<1>)
346
347=item $b->keepalive( 0|1 )
348
349Whether or not to use HTTP Keep-Alive feature for requests configured in this
350object.  This can be overridden on a per-url/per-run basis (see below).
351
352B<Warning>: If you configure runs which contain requests to more than one
353hostname/port, be aware that setting $b->keepalive() may not improve
354performance.  See the discussion in the $run->keepalive() section for more
355details.
356
357(default: B<0>)
358
359=item $b->timelimit( $sec_to_max_wait )
360
361Set the maximum number of seconds to wait for requests configured in this
362object to complete (i.e. receive the full response from the server).
363B<Warning>: once the specified number of seconds have elapsed, ApacheBench
364will read one last chunk of data from each open socket, and exit.  This could
365result in partially received responses, which will cause broken sockets for
366the server.
367
368Per-url/per-run time limits can also be specified using the $run->timelimits()
369method, but it does B<not> override this global setting.  If $b->timelimit()
370is set, ApacheBench will exit after $sec_to_max_wait (or slightly over, due to
371final connection reads and building regression data), regardless of any other
372settings.
373
374(default: B<0>, which means wait an unlimited amount of time)
375
376=item $b->buffersize( $bufsz )
377
378The size of the buffer in which to store HTTP response bodies, in bytes.
379If an HTTP response is received with a size larger than this limit,
380the content is truncated at length $bufsz, and a warning is issued.
381This method has no effect if $b->memory() < 3.
382This can be overridden on a per-run basis (see below).
383
384(default: B<262144> or 256K)
385
386=item $b->request_buffersize( $req_bufsz )
387
388The size of the buffer in which to store HTTP requests, in bytes.  If you
389configure an HTTP request that is larger than this limit, unpredictable
390things will happen. (most likely a segmentation fault)
391
392(default: B<8192> or 8K)
393
394=item $b->memory( $memlvl )
395
396The memory level.  Controls how much data ApacheBench will remember and
397return in the regression object.  This can be overridden on a per-run basis
398(see below).
399
400(default: B<1>)
401
402Key:
403 $memlvl => Description
404
405  0  =>  Remember nothing. (actually still keeps global
406         regression data: total_time, bytes_received,
407         and warnings)
408
409  1  =>  Remember connect/response times and minimal
410         summary information about size of requests and
411         responses.
412
413  2  =>  Remember connect/response times and all
414         information about size of requests and responses.
415         Also keeps an array of all HTTP response
416         headers returned by the server for each request.
417
418  3  =>  Remember connect/response times, all information
419         about request/response sizes, HTTP response
420         headers, and also all content of every HTTP
421         response returned by the server.  B<Warning>:
422         this can quickly eat up all available main
423         memory if used with large runs.
424
425=item $b->add_run( $run_object )
426
427Schedule a run for this ApacheBench object.  Returns the run number where
428this object was inserted, or undef on failure.  See below for details on
429$run_object.
430
431=item $run = $b->run( $run_no, [$run_object] )
432
433Returns the run object stored in location $run_no.  If a run object is passed
434as the optional second argument, it is stored in location $run_no, displacing
435whatever was there.  The displaced run object is then returned.
436
437=item $b->delete_run( $run_no )
438
439Delete the run object stored in location $run_no.  The deleted run object
440is returned to the caller for safety sake.
441
442=item $b->num_runs()
443
444Returns the number of runs currently configured in $b.
445
446=back
447
448=head2 Run configuration methods
449
450You need to configure one or more benchmark runs.  A run is defined as an
451ordered sequence of HTTP requests which can be repeated multiple times,
452and scheduled to be sent in different ways.
453
454=over 4
455
456=item $run = HTTPD::Bench::ApacheBench::Run->new({ urls => [ @url_list ] })
457
458Construct a run object with the ordered sequence of HTTP requests
459in @url_list.
460
461=item $run->repeat( $n )
462
463Number of times to repeat this request sequence.
464
465(default: B<1>, or whatever is specified in global configuration)
466
467=item $run->use_auto_cookies( 0|1 )
468
469Controls whether to enable dynamic setting of cookies based on previous
470response headers in this run.  If set, will parse the Set-Cookie: headers
471in each response in the run, and set the corresponding Cookie: headers in
472all subsequent requests in this run.  (basically a crude, but fast emulation
473of a browser's cookie handling mechanism)  The cookies are cumulative for
474each iteration of the run, so they will accumulate with each request/response
475pair until the next iteration, when they get reset.
476
477(default: B<1>)
478
479=item $run->cookies( \@cookies )
480
481Set any extra HTTP Cookie: headers for each B<repetition> of this run.
482Length of @cookies should equal $n (whatever you set $run->repeat to).
483If this option is omitted, only auto-set cookies will be sent in
484requests for this run.
485
486If you need to set different cookies within a single URL sequence, use
487the request_headers() method.
488
489Note: this is somewhat obsolete now that there is support for dynamic
490cookies, but is kept for backwards compatibility and in case you want to
491add your own "static" cookies.
492
493Example usage:  You could simulate $n users all doing the same transaction
494simultaneously by giving $n different login cookies here.  Say you have
495login cookies in an array called @login of length $n.  Set $run->repeat($n),
496$run->order("breadth_first"), $run->cookies([map {$login[$_]} 0..$n-1]), and
497ApacheBench will perform the transaction sequence set by $run->urls $n times,
498each with a separate login cookie.
499
500=item $run->urls( \@url_list )
501
502Set the HTTP request URLs for this run.  A @url_list B<must> be given for
503each run, otherwise the run will not execute.  Typically @url_list is set
504using the constructor.
505
506=item $run->postdata( \@postdata )
507
508Set the HTTP POST request body contents.  If an undef value is encountered
509in @postdata, that request will be a GET request (or a HEAD request if you
510used $run->head_requests() below).  If this option is omitted, all requests
511for this run will be GET (or HEAD) requests.
512
513Length of @postdata should equal the length of @url_list.
514
515The @postdata array should consist of either strings or code refs (or undef).
516A string value will make this request always a POST.  An undef makes this
517request always a GET.  If a code ref, it will depend on the return value of
518the called code. (see below for details)
519
520The strings should contain exactly what you want sent in the HTTP POST request
521body.  For example,
522
523  @postdata = (undef, undef, 'cgikey1=val1&cgikey2=val2');
524
525will send two GET requests, then a POST request with the CGI parameter
526'cgikey1' set to 'val1' and the CGI parameter 'cgikey2' set to 'val2'.
527
528The code refs should be references to subroutines that take one string
529argument, which will be the http response (both headers and body) returned
530from the *previous* request in this run, and return CGI post data as above.
531If a subroutine returns undef, then the request will be a GET rather than a
532POST.  If the return value is a string (even empty string), the request sent
533will be a POST with the string as the request body.
534
535For example,
536
537  @postdata = (undef, sub($) {
538    my $prev_response = shift;
539    my ($username) = ( $prev_response =~ m|<div id="userName">([^<>]+)</div>|i );
540    return $username ? 'cgi-username='.$username : undef;
541  });
542
543will send one GET request, then it will scan the HTTP response body returned by
544the server for that GET request, and pull out any text (non-HTML) inside the HTML div
545with id = "userName".
546
547This text is then sent in a POST request as the value of CGI parameter 'cgi-username'.
548If the <div id="userName"> element is not found in the most recent http response,
549then the second request will be a GET instead of a POST request.
550
551WARNING: when using code refs in @postdata, you need to be running with $run->memory(3),
552since it needs to remember the most recent http response content.  If you forget, you will
553be warned at runtime (and your POST requests will likely not send the desired content).
554
555NOTES:
556
557 1. The time taken to call your postdata function is not included in the
558    times reported in the Regression object.  This is intentional, so you can
559    benchmark your server response time, not your load testing script.
560
561 2. You can use the above property to implement a random delay between http
562    requests; while this is sort of a hack of the postdata function feature,
563    it is much easier to implement a random delay this way than to add it in C.
564
565    e.g. the following postdata coderef would produce a GET request with a
566         random 1-5 sec delay:
567
568    sub($) {
569        sleep(int(rand(5)) + 1);
570        return undef;
571    }
572
573
574=item $run->head_requests( \@head_reqs )
575
576Send HTTP HEAD requests for the specified requests in this run.  The
577length of @head_reqs should equal the length of @url_list, and it is
578interpreted as an array of booleans.  Any true value in @head_reqs will
579result in a HEAD request being sent for the corresponding URL (unless the
580corresponding postdata() value is defined, in which case a POST will be sent).
581
582You can configure a run composed of any combination of the three HTTP
583request types (GET, HEAD, and POST), but note that an individual URL with a
584defined postdata() value will cause a POST request regardless of whether
585head_requests() is set for that URL.  The following precedence table
586illustrates which type of request will be sent for URL $url_no in the sequence.
587
588  defined $run->postdata->[$url_no] && ! ref($run->postdata->[$url_no])
589                                     ?  ==> POST request
590  defined $run->postdata->[$url_no] && ref($run->postdata->[$url_no]) eq 'CODE'
591           && defined $run->postdata->[$url_no]->( $preceding_response )
592                                     ?  ==> POST request
593  defined $run->postdata->[$url_no] && ref($run->postdata->[$url_no]) eq 'CODE'
594           && ! defined $run->postdata->[$url_no]->( $preceding_response )
595                                     ?  ==> GET request
596  $run->head_requests->[$url_no]     ?  ==> HEAD request
597  else                               :  ==> GET request
598
599=item $run->content_types( \@ctypes )
600
601Set the Content-type: header for each POST request in this run.  Default
602is "application/x-www-form-urlencoded" which will be used if an undef
603value is encountered in @ctypes.  Length of @ctypes should equal the
604length of @postdata.  Only sends the Content-type: header for POST requests:
605a defined value in @ctypes with an undef in the corresponding @postdata will
606result in no Content-type: header being sent.
607
608=item $run->request_headers( \@req_headers )
609
610Set arbitrary HTTP request headers for each request in this run, which will
611be inserted after all normal headers.  Multiple extra headers for a single
612url should be separated with "\r\n".  An undef value in @req_headers results
613in no extra HTTP request headers being sent for the corresponding url.
614If this option is omitted, no extra HTTP request headers will be sent in
615any of the requests for this run.  Length of @req_headers should equal the
616length of @url_list.
617
618The following example for a @url_list of length 4 produces two requests with
619no extra headers, one with 1 extra header, and one with 2 extra headers.
620
621  $run->request_headers([ undef, undef, "Extra-Header: bread",
622                          "Extra-Header1: butter\r\nExtra-Header2: toaster" ])
623
624=item $run->keepalive( \@keepalives )
625
626Use HTTP Keep-Alive feature for the specified requests in this run.  The
627length of @keepalives should equal the length of @url_list, and it is
628interpreted as an array of booleans, with undef indicating to use the
629object default set by $b->keepalive().  Any true value in @keepalives will
630result in a Keep-Alive HTTP request being sent for the corresponding URL.
631
632To achieve full performance benefits from this feature, you need to be sure
633your Keep-Alive requests are consecutive.  If a non-Keep-Alive request or
634a request for a different hostname or port immediately follows a Keep-Alive
635request B<in the connection slot>, I<the connection will be closed> and a
636new connection will be opened.
637
638Further, keep in mind that for $b->concurrency() > 1, there are many
639connection slots open and even though requests in @url_list will be sent
640in order, there is no guarantee they will all use the same connection slot.
641The HTTP Keep-Alive feature only yields performance benefits when consecutive
642Keep-Alive requests use the same connection slot.  Otherwise ApacheBench has
643to close and re-open connections, resulting in the same performance as not
644using keepalive() at all.
645
646To guarantee consecutive Keep-Alive requests with $b->concurrency() > 1,
647I recommend you either declare I<all> URLs in all runs as keepalive()
648(this can be done by setting $b->keepalive( 1 ) and not overriding it by
649calling keepalive() for any runs), or set $run->order( "depth_first" ) and
650$b->priority( "run_priority" ).  This is the only combination of configuration
651options that guarantees consecutive, same-slot Keep-Alive requests
652regardless of your concurrency setting.
653
654For $b->concurrency() == 1, things are simpler.  At any given time, there
655is only one connection slot open, so just make sure your keepalive URLs are
656consecutive within each run (if in "run_priority" mode), or that
657equal-numbered repetitions of URLs in all runs are keepalive (if in
658"equal_opportunity" mode), and be sure that all requests are to the
659same hostname/port.
660
661=item $run->timelimits( \@timelimits )
662
663Set the maximum number of seconds to wait for requests in this
664run to complete (i.e. receive the full response from the server).  The
665length of @timelimits should equal the length of @url_list, and it is
666interpreted as an array of double precision floating point numbers
667(representing the number of seconds to wait for a response).  An undef or 0
668represents waiting an indefinite amount of time for that particular response.
669If this option is not configured, there will be no time limit on any responses.
670
671B<Warning>: once the specified number of seconds have elapsed on the specified
672URL, ApacheBench will close the connection immediately.  This can cause strange
673results in the regression data for this request.  It could also result in
674partially received responses, which will cause broken sockets for the server.
675
676=item $run->order( $order )
677
678Either "B<depth_first>" or "B<breadth_first>"
679
680"B<breadth_first>" mode sends $n of the first request in the
681@url_list, then $n of the second request in the @urls_list,
682then $n of the third... and so on.  (e.g. If $n == 3 and @url_list
683contains two requests, then ApacheBench would send the first
684request 3 times, and then the second request 3 times.)
685
686"B<depth_first>" mode ensures that HTTP requests in the sequence are
687sent in order, completing a full sequence before starting again for the
688next repeat() iteration.  (e.g. If $n == 3 and @url_list contains two
689requests, then ApacheBench would send the @url_list sequence in order,
690then send it again, and then again.  A total of six requests would be sent.)
691
692See L<"EXAMPLES"> near the bottom of this manual for a tutorial on the
693effects of "B<breadth_first>" vs. "B<depth_first>".
694
695(default: "B<breadth_first>")
696
697B<Note:> if $run->repeat() == 1, or the length of $run->urls() is 1, then the
698B<order> option has no effect
699
700=item $run->buffersize( $bufsz )
701
702The size of the buffer in which to store HTTP response bodies.
703If an HTTP response is received with a size larger than this limit,
704the content is truncated at length $bufsz, and a warning is issued.
705This method has no effect if $run->memory() < 3.
706
707(default: B<262144> or 256K, or whatever is specified in global configuration)
708
709=item $run->memory( $memlvl )
710
711The memory level.  Controls how much data ApacheBench will remember and
712return in the regression object for this run.  See global configuration
713method of same name for detailed description.
714
715(default: B<1>, or whatever is specified in global configuration)
716
717=back
718
719=head1 EXECUTION METHODS
720
721=head2 Global execution methods
722
723=over 4
724
725=item $b->execute
726
727Send all scheduled runs to their respective destinations, and record
728response data.
729
730=back
731
732=head2 Run execution methods
733
734=over 4
735
736=item $run->ready_to_execute
737
738Check all run configuration parameters for sanity.  Returns 1 to
739indicate a call to $b->execute() I<should> complete safely.  Returns 0 to
740indicate some more parameters need setting before $b->execute() should be
741called.  Some of these parameters are automatically set by
742prepare_for_execute() (below).
743
744=item $run->prepare_for_execute
745
746Set any run configuration parameters that were unspecified to sane values
747in order to prevent a segmentation fault on execute().  Returns 1 if it
748could set all parameters necessary to cause ready_to_execute() to return 1.
749Returns 0 otherwise.
750
751Note: this method is automatically called from execute() before entering
752any XS code.
753
754=back
755
756=head1 REGRESSION METHODS
757
758All of the following methods will return B<undef> unless the underlying
759ApacheBench object has been execute()'d at least once.
760
761=head2 Global regression methods
762
763=over 4
764
765=item $b->total_time
766
767Total time, in milliseconds, between the start of the first request in the
768first run, and the end of the final response in the final run.
769
770=item $b->bytes_received
771
772Total bytes received from all responses in all runs.
773
774=item $b->total_requests
775
776Total number of HTTP requests which were configured in this object.
777
778=item $b->total_requests_sent
779
780Total number of HTTP requests which were successfully sent to the server(s).
781
782=item $b->total_responses_received
783
784Total number of complete, successful HTTP responses received.
785
786=item $b->total_responses_failed
787
788Total number of HTTP responses which were not received successfully.
789Check the warning messages for possible explanations of why they failed.
790
791=item $b->warnings
792
793Various warning messages.
794
795=back
796
797=head2 Run regression methods
798
799=over 4
800
801=item $run->sent_requests( $url_no )
802
803Returns the number of HTTP requests which were B<successfully> sent to the
804server for the URL specified by $url_no.
805
806If $url_no is not given, returns a reference to an array the same length as
807$run->urls() which contains the number of B<successful> HTTP requests sent
808for each url in this run.
809
810=item $run->good_responses( $url_no )
811
812Returns the number of complete, B<successful> HTTP responses received for the
813URL specified by $url_no.
814
815If $url_no is not given, returns a reference to an array the same length as
816$run->urls() which contains the number of B<successful> HTTP responses
817received for each HTTP request in this run.
818
819=item $run->failed_responses( $url_no )
820
821Returns the number of HTTP responses which failed or were otherwise
822B<unsuccessful>, for the URL specified by $url_no.
823
824If $url_no is not given, returns a reference to an array the same length as
825$run->urls() which contains the number of B<unsuccessful> HTTP responses
826received for each HTTP request in this run.
827
828=item $i = $run->iteration( $iter_no )
829
830Return a regression object specific to iteration $iter_no of this run.
831If $iter_no is not given, it assumes 0, or the first iteration of the run.
832The number of iterations for the run can be retrieved with $run->repeat().
833
834=item $i->connect_times( $url_no )
835
836Returns the connection time, in milliseconds, for the URL specified by $url_no.
837
838If $url_no is not given, returns a reference to an array the same length as
839$run->urls() which contains connection times, in milliseconds, for each HTTP
840request in this iteration of the sequence.
841
842=item $i->min_connect_time
843
844The minimum connect time of all requests in the sequence.
845
846=item $i->max_connect_time
847
848The maximum connect time of all requests in the sequence.
849
850=item $i->avg_connect_time
851
852The average connect time of all requests in the sequence.
853
854=item $i->sum_connect_time
855
856The total connect time of all requests in the sequence (equal to the
857summation of all elements of $i->connect_times).
858
859=item $i->request_times( $url_no )
860
861Returns the time taken to send a request to the server, in milliseconds,
862for the URL specified by $url_no.
863
864If $url_no is not given, returns a reference to an array the same length as
865$run->urls() which contains the times taken to send a request to the server,
866in milliseconds, for each HTTP request in the sequence.
867
868=item $i->min_request_time
869
870The minimum request time of all requests in the sequence.
871
872=item $i->max_request_time
873
874The maximum request time of all requests in the sequence.
875
876=item $i->avg_request_time
877
878The average request time of all requests in the sequence.
879
880=item $i->sum_request_time
881
882The total request time of all requests in the sequence (equal to the
883summation of all elements of $i->request_times).
884
885=item $i->response_times( $url_no )
886
887Returns the time taken to receive a response from the server, in milliseconds,
888for the URL specified by $url_no.
889
890If $url_no is not given, returns a reference to an array the same length as
891$run->urls() which contains the times taken to receive a response from the
892server, in milliseconds, for each HTTP request in the sequence.
893
894=item $i->min_response_time
895
896The minimum response time of all requests in the sequence.
897
898=item $i->max_response_time
899
900The maximum response time of all requests in the sequence.
901
902=item $i->avg_response_time
903
904The average response time of all requests in the sequence.
905
906=item $i->sum_response_time
907
908The total response time of all requests in the sequence (equal to the
909summation of all elements of $i->response_times).
910
911=item $i->bytes_posted( $url_no )
912
913Returns the length of the HTTP POST request body for the URL specified
914by $url_no.
915
916If $url_no is not given, returns a reference to an array the same length as
917$run->urls() which contains the number of bytes posted to the server for each
918HTTP request in the sequence.
919
920(return value will be undefined if B<memory> < 2)
921
922=item $i->sum_bytes_posted
923
924The total number of bytes posted to the server in HTTP requests over this
925iteration of the request sequence.
926
927=item $i->bytes_read( $url_no )
928
929Returns the length of the HTTP response body for the URL specified by $url_no.
930
931If $url_no is not given, returns a reference to an array the same length as
932$run->urls() which contains the total number of bytes read from the server in
933each HTTP response in the sequence.
934
935(return value will be undefined if B<memory> < 2)
936
937=item $i->sum_bytes_read
938
939The total number of bytes read from the server in HTTP responses over this
940iteration of the request sequence.
941
942=item $i->request_body( $url_no )
943
944Returns the full HTTP request sent to the server for the URL specified
945by $url_no.
946
947If $url_no is not given, returns a reference to an array the same length as
948$run->urls() which contains the full HTTP request (including HTTP request
949headers) sent to the server for each URL in the sequence.
950
951(return value will be undefined if B<memory> < 3)
952
953=item $i->response_headers( $url_no )
954
955Returns the HTTP response header returned by the server for the URL specified
956by $url_no.
957
958If $url_no is not given, returns a reference to an array the same length as
959$run->urls() which contains the HTTP response headers returned by the server
960for each URL in the sequence.
961
962(return value will be undefined if B<memory> < 2)
963
964=item $i->response_body( $url_no )
965
966Returns the full HTTP response page content returned by the server
967(including HTTP headers) for the URL specified by $url_no.
968
969If $url_no is not given, returns a reference to an array the same length as
970$run->urls() which contains the full HTTP responses returned by the server for
971each URL in the sequence.
972
973(return value will be undefined if B<memory> < 3)
974
975=item $i->response_body_lengths( $url_no )
976
977Returns the length of the document (in bytes) returned by the server,
978for the URL specified by $url_no.
979
980If $url_no is not given, returns a reference to an array the same length as
981$run->urls() which contains the length of the document (in bytes) returned by
982the server for each HTTP response in the sequence.  This should be equivalent
983to the Content-Length: line in the response headers, if the server set them
984correctly.  The following should also be true,
985for 1 <= $j <= size($run->urls()):
986
987( $i->response_body_lengths()->[$j] == $i->bytes_read()->[$j] - length($i->response_headers()->[$j]) )
988
989(return value will be undefined if B<memory> < 2)
990
991=back
992
993=head1 HTTP Proxies
994
995ApacheBench can be used to make HTTP requests via an HTTP proxy.  To do so,
996one passes a "special" URI to the ApacheBench::Run constructor or urls
997method.  This special URI is of the form
998http://proxyserver:proxyport/http://realserver:realport/...
999
1000=head1 EXAMPLES
1001
1002The following examples of ApacheBench usage are paired with the resulting
1003output from an Apache access_log.  This should give you a feel for how the
1004global $b->priority() method and the per-run $run->order() method affect how
1005HTTP requests are sent.
1006
1007First, let's set $b->priority("B<equal_opportunity>") (its default).
1008
1009  my $b = HTTPD::Bench::ApacheBench->new;
1010  $b->concurrency(1);
1011  $b->priority("equal_opportunity");
1012
1013Add a single run and execute, then look at what gets sent to Apache.
1014
1015  my $run = HTTPD::Bench::ApacheBench::Run->new
1016    ({
1017       repeat    => 3,
1018       urls      => [ "http://localhost/",
1019                      "http://localhost/server-status" ],
1020       order     => "breadth_first",
1021     });
1022  $b->add_run($run);
1023  $b->execute;
1024
1025Apache access_log output:
1026
1027  127.0.0.1 - - [20/Sep/2000:18:43:32 -0400] "GET / HTTP/1.0" 200 5565
1028  127.0.0.1 - - [20/Sep/2000:18:43:32 -0400] "GET / HTTP/1.0" 200 5565
1029  127.0.0.1 - - [20/Sep/2000:18:43:32 -0400] "GET / HTTP/1.0" 200 5565
1030  127.0.0.1 - - [20/Sep/2000:18:43:32 -0400] "GET /server-status HTTP/1.0" 200 5294
1031  127.0.0.1 - - [20/Sep/2000:18:43:32 -0400] "GET /server-status HTTP/1.0" 200 5294
1032  127.0.0.1 - - [20/Sep/2000:18:43:32 -0400] "GET /server-status HTTP/1.0" 200 5294
1033
1034Let's add another run and execute, and see what Apache sees.
1035
1036  my $run2 = HTTPD::Bench::ApacheBench::Run->new
1037    ({
1038       repeat       => 3,
1039       urls         => [ "http://localhost/perl-status",
1040                         "http://localhost/proxy-status" ],
1041       order        => "breadth_first",
1042     });
1043  $b->add_run($run2);
1044  $b->execute;
1045
1046Notice that both the first and second runs get equal opportunity.
1047Apache access_log output:
1048
1049  127.0.0.1 - - [20/Sep/2000:18:49:10 -0400] "GET / HTTP/1.0" 200 5565
1050  127.0.0.1 - - [20/Sep/2000:18:49:11 -0400] "GET / HTTP/1.0" 200 5565
1051  127.0.0.1 - - [20/Sep/2000:18:49:11 -0400] "GET / HTTP/1.0" 200 5565
1052  127.0.0.1 - - [20/Sep/2000:18:49:11 -0400] "GET /perl-status HTTP/1.0" 200 848
1053  127.0.0.1 - - [20/Sep/2000:18:49:11 -0400] "GET /perl-status HTTP/1.0" 200 848
1054  127.0.0.1 - - [20/Sep/2000:18:49:11 -0400] "GET /perl-status HTTP/1.0" 200 848
1055  127.0.0.1 - - [20/Sep/2000:18:49:11 -0400] "GET /server-status HTTP/1.0" 200 5294
1056  127.0.0.1 - - [20/Sep/2000:18:49:11 -0400] "GET /server-status HTTP/1.0" 200 5294
1057  127.0.0.1 - - [20/Sep/2000:18:49:11 -0400] "GET /server-status HTTP/1.0" 200 5294
1058  127.0.0.1 - - [20/Sep/2000:18:49:11 -0400] "GET /proxy-status HTTP/1.0" 200 5886
1059  127.0.0.1 - - [20/Sep/2000:18:49:11 -0400] "GET /proxy-status HTTP/1.0" 200 5888
1060  127.0.0.1 - - [20/Sep/2000:18:49:11 -0400] "GET /proxy-status HTTP/1.0" 200 5889
1061
1062Now let's set $b->priority("B<run_priority>").
1063
1064  $b->priority("run_priority");
1065  $b->execute;
1066
1067Notice that now ApacheBench completes the entire first run before it starts
1068the second.  Here's the Apache access_log output:
1069
1070  127.0.0.1 - - [20/Sep/2000:18:52:47 -0400] "GET / HTTP/1.0" 200 5565
1071  127.0.0.1 - - [20/Sep/2000:18:52:47 -0400] "GET / HTTP/1.0" 200 5565
1072  127.0.0.1 - - [20/Sep/2000:18:52:47 -0400] "GET / HTTP/1.0" 200 5565
1073  127.0.0.1 - - [20/Sep/2000:18:52:47 -0400] "GET /server-status HTTP/1.0" 200 5294
1074  127.0.0.1 - - [20/Sep/2000:18:52:47 -0400] "GET /server-status HTTP/1.0" 200 5294
1075  127.0.0.1 - - [20/Sep/2000:18:52:47 -0400] "GET /server-status HTTP/1.0" 200 5294
1076  127.0.0.1 - - [20/Sep/2000:18:52:47 -0400] "GET /perl-status HTTP/1.0" 200 848
1077  127.0.0.1 - - [20/Sep/2000:18:52:47 -0400] "GET /perl-status HTTP/1.0" 200 848
1078  127.0.0.1 - - [20/Sep/2000:18:52:47 -0400] "GET /perl-status HTTP/1.0" 200 848
1079  127.0.0.1 - - [20/Sep/2000:18:52:47 -0400] "GET /proxy-status HTTP/1.0" 200 5858
1080  127.0.0.1 - - [20/Sep/2000:18:52:47 -0400] "GET /proxy-status HTTP/1.0" 200 5861
1081  127.0.0.1 - - [20/Sep/2000:18:52:47 -0400] "GET /proxy-status HTTP/1.0" 200 5864
1082
1083Let's now set our runs to $run->order("B<depth_first>") instead of
1084"B<breadth_first>".  With "B<depth_first>", $b->priority() has no effect,
1085since each run can only use a maximum of one concurrent server
1086(by definition of "B<depth_first>", it can only be sending one request
1087at a time).
1088
1089  $b->run(0)->order("depth_first");
1090  $b->run(1)->order("depth_first");
1091  $b->execute;
1092
1093Notice each sequence gets sent in full before it repeats.
1094Apache access_log output:
1095
1096  127.0.0.1 - - [20/Sep/2000:19:02:01 -0400] "GET / HTTP/1.0" 200 5565
1097  127.0.0.1 - - [20/Sep/2000:19:02:01 -0400] "GET /server-status HTTP/1.0" 200 5294
1098  127.0.0.1 - - [20/Sep/2000:19:02:01 -0400] "GET / HTTP/1.0" 200 5565
1099  127.0.0.1 - - [20/Sep/2000:19:02:01 -0400] "GET /server-status HTTP/1.0" 200 5294
1100  127.0.0.1 - - [20/Sep/2000:19:02:01 -0400] "GET / HTTP/1.0" 200 5565
1101  127.0.0.1 - - [20/Sep/2000:19:02:01 -0400] "GET /server-status HTTP/1.0" 200 5294
1102  127.0.0.1 - - [20/Sep/2000:19:02:01 -0400] "GET /perl-status HTTP/1.0" 200 848
1103  127.0.0.1 - - [20/Sep/2000:19:02:01 -0400] "GET /proxy-status HTTP/1.0" 200 5858
1104  127.0.0.1 - - [20/Sep/2000:19:02:01 -0400] "GET /perl-status HTTP/1.0" 200 848
1105  127.0.0.1 - - [20/Sep/2000:19:02:01 -0400] "GET /proxy-status HTTP/1.0" 200 5860
1106  127.0.0.1 - - [20/Sep/2000:19:02:01 -0400] "GET /perl-status HTTP/1.0" 200 848
1107  127.0.0.1 - - [20/Sep/2000:19:02:01 -0400] "GET /proxy-status HTTP/1.0" 200 5860
1108
1109Now let's send the same runs, but with a higher concurrency level.
1110
1111  $b->concurrency(2);
1112  $b->execute;
1113
1114Notice that ApacheBench sends requests from all runs in order to fill up
1115the specified level of concurrent requests.  Apache access_log output:
1116
1117  127.0.0.1 - - [20/Sep/2000:19:04:38 -0400] "GET / HTTP/1.0" 200 5565
1118  127.0.0.1 - - [20/Sep/2000:19:04:38 -0400] "GET /perl-status HTTP/1.0" 200 848
1119  127.0.0.1 - - [20/Sep/2000:19:04:38 -0400] "GET /server-status HTTP/1.0" 200 5294
1120  127.0.0.1 - - [20/Sep/2000:19:04:38 -0400] "GET /proxy-status HTTP/1.0" 200 5891
1121  127.0.0.1 - - [20/Sep/2000:19:04:38 -0400] "GET / HTTP/1.0" 200 5565
1122  127.0.0.1 - - [20/Sep/2000:19:04:38 -0400] "GET /perl-status HTTP/1.0" 200 848
1123  127.0.0.1 - - [20/Sep/2000:19:04:38 -0400] "GET /server-status HTTP/1.0" 200 5294
1124  127.0.0.1 - - [20/Sep/2000:19:04:38 -0400] "GET /proxy-status HTTP/1.0" 200 5878
1125  127.0.0.1 - - [20/Sep/2000:19:04:38 -0400] "GET / HTTP/1.0" 200 5565
1126  127.0.0.1 - - [20/Sep/2000:19:04:38 -0400] "GET /perl-status HTTP/1.0" 200 848
1127  127.0.0.1 - - [20/Sep/2000:19:04:38 -0400] "GET /server-status HTTP/1.0" 200 5294
1128  127.0.0.1 - - [20/Sep/2000:19:04:38 -0400] "GET /proxy-status HTTP/1.0" 200 5878
1129
1130Let's take a look at the regression data from that last execute() call.
1131
1132  foreach my $run_no (0..1) {
1133    foreach my $repeat (0..2) {
1134      print "response times (in ms) for run $run_no, iteration ".($repeat+1).":\n  ";
1135      print join("\n  ", @{$b->run($run_no)->iteration($repeat)->response_times});
1136      print "\n";
1137    }
1138  }
1139
1140Perl output:
1141
1142  response times (in ms) for run 0, iteration 1:
1143    69
1144    39
1145  response times (in ms) for run 0, iteration 2:
1146    67
1147    39
1148  response times (in ms) for run 0, iteration 3:
1149    65
1150    41
1151  response times (in ms) for run 1, iteration 1:
1152    67
1153    40
1154  response times (in ms) for run 1, iteration 2:
1155    66
1156    39
1157  response times (in ms) for run 1, iteration 3:
1158    65
1159    39
1160
1161
1162=head1 BUGS
1163
1164Error checking in configuration is good but not perfect.
1165If you do not configure correctly, you may experience a
1166segmentation fault on execute().
1167
1168The body of any response which comes back larger than the B<buffersize>
1169applicable to it may be truncated to zero length.  If you expect to receive
1170responses larger than the default 256K buffersize, make sure to set your
1171$run->buffersize() big enough for the largest page you anticipate receiving.
1172
1173If you are running in perl's taint-checking mode, and you pass tainted data
1174to ApacheBench (e.g. a tainted URL), it will barf.  Don't ask me why.
1175
1176HTTP Proxy support needs to be expanded to allow for a username and
1177password.
1178
1179=head1 AUTHORS
1180
1181The ApacheBench Perl API is based on code from
1182Apache 1.3.12 ab (src/support/ab.c), by the Apache group.
1183
1184The ApacheBench Perl API was originally written by Ling Wu <ling@certsite.com>
1185
1186Rewritten and currently maintained by Adi Fairbank <adi@adiraj.org>
1187
1188Recent efforts have been made to incorporate the newest ab code from the Apache
1189Group.  As of version 0.62, most features of Apache 1.3.22 ab are supported.
1190The main exception being SSL.
1191
1192Please e-mail Adi with bug reports, or preferably patches.
1193
1194=head1 LICENSE
1195
1196This package is free software and is provided AS IS without express or
1197implied warranty.  It may be used, redistributed and/or modified under the
1198terms of the Perl Artistic License
1199(http://www.perl.com/perl/misc/Artistic.html)
1200
1201=cut
1202