Skip Menu |

This queue is for tickets about the Net-Async-HTTP CPAN distribution.

Report information
The Basics
Id: 98093
Status: resolved
Priority: 0/
Queue: Net-Async-HTTP

People
Owner: Nobody in particular
Requestors: TEAM [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: (no value)
Broken in: 0.35
Fixed in: 0.36



Subject: 304 Not Modified - treated as redirect
$response->is_redirect is true for a 304 reponse, but since there's no Location header this causes a few warnings to appear. See attached script - remove the max_redirects to disable the workaround and hopefully trigger the issue (might need to wait until after cpanday or pick a less-updated file!) cheers, Tom
Subject: cpdl.pl
#!/usr/bin/env perl use strict; use warnings; use feature qw(say); use Net::Async::HTTP; use IO::Async::Loop; my $loop = IO::Async::Loop->new; $loop->add( my $ua = Net::Async::HTTP->new( ) ); my $uri = URI->new('http://cpan.cpantesters.org/RECENT-1h.json'); my $resp = $ua->GET($uri)->get; my $last = $resp->header('Last-Modified'); my $etag = $resp->header('Etag'); say "Date - " . $last; say "Etag - " . $etag; my $req = HTTP::Request->new( GET => $uri, ); $req->header('If-Modified-Since', $last); # $req->header('If-None-Match', '"' . $etag . '"'); use Data::Dumper; my $rslt = $ua->do_request( request => $req, host => $uri->host, max_redirects => 0, )->get; warn Dumper $rslt;
On Sat Aug 16 15:11:37 2014, TEAM wrote: Show quoted text
> $response->is_redirect is true for a 304 reponse, but since there's no > Location header this causes a few warnings to appear.
Ugh; that's annoying: HTTP/Response.pm: sub is_redirect { HTTP::Status::is_redirect (shift->{'_rc'}); } HTTP/Status.pm: sub is_redirect ($) { $_[0] >= 300 && $_[0] < 400; } It'll treat anything as a redirect if it's a 3xx code -- Paul Evans
Fixed. Also fixed is the fact that only GET and HEAD should redirect. -- Paul Evans
Subject: rt98093.patch
=== modified file 'lib/Net/Async/HTTP.pm' --- lib/Net/Async/HTTP.pm 2014-10-14 10:17:32 +0000 +++ lib/Net/Async/HTTP.pm 2014-10-14 10:50:34 +0000 @@ -640,6 +640,18 @@ } ); } +sub _should_redirect +{ + my ( $response ) = @_; + + # Should only redirect if we actually have a Location header + return 0 unless $response->is_redirect and defined $response->header( "Location" ); + + my $req_method = $response->request->method; + # Should only redirect GET or HEAD requests + return $req_method eq "GET" || $req_method eq "HEAD"; +} + sub _do_request { my $self = shift; @@ -725,7 +737,7 @@ while => sub { my $f = shift; return 0 if $f->failure or $f->is_cancelled; - return $response->is_redirect && $redirects--; + return _should_redirect( $response ) && $redirects--; } ); if( $self->{fail_on_error} ) { === modified file 't/05redir.t' --- t/05redir.t 2013-09-10 00:28:12 +0000 +++ t/05redir.t 2014-10-14 10:50:34 +0000 @@ -186,4 +186,77 @@ is( $response->content, "Directory", 'Content of final response to local redirect' ); } +# 304 Not Modified should not redirect (RT98093) +{ + my $peersock; + no warnings 'redefine'; + local *IO::Async::Handle::connect = sub { + my $self = shift; + my %args = @_; + + $args{host} eq "host2" or die "Expected $args{host} eq host2"; + + ( my $selfsock, $peersock ) = IO::Async::OS->socketpair() or die "Cannot create socket pair - $!"; + $self->set_handle( $selfsock ); + + return Future->new->done( $self ); + }; + + my $f = $http->do_request( + uri => URI->new( "http://host2/unmod" ), + + on_redirect => sub { die "Should not be redirected" }, + ); + + my $request_stream = ""; + wait_for_stream { $request_stream =~ m/$CRLF$CRLF/ } $peersock => $request_stream; + + $peersock->syswrite( "HTTP/1.1 304 Not Modified$CRLF" . + $CRLF ); # 304 has no body + + wait_for { $f->is_ready }; + + my $response = $f->get; + is( $response->code, 304, 'HTTP 304 response not redirected' ); +} + +# Methods other than GET and HEAD should not redirect +{ + my $peersock; + no warnings 'redefine'; + local *IO::Async::Handle::connect = sub { + my $self = shift; + my %args = @_; + + $args{host} eq "host3" or die "Expected $args{host} eq host3"; + + ( my $selfsock, $peersock ) = IO::Async::OS->socketpair() or die "Cannot create socket pair - $!"; + $self->set_handle( $selfsock ); + + return Future->new->done( $self ); + }; + + my $f = $http->do_request( + method => "PUT", + uri => URI->new( "http://host3/somewhere" ), + content => "new content", + content_type => "text/plain", + + on_redirect => sub { die "Should not be redirected" }, + ); + + my $request_stream = ""; + wait_for_stream { $request_stream =~ m/$CRLF$CRLF/ } $peersock => $request_stream; + + $peersock->syswrite( "HTTP/1.1 301 Moved Permanently$CRLF" . + "Content-Length: 0$CRLF" . + "Location: /somewhere/else$CRLF" . + $CRLF ); + + wait_for { $f->is_ready }; + + my $response = $f->get; + is( $response->code, 301, 'POST request not redirected' ); +} + done_testing;
Released -- Paul Evans