File Coverage

blib/lib/POE/Component/SmokeBox/Recent/HTTP.pm
Criterion Covered Total %
statement 97 111 87.3
branch 14 28 50.0
condition 10 24 41.6
subroutine 19 20 95.0
pod 1 1 100.0
total 141 184 76.6


line stmt bran cond sub pod time code
1             package POE::Component::SmokeBox::Recent::HTTP;
2             $POE::Component::SmokeBox::Recent::HTTP::VERSION = '1.54';
3             #ABSTRACT: an extremely minimal HTTP client
4              
5 3     3   520890 use strict;
  3         28  
  3         107  
6 3     3   19 use warnings;
  3         6  
  3         137  
7 3     3   17 use POE qw(Filter::HTTP::Parser Component::Client::DNS);
  3         6  
  3         35  
8 3     3   350260 use Net::IP::Minimal qw(ip_get_version);
  3         2569  
  3         301  
9 3     3   1756 use Test::POE::Client::TCP;
  3         66675  
  3         133  
10 3     3   32 use Carp qw(carp croak);
  3         50  
  3         183  
11 3     3   24 use HTTP::Request;
  3         15  
  3         114  
12 3     3   21 use URI;
  3         7  
  3         4423  
13              
14             sub spawn {
15 3     3 1 33509 my $package = shift;
16 3         17 my %opts = @_;
17 3         27 $opts{lc $_} = delete $opts{$_} for keys %opts;
18             croak( "You must provide the 'uri' parameter and it must a URI object and a supported scheme\n" )
19             unless $opts{uri} and $opts{uri}->isa('URI')
20             and $opts{uri}->scheme and $opts{uri}->scheme =~ /^http$/
21 3 50 33     21 and $opts{uri}->host;
      33        
      33        
      33        
22 3         525 my $options = delete $opts{options};
23 3 50       18 $opts{prefix} = 'http_' unless $opts{prefix};
24 3 50       25 $opts{prefix} .= '_' unless $opts{prefix} =~ /\_$/;
25 3         11 my $self = bless \%opts, $package;
26             $self->{session_id} = POE::Session->create(
27             object_states => [
28 3 50       16 $self => { map { ($_,"_$_" ) } qw(web_socket_failed web_connected web_input web_disconnected) },
  12         80  
29             $self => [qw(
30             _start
31             _resolve
32             _response
33             _connect
34             _shutdown
35             _timeout
36             )],
37             ],
38             heap => $self,
39             ( ref($options) eq 'HASH' ? ( options => $options ) : () ),
40             )->ID();
41 3         482 return $self;
42             }
43              
44             sub _start {
45 3     3   996 my ($kernel,$sender,$self) = @_[KERNEL,SENDER,OBJECT];
46 3         45 $self->{session_id} = $_[SESSION]->ID();
47 3 50 33     32 if ( $kernel == $sender and !$self->{session} ) {
48 0         0 croak "Not called from another POE session and 'session' wasn't set\n";
49             }
50 3         6 my $sender_id;
51 3 50       12 if ( $self->{session} ) {
52 0 0       0 if ( my $ref = $kernel->alias_resolve( $self->{session} ) ) {
53 0         0 $sender_id = $ref->ID();
54             }
55             else {
56 0         0 croak "Could not resolve 'session' to a valid POE session\n";
57             }
58             }
59             else {
60 3         10 $sender_id = $sender->ID();
61             }
62 3         24 $kernel->refcount_increment( $sender_id, __PACKAGE__ );
63 3         121 $self->{sender_id} = $sender_id;
64              
65             $self->{_resolver} = POE::Component::Client::DNS->spawn(
66             Alias => 'Resolver-' . $self->{session_id},
67 3         34 );
68              
69 3         3315 $self->{address} = $self->{uri}->host;
70 3         133 $self->{port} = $self->{uri}->port;
71              
72 3         94 $kernel->yield( '_resolve' );
73 3         225 return;
74             }
75              
76             sub _resolve {
77 3     3   2282 my ($kernel,$self) = @_[KERNEL,OBJECT];
78 3 100       20 if ( ip_get_version( $self->{address} ) ) {
79             # It is an address already
80 2         117 $kernel->yield( '_connect', $self->{address} );
81 2         130 return;
82             }
83             my $resp = $self->{_resolver}->resolve(
84             host => $self->{address},
85 1         43 context => { },
86             event => '_response',
87             );
88 1 50       9854 $kernel->yield( '_response', $resp ) if $resp;
89 1         17 return;
90             }
91              
92             sub _response {
93 1     1   19497 my ($kernel,$self,$resp) = @_[KERNEL,OBJECT,ARG0];
94 1 50 33     12 if ( $resp->{error} and $resp->{error} ne 'NOERROR' ) {
95 1         6 $kernel->yield( 'web_socket_failed', $resp->{error} );
96 1         94 return;
97             }
98 0         0 my @answers = $resp->{response}->answer;
99 0         0 foreach my $answer ( $resp->{response}->answer() ) {
100 0 0       0 next if $answer->type !~ /^A/;
101 0         0 $kernel->yield( '_connect', $answer->rdatastr );
102 0         0 return;
103             }
104 0         0 $kernel->yield( 'web_socket_failed', 'Could not resolve address' );
105 0         0 return;
106             }
107              
108             sub _connect {
109 2     2   386 my ($self,$address) = @_[OBJECT,ARG0];
110             $self->{web} = Test::POE::Client::TCP->spawn(
111             address => $address,
112 2   50     44 port => $self->{port} || 80,
113             prefix => 'web',
114             autoconnect => 1,
115             filter => POE::Filter::HTTP::Parser->new( type => 'client' ),
116             );
117 2         2164 return;
118             }
119              
120             sub _web_connected {
121 2     2   8515 my $self = $_[OBJECT];
122 2         26 my $req = HTTP::Request->new( GET => $self->{uri}->path_query );
123 2         353 $req->protocol( 'HTTP/1.1' );
124 2 50       53 $req->header( 'Host', $self->{address} . ( $self->{port} ne '80' ? ":$self->{port}" : '' ) );
125 2         338 $req->user_agent( sprintf( 'POE-Component-SmokeBox-Recent-HTTP/%s (perl; N; POE; en; rv:%f)', $POE::Component::SmokeBox::Recent::HTTP::VERSION, $POE::Component::SmokeBox::Recent::HTTP::VERSION ) );
126 2         211 $self->{web}->send_to_server( $req );
127 2   100     847 $poe_kernel->delay( '_timeout', $self->{timeout} || 60 );
128 2         234 return;
129             }
130              
131             sub _timeout {
132 1     1   10010304 my ($kernel,$self) = @_[KERNEL,OBJECT];
133 1   50     27 $self->_send_event( $self->{prefix} . 'timeout', "Timed out connection after " . ( $self->{timeout} || 60 ) . " seconds." );
134 1         7 $kernel->refcount_decrement( $self->{sender_id}, __PACKAGE__ );
135 1         62 $kernel->yield( '_shutdown' );
136 1         75 return;
137             }
138              
139             sub _web_socket_failed {
140 1     1   236 my ($kernel,$self,@errors) = @_[KERNEL,OBJECT,ARG0..$#_];
141 1         24 $self->_send_event( $self->{prefix} . 'sockerr', @errors );
142 1         12 $kernel->refcount_decrement( $self->{sender_id}, __PACKAGE__ );
143 1         61 $kernel->yield( '_shutdown' );
144 1         85 return;
145             }
146              
147             sub _web_input {
148 1     1   7153 my ($kernel,$self,$resp) = @_[KERNEL,OBJECT,ARG0];
149 1         5 $kernel->delay( '_timeout' );
150 1         101 $self->_send_event( $self->{prefix} . 'response', $resp );
151 1         5 $kernel->refcount_decrement( $self->{sender_id}, __PACKAGE__ );
152 1         55 $self->{web}->shutdown();
153 1         650 delete $self->{web};
154 1         3 return;
155             }
156              
157             sub _web_disconnected {
158 0     0   0 my ($kernel,$self) = @_[KERNEL,OBJECT];
159 0         0 $kernel->yield( '_shutdown' );
160 0         0 return;
161             }
162              
163             sub _send_event {
164 3     3   10 my $self = shift;
165 3         21 $poe_kernel->post( $self->{sender_id}, @_ );
166 3         414 return;
167             }
168              
169             sub _shutdown {
170 2     2   2359 my $self = $_[OBJECT];
171 2         13 $poe_kernel->delay( '_timeout' );
172 2 100       165 $self->{web}->shutdown() if $self->{web};
173 2 50       842 $self->{_resolver}->shutdown() if $self->{_resolver};
174 2         501 delete $self->{web};
175 2         6 delete $self->{_resolver};
176 2         8 return;
177             }
178              
179             'Get me that file, sucker'
180              
181             __END__