ApacheBench

 view release on metacpan or  search on metacpan

ab  view on Meta::CPAN

    $postdata = join('', <FILE>);
    close FILE;
}

my $c = $options{'c'} || 1;
my $n = $options{'n'} || 1;


my $b = HTTPD::Bench::ApacheBench->new;
$b->concurrency($c);
$b->keepalive( $options{'k'} );
$b->timelimit( $options{'t'} );
my $run = HTTPD::Bench::ApacheBench::Run->new
  ({
    urls      => [ $ARGV[0] ],
    repeat    => $n,
    memory    => 3,
   });
$run->cookies([ $options{'C'} ]);
$run->postdata([ $postdata ]);
$run->head_requests([ $options{'i'} ]);

doc/C_Functions_Tree  view on Meta::CPAN

  read a chunk of data from a socket;
  if (all data has been read OR an error occurred)
    -> close_connection(); return;
  if (there is no data available to read at the moment: read returned EAGAIN)
    -> return;
  if (we have not received the full http header)
    if (this chunk of data contains end of http header: "\r\n\r\n" or "\n\n")
      -> check response header for Keep-Alive and mark accordingly;
    else
      -> return;
  if (response indicated keepalive and we've received entire response)
    -> store_regression_data();
    -> schedule_next_request();
    if (next scheduled request is keepalive and the hostname/port are same)
      -> write_request();
    else
      -> close the connection and start a fresh one: start_connect();

write_request()
  reset_request();
  write full http request to socket;

reset_request()
  build http request according to GET/HEAD/POST settings;

lib/HTTPD/Bench/ApacheBench.pm  view on Meta::CPAN

##################################################
sub initialize {
    my ($self) = @_;
    $self->{runs} = [] if ref $self->{runs} ne 'ARRAY';
    $self->{concurrency} ||= 1;
    $self->{repeat} ||= 1;
    $self->{priority} ||= "equal_opportunity";
    $self->{buffersize} ||= 262144;
    $self->{request_buffersize} ||= 8192;
    $self->{timelimit} = undef if ! defined $self->{timelimit};
    $self->{keepalive} = 0 if ! defined $self->{keepalive};
    $self->{memory} = 1 if ! defined $self->{memory};
}


##################################################
## configure the global parameters              ##
##################################################
sub config {
    my ($self, $opt) = @_;
    foreach (qw(concurrency priority buffersize repeat memory)) {

lib/HTTPD/Bench/ApacheBench.pm  view on Meta::CPAN

    $self->{memory} = $arg if defined $arg;
    return $self->{memory};
}

sub repeat {
    my ($self, $arg) = @_;
    $self->{repeat} = $arg if $arg;
    return $self->{repeat};
}

sub keepalive {
    my ($self, $arg) = @_;
    $self->{keepalive} = $arg if $arg;
    return $self->{keepalive};
}

sub timelimit {
    my ($self, $arg) = @_;
    $self->{timelimit} = $arg if $arg;
    return $self->{timelimit};
}

sub buffersize {
    my ($self, $arg) = @_;

lib/HTTPD/Bench/ApacheBench.pm  view on Meta::CPAN


(default: "B<equal_opportunity>")

=item $b->repeat( $n )

The number of times to repeat the request sequence in each run.
This can be overridden on a per-run basis (see below).

(default: B<1>)

=item $b->keepalive( 0|1 )

Whether or not to use HTTP Keep-Alive feature for requests configured in this
object.  This can be overridden on a per-url/per-run basis (see below).

B<Warning>: If you configure runs which contain requests to more than one
hostname/port, be aware that setting $b->keepalive() may not improve
performance.  See the discussion in the $run->keepalive() section for more
details.

(default: B<0>)

=item $b->timelimit( $sec_to_max_wait )

Set the maximum number of seconds to wait for requests configured in this
object to complete (i.e. receive the full response from the server).
B<Warning>: once the specified number of seconds have elapsed, ApacheBench
will read one last chunk of data from each open socket, and exit.  This could

lib/HTTPD/Bench/ApacheBench.pm  view on Meta::CPAN

If this option is omitted, no extra HTTP request headers will be sent in
any of the requests for this run.  Length of @req_headers should equal the
length of @url_list.

The following example for a @url_list of length 4 produces two requests with
no extra headers, one with 1 extra header, and one with 2 extra headers.

  $run->request_headers([ undef, undef, "Extra-Header: bread",
                          "Extra-Header1: butter\r\nExtra-Header2: toaster" ])

=item $run->keepalive( \@keepalives )

Use HTTP Keep-Alive feature for the specified requests in this run.  The
length of @keepalives should equal the length of @url_list, and it is
interpreted as an array of booleans, with undef indicating to use the
object default set by $b->keepalive().  Any true value in @keepalives will
result in a Keep-Alive HTTP request being sent for the corresponding URL.

To achieve full performance benefits from this feature, you need to be sure
your Keep-Alive requests are consecutive.  If a non-Keep-Alive request or
a request for a different hostname or port immediately follows a Keep-Alive
request B<in the connection slot>, I<the connection will be closed> and a
new connection will be opened.

Further, keep in mind that for $b->concurrency() > 1, there are many
connection slots open and even though requests in @url_list will be sent
in order, there is no guarantee they will all use the same connection slot.
The HTTP Keep-Alive feature only yields performance benefits when consecutive
Keep-Alive requests use the same connection slot.  Otherwise ApacheBench has
to close and re-open connections, resulting in the same performance as not
using keepalive() at all.

To guarantee consecutive Keep-Alive requests with $b->concurrency() > 1,
I recommend you either declare I<all> URLs in all runs as keepalive()
(this can be done by setting $b->keepalive( 1 ) and not overriding it by
calling keepalive() for any runs), or set $run->order( "depth_first" ) and
$b->priority( "run_priority" ).  This is the only combination of configuration
options that guarantees consecutive, same-slot Keep-Alive requests
regardless of your concurrency setting.

For $b->concurrency() == 1, things are simpler.  At any given time, there
is only one connection slot open, so just make sure your keepalive URLs are
consecutive within each run (if in "run_priority" mode), or that
equal-numbered repetitions of URLs in all runs are keepalive (if in
"equal_opportunity" mode), and be sure that all requests are to the
same hostname/port.

=item $run->timelimits( \@timelimits )

Set the maximum number of seconds to wait for requests in this
run to complete (i.e. receive the full response from the server).  The
length of @timelimits should equal the length of @url_list, and it is
interpreted as an array of double precision floating point numbers
(representing the number of seconds to wait for a response).  An undef or 0

lib/HTTPD/Bench/ApacheBench/Run.pm  view on Meta::CPAN


#####################################################
## sanity check on run object variables:           ##
##  this method is intended to hopefully catch     ##
##  errors that cause a segmentation fault in ab() ##
#####################################################
sub ready_to_execute {
    my ($self) = @_;

    foreach (qw(urls cookies postdata head_requests
		content_types request_headers keepalive timelimits)) {
	return 0 unless ref $self->{$_} eq "ARRAY";
    }
    return 0 if grep { ref($_) || m/\s$/ } @{$self->{urls}};

    return 1;
}

#####################################################
## do a pre-execute fixup of run object            ##
#####################################################

lib/HTTPD/Bench/ApacheBench/Run.pm  view on Meta::CPAN


    # whitespace at the end of urls will cause trouble
    map { chomp $_ } @{$self->{urls}};

    # set 'cookies' to undef if not specified
    $self->{cookies} = [undef] unless ref $self->{cookies} eq "ARRAY";

    # set 'postdata', 'content_types', and 'request_headers' to undef
    #  if not specified in run
    foreach my $param (qw(postdata head_requests content_types
			  request_headers keepalive timelimits)) {
	$self->{$param} = [ map {undef} @{$self->{urls}} ]
	  if ref $self->{$param} ne "ARRAY";
    }

    return 1;
}

sub pre_execute_warnings {
    my ($self) = @_;

lib/HTTPD/Bench/ApacheBench/Run.pm  view on Meta::CPAN

    $self->{head_requests} = $arg if $arg;
    return $self->{head_requests};
}

sub content_types {
    my ($self, $arg) = @_;
    $self->{content_types} = $arg if $arg;
    return $self->{content_types};
}

sub keepalive {
    my ($self, $arg) = @_;
    $self->{keepalive} = $arg if $arg;
    return $self->{keepalive};
}

sub timelimits {
    my ($self, $arg) = @_;
    $self->{timelimits} = $arg if $arg;
    return $self->{timelimits};
}

sub append {
    my ($self, $opt) = @_;

src/ApacheBench.xs  view on Meta::CPAN

HV *
ab(input_hash)
    SV * input_hash;

    PREINIT:
    char *pt,**url_keys;
    int i,j,k,arrlen,arrlen2;
    int def_buffersize; /* default buffersize for all runs */
    int def_repeat; /* default number of repeats if unspecified in runs */
    int def_memory; /* default memory setting if unspecified in runs */
    bool def_keepalive = 0; /* default keepalive setting */
    struct global *registry = calloc(1, sizeof(struct global));
    int total_started = 0, total_good = 0, total_failed = 0;

    CODE:
    SV * runs;
    SV * urls = 0;
    SV * post_data = 0;
    SV * head_requests = 0;
    SV * cookies = 0;
    SV * ctypes = 0;
    SV * req_headers = 0;
    SV * keepalive = 0;
    SV * url_tlimits = 0;
    AV * run_group, *tmpav, *tmpav2;
    SV * tmpsv, *tmpsv2 = 0, *tmpsv3;
    HV * tmphv;
    STRLEN len;

    if (AB_DEBUG_XS) printf("AB_DEBUG: start of ab()\n");

    registry->concurrency = 1;
    registry->requests = 0;

src/ApacheBench.xs  view on Meta::CPAN

    def_buffersize = SvIV(tmpsv);

    tmpsv = *(hv_fetch(tmphv, "repeat", 6, 0));
    def_repeat = SvIV(tmpsv);

    tmpsv = *(hv_fetch(tmphv, "memory", 6, 0));
    def_memory = SvIV(tmpsv);

    if (AB_DEBUG_XS) printf("AB_DEBUG: ab() init - stage 1\n");

    tmpsv = *(hv_fetch(tmphv, "keepalive", 9, 0));
    if (SvTRUE(tmpsv))
        def_keepalive = 1;

    if (AB_DEBUG_XS) printf("AB_DEBUG: ab() init - stage 2\n");

    tmpsv = *(hv_fetch(tmphv, "priority", 8, 0));
    pt = SvPV(tmpsv, len);
    if (strcmp(pt, "run_priority") == 0)
        registry->priority = RUN_PRIORITY;
    else {
        registry->priority = EQUAL_OPPORTUNITY;
        if (strcmp(pt, "equal_opportunity") != 0)

src/ApacheBench.xs  view on Meta::CPAN

        if (hv_exists(tmphv, "postdata", 8))
            post_data = *(hv_fetch(tmphv, "postdata", 8, 0));
        if (hv_exists(tmphv, "head_requests", 13))
            head_requests = *(hv_fetch(tmphv, "head_requests", 13, 0));
        if (hv_exists(tmphv, "cookies", 7))
            cookies = *(hv_fetch(tmphv, "cookies", 7, 0));
        if (hv_exists(tmphv, "content_types", 13))
            ctypes = *(hv_fetch(tmphv, "content_types", 13, 0));
        if (hv_exists(tmphv, "request_headers", 15))
            req_headers = *(hv_fetch(tmphv, "request_headers", 15, 0));
        if (hv_exists(tmphv, "keepalive", 9))
            keepalive = *(hv_fetch(tmphv, "keepalive", 9, 0));
        if (hv_exists(tmphv, "timelimits", 10))
            url_tlimits = *(hv_fetch(tmphv, "timelimits", 10, 0));

        /* configure urls */
        for (i = registry->position[k]; i < registry->position[k+1]; i++) {
            tmpav =(AV *) SvRV(urls);
            tmpsv = *(av_fetch(tmpav, i - registry->position[k], 0));
            if (SvPOK(tmpsv)) {
                pt = SvPV(tmpsv, len);
                url_keys[i] = pt;

src/ApacheBench.xs  view on Meta::CPAN

        }

        /*If the number of ctypes strings is less than
          that of urls, then assign NULL (undef) */
        for (j = i; j < registry->position[k+1]; j++)
            registry->ctypes[j] = 0;


        if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 6\n", k);

        /* find smaller of keepalive array length and urls array length */
        tmpav = (AV *) SvRV(keepalive);
        i = ap_min(registry->position[k+1],
                   registry->position[k] + av_len(tmpav) + 1);

        if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 6.1\n", k);

        /* configure keepalive */
        for (j = registry->position[k]; j < i; j++) {
            if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 6.2, j=%d\n", k, j);
            tmpsv = *(av_fetch(tmpav, j - registry->position[k], 0));
            if (SvOK(tmpsv))
                if (SvTRUE(tmpsv))
                    registry->keepalive[j] = 1;
                else
                    registry->keepalive[j] = 0;
            else
                registry->keepalive[j] = def_keepalive;
            if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 6.3, j=%d\n", k, j);
        }

        /*If the number of keepalive strings is less than
          that of urls, then assign object's default keepalive value */
        for (j = i; j < registry->position[k+1]; j++)
            registry->keepalive[j] = def_keepalive;


        if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 7\n", k);

        /* find smaller of url_tlimits array length and urls array length */
        tmpav = (AV *) SvRV(url_tlimits);
        i = ap_min(registry->position[k+1],
                   registry->position[k] + av_len(tmpav) + 1);

        if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 7.1\n", k);

src/apachebench/execute.c  view on Meta::CPAN

                registry->ready_to_run_queue[registry->tail].run = i;
                registry->ready_to_run_queue[registry->tail].thread = j;
                registry->ready_to_run_queue[registry->tail++].url = registry->position[i];
            }
    }
    registry->hostname = malloc(registry->number_of_urls * sizeof(char *));
    registry->path = malloc(registry->number_of_urls * sizeof(char *));
    registry->port = malloc(registry->number_of_urls * sizeof(int));
    registry->ctypes = malloc(registry->number_of_urls * sizeof(char *));
    registry->req_headers = malloc(registry->number_of_urls * sizeof(char *));
    registry->keepalive = malloc(registry->number_of_urls * sizeof(bool));
    registry->url_tlimit = malloc(registry->number_of_urls * sizeof(double));
    registry->started = malloc(registry->number_of_urls * sizeof(int));
    registry->finished = malloc(registry->number_of_urls * sizeof(int));
    registry->failed = malloc(registry->number_of_urls * sizeof(int));
    registry->good = malloc(registry->number_of_urls * sizeof(int));
    registry->postdata = malloc(registry->number_of_urls * sizeof(char *));
    registry->postsubs = malloc(registry->number_of_urls * sizeof(SV *));
    registry->postlen = malloc(registry->number_of_urls * sizeof(int));
    registry->posting = malloc(registry->number_of_urls * sizeof(int));
    registry->totalposted = malloc(registry->number_of_urls * sizeof(int));

src/apachebench/socket_io.c  view on Meta::CPAN

}

/* --------------------------------------------------------- */

/* start asnchronous non-blocking connection */

static void
start_connect(struct global * registry, struct connection * c) {
    c->read = 0;
    c->bread = 0;
    c->keepalive = 0;
    c->cbx = 0;
    c->gotheader = 0;
    c->fd = socket(AF_INET, SOCK_STREAM, 0);

#ifdef AB_DEBUG
    printf("AB_DEBUG: start of start_connect()\n");
#endif

    if (c->fd < 0) {
        myerr(registry->warn_and_error, "socket error");

src/apachebench/socket_io.c  view on Meta::CPAN

             * needs to be extended to handle whatever servers folks want to
             * test against. -djg
             */

            c->gotheader = 1;
            *s = 0;                /* terminate at end of header */
            if (registry->memory[c->run] >= 2) {
                c->response_headers = malloc(CBUFFSIZE * sizeof(char));
                strcpy(c->response_headers, c->cbuff);
            }
            if (registry->keepalive[c->url] &&
                (strstr(c->cbuff, "Keep-Alive") ||
                 strstr(c->cbuff, "keep-alive"))) { /* for benefit of MSIIS */
                char *cl;
                cl = strstr(c->cbuff, "Content-Length:");
                /* handle NCSA, which sends Content-length: */
                if (!cl)
                    cl = strstr(c->cbuff, "Content-length:");
                if (cl) {
                    c->keepalive = 1;
                    c->length = atoi(cl + 16);
                }
            }
            c->bread += c->cbx - (s + wslen - c->cbuff) + r - tocopy;
        }
    } else {
        /* outside header, everything we have read is entity body */
        c->bread += r;
    }

    /*
     * cater for the case where we're using keepalives and doing HEAD
     * requests
     */
    if (c->keepalive &&
        ((c->bread >= c->length) || (registry->posting[c->url] < 0))) {
        /* save current url for checking for hostname/port changes */
        int prev = c->url;

        /* finished a keep-alive connection */
        registry->good[c->url]++;
        registry->finished[c->url]++;

        store_regression_data(registry, c);

        if (++registry->done >= registry->need_to_be_done)
            return;

        if (!schedule_next_request(registry, c))
            return;

        c->length = 0;
        c->gotheader = 0;
        c->cbx = 0;
        c->read = c->bread = 0;
        c->keepalive = 0;

        /* if new hostname/port is different from last hostname/port, or new
           url is *not* keepalive, then we need to close connection and start
           a new connection */
        if (registry->keepalive[c->url] &&
            strcmp(registry->hostname[c->url], registry->hostname[prev]) == 0
            && registry->port[c->url] == registry->port[prev]) {
            write_request(registry, c);
            registry->started[c->url]++;
            c->start_time = c->connect_time;        /* zero connect time with keep-alive */
        } else {
            ab_close(c->fd);
            FD_CLR(c->fd, &registry->readbits);
            FD_CLR(c->fd, &registry->writebits);
            start_connect(registry, c);
        }
    }
}

/* --------------------------------------------------------- */

src/apachebench/socket_io.c  view on Meta::CPAN

                registry->version,
                registry->hostname[i],
                registry->postlen[i],
                ctype);
    }

