changes regarding how events are processed. We now state what events
we are waiting for when calling run_loop. The central event dispatcher
monitors this list and decides when to pause the loop. Return value
from run_loop is the event name and whatever arguments were sent by
the event creator, if the event triggered no explicit handler, or
otherwise, the return value(s) from the event handler. If subsequent
events occur between a loop-pausing event and the time the loop
actually pauses, such events are queued and eligible candidates for
return value of the next call to run_loop. This way, events will not
be lost accidentally, which might happen in previous revisions.
git-svn-id: svn+ssh://projects.linpro.no/svn/varnish/trunk@1513
d4fa192b-c00b-0410-8231-
f00ffab90ce4
return if defined $self->{'engine'};
$self->{'engine'} = Varnish::Test::Engine->new(@args);
- $self->{'engine'}->run_loop;
+ $self->{'engine'}->run_loop('ev_varnish_started');
}
sub stop_engine($;$) {
my $vcl = $varnish->backend_block('main') . ${ref($self)."::VCL"};
$varnish->send_vcl(ref($self), $vcl);
- $self->run_loop();
+ $self->run_loop('ev_varnish_command_ok');
$varnish->use_vcl(ref($self));
- $self->run_loop();
+ $self->run_loop('ev_varnish_command_ok');
}
# Start the child
$varnish->start_child();
- $self->run_loop();
+ $self->run_loop('ev_varnish_child_started');
}
sub fini($) {
# Stop the worker process
$varnish->stop_child();
- $self->run_loop();
+ # Wait for both events, the order is unpredictable, so wait for
+ # any of them both times.
+ $self->run_loop('ev_varnish_child_stopped', 'ev_varnish_command_ok');
+ $self->run_loop('ev_varnish_child_stopped', 'ev_varnish_command_ok');
# Revert to initial VCL script
no strict 'refs';
if (${ref($self)."::VCL"}) {
$varnish->use_vcl('boot');
- $self->run_loop();
+ $self->run_loop('ev_varnish_command_ok', 'ev_varnish_command_unknown');
}
delete $self->{'engine'}->{'case'};
}
}
-sub run_loop($) {
- my ($self) = @_;
-
- $self->{'engine'}->run_loop;
-}
-
-sub pause_loop($;@) {
- my ($self, @args) = @_;
+sub run_loop($@) {
+ my ($self, @wait_for) = @_;
- $self->{'engine'}->pause_loop(@args);
+ return $self->{'engine'}->run_loop(@wait_for);
}
sub new_client($) {
return Varnish::Test::Client->new($self->{'engine'});
}
-sub ev_varnish_command_ok($) {
- my ($self) = @_;
-
- $self->pause_loop;
-}
-
sub ev_client_response($$$) {
my ($self, $client, $response) = @_;
- $self->{'engine'}->pause_loop($response);
+ return $response;
}
sub ev_client_timeout($$) {
my ($self, $client) = @_;
$client->shutdown(2);
- $self->{'engine'}->pause_loop;
+ return $client;
}
1;
$request->protocol($cv);
$client->send_request($request, 2);
- my $response = $self->run_loop;
+ my ($event, $response) = $self->run_loop('ev_client_response', 'ev_client_timeout');
- croak 'No (complete) response received' unless defined($response);
+ croak 'Client time-out before receiving a (complete) response'
+ if $event eq 'ev_client_timeout';
croak 'Server was not contacted by Varnish'
if $self->{'engine'}->{'server'}->{'requests'} != $requests + 1;
croak sprintf('Protocol version mismatch: got: %s expected: %s',
my $request = HTTP::Request->new('POST', '/');
$request->protocol('HTTP/1.1');
$client->send_request($request, 2);
- my $response = $self->run_loop;
- croak 'No (complete) response received' unless defined($response);
+
+ my ($event, $response) = $self->run_loop('ev_client_response', 'ev_client_timeout');
+
+ croak 'Client time-out before receiving a (complete) response'
+ if $event eq 'ev_client_timeout';
croak 'Empty body' if $response->content eq '';
croak 'Incorrect body' if $response->content ne $body;
}
+
+ return 'OK';
}
sub ev_server_request($$$$) {
$self->got_response($response);
}
elsif ($data_length < $content_length) {
+ $self->log(sprintf('Partial response. Bytes in body: %d received, %d expected, %d remaining',
+ $data_length, $content_length, $content_length - $data_length));
last;
}
else {
}
}
else {
+ $self->log('Partial response. Content-Length unknown. Expecting CLOSE as end-of-response.');
last;
}
}
my $self = bless({ 'mux' => IO::Multiplex->new,
'controller' => $controller,
- 'config' => \%config }, $class);
+ 'config' => \%config,
+ 'pending' => [] }, $class);
$self->{'server'} = Varnish::Test::Server->new($self);
$self->{'varnish'} = Varnish::Test::Varnish->new($self);
print STDERR $str;
}
-sub run_loop($) {
- my ($self) = @_;
+sub run_loop($@) {
+ my ($self, @wait_for) = @_;
- croak 'Engine::run: Already inside select-loop. Your code is buggy.'
+ croak 'Engine::run_loop: Already inside select-loop. Your code is buggy.'
if exists($self->{'in_loop'});
+ croak 'Engine::run_loop: No events to wait for.'
+ if @wait_for == 0;
+
+ while (@{$self->{'pending'}} > 0) {
+ my ($event, @args) = @{shift @{$self->{'pending'}}};
+ return ($event, @args) if grep({ $_ eq $event } @wait_for);
+ }
+
+ $self->{'wait_for'} = \@wait_for;
$self->{'in_loop'} = 1;
$self->{'mux'}->loop;
delete $self->{'in_loop'};
+ delete $self->{'wait_for'};
- return delete $self->{'return'} if exists $self->{'return'};
+ return @{shift @{$self->{'pending'}}} if @{$self->{'pending'}} > 0;
return undef;
}
-sub pause_loop($;$) {
- my ($self, $return) = @_;
-
- croak 'Engine::pause: Not inside select-loop. Your code is buggy.'
- unless exists($self->{'in_loop'});
-
- $self->{'return'} = $return if defined($return);
- $self->{'mux'}->endloop;
-}
-
sub shutdown($) {
my ($self) = @_;
}
}
-sub ev_varnish_started($) {
- my ($self) = @_;
-
- $self->pause_loop;
-}
-
sub AUTOLOAD ($;@) {
my ($self, @args) = @_;
- (my $event_handler = our $AUTOLOAD) =~ s/.*://;
+ (my $event = our $AUTOLOAD) =~ s/.*://;
+
+ return if $event eq 'DESTROY';
- return if $event_handler eq 'DESTROY';
+ croak sprintf('Unknown method "%s"', $event)
+ unless $event =~ /^ev_(.*)$/;
- croak sprintf('received event (%s) while not running a case', $event_handler)
- unless defined $self->{'case'};
+ $self->log($self, 'ENG: ', sprintf('EVENT "%s"', $1));
- croak sprintf('Unknown method "%s"', $event_handler)
- unless $event_handler =~ /^ev_(.*)$/;
+ @args = $self->{'case'}->$event(@args)
+ if (defined($self->{'case'}) and $self->{'case'}->can($event));
- if ($self->{'case'}->can($event_handler)) {
- $self->log($self, 'ENG: ', sprintf('EVENT "%s"', $1));
- return $self->{'case'}->$event_handler(@args);
+ if (@{$self->{'pending'}} > 0) {
+ push(@{$self->{'pending'}}, [ $event, @args ]);
}
- else {
- $self->log($self, 'ENG: ', sprintf('EVENT "%s" IGNORED', $1));
- return undef;
+ elsif (grep({ $_ eq $event} @{$self->{'wait_for'}}) > 0) {
+ push(@{$self->{'pending'}}, [ $event, @args ]);
+ $self->{'mux'}->endloop;
}
}
$self->log($$data);
- if ($$data =~ /rolling\(2\)\.\.\./) {
+ if ($$data =~ /^rolling\(2\)\.\.\./m) {
$self->{'state'} = 'stopped';
$self->{'engine'}->ev_varnish_started;
}
}
$self->{'engine'}->ev_varnish_command_ok(delete $self->{'pending'})
- if ($$data =~ /^200 0/ and $self->{'pending'});
+ if ($$data =~ /^200 \d+/m and $self->{'pending'});
+
+ $self->{'engine'}->ev_varnish_command_unknown(delete $self->{'pending'})
+ if ($$data =~ /^300 \d+/m and $self->{'pending'});
$$data = '';
}