File Coverage

blib/lib/Message/Passing/Input/Freeswitch.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package Message::Passing::Input::Freeswitch;
2 2     2   38127 use Moose;
  0            
  0            
3             use AnyEvent;
4             use Scalar::Util qw/ weaken /;
5             use Try::Tiny qw/ try catch /;
6             use namespace::autoclean;
7              
8             our $VERSION = '0.006';
9             $VERSION = eval $VERSION;
10              
11             with qw/
12             Message::Passing::Role::Input
13             Message::Passing::Role::HasHostnameAndPort
14             /;
15              
16             sub _default_port { 8021 }
17              
18             has secret => (
19             is => 'ro',
20             isa => 'Str',
21             required => 1,
22             );
23              
24             has connection_retry_timeout => (
25             is => 'ro',
26             isa => 'Int',
27             default => 3,
28             );
29              
30             has event_types => (
31             is => 'ro',
32             isa => 'ArrayRef[Str]',
33             default => sub { ['all'] },
34             );
35              
36             has '_connection' => (
37             isa => 'ESL::ESLconnection',
38             lazy => 1,
39             default => sub {
40             my $self = shift;
41             require ESL;
42             # FIXME Retarded SWIG bindings want the port number as a string, not an int
43             # so we explicitly stringify it
44             my $con = new ESL::ESLconnection($self->hostname, $self->port."", $self->secret);
45             unless ($con) {
46             warn("Could not connect to freeswitch on " . $self->hostname . ":" . $self->port);
47             $self->_terminate_connection($self->connection_retry_timeout);
48             $con = bless {}, 'ESL::ESLconnection';
49             }
50             foreach my $name (@{$self->event_types}) {
51             $con->events("plain", $name);
52             }
53             $con->connected() || do {
54             $con->disconnect;
55             $self->_terminate_connection($self->connection_retry_timeout);
56             };
57             return $con;
58             },
59             is => 'ro',
60             clearer => '_clear_connection',
61             handles => {
62             _connection_fd => 'socketDescriptor',
63             },
64             );
65              
66             sub _try_rx {
67             my $self = shift;
68             my $con = $self->_connection;
69             if (!$con->connected) {
70             $self->_terminate_connection;
71             return;
72             }
73             my $e = $con->recvEventTimed(0);
74              
75             if ($e) {
76             my %data;
77             my $h = $e->firstHeader();
78             while ($h) {
79             $data{$h} = $e->getHeader($h);
80             $h = $e->nextHeader();
81             }
82             $self->output_to->consume(\%data);
83             return 1;
84             }
85             return;
86             }
87              
88             has _io_reader => (
89             is => 'ro',
90             lazy => 1,
91             default => sub {
92             my $weak_self = shift;
93             weaken($weak_self);
94             my $fd = $weak_self->_connection_fd;
95             return unless $fd >= 0;
96             AE::io $fd, 0,
97             sub { my $more; do { $more = $weak_self->_try_rx } while ($more) };
98             },
99             clearer => '_clear_io_reader',
100             );
101              
102             sub _terminate_connection {
103             my ($self, $retry) = @_;
104             $retry ||= 0;
105             weaken($self);
106             # Push building the io reader here as an idle task,
107             # to avoid blowing up the stack.... (We're already in a callback here)
108             # This probably isn't totally necessary, but avoids potential recursion issues.
109             my $i; $i = AnyEvent->timer(
110             after => $retry,
111             cb => sub {
112             undef $i;
113             $self->_clear_io_reader;
114             $self->_clear_connection;
115             $self->_io_reader;
116             },
117             );
118             }
119              
120             has _connection_checker => (
121             is => 'ro',
122             lazy => 1,
123             default => sub {
124             my $self = shift;
125             weaken($self);
126             AnyEvent->timer( after => $self->connection_retry_timeout, every => $self->connection_retry_timeout,
127             cb => sub {
128             $self->_try_rx;
129             },
130             );
131             },
132             );
133              
134             sub BUILD {
135             my $self = shift;
136             $self->_io_reader;
137             $self->_connection_checker;
138             }
139              
140             1;
141              
142             =head1 NAME
143              
144             Message::Passing::Input::Freeswitch - input messages from Freeswitch.
145              
146             =head1 SYNOPSIS
147              
148             message-pass --input Freeswitch --input_options \
149             '{"hostname":"127.0.0.1","secret":"s3kriTk3y"}' \
150             --output STDOUT
151              
152             =head1 DESCRIPTION
153              
154             Produces a message stream from a L<Freeswitch|http://www.freeswitch.org/>
155             instance.
156              
157             Uses the Freeswitch L<|Event Socket Library|http://wiki.freeswitch.org/wiki/Event_Socket_Library>
158             to connect to a local or remote Freeswitch instance and stream event messages.
159              
160             =head1 ATTRIBUTES
161              
162             =head2 hostname
163              
164             The Freeswitch host to connect to.
165              
166             =head2 secret
167              
168             The secret configured in Freeswitch for connecting to the event listener socket.
169              
170             =head2 connection_retry_timeout
171              
172             The number of seconds to wait after a disconnect before reconnecting. Default 3.
173              
174             =head2 port
175              
176             The port that Freeswitch's ESL socket is listening on. Defaults to 8021.
177              
178             =head2 events
179              
180             An arrayref of types of events to listen for. These types are documented
181             L<on the Freeswitch wiki|http://wiki.freeswitch.org/wiki/Event_List>.
182              
183             By default, the special type, C<all> is used - which means all event types
184             are subscribed to.
185              
186             =head1 SEE ALSO
187              
188             =over
189              
190             =item L<Message::Passing>
191              
192             =item L<http://www.freeswitch.org/>
193              
194             =item L<http://wiki.freeswitch.org/wiki/Event_Socket_Library>
195              
196             =item L<http://wiki.freeswitch.org/wiki/Event_List>
197              
198             =back
199              
200             =head1 AUTHOR
201              
202             Tomas (t0m) Doran <bobtfish@bobtfish.net>
203              
204             =head1 SPONSORSHIP
205              
206             This module exists due to the wonderful people at Suretec Systems Ltd.
207             <http://www.suretecsystems.com/> who sponsored it's development for its
208             VoIP division called SureVoIP <http://www.surevoip.co.uk/> for use with
209             the SureVoIP API -
210             <http://www.surevoip.co.uk/support/wiki/api_documentation>
211              
212             =head1 COPYRIGHT
213              
214             Copyright Suretec Systems Ltd. 2012.
215              
216             Logstash (upon which many ideas for this project is based, but
217             which we do not reuse any code from) is copyright 2010 Jorden Sissel.
218              
219             =head1 LICENSE
220              
221             GNU Affero General Public License, Version 3
222              
223             If you feel this is too restrictive to be able to use this software,
224             please talk to us as we'd be willing to consider re-licensing under
225             less restrictive terms.
226              
227             =cut
228