#ifdef AB_DEBUG
    printf("AB_DEBUG: reset_request() - stage 2\n");
#endif

    if (registry->keepalive[i])
        strcat(c->request_headers, "Connection: Keep-Alive\r\n");
    if (registry->cookie[c->run]) {
        strcat(c->request_headers, "Cookie: ");
        strcat(c->request_headers, registry->cookie[c->run]);
        strcat(c->request_headers, "\r\n");
    }

#ifdef AB_DEBUG
    printf("AB_DEBUG: reset_request() - stage 2.1: c->run %d; c->thread %d\n", c->run, c->thread);
#endif

src/apachebench/types.h  view on Meta::CPAN

#define BREADTH_FIRST	  0

/* ------------------- STRUCTS -------------------------- */

struct connection {
    int fd;
    int state;
    int url;			/* which url are we testing */
    int read;			/* amount of bytes read */
    int bread;			/* amount of body read */
    int length;			/* Content-Length value used for keep-alive */
    char cbuff[CBUFFSIZE];	/* a buffer to store server response header */
    int cbx;			/* offset in cbuffer */
    int keepalive;		/* non-zero if a keep-alive request */
    int gotheader;		/* non-zero if we have the entire header in
				 * cbuff */
    int thread;			/* Thread number */
    int run;

