App-livehttperf
view release on metacpan or search on metacpan
lib/App/livehttperf.pm view on Meta::CPAN
max_redirect => 0,
timeout => $OPTS{timeout},
keep_alive => 0,
);
if ( $OPTS{concurrency_max} && $OPTS{concurrency_step} ) {
push @concurrency, 1
unless $OPTS{concurrency_step} == 1;
for ( my $c = $OPTS{concurrency_step}; $c <= $OPTS{concurrency_max}; $c += $OPTS{concurrency_step} ) {
push @concurrency, $c;
}
push @concurrency, $OPTS{concurrency_max}
unless $concurrency[-1] == $OPTS{concurrency_max};
} else {
push @concurrency, sort { $a <=> $b } @{ $OPTS{concurrency} };
}
if ( my $xlsx_file = $OPTS{output} ) {
require Excel::Writer::XLSX;
$xls = Excel::Writer::XLSX->new( $xlsx_file );
$xls->set_optimization();
$xls->set_properties(
title => 'Performance tests',
comments => "Generated by App::livehttperf/$App::livehttperf::VERSION",
);
$bold = $xls->add_format();
$bold->set_bold();
$xls_summary = $xls->add_worksheet('Summary');
$xls_urls = $xls->add_worksheet('URLs');
}
}
sub parse_livehttp_log {
local $/ = "----------------------------------------------------------\r\n";
open(my $ifh, "<$OPTS{input}") or die "Cannot open $OPTS{input}: $!\n";
while(my $rrb = <$ifh>) { # Request-Response block
trim($rrb);
my ($url, $req, $res, $req_bytes, $res_bytes);
my @fh = split(/^/, $rrb);
RRB: for(my $i = 0; $i < @fh; $i++) {
my $l = $fh[$i]; # single line
unless ( defined $url ) {
trim($l);
$url = $l;
$i++;
next;
}
# request
if ( ! defined $req && $l =~ /^[A-Z]+ /) {
my $req_hdrs = $l;
my $cl;
REQ: while( defined( $l = $fh[++$i] ) ) {
if ( ! $OPTS{reuse_cookies} && $l =~ /^Cookie/i ) {
next REQ;
}
if ( $l =~ /^HTTP\// ) { # reached response block
$i--;
last REQ;
}
if ( $l =~ /^Content-Length:[ \t]+(\d+)/i ) {
$cl = int($1);
}
$req_hdrs .= $l;
}
$req_hdrs =~ s/\r?\n\z//;
my $post_data;
if ( $cl ) { # post data requires Content-Length
$post_data = substr($req_hdrs, -1 * $cl);
$req_hdrs = substr($req_hdrs, 0, -1 * $cl);
}
$req = HTTP::Request->parse($req_hdrs);
if ( defined $post_data ) {
unless ( length($post_data) == $req->header('Content-Length')) {
die "Content-Length header doesn't match the length of post data:\n$rrb\n$post_data\n",
};
$req->content( $post_data );
}
$req->uri( $url );
if ( $OPTS{hostname} ) {
if ( $req->header('Host') ) {
$req->header( Host => $OPTS{hostname} );
}
my $new_host = $req->uri;
$new_host->host( $OPTS{hostname} );
$req->uri( $new_host );
}
next RRB;
# response
} elsif ( defined $req && $l =~ /^HTTP/ ) {
$l =~ s/\r?\n\z//;
# status line is parsed up to \n by HTTP::Response->parse()
my $res_hdrs = "$l\n";
RES: while( $l = $fh[++$i] ) {
last RES if $l =~ /^\-{58}/;
unless ( $OPTS{reuse_cookies} ) {
next if $l =~ /^Set-Cookie/i;
}
$res_hdrs .= $l;
}
$res = HTTP::Response->parse($res_hdrs);
unless ( $ua_opts{keep_alive} ) {
if ( my $ka = $res->header('Keep-Alive') ) {
my ($max) = $ka =~ /max=(\d+)/;
$ua_opts{keep_alive} = $max || 100;
}
}
last RRB;
}
}
if ( $req ) {
if ( $OPTS{use_delay} ) {
if ( @recs > 0 ) {
my $prev_date = $recs[-1]->{res}->headers->date;
my $cur_date = $res->headers->date;
my $delay = $cur_date - $prev_date;
if ( $delay > 0 ) {
my $delay_sec = $OPTS{max_delay} && $delay > $OPTS{max_delay} ?
$OPTS{max_delay} : $delay;
$total_delays += $delay_sec;
push @recs, $delay_sec;
}
}
}
push @recs, {
req => $req,
res => $res,
req_bytes => length $req->as_string,
res_bytes => 0,
};
$total_urls++;
};
}
}
sub run_tests {
$test_started = [ gettimeofday ];
$|=1;
for my $concurrency ( @concurrency ) {
LOG "\nRunning with concurrency of $concurrency" if INFO;
my $pm = Parallel::ForkManager->new( $concurrency );
$stats{$concurrency} = {
reqs => Statistics::Descriptive::Full->new(),
recs => {},
counts => {
successful_requests => 0,
failed_requests => 0,
bytes_sent => 0,
bytes_recv => 0,
lib/App/livehttperf.pm view on Meta::CPAN
}
$xls_s_row++;
print "Data transfers:\n";
print $t->render, "\n";
print "\n";
}
{
my @columns = ('Concurrency', 'URL', 'Min', 'Max', 'Avg', 'StdDev', 'Median', 'Errors');
$xls_urls->write_row($xls_u_row++, 0, xlsx_row(@columns), $bold) if $xls;
my $t = Text::TabularDisplay->new(@columns);
for (my $rec_no = 1; $rec_no <= @recs; $rec_no++) {
next unless ref $recs[$rec_no-1];
for my $concurrency ( @concurrency ) {
my $rec_stats = $stats{$concurrency}->{recs};
my $rec_errors = $stats{$concurrency}->{errors}->{recs};
my $url = $recs[$rec_no-1]->{req}->uri;
my @row = (
$concurrency,
$url,
( map { sprintf("%.6f", $rec_stats->{$rec_no}->$_() ) } qw( min max mean standard_deviation median ) ),
$rec_errors->{$rec_no} || 0
);
$t->add( @row );
$xls_urls->write_row($xls_u_row++, 0, xlsx_row(@row)) if $xls;
}
}
print "URLs:\n";
print $t->render, "\n";
print "\n";
}
$xls->close if $xls;
}
sub print_usage {
print <<'EOH';
Usage: livehttperf [OPTIONS]
Input:
-i, --input=file Input file with recoreded session from LiveHTTP headers
Firefox extension.
Default: "-" (STDIN)
-nd, --no_delay Send requests one after another without detected delays.
Default: use delay
-md, --max_delay=NUM If using delay, wait for no more then NUM seconds
Default: none
-h, --hostname=STRING Override hostname in requests and set Host header to
STRING.
Default: no change
-rc, --reuse_cookies Use Cookie/Set-Cookie headers from recorded session.
Default: do not reuse
Sessions:
-n, --repeat=NUM Repeat recorded session NUM times.
Default: 10
-t, --timeout=NUM Connection timeout.
Default: 10
-m, --match=STRING In addition to comparing HTTP response status line,
use specified STRING header to confirm successful
request. If provided multiple times all headers have
to match.
Default: none
-c, --concurrency=NUM Run NUM concurrent connections. Can be provided
multiple times.
Default: 1
-cs, --concurrency_step=NUM
-cm, --concurrency_max=NUM
Alternatively specify maximum concurrency and
incremented by provided step value.
Default max: none
Default step: 5
Display and results:
-o, --output=file Save results in Excel 2007 (XLSX) format.
Default: none
-v, --verbose Repeat to increase verbosity level.
Available levels: WARN, INFO, DEBUG, TRACE.
Default: WARN
-q, --quiet Display only results.
Default: none
Examples:
livehttperf -md 1 -cm 20 -o test.xlsx < session.txt
Read session from session.txt, set maximum delay between requests to
1 second and run with concurrency: 1, 5, 10, 15 and 20. Save results
in test.xlsx
livehttperf -nd -m Content-Length -t 5 -c 20 -c 50 < session.txt
Read session from session.txt, require matching Content-Length header
and run without any delay with concurrency: 20 and 50. Save results
in test.xlsx
EOH
}
sub run {
# configure
configure();
# parse input
parse_livehttp_log();
( run in 2.079 seconds using v1.01-cache-2.11-cpan-ecdf5575e8d )