File Coverage

blib/lib/POE/Component/Server/Bayeux/Message/Meta.pm
Criterion Covered Total %
statement 18 145 12.4
branch 0 62 0.0
condition 0 18 0.0
subroutine 6 11 54.5
pod 5 5 100.0
total 29 241 12.0


line stmt bran cond sub pod time code
1             package POE::Component::Server::Bayeux::Message::Meta;
2              
3             =head1 NAME
4              
5             POE::Component::Server::Bayeux::Message::Meta - handles /meta/ channels
6              
7             =head1 DESCRIPTION
8              
9             Subclasses L to implement the /meta/* channels.
10              
11             =cut
12              
13 3     3   2100 use strict;
  3         4  
  3         100  
14 3     3   14 use warnings;
  3         5  
  3         69  
15 3     3   15 use JSON::Any qw(XS);
  3         3  
  3         24  
16 3     3   2875 use Switch;
  3         80008  
  3         23  
17 3     3   564195 use Params::Validate qw(:types);
  3         10  
  3         754  
18 3     3   21 use base qw(POE::Component::Server::Bayeux::Message);
  3         7  
  3         5238  
19              
20             __PACKAGE__->mk_accessors(qw(type));
21              
22             my %known_types = (
23             handshake => {
24             version => 1,
25             supportedConnectionTypes => { type => ARRAYREF },
26             minimumVersion => 0,
27             },
28             connect => {
29             clientId => 1,
30             connectionType => 1,
31             },
32             disconnect => {
33             clientId => 1,
34             },
35             subscribe => {
36             clientId => 1,
37             subscription => 1,
38             },
39             unsubscribe => {
40             clientId => 1,
41             subscription => 1,
42             },
43             );
44              
45             sub new {
46 0     0 1   my $class = shift;
47 0           my $self = $class->SUPER::new(@_);
48              
49             # Extract and save the type of meta message
50              
51 0           my ($type) = $self->channel =~ m{^/meta/(.+)$};
52 0 0 0       if (! $type || ! $known_types{$type}) {
53 0           $self->request->error("Invalid channel ".$self->channel);
54 0           return;
55             }
56 0           $self->type($type);
57              
58 0           return $self;
59             }
60              
61             sub validate_fields {
62 0     0 1   my ($self) = @_;
63              
64 0           my %validate_spec = %{ $known_types{ $self->type } };
  0            
65 0           $self->SUPER::validate_fields(%validate_spec);
66             }
67              
68             sub pre_handle {
69 0     0 1   my ($self) = @_;
70              
71 0 0         return if $self->is_error;
72              
73 0 0         if ($self->type eq 'connect') {
74             # Connect needs to be the only connect message in the stack, and must be first
75              
76 0           my @new_order = ( $self );
77 0           foreach my $message (@{ $self->request->messages }) {
  0            
78             # Stringify hashref to find self
79 0 0         next if $message eq $self;
80              
81 0 0 0       if ($message->isa(__PACKAGE__) && $message->type eq 'connect') {
82 0           $message->is_error("Can only have on connect message per request");
83 0           next;
84             }
85              
86 0           push @new_order, $message;
87             }
88              
89 0           $self->request->messages( \@new_order );
90             }
91             }
92              
93             sub handle {
94 0     0 1   my ($self) = @_;
95              
96             # Class handle() will call validate_fields()
97 0           $self->SUPER::handle();
98              
99             # Message may be in error, but the format of error return is dependent on
100             # the type of message we're responding to.
101              
102 0           my @responses;
103              
104 0           switch ($self->type) {
  0            
  0            
  0            
105 0 0         case 'handshake' {
  0            
106             # Must ignore any other messages sent in this request
107 0           $self->request->clear_stack();
108              
109 0           my $client;
110 0 0         if (! $self->is_error) {
111             # Get the client by (possibly) generating a new client, passing the extra
112             # params in case they contain auth info.
113 0           $client = $self->request->client();
114              
115             # Run through acl (may set is_error flag)
116 0           $client->message_acl($self);
117             }
118              
119 0 0         if ($self->is_error) {
120 0           push @responses, {
121             successful => JSON::XS::false,
122             error => $self->is_error,
123             };
124 0           last;
125             }
126              
127             # TODO: Find a common connectionType
128 0           my $supported_connection_types = $POE::Component::Server::Bayeux::supported_connection_types;
129              
130 0           my %response = (
131             version => $POE::Component::Server::Bayeux::protocol_version,
132             minimumVersion => $POE::Component::Server::Bayeux::protocol_version,
133             supportedConnectionTypes => $supported_connection_types,
134             successful => JSON::XS::true,
135             clientId => $client->id,
136             advice => {
137             timeout => 2 * 60 * 1000,
138             interval => 0,
139             reconnect => 'retry',
140             },
141             ext => {
142             'json-comment-filtered' => JSON::XS::true,
143             },
144             );
145              
146             # Remember client support for json-comment-filtered
147 0 0 0       if ($self->ext && $self->ext->{'json-comment-filtered'}) {
148 0           $client->flags->{'json-comment-filtered'} = 1;
149             }
150              
151 0           push @responses, \%response;
152 0           }
  0            
  0            
  0            
153 0 0         case 'connect' {
  0            
154 0           my $client;
155 0 0         if (! $self->is_error) {
156 0           $client = $self->request->client($self->clientId);
157 0           $client->message_acl($self);
158 0 0         $self->is_error($client->is_error) if $client->is_error;
159             }
160              
161 0 0 0       if (! $self->error && $client->flags->{is_polling}) {
162 0           $self->is_error("Client ".$self->clientId." is polling already (".$client->flags->{is_polling}.")");
163             }
164              
165 0 0         if ($self->is_error) {
166 0           push @responses, {
167             successful => JSON::XS::false,
168             error => $self->is_error,
169             clientId => $self->clientId,
170             advice => {
171             reconnect => 'handshake',
172             },
173             };
174 0           last;
175             }
176              
177 0           $client->flags->{is_polling} = $self->request->id;
178              
179 0           push @responses, {
180             successful => JSON::XS::true,
181             clientId => $client->id,
182             advice => {
183             timeout => 2 * 60 * 1000,
184             interval => 0,
185             reconnect => 'retry',
186             },
187             };
188              
189 0           my $no_delay = 0;
190              
191             # Handle queued responses
192 0 0         if (my $queue = delete $client->heap->{queued_responses}) {
193 0           push @responses, @$queue;
194 0           $no_delay = 1;
195             }
196              
197             # Don't delay the first time they connect
198 0 0         if (++$client->flags->{connect_times} == 1) {
199 0           $no_delay = 1;
200             }
201 0           $client->flags->{last_connect} = time;
202              
203             # Come back to me to record last_connect time at end of connect
204 0           $self->request->add_post_handle($self);
205              
206 0 0         $self->request->delay(120) unless $no_delay;
207 0           }
  0            
  0            
  0            
208 0 0         case 'disconnect' {
  0            
209 0           my $client;
210 0 0         if (! $self->is_error) {
211 0           $client = $self->request->client($self->clientId);
212 0           $client->message_acl($self);
213 0           $client->disconnect();
214             }
215              
216 0 0         push @responses, {
    0          
217             successful => $self->is_error ? JSON::XS::false : JSON::XS::true,
218             clientId => $client->id,
219             ($self->is_error ? ( error => $self->is_error ) : () ),
220             };
221 0           }
  0            
  0            
  0            
222 0 0         case 'subscribe' {
  0            
223 0           my $client;
224 0 0         if (! $self->is_error) {
225 0           $client = $self->request->client($self->clientId);
226 0           $client->message_acl($self);
227             }
228              
229 0 0         if ($self->is_error) {
230 0           push @responses, {
231             successful => JSON::XS::false,
232             error => $self->is_error,
233             clientId => $self->clientId,
234             subscription => $self->subscription,
235             };
236 0           last;
237             }
238              
239 0           push @responses, {
240             successful => JSON::XS::true,
241             clientId => $client->id,
242             subscription => $self->subscription,
243             };
244              
245             # Don't record a subscription to /meta or /service
246 0 0         if ($self->subscription !~ m{^/(meta|service)/}) {
247 0           $self->request->subscribe($client->id, $self->subscription);
248             }
249 0           }
  0            
  0            
  0            
250 0 0         case 'unsubscribe' {
  0            
251 0           my $client;
252 0 0         if (! $self->is_error) {
253 0           $client = $self->request->client($self->clientId);
254 0           $client->message_acl($self);
255             }
256            
257 0 0 0       if (! $self->is_error && ! $client->is_subscribed($self->subscription)) {
258 0           $self->is_error("Client not subscribed to '" . $self->subscription . "'");
259             }
260              
261 0           $self->request->unsubscribe($client->id, $self->subscription);
262              
263 0 0         push @responses, {
264             clientId => $self->clientId,
265             subscription => $self->subscription,
266             ($self->is_error ? (
267             successful => JSON::XS::false,
268             error => $self->is_error,
269             ) : (
270             successful => JSON::XS::true,
271             ))
272             };
273 0           }
  0            
  0            
  0            
274             }
275              
276 0           foreach my $response (@responses) {
277 0   0       $response->{channel} ||= $self->channel;
278 0 0         $response->{id} = $self->id if $self->id;
279 0           $self->request->add_response($response);
280             }
281             }
282              
283             sub post_handle {
284 0     0 1   my ($self) = @_;
285              
286 0 0         return unless $self->type eq 'connect';
287              
288 0           my $client = $self->request->client($self->clientId);
289 0           $client->flags->{last_connect} = time;
290             }
291              
292             =head1 COPYRIGHT
293              
294             Copyright (c) 2008 Eric Waters and XMission LLC (http://www.xmission.com/).
295             All rights reserved. This program is free software; you can redistribute it
296             and/or modify it under the same terms as Perl itself.
297              
298             The full text of the license can be found in the LICENSE file included with
299             this module.
300              
301             =head1 AUTHOR
302              
303             Eric Waters
304              
305             =cut
306              
307             1;