    struct timeval start_time, connect_time, before_postdata_time, sent_request_time, done_time;

    char *request;		/* HTTP request */
    char *request_headers;
    int reqlen;

src/apachebench/types.h  view on Meta::CPAN

    int requests;		/* the max of the repeats */
    double tlimit;		/* global time limit, in seconds */
    struct timeval min_tlimit;	/* minimum of all time limits */
    int *position;		/* The position next run starts */

    char **hostname;		/* host name */
    int *port;			/* port numbers */
    char **path;		/* path name */
    char **ctypes;		/* values for Content-type: headers */
    double *url_tlimit;		/* time limit in seconds for each url */
    bool *keepalive;		/* whether to use Connection: Keep-Alive */

    int *posting;		/* GET if ==0, POST if >0, HEAD if <0 */
    char **postdata, **cookie;	/* datas for post and optional cookie line */
    SV **postsubs;		/* coderefs for post */
    char **req_headers;		/* optional arbitrary request headers to add */
    char ***auto_cookies;	/* cookies extracted from response_headers for the run, i.e. set by http server */
    bool *use_auto_cookies;	/* whether to use auto_cookie feature for the run */
    int *postlen;		/* length of data to be POSTed */
    int *totalposted;		/* total number of bytes posted, inc. headers*/

t/test3_execute.t  view on Meta::CPAN

ok($b->run(0)->request_headers([map {"Accept-Encoding: text/html"} @urls]));

# we make three identical runs except the first will GET, the second will POST,
# and the third HEAD; the second run also uses the HTTP Keep-Alive feature
my $run1 = HTTPD::Bench::ApacheBench::Run->new({
    repeat => $n,
    urls   => [ @urls ],
    order  => "depth_first",
});
$run1->postdata([ map {"post"} @urls ]);
$run1->keepalive([map {1} @urls]);
$b->add_run($run1);
ok($b->run(1), $run1);

my $run2 = HTTPD::Bench::ApacheBench::Run->new({
    repeat => $n,
    urls   => [ @urls ],
    order  => "depth_first",
});
$run2->head_requests([ map {1} @urls ]);
$b->add_run($run2);



( run in 2.055 seconds using v1.01-cache-2.11-cpan-df04353d9ac )