Skip Menu |

This queue is for tickets about the Test-WWW-Mechanize-Catalyst CPAN distribution.

Report information
The Basics
Id: 76614
Status: resolved
Priority: 0/
Queue: Test-WWW-Mechanize-Catalyst

People
Owner: Nobody in particular
Requestors: steve [...] purkis.ca
Cc: spurkis [...] sitesell.com
AdminCc:

Bug Information
Severity: Critical
Broken in: 0.57
Fixed in: (no value)



CC: spurkis [...] sitesell.com
Subject: Infinite Loops! max_redirects not respected
On a catalyst page that redirects to itself (or indeed a redirect loop across different pages), Test::WWW::Mechanize::Catalyst will follow redirects infinitely. It should respect LWP::UserAgent's 'max_redirects' property, which it inherits. The attached patch fixes this against v0.57 & trunk (who's tests are not currently passing, but not due to this patch).
Subject: T-W-Mech-Catalyst-max_redirects-0.57.patch
diff -ru Test-WWW-Mechanize-Catalyst-0.57/lib/Test/WWW/Mechanize/Catalyst.pm Test-WWW-Mechanize-Catalyst-0.57-max_redirects/lib/Test/WWW/Mechanize/Catalyst.pm --- Test-WWW-Mechanize-Catalyst-0.57/lib/Test/WWW/Mechanize/Catalyst.pm 2012-04-04 09:05:15.000000000 -0400 +++ Test-WWW-Mechanize-Catalyst-0.57-max_redirects/lib/Test/WWW/Mechanize/Catalyst.pm 2012-04-16 21:53:58.000000000 -0400 @@ -72,7 +72,7 @@ } sub _make_request { - my ( $self, $request ) = @_; + my ( $self, $request, $arg, $size, $previous) = @_; my $response = $self->_do_catalyst_request($request); $response->header( 'Content-Base', $response->request->uri ) @@ -94,31 +94,31 @@ $response->content_type(''); } + # NOTE: cargo-culted redirect checking from LWP::UserAgent: + $response->previous($previous) if $previous; + if ($response->redirects >= $self->max_redirect) { + $response->header("Client-Warning" => + "Redirect loop detected (max_redirect = $self->{max_redirect})"); + return $response; + } + # check if that was a redirect if ( $response->header('Location') && $response->is_redirect && $self->redirect_ok( $request, $response ) ) { - - # remember the old response - my $old_response = $response; + # TODO: this should probably create the request by cloning the original + # request and modifying it as LWP::UserAgent::request does. But for now... # *where* do they want us to redirect to? - my $location = $old_response->header('Location'); + my $location = $response->header('Location'); # no-one *should* be returning non-absolute URLs, but if they # are then we'd better cope with it. Let's create a new URI, using # our request as the base. my $uri = URI->new_abs( $location, $request->uri )->as_string; - - # make a new response, and save the old response in it - $response = $self->_make_request( HTTP::Request->new( GET => $uri ) ); - my $end_of_chain = $response; - while ( $end_of_chain->previous ) # keep going till the end - { - $end_of_chain = $end_of_chain->previous; - } # of the chain... - $end_of_chain->previous($old_response); # ...and add us to it + my $referral = HTTP::Request->new( GET => $uri ); + return $self->request( $referral, $arg, $size, $response ); } else { $response->{_raw_content} = $response->content; } diff -ru Test-WWW-Mechanize-Catalyst-0.57/t/redirect.t Test-WWW-Mechanize-Catalyst-0.57-max_redirects/t/redirect.t --- Test-WWW-Mechanize-Catalyst-0.57/t/redirect.t 2012-04-04 08:51:29.000000000 -0400 +++ Test-WWW-Mechanize-Catalyst-0.57-max_redirects/t/redirect.t 2012-04-16 21:56:08.000000000 -0400 @@ -2,7 +2,7 @@ use strict; use warnings; use lib 'lib'; -use Test::More tests => 29; +use Test::More tests => 33; use lib 't/lib'; use Test::WWW::Mechanize::Catalyst 'Catty'; @@ -37,3 +37,13 @@ $m->get_ok( "$root/redirect_to_utf8_upgraded_string", "redirect using an upgraded utf8 string" ); +# Check for max_redirects support +{ + $m = Test::WWW::Mechanize::Catalyst->new(max_redirect => 1); + is( $m->max_redirect, 1, 'max_redirect set' ); + $m->get( "$root/bonjour" ); + ok( !$m->success, "get /bonjour with max_redirect=1 is not a success" ); + is( $m->response->redirects, 1, 'redirects only once' ); + like( $m->response->header('Client-Warning'), qr/Redirect loop detected/i, + 'sets Client-Warning header' ); +}
Subject: T-W-Mech-Catalyst-max_redirects-trunk.patch
diff -ru trunk/lib/Test/WWW/Mechanize/Catalyst.pm Test-WWW-Mechanize-Catalyst/lib/Test/WWW/Mechanize/Catalyst.pm --- trunk/lib/Test/WWW/Mechanize/Catalyst.pm 2012-04-16 21:46:02.000000000 -0400 +++ Test-WWW-Mechanize-Catalyst/lib/Test/WWW/Mechanize/Catalyst.pm 2012-04-16 21:40:08.000000000 -0400 @@ -72,7 +72,7 @@ } sub _make_request { - my ( $self, $request ) = @_; + my ( $self, $request, $arg, $size, $previous) = @_; my $response = $self->_do_catalyst_request($request); $response->header( 'Content-Base', $response->request->uri ) @@ -94,31 +94,31 @@ $response->content_type(''); } + # NOTE: cargo-culted redirect checking from LWP::UserAgent: + $response->previous($previous) if $previous; + if ($response->redirects >= $self->max_redirect) { + $response->header("Client-Warning" => + "Redirect loop detected (max_redirect = $self->{max_redirect})"); + return $response; + } + # check if that was a redirect if ( $response->header('Location') && $response->is_redirect && $self->redirect_ok( $request, $response ) ) { - - # remember the old response - my $old_response = $response; + # TODO: this should probably create the request by cloning the original + # request and modifying it as LWP::UserAgent::request does. But for now... # *where* do they want us to redirect to? - my $location = $old_response->header('Location'); + my $location = $response->header('Location'); # no-one *should* be returning non-absolute URLs, but if they # are then we'd better cope with it. Let's create a new URI, using # our request as the base. my $uri = URI->new_abs( $location, $request->uri )->as_string; - - # make a new response, and save the old response in it - $response = $self->_make_request( HTTP::Request->new( GET => $uri ) ); - my $end_of_chain = $response; - while ( $end_of_chain->previous ) # keep going till the end - { - $end_of_chain = $end_of_chain->previous; - } # of the chain... - $end_of_chain->previous($old_response); # ...and add us to it + my $referral = HTTP::Request->new( GET => $uri ); + return $self->request( $referral, $arg, $size, $response ); } else { $response->{_raw_content} = $response->content; } diff -ru trunk/t/redirect.t Test-WWW-Mechanize-Catalyst/t/redirect.t --- trunk/t/redirect.t 2012-04-16 21:46:02.000000000 -0400 +++ Test-WWW-Mechanize-Catalyst/t/redirect.t 2012-04-16 21:44:47.000000000 -0400 @@ -2,7 +2,7 @@ use strict; use warnings; use lib 'lib'; -use Test::More tests => 30; +use Test::More tests => 34; use lib 't/lib'; use Test::WWW::Mechanize::Catalyst 'Catty'; use HTTP::Request::Common; @@ -42,3 +42,15 @@ my $uri = URI->new_abs( $loc, $req->uri )->as_string; is_sane_utf8($uri); isnt_flagged_utf8($uri); + + +# Check for max_redirects support +{ + $m = Test::WWW::Mechanize::Catalyst->new(max_redirect => 1); + is( $m->max_redirect, 1, 'max_redirect set' ); + $m->get( "$root/bonjour" ); + ok( !$m->success, "get /bonjour with max_redirect=1 is not a success" ); + is( $m->response->redirects, 1, 'redirects only once' ); + like( $m->response->header('Client-Warning'), qr/Redirect loop detected/i, + 'sets Client-Warning header' ); +}
I've fixed a bug in my patch: when max_redirect=0, Client-Warning headers were still being set. New patch against 0.57 attached. -Steve
Subject: T-W-Mech-Catalyst-max_redirects-0.57.patch
diff -ru Test-WWW-Mechanize-Catalyst-0.57/lib/Test/WWW/Mechanize/Catalyst.pm Test-WWW-Mechanize-Catalyst-0.57-max_redirects/lib/Test/WWW/Mechanize/Catalyst.pm --- Test-WWW-Mechanize-Catalyst-0.57/lib/Test/WWW/Mechanize/Catalyst.pm 2012-04-04 09:05:15.000000000 -0400 +++ Test-WWW-Mechanize-Catalyst-0.57-max_redirects/lib/Test/WWW/Mechanize/Catalyst.pm 2012-04-24 10:00:08.000000000 -0400 @@ -72,7 +72,7 @@ } sub _make_request { - my ( $self, $request ) = @_; + my ( $self, $request, $arg, $size, $previous) = @_; my $response = $self->_do_catalyst_request($request); $response->header( 'Content-Base', $response->request->uri ) @@ -94,31 +94,32 @@ $response->content_type(''); } + # NOTE: cargo-culted redirect checking from LWP::UserAgent: + $response->previous($previous) if $previous; + my $redirects = defined $response->redirects ? $response->redirects : 0; + if ($redirects > 0 and $redirects >= $self->max_redirect) { + $response->header("Client-Warning" => + "Redirect loop detected (max_redirect = $self->{max_redirect})"); + return $response; + } + # check if that was a redirect if ( $response->header('Location') && $response->is_redirect && $self->redirect_ok( $request, $response ) ) { - - # remember the old response - my $old_response = $response; + # TODO: this should probably create the request by cloning the original + # request and modifying it as LWP::UserAgent::request does. But for now... # *where* do they want us to redirect to? - my $location = $old_response->header('Location'); + my $location = $response->header('Location'); # no-one *should* be returning non-absolute URLs, but if they # are then we'd better cope with it. Let's create a new URI, using # our request as the base. my $uri = URI->new_abs( $location, $request->uri )->as_string; - - # make a new response, and save the old response in it - $response = $self->_make_request( HTTP::Request->new( GET => $uri ) ); - my $end_of_chain = $response; - while ( $end_of_chain->previous ) # keep going till the end - { - $end_of_chain = $end_of_chain->previous; - } # of the chain... - $end_of_chain->previous($old_response); # ...and add us to it + my $referral = HTTP::Request->new( GET => $uri ); + return $self->request( $referral, $arg, $size, $response ); } else { $response->{_raw_content} = $response->content; } diff -ru Test-WWW-Mechanize-Catalyst-0.57/t/redirect.t Test-WWW-Mechanize-Catalyst-0.57-max_redirects/t/redirect.t --- Test-WWW-Mechanize-Catalyst-0.57/t/redirect.t 2012-04-04 08:51:29.000000000 -0400 +++ Test-WWW-Mechanize-Catalyst-0.57-max_redirects/t/redirect.t 2012-04-24 09:58:37.000000000 -0400 @@ -2,7 +2,7 @@ use strict; use warnings; use lib 'lib'; -use Test::More tests => 29; +use Test::More tests => 36; use lib 't/lib'; use Test::WWW::Mechanize::Catalyst 'Catty'; @@ -37,3 +37,23 @@ $m->get_ok( "$root/redirect_to_utf8_upgraded_string", "redirect using an upgraded utf8 string" ); +# Check for max_redirects support +{ + $m = Test::WWW::Mechanize::Catalyst->new(max_redirect => 1); + is( $m->max_redirect, 1, 'max_redirect set' ); + + $m->get( "$root/bonjour" ); + ok( !$m->success, "get /bonjour with max_redirect=1 is not a success" ); + is( $m->response->redirects, 1, 'redirects only once' ); + like( $m->response->header('Client-Warning'), qr/Redirect loop detected/i, + 'sets Client-Warning header' ); +} + +# Make sure we can handle max_redirects=0 +{ + $m = Test::WWW::Mechanize::Catalyst->new(max_redirect => 0); + $m->get( "$root/hello" ); + ok( $m->success, "get /hello with max_redirect=1 succeeds" ); + is( $m->response->redirects, 0, 'no redirects' ); + ok( !$m->response->header('Client-Warning'), 'no Client-Warning header' ); +}
And fixed another bug: when max_redirects = 0, it shouldn't redirect but does. See patch.
Subject: T-W-Mech-Catalyst-max_redirects-0.57.patch
diff -ru Test-WWW-Mechanize-Catalyst-0.57/lib/Test/WWW/Mechanize/Catalyst.pm Test-WWW-Mechanize-Catalyst-0.57-max_redirects/lib/Test/WWW/Mechanize/Catalyst.pm --- Test-WWW-Mechanize-Catalyst-0.57/lib/Test/WWW/Mechanize/Catalyst.pm 2012-04-04 09:05:15.000000000 -0400 +++ Test-WWW-Mechanize-Catalyst-0.57-max_redirects/lib/Test/WWW/Mechanize/Catalyst.pm 2012-04-24 13:55:54.000000000 -0400 @@ -72,7 +72,7 @@ } sub _make_request { - my ( $self, $request ) = @_; + my ( $self, $request, $arg, $size, $previous) = @_; my $response = $self->_do_catalyst_request($request); $response->header( 'Content-Base', $response->request->uri ) @@ -94,31 +94,32 @@ $response->content_type(''); } + # NOTE: cargo-culted redirect checking from LWP::UserAgent: + $response->previous($previous) if $previous; + my $redirects = defined $response->redirects ? $response->redirects : 0; + if ($redirects > 0 and $redirects >= $self->max_redirect) { + return $self->_redirect_loop_detected($response); + } + # check if that was a redirect if ( $response->header('Location') && $response->is_redirect && $self->redirect_ok( $request, $response ) ) { + return $self->_redirect_loop_detected($response) if $self->max_redirect <= 0; - # remember the old response - my $old_response = $response; + # TODO: this should probably create the request by cloning the original + # request and modifying it as LWP::UserAgent::request does. But for now... # *where* do they want us to redirect to? - my $location = $old_response->header('Location'); + my $location = $response->header('Location'); # no-one *should* be returning non-absolute URLs, but if they # are then we'd better cope with it. Let's create a new URI, using # our request as the base. my $uri = URI->new_abs( $location, $request->uri )->as_string; - - # make a new response, and save the old response in it - $response = $self->_make_request( HTTP::Request->new( GET => $uri ) ); - my $end_of_chain = $response; - while ( $end_of_chain->previous ) # keep going till the end - { - $end_of_chain = $end_of_chain->previous; - } # of the chain... - $end_of_chain->previous($old_response); # ...and add us to it + my $referral = HTTP::Request->new( GET => $uri ); + return $self->request( $referral, $arg, $size, $response ); } else { $response->{_raw_content} = $response->content; } @@ -126,6 +127,14 @@ return $response; } +sub _redirect_loop_detected { + my ( $self, $response ) = @_; + $response->header("Client-Warning" => + "Redirect loop detected (max_redirect = " . $self->max_redirect . ")"); + $response->{_raw_content} = $response->content; + return $response; +} + sub _set_host_header { my ( $self, $request ) = @_; # If there's no Host header, set one. diff -ru Test-WWW-Mechanize-Catalyst-0.57/t/redirect.t Test-WWW-Mechanize-Catalyst-0.57-max_redirects/t/redirect.t --- Test-WWW-Mechanize-Catalyst-0.57/t/redirect.t 2012-04-04 08:51:29.000000000 -0400 +++ Test-WWW-Mechanize-Catalyst-0.57-max_redirects/t/redirect.t 2012-04-24 13:56:27.000000000 -0400 @@ -2,7 +2,7 @@ use strict; use warnings; use lib 'lib'; -use Test::More tests => 29; +use Test::More tests => 39; use lib 't/lib'; use Test::WWW::Mechanize::Catalyst 'Catty'; @@ -37,3 +37,30 @@ $m->get_ok( "$root/redirect_to_utf8_upgraded_string", "redirect using an upgraded utf8 string" ); +# Check for max_redirects support +{ + $m = Test::WWW::Mechanize::Catalyst->new(max_redirect => 1); + is( $m->max_redirect, 1, 'max_redirect set' ); + + $m->get( "$root/bonjour" ); + ok( !$m->success, "get /bonjour with max_redirect=1 is not a success" ); + is( $m->response->redirects, 1, 'redirects only once' ); + like( $m->response->header('Client-Warning'), qr/Redirect loop detected/i, + 'sets Client-Warning header' ); +} + +# Make sure we can handle max_redirects=0 +{ + $m = Test::WWW::Mechanize::Catalyst->new(max_redirect => 0); + $m->get( "$root/hello" ); + ok( $m->success, "get /hello with max_redirect=0 succeeds" ); + is( $m->response->redirects, 0, 'no redirects' ); + ok( !$m->response->header('Client-Warning'), 'no Client-Warning header' ); + + # shouldn't be redirected if max_redirect == 0 + $m->get( "$root/bonjour" ); + ok( !$m->success, "get /bonjour with max_redirect=0 is not a success" ); + is( $m->response->redirects, 0, 'no redirects' ); + like( $m->response->header('Client-Warning'), qr/Redirect loop detected/i, + 'sets Client-Warning header' ); +}
Thanks for the patch! I've applied it, and it'll be in the next release - which should be as soon as we've worked out another issue. Cheers t0m