File Coverage

blib/lib/Device/Leap.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package Device::Leap;
2              
3 1     1   37964 use 5.000001;
  1         4  
  1         43  
4 1     1   5 use strict;
  1         2  
  1         34  
5 1     1   5 use warnings;
  1         7  
  1         38  
6 1     1   2670 use Socket; # Leap communicates over WebSockets
  1         6071  
  1         650  
7 1     1   476 use JSON; # ... in JSON
  0            
  0            
8              
9             require Exporter;
10              
11             our @ISA = qw(Exporter);
12              
13             # Items to export into callers namespace by default. Note: do not export
14             # names by default without a very good reason. Use EXPORT_OK instead.
15             # Do not simply export all your public functions/methods/constants.
16              
17             # This allows declaration use Device::Leap ':all';
18             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
19             # will save memory.
20             our %EXPORT_TAGS = ( 'all' => [ qw(
21            
22             ) ] );
23              
24             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
25              
26             our @EXPORT = qw(
27             Leap
28             );
29              
30              
31             our $VERSION = '0.01';
32              
33             my($LEAP_HANDLE); # After connection, this contains our websocket handle
34             my($SEND); # Data to send, when other end is ready for it
35             my($BUFF)=''; # Buffer in case calling script misses a few events
36              
37             sub Leap {
38             my $ret;
39              
40             if($BUFF eq '') { # Nothing in the buffer - try to get some more
41             unless($LEAP_HANDLE) { # Establish a new connection
42              
43             # Make the socket
44             socket($LEAP_HANDLE, PF_INET, SOCK_STREAM, getprotobyname('tcp')) || return (\{'error' => "socket: $!"});
45              
46             # Don't let stuff like UTF-style characters get screwed up
47             binmode($LEAP_HANDLE) || return (\{'error' =>,"binmode(socket handle): $!"});
48              
49             # Allow more than one connection
50             setsockopt($LEAP_HANDLE, SOL_SOCKET, SO_REUSEADDR, 1) || return (\{'error' =>,"SO_REUSEADDR: $!"});
51              
52             # Don't block on close (Remember not to close it until we've sent everything we want):-
53             if($^O =~/Win32/i) {
54             setsockopt($LEAP_HANDLE, SOL_SOCKET, SO_DONTLINGER, 1) || return (\{'error' =>,"SO_DONTLINGER: $!"});
55             }
56              
57             my $temp = 1; ioctl($LEAP_HANDLE, 0x8004667E, \$temp); # Don't let it block us.
58              
59             # connect to the remote smtp server address with our socket. # Could use INADDR_LOOPBACK instead of inet_aton("127.0.0.1")
60             my $rc=connect($LEAP_HANDLE,sockaddr_in(6437,inet_aton('127.0.0.1')));
61              
62             my $mask='XhKY' . unpack('H*',pack('d',rand())) . 'eA==';
63              
64             $SEND="GET / HTTP/1.1\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nHost: localhost:6437\r\nOrigin: null\r\nPragma: no-cache\r\nCache-Control: no-cache\r\nSec-WebSocket-Key: $mask\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Extensions: x-webkit-deflate-frame\r\n\r\n";
65              
66             } # Establish
67              
68             # See if the non-blocking socket is ready for a read or a write...
69             my($bitsr,$bitsw,$null,$rcr,$rcw,$rce,$rc)=('','','');
70             vec($bitsr,fileno($LEAP_HANDLE),1)=1; # This tells select() which socket we want to query
71             vec($null,fileno($LEAP_HANDLE),1)=0;
72             if(defined $SEND) {$bitsw=$bitsr} else {$bitsw=$null} # We only care about write-status if we've got data to write
73             $rc=select($rcr=$bitsr, $rcw=$bitsw, $rce=$bitsr, 0); # See if our socket has any data to read (or write, or errors)
74              
75              
76             if($rc) {
77             if($rce ne $null) { # Ugh - what to do with errors?
78             return (\{'error' =>,"Socket read error: $!"});
79             }
80              
81             if($rcr ne $null) { # Is there stuff to "read"?
82             my $stuff=sysread($LEAP_HANDLE, $BUFF, 16384); # Read upto 16K
83              
84             if(length($BUFF)==0) {
85             close($LEAP_HANDLE); undef $LEAP_HANDLE;
86             return (\{'error' =>,"Socket closed"});
87             } else {
88             # We read some new data!
89             $BUFF='' unless(substr($BUFF,0,1) eq "\x81"); # discard non-ws return data
90             }
91             }
92              
93             if(($rcw ne $null)&&(defined $SEND)) { # Am I able to write?
94             syswrite($LEAP_HANDLE, $SEND);
95             undef $SEND;
96             }
97              
98             } else {
99             # Socket reports no activity...
100             }
101              
102             } # BUFF
103              
104             if($BUFF ne '') { # Decode some WS data
105             my $offset=2;
106             my $len=unpack('C',substr($BUFF,1,1));
107             if($len==126) { # Extended 16bit len
108             $len=unpack('n',substr($BUFF,2,2));
109             $offset+=2;
110             } elsif($len==127) {
111             $len=unpack('N',substr($BUFF,2,6));
112             $offset+=6;
113             }
114             $ret=substr($BUFF,$offset,$len);
115             $BUFF=substr($BUFF,$offset+$len);
116             if(length($ret)!=$len) {
117             $ret=''; # too much data came in to process all at once - some got truncated...
118             } else {
119             $ret=from_json($ret);
120             }
121             }
122              
123             return $ret;
124             } # Leap
125              
126              
127              
128              
129             1;
130             __END__