File Coverage

blib/lib/SRS/EPP/Proxy/UA.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1              
2             package SRS::EPP::Proxy::UA;
3             {
4             $SRS::EPP::Proxy::UA::VERSION = '0.22';
5             }
6              
7 2     2   5785 use Moose;
  2         500392  
  2         22  
8 2     2   20236 use MooseX::Params::Validate;
  2         16748  
  2         22  
9 2     2   12889 use LWP::UserAgent;
  2         126552  
  2         112  
10 2     2   1270 use Net::SSLeay::OO;
  0            
  0            
11             use Moose::Util::TypeConstraints;
12             use IO::Handle;
13             use Storable qw(store_fd retrieve_fd);
14              
15             with 'MooseX::Log::Log4perl::Easy';
16              
17             enum __PACKAGE__."::states" => qw(waiting busy ready);
18              
19             BEGIN {
20             class_type "HTTP::Request";
21             class_type "HTTP::Response";
22             class_type "IO::Handle";
23             }
24              
25             has 'write_fh' =>
26             is => "rw",
27             isa => "IO::Handle|GlobRef",
28             ;
29              
30             has 'read_fh' =>
31             is => "rw",
32             isa => "IO::Handle|GlobRef",
33             ;
34              
35             has 'pid' =>
36             is => "rw",
37             isa => "Int",
38             ;
39              
40             has 'state' =>
41             is => "rw",
42             isa => __PACKAGE__."::states",
43             default => "waiting",
44             ;
45              
46             sub busy {
47             my $self = shift;
48            
49             $self->state eq "busy";
50             }
51              
52             sub ready {
53             my $self = shift;
54            
55             if ( $self->busy ) {
56             $self->check_reader_ready;
57             }
58             $self->state eq "ready";
59             }
60              
61             sub waiting {
62             my $self = shift;
63            
64             $self->state eq "waiting";
65             }
66              
67             sub check_reader_ready {
68             my $self = shift;
69            
70             my ( $timeout ) = pos_validated_list(
71             \@_,
72             { isa => 'Num', default => 0 },
73             );
74            
75             my $fh = $self->read_fh;
76             my $rin = '';
77             vec($rin, fileno($fh), 1) = 1;
78             my $win = '';
79             my $ein = $rin;
80             my ($nfound) = select($rin, $win, $ein, $timeout);
81             if ($nfound) {
82             if ( vec($ein, fileno($fh), 1) ) {
83             die "reader handle in error state";
84             }
85             elsif ( vec($rin, fileno($fh), 1) ) {
86             $self->state("ready");
87             return 1;
88             }
89             else {
90             die "??";
91             }
92             }
93             else {
94             return;
95             }
96             }
97              
98             sub BUILD {
99             my $self = shift;
100             {
101             $self->log_trace("setting up pipes...");
102             pipe(my $rq_rdr, my $rq_wtr);
103             pipe(my $rs_rdr, my $rs_wtr);
104             $self->log_trace("forking...");
105             my $pid = fork;
106             defined $pid or die "fork failed; $!";
107             if ($pid) {
108             $self->log_trace(
109             "parent, child pid = $pid, reading from ".fileno($rs_rdr)
110             .", writing to ".fileno($rq_wtr)
111             );
112             $self->pid($pid);
113             $self->read_fh($rs_rdr);
114             $self->write_fh($rq_wtr);
115             return;
116             }
117             else {
118             $self->log_trace(
119             "child, I am $$, reading from "
120             .fileno($rq_rdr).", writing to ".fileno($rs_wtr)
121             );
122             $0 = __PACKAGE__;
123             $self->read_fh($rq_rdr);
124             $self->write_fh($rs_wtr);
125             }
126             }
127             $self->loop;
128             }
129              
130             sub DESTROY {
131             my $self = shift;
132             if (my $pid = $self->pid) {
133             kill 15, $pid;
134             waitpid($pid,0);
135             }
136             }
137              
138             use Storable qw(fd_retrieve store_fd);
139              
140             has 'ua' =>
141             is => "ro",
142             isa => "LWP::UserAgent",
143             lazy => 1,
144             default => sub {
145             LWP::UserAgent->new(
146             agent => __PACKAGE__,
147             timeout => 30, # 'fast' timeout for EPP sessions
148             )
149             };
150              
151             sub loop {
152             my $self = shift;
153            
154             $SIG{TERM} = sub { exit(0) };
155             while (1) {
156             $self->log_debug("UA waiting for request");
157             $0 = __PACKAGE__." - idle";
158             my $request = eval { fd_retrieve($self->read_fh) }
159             or do {
160              
161             #$self->log_error("failed to read request; $@");
162             last;
163             };
164             $self->log_debug("sending a request to back-end");
165             $0 = __PACKAGE__." - active";
166             my $response = $self->ua->request($request);
167             $self->log_debug("got response - writing to response socket");
168             $0 = __PACKAGE__." - responding";
169             store_fd $response, $self->write_fh;
170             $self->write_fh->flush;
171             }
172             $self->log_trace("UA exiting");
173             exit(0);
174             }
175              
176             sub request {
177             my $self = shift;
178            
179             my ( $request ) = pos_validated_list(
180             \@_,
181             { isa => 'HTTP::Request' },
182             );
183            
184             die "sorry, can't handle a request in state '".$self->state."'"
185             unless $self->waiting;
186             $self->log_trace("writing request to child UA socket");
187             store_fd $request, $self->write_fh;
188             $self->write_fh->flush;
189             $self->log_trace("flushed");
190             $self->state("busy");
191             }
192              
193             sub get_response {
194             my $self = shift;
195            
196             die "sorry, not ready yet" unless $self->ready;
197             my $response = retrieve_fd($self->read_fh);
198             $self->state("waiting");
199             return $response;
200             }
201              
202             1;
203              
204             __END__
205              
206             =head1 NAME
207              
208             SRS::EPP::Proxy::UA - subprocess-based UserAgent
209              
210             =head1 SYNOPSIS
211              
212             my $ua = SRS::EPP::Proxy::UA->new; # creates sub-process.
213              
214             $ua->request($req); # off it goes!
215             print "yes" if $ua->busy; # it's busy!
216             sleep 1 until $ua->ready; # do other stuff
217             my $response = $ua->get_response;
218             print "yes" if $ua->waiting; # it's waiting for you!
219              
220             =head1 DESCRIPTION
221              
222             This class provides non-blocking UserAgent behaviour, by using a slave
223             sub-process to call all the blocking L<LWP::UserAgent> functions to do
224             the retrieval.
225              
226             This is done because the L<SRS::EPP::Session> class is designed to be
227             a non-blocking system.
228              
229             =head1 SEE ALSO
230              
231             L<LWP::UserAgent>, L<SRS::EPP::Session>
232              
233             =head1 AUTHOR AND LICENCE
234              
235             Development commissioned by NZ Registry Services, and carried out by
236             Catalyst IT - L<http://www.catalyst.net.nz/>
237              
238             Copyright 2009, 2010, NZ Registry Services. This module is licensed
239             under the Artistic License v2.0, which permits relicensing under other
240             Free Software licenses.
241              
242             =cut