File Coverage

blib/lib/Feed/Find.pm
Criterion Covered Total %
statement 75 79 94.9
branch 22 30 73.3
condition 3 6 50.0
subroutine 14 14 100.0
pod 2 2 100.0
total 116 131 88.5


line stmt bran cond sub pod time code
1             package Feed::Find;
2 3     3   218364 use strict;
  3         26  
  3         136  
3 3     3   19 use warnings;
  3         5  
  3         77  
4 3     3   62 use 5.008_001;
  3         12  
5              
6 3     3   16 use base qw( Class::ErrorHandler );
  3         6  
  3         1479  
7 3     3   2244 use LWP::UserAgent;
  3         101382  
  3         105  
8 3     3   1753 use HTML::Parser;
  3         17731  
  3         113  
9 3     3   19 use URI;
  3         8  
  3         86  
10 3     3   15 use Carp;
  3         6  
  3         226  
11              
12 3     3   17 use vars qw( $VERSION $ua );
  3         8  
  3         192  
13             $VERSION = '0.13';
14              
15 3         2673 use constant FEED_MIME_TYPES => [
16             'application/x.atom+xml',
17             'application/atom+xml',
18             'application/xml',
19             'text/xml',
20             'application/rss+xml',
21             'application/rdf+xml',
22 3     3   20 ];
  3         6  
23              
24             my $FEED_EXT = qr/\.(?:rss|xml|rdf|atom)$/;
25             my %IsFeed = map { $_ => 1 } @{ FEED_MIME_TYPES() };
26              
27             sub find {
28 4     4 1 6655 my $class = shift;
29 4         25 my($uri) = @_;
30 4 100       24 $ua = LWP::UserAgent->new unless defined $ua;
31 4         3170 $ua->env_proxy;
32 4         35377 $ua->agent(join '/', $class, $class->VERSION);
33 4         317 $ua->parse_head(0); ## We're already basically doing this ourselves.
34 4         322 my $req = HTTP::Request->new(GET => $uri);
35 4         13805 my $p = HTML::Parser->new(api_version => 3,
36             start_h => [ \&_find_links, 'self,tagname,attr' ]);
37 4         223 $p->{base_uri} = $uri;
38 4         13 $p->{feeds} = [];
39             my $res = $ua->request($req, sub {
40 7     7   26366 my($chunk, $res, $proto) = @_;
41 7 50       35 if ($IsFeed{$res->content_type}) {
42 0         0 push @{ $p->{feeds} }, $uri;
  0         0  
43 0         0 croak 'Done parsing';
44             }
45 7 100       470 $p->parse($chunk) or croak 'Done parsing';
46 4         43 });
47 4 50       1574 return $class->error($res->status_line) unless $res->is_success;
48 4         36 return @{ $p->{feeds} };
  4         55  
49             }
50              
51             sub find_in_html {
52 2     2 1 2512 my $class = shift;
53 2         5 my($html, $base_uri) = @_;
54 2         12 my $p = HTML::Parser->new(api_version => 3,
55             start_h => [ \&_find_links, 'self,tagname,attr' ]);
56 2         80 $p->{base_uri} = $base_uri;
57 2         5 $p->{feeds} = [];
58 2         17 $p->parse($$html);
59 2         3 return @{ $p->{feeds} };
  2         12  
60             }
61              
62             sub _find_links {
63 294     294   524 my($p, $tag, $attr) = @_;
64              
65 294         454 my %head_tag = map { $_ => 1 }
  2058         3014  
66             qw[ meta isindex title script style head html ];
67              
68 294         507 my $base_uri = $p->{base_uri};
69 294 100       606 if ($tag eq 'link') {
    50          
    100          
    100          
70 114 50       201 return unless $attr->{rel};
71 114         267 my %rel = map { $_ => 1 } split /\s+/, lc($attr->{rel});
  114         257  
72 114         176 my $type = '';
73 114 100       201 if ($attr->{type}) {
74 51         161 ($type = lc $attr->{type}) =~ s/^\s*//;
75 51         202 $type =~ s/\s*$//;
76             }
77 3         39 push @{ $p->{feeds} }, URI->new_abs($attr->{href}, $base_uri)->as_string
78             if $IsFeed{$type} &&
79 114 50 33     275 ($rel{alternate} || $rel{'service.feed'});
      66        
80             } elsif ($tag eq 'base') {
81 0 0       0 $p->{base_uri} = $attr->{href} if $attr->{href};
82             } elsif ($head_tag{$tag}) {
83             ## Ignore other valid tags inside of .
84             } elsif ($tag eq 'a') {
85 12 50       26 my $href = $attr->{href} or return;
86 12         39 my $uri = URI->new($href);
87 12 100       2370 push @{ $p->{feeds} }, URI->new_abs($href, $base_uri)->as_string
  3         55  
88             if $uri->path =~ /$FEED_EXT/io;
89             } else {
90             ## Anything else indicates the start of the ,
91             ## so we stop parsing.
92 45 100       52 $p->eof if @{ $p->{feeds} };
  45         117  
93             }
94              
95 294         6550 return;
96             }
97              
98             1;
99             __END__