File Coverage

blib/lib/HTTP/Recorder/Httperf.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package HTTP::Recorder::Httperf;
2 2     2   3669 use base 'HTTP::Recorder';
  2         4  
  2         3312  
3 2     2   14 use strict;
  2         8  
  2         70  
4 2     2   193 use warnings;
  2         4  
  2         158  
5 2     2   1309 use HTTP::Recorder::Httperf::Logger;
  0            
  0            
6             use File::Temp ();
7              
8             $HTTP::Recorder::Httperf::VERSION = 0.02;
9             =pod
10              
11             =head1 NAME
12              
13             HTTP::Recorder::Httperf - An HTTP::Recorder subclass to record user actions as input for httperf for load testing
14              
15             =head1 SYNOPSIS
16              
17             Use it almost exactly like you would use L. In fact, this synopsis comes almost
18             exactly 'as is' from the L documentation only changing 'HTTP::Recorder' to 'HTTP::Recorder::Httperf'.
19              
20             #!/usr/bin/perl
21            
22             use HTTP::Proxy;
23             use HTTP::Recorder::Httperf;
24            
25             my $proxy = HTTP::Proxy->new();
26            
27             # create a new HTTP::Recorder::Httperf object
28             my $agent = new HTTP::Recorder::Httperf;
29            
30             # set the log file (optional)
31             $agent->file("/tmp/myfile");
32            
33             # set HTTP::Recorder as the agent for the proxy
34             $proxy->agent( $agent );
35            
36             # start the proxy
37             $proxy->start();
38            
39             1;
40              
41             Now let's look at our 'file' (I) and it might look something like this...
42              
43             #new session definition
44             /foo.html think=2.0
45             /pict1.gif
46             /pict2.gif
47             /foo2.html method=POST contents=’Post data’
48             /pict3.gif
49             /pict4.gif
50            
51             #new session definition
52             /foo3.html method=POST contents="Multiline\ndata"
53             /foo4.html method=HEAD
54              
55             This sample httperf session file comes straight from the httperf manpage. If you would like
56             more information on the specific syntax of this file or how to edit it then please see the
57             httperf documentation.
58              
59             Then you can run httperf to load test your recorded session with something like this
60              
61             httperf --server 192.168.1.2 --wsesslog 100,2,/tmp/myfile --max-piped-calls=5 --rate 10
62              
63              
64             =head1 DESCRIPTION
65              
66             This module is a subclass of L but instead of recording the user's actions
67             as a L script they are instead recorded into a session file to be used by
68             httperf, a load testing engine for testing websevers (L).
69              
70             It's use is almost exactly the same as L. Some methods have been added for convenience
71             for httperf specific functionality. Please be familiar with L and it's documentation
72             before proceeding to use this module as it will probably answer most of your questions.
73              
74             =head1 METHODS
75              
76             =head2 new([%args])
77              
78             This is the constuctor method. Any arguments passed into this method will passed directly to
79             L except for the 'logger' argument which will be overridden with a new L
80             object.
81              
82             In addition to the name-value pairs that L takes, this method will also accept the
83             following arguments.
84              
85             =over 8
86              
87             =item default_think
88              
89             This value set's the default 'think' value (time in seconds) for each request in this
90             session (see httperf documentation). If this value isn't set (or undef) then HTTP::Recorder::Httperf will
91             try and estimate the think time looking at the user's actual browsing. By default it is 'undef'
92              
93             =item burst_threshold
94              
95             This value set's the time in seconds between requests where they would be considered a part of
96             a burst. If this is not set then it defaults to 1 sec.
97              
98             =item temp_file
99              
100             HTTP::Recorder::Httperf uses a temporary file to store data about the time of requests. By default
101             this is named '.httperf_recorder_time'. You can change it as you see fit.
102              
103             =back
104              
105             =head2 default_think([$value])
106              
107             This accessor/mutator method will return the current value for the 'default_think' time in seconds between
108             requests in this session. If $value is given it will set the current 'default_think' first to $value and
109             then return it. If it hasn't been set, it will return undef.
110              
111             =head2 burst_threshold([$value])
112            
113             This accessor/mutator method will return the current value for the 'burst_threshold' time in seconds.
114             See L. If $value is given it will set the current 'burst_threshold' first to $value and
115             then return it.
116              
117             =head2 temp_file([$value])
118            
119             This accessor/mutator method will return the current name of the 'temp_file'.
120             See L. If $value is given it will set the current 'temp_file' first to $value and
121             then return it.
122              
123             =head1 CAVEATS
124              
125             =over 8
126            
127             =item *
128              
129             HTTP::Recorder::Httperf will try and create files (the session log and temp files) in the current directory
130             so the user running the proxy script must have appropriate permissions for the current working directory.
131              
132             =back
133              
134             =head1 AUTHOR
135              
136             Michael Peters
137              
138             =head1 SEE ALSO
139              
140             httperf L,
141             L, L, L
142              
143             =cut
144             sub new
145             {
146             my ($class, %args) = @_;
147              
148             my $default_think = $args{default_think};
149             delete($args{default_think});
150             my $temp_file = $args{temp_file};
151             delete($args{temp_file});
152             my $burst_threshold = $args{burst_threshold};
153             delete($args{burst_threshold});
154              
155             my $self = $class->SUPER::new(%args);
156             bless $self, $class;
157             $self->{default_think} = $default_think || undef;
158             $self->{temp_file} = $temp_file || '.httperf_recorder_time';
159             $self->{burst_threshold} = $burst_threshold || 1;
160              
161             #create a new HTTP::Recorder::httper::Logger object and store it as my 'logger'
162             $self->logger(HTTP::Recorder::Httperf::Logger->new(file => $args{file}));
163             return $self;
164             }
165              
166              
167             #this is where the fun stuff of logging the httperf session file takes place
168             sub modify_request
169             {
170             my ($self, $request) = @_;
171             my ($think, $indent) = ($self->{default_think}, 0);
172            
173             #if we don't have the default_think time then go and get it
174             if(!defined($think))
175             {
176             #get the current time
177             my $cur_time = time();
178             #get the last time a request was run
179             my $last_time = $self->_get_temp_time();
180             #now set think and indent
181             $think = $last_time ? $cur_time - $last_time: $last_time;
182             $self->_set_temp_time(time());
183             #it can only be indented if it isn't the first (ie, there was a last time)
184             $indent = $think <= $self->{burst_threshold} if($last_time);
185             }
186              
187             #get the uri of the request
188             my $uri = $request->uri->path();
189             $uri = '/' if(!$uri); #add an empty '/' if there is no path
190             $uri .= '?' . $request->uri->query if($request->uri->query);
191             #now log this line
192             my $content = $request->content();
193             if($content)
194             {
195             $content =~ s/\r?\n/\\/g;
196             $content =~ s/"/\\"/g;
197             }
198             my $line = $indent ? ' ' : '';
199             $line .= "$uri method=" . $request->method();
200             $line .= " contents=\"$content\"" if($content);
201             $line .= " think=$think" if($think && !$indent);
202             $line .= "\n";
203             $self->{logger}->Log($line);
204              
205             return $request;
206             }
207              
208              
209             sub _get_temp_time
210             {
211             my $self = shift;
212             my $line = 0;
213             #open up the temp file
214             if( -e $self->{temp_file})
215             {
216             open(FILE, $self->{temp_file}) or
217             die "Couldn't open " . $self->{temp_file} . ": $!";
218             #get the time from the first line
219             $line = || 0;
220             close(FILE) or die "Couldn't close " . $self->{temp_file} . ": $!";
221             chomp($line) if($line);
222             }
223             return $line;
224             }
225              
226              
227             sub _set_temp_time
228             {
229             my ($self, $time) = @_;
230             #now write the time
231             my $fh = File::Temp->new(TEMPLATE => '/tmp/httperf_recorder_XXXX', UNLINK => 0);
232             print $fh $time;
233             rename($fh->filename(), $self->{temp_file}) or
234             die "Couln't rename " . $self->{temp_file} . ": $!";
235             }
236              
237              
238              
239             #just a blank method so that the response isn't modified, so just return the response
240             sub modify_response
241             { return $_[1] }
242              
243             #accessor/mutators
244             sub default_think
245             {
246             my ($self, $value) = @_;
247             $self->{default_think} = $value if(defined $value);
248             return $self->{default_think};
249             }
250              
251             sub temp_file
252             {
253             my ($self, $value) = @_;
254             $self->{temp_file} = $value if(defined $value);
255             return $self->{temp_file};
256             }
257              
258             sub burst_threshold
259             {
260             my ($self, $value) = @_;
261             $self->{burst_threshold} = $value if(defined $value);
262             return $self->{burst_threshold};
263             }
264              
265              
266              
267              
268              
269             1;
270