File Coverage

blib/lib/POE/Component/Server/Bayeux/Client.pm
Criterion Covered Total %
statement 18 91 19.7
branch 0 44 0.0
condition 0 23 0.0
subroutine 6 17 35.2
pod 10 10 100.0
total 34 185 18.3


line stmt bran cond sub pod time code
1             package POE::Component::Server::Bayeux::Client;
2              
3             =head1 NAME
4              
5             POE::Component::Server::Bayeux::Client - An object representing a single client of the server
6              
7             =head1 DESCRIPTION
8              
9             Used internally by L.
10              
11             =cut
12              
13 3     3   5511 use strict;
  3         6  
  3         106  
14 3     3   14 use warnings;
  3         6  
  3         75  
15 3     3   3509 use Params::Validate;
  3         43422  
  3         205  
16 3     3   2615 use Data::UUID;
  3         2643  
  3         215  
17 3     3   23 use POE;
  3         7  
  3         46  
18              
19 3     3   1087 use base qw(Class::Accessor);
  3         8  
  3         3643  
20             __PACKAGE__->mk_accessors(qw(
21             request id ip
22             is_error
23             flags
24             server_heap
25             heap
26             session
27             ));
28              
29             my $uuid = Data::UUID->new();
30              
31             =head1 USAGE
32              
33             =head2 new (...)
34              
35             =over 4
36              
37             Arguments:
38              
39             =over 4
40              
41             =item I (required)
42              
43             The server's heap object
44              
45             =item I
46              
47             A L object representing an HTTP-connected client.
48              
49             =item I
50              
51             The clientId. If not given, generates one using L.
52              
53             =item I
54              
55             For locally connected clients, the POE session alias or ID to post back to.
56              
57             =back
58              
59             =back
60              
61             =cut
62              
63             sub new {
64 0     0 1   my $class = shift;
65              
66 0           my %self = validate(@_, {
67             server_heap => 1,
68             request => 0,
69             id => 0,
70             session => 0,
71              
72             flags => { default => {} },
73             heap => { default => {} },
74             });
75 0           my $self = bless \%self, $class;
76              
77 0 0         if ($self->request) {
78 0           $self->ip( $self->request->ip );
79             }
80              
81             # Don't let the client id be arbitrarily defined save by a POE session
82 0 0 0       if ($self->id && ! $self->session && ! $self->server_heap->{clients}{$self->id}) {
      0        
83 0           $self->is_error("Client id '".$self->id."' is invalid");
84 0           return $self;
85             }
86              
87 0 0 0       if (! $self->id || ($self->session && ! $self->server_heap->{clients}{$self->id})) {
      0        
88             # Create a new client id
89 0 0         $self->id( $uuid->create_str() ) unless $self->id();
90 0           my $heap = {
91             created => time,
92             ip => $self->ip,
93             flags => {
94             last_connect => time,
95             },
96             session => $self->session,
97             };
98 0           $self->server_heap->{clients}{ $self->id } = $heap;
99              
100             # Let the manager server know so it can do notifications
101 0 0         $poe_kernel->post( $self->server_heap->{manager},
102             'client_connect', {
103             client_id => $self->id,
104             ($self->session ? (
105             session => $self->session,
106             ) : (
107             ip => $self->ip,
108             )),
109             },
110             );
111             }
112              
113 0           $self->heap( $self->server_heap->{clients}{$self->id} );
114 0 0 0       $self->session( $self->heap->{session} ) if ! $self->session && $self->heap->{session};
115 0           $self->flags( $self->heap->{flags} );
116              
117             # Special: if is_polling, make sure it's still a pending request
118 0 0         if (my $req_id = $self->heap->{flags}{is_polling}) {
119 0 0         delete $self->heap->{flags}{is_polling}
120             if ! defined $self->server_heap->{requests}{$req_id};
121             }
122              
123 0           return $self;
124             }
125              
126             =head1 METHODS
127              
128             =head2 disconnect ()
129              
130             =head2 complete_poll ()
131              
132             =over 4
133              
134             Completes an active poll if there is one
135              
136             =back
137              
138             =cut
139              
140             sub disconnect {
141 0     0 1   my ($self) = @_;
142              
143 0           $self->complete_poll();
144              
145             # Let the manager server know so it can do notifications and unsubscribes
146 0           $poe_kernel->post( $self->server_heap->{manager},
147             'client_disconnect', { client_id => $self->id });
148             }
149              
150             sub complete_poll {
151 0     0 1   my ($self) = @_;
152 0 0         if (my $req_id = $self->flags->{is_polling}) {
153 0           $poe_kernel->post( $self->server_heap->{manager},
154             'complete_request', $req_id );
155             }
156             }
157              
158             =head2 message_acl ($message)
159              
160             =over 4
161              
162             Called with a L, the client is to evaluate
163             wether the message is invalid within the context of the client - as in, perform an
164             authorization check. If there's an error, the message will have it's is_error() field
165             set with the error.
166              
167             =back
168              
169             =cut
170              
171             sub message_acl {
172 0     0 1   my ($self, $message) = @_;
173              
174             # If the client has asked for comment filtered JSON, pass this along to the
175             # request which will be encapsulating the results.
176 0 0         if ($self->flags->{'json-comment-filtered'}) {
177 0           $message->request->json_comment_filtered(1);
178             }
179              
180             # All messages fail if I'm in error
181 0 0         if ($self->is_error) {
182 0           $message->is_error($self->is_error);
183 0           return;
184             }
185              
186 0           $self->server_config->{MessageACL}->($self, $message);
187 0 0         return if $message->is_error;
188             }
189              
190             =head2 is_subscribed ($channel)
191              
192             =over 4
193              
194             Returns boolean of wether the client is subscribed to the literal channel provided
195              
196             =back
197              
198             =cut
199              
200             sub is_subscribed {
201 0     0 1   my ($self, $channel) = @_;
202              
203 0           return exists $self->heap->{subscriptions}{$channel};
204             }
205              
206             =head2 send_message ($message, $subscription_args)
207              
208             =over 4
209              
210             Sends, or queues, the message to the client. $subscription_args is the same hashref that
211             was passed to the server's subscribe() method when this client subscribed to the channel.
212             Structure of the message is same as Bayeux '5.2. Deliver Event message'.
213              
214             =back
215              
216             =cut
217              
218             sub send_message {
219 0     0 1   my ($self, $message, $subscription_args) = @_;
220              
221 0 0         if ($subscription_args->{no_callback}) {
222 0           return;
223             }
224              
225 0 0         if ($self->session) {
226 0   0       my $state = $subscription_args->{state} || 'deliver';
227 0           $poe_kernel->post( $self->session, $state, $message );
228 0           return;
229             }
230              
231 0           $self->check_timeout();
232 0 0         if ($self->is_error()) {
233 0           $self->logger->error("Not sending message to client ".$self->id.": ".$self->is_error);
234 0           return;
235             }
236              
237 0           $self->logger->debug("Queuing message to client ".$self->id);
238 0           push @{ $self->heap->{queued_responses} }, $message;
  0            
239              
240             # Delay flush_queue so that if other responses need to be queued, they'll go out at the same time
241 0     0     $poe_kernel->post($self->server_heap->{manager}, 'delay_sub', 'flush_queue.' . $self->id, 0, sub { $self->flush_queue });
  0            
242             }
243              
244             =head2 check_timeout ()
245              
246             =over 4
247              
248             Checks last time HTTP-connected client performed connected, and removes client if
249             it's stale (according to server arg ConnectTimeout).
250              
251             =back
252              
253             =cut
254              
255             sub check_timeout {
256 0     0 1   my ($self) = @_;
257              
258 0 0         return if $self->session;
259 0 0         return if $self->flags->{is_polling};
260 0           my $connect_timeout = $self->server_heap->{args}{ConnectTimeout};
261 0 0         if (time - $self->flags->{last_connect} < $connect_timeout) {
262 0           return;
263             }
264              
265 0           $self->is_error("Connect timeout; removing client");
266 0           $self->disconnect();
267             }
268              
269             =head2 flush_queue ()
270              
271             =over 4
272              
273             Flush the queue of messages, if there is any, and only if client is currently
274             connected. Only used for HTTP-connected clients.
275              
276             =back
277              
278             =cut
279            
280             sub flush_queue {
281 0     0 1   my ($self) = @_;
282              
283 0 0         return if ! $self->heap->{queued_responses};
284 0 0         return if ! $self->flags->{is_polling};
285              
286 0           my $request = $self->server_heap->{requests}{ $self->flags->{is_polling} };
287 0 0         return if ! $request;
288              
289 0           my $queue = delete $self->heap->{queued_responses};
290 0 0 0       return if ! ref $queue || ref $queue ne 'ARRAY' || int @$queue == 0;
      0        
291              
292 0           $self->logger->debug("Flushing queue to active request on ".$self->id);
293              
294 0           $request->add_response($_) foreach @$queue;
295 0           $self->complete_poll();
296             }
297              
298             =head2 logger ()
299              
300             =over 4
301              
302             Return a reference to the servers logger.
303              
304             =back
305              
306             =cut
307              
308             sub logger {
309 0     0 1   my ($self) = @_;
310              
311 0           return $self->server_heap->{logger};
312             }
313              
314             =head2 server_config ()
315              
316             =over 4
317              
318             Returns the server's args
319              
320             =back
321              
322             =cut
323              
324             sub server_config {
325 0     0 1   my ($self) = @_;
326              
327 0           return $self->server_heap->{args};
328             }
329             =head1 COPYRIGHT
330              
331             Copyright (c) 2008 Eric Waters and XMission LLC (http://www.xmission.com/).
332             All rights reserved. This program is free software; you can redistribute it
333             and/or modify it under the same terms as Perl itself.
334              
335             The full text of the license can be found in the LICENSE file included with
336             this module.
337              
338             =head1 AUTHOR
339              
340             Eric Waters
341              
342             =cut
343              
344             1;