line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package AnyEvent::MtGox::Stream; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
54214
|
use strict; |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
41
|
|
4
|
1
|
|
|
1
|
|
6
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
36
|
|
5
|
|
|
|
|
|
|
|
6
|
1
|
|
|
1
|
|
1868
|
use AnyEvent; |
|
1
|
|
|
|
|
12878
|
|
|
1
|
|
|
|
|
82
|
|
7
|
1
|
|
|
1
|
|
1330
|
use AnyEvent::HTTP qw(http_post); |
|
1
|
|
|
|
|
55043
|
|
|
1
|
|
|
|
|
113
|
|
8
|
1
|
|
|
1
|
|
11
|
use AnyEvent::Util qw(guard); |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
49
|
|
9
|
1
|
|
|
1
|
|
7
|
use Carp qw(croak); |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
53
|
|
10
|
1
|
|
|
1
|
|
7
|
use Errno qw(EPIPE); |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
42
|
|
11
|
1
|
|
|
1
|
|
1148
|
use JSON (); |
|
1
|
|
|
|
|
21927
|
|
|
1
|
|
|
|
|
30
|
|
12
|
1
|
|
|
1
|
|
1162
|
use Protocol::WebSocket::Frame; |
|
1
|
|
|
|
|
355432
|
|
|
1
|
|
|
|
|
44
|
|
13
|
1
|
|
|
1
|
|
1564
|
use Protocol::WebSocket::Handshake::Client; |
|
1
|
|
|
|
|
89308
|
|
|
1
|
|
|
|
|
32
|
|
14
|
1
|
|
|
1
|
|
12366
|
use URI; |
|
1
|
|
|
|
|
25380
|
|
|
1
|
|
|
|
|
5249
|
|
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
our $VERSION = '0.02'; |
17
|
|
|
|
|
|
|
$VERSION = eval $VERSION; |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
sub new { |
20
|
0
|
|
|
0
|
1
|
|
my ($class, %params) = @_; |
21
|
|
|
|
|
|
|
|
22
|
0
|
|
0
|
|
|
|
my $secure = $params{secure} || 0; |
23
|
0
|
|
0
|
0
|
|
|
my $on_disconnect = $params{on_disconnect} || sub { croak 'Disconnected' }; |
|
0
|
|
|
|
|
|
|
24
|
0
|
|
0
|
0
|
|
|
my $on_error = $params{on_error} || sub { croak @_ }; |
|
0
|
|
|
|
|
|
|
25
|
0
|
|
0
|
0
|
|
|
my $on_message = $params{on_message} || sub { }; |
|
0
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
|
27
|
0
|
|
|
|
|
|
my $server = 'socketio.mtgox.com'; |
28
|
0
|
|
|
|
|
|
my $handle; |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
# Socket.IO handshake. |
31
|
0
|
|
|
|
|
|
my $uri = URI->new("http://$server/socket.io/1"); |
32
|
0
|
0
|
|
|
|
|
$uri->scheme('https') if $secure; |
33
|
0
|
|
|
|
|
|
AE::log debug => "Making Socket.IO handshake to $uri"; |
34
|
|
|
|
|
|
|
http_post $uri, undef, sub { |
35
|
0
|
|
|
0
|
|
|
my ($body, $headers) = @_; |
36
|
0
|
0
|
0
|
|
|
|
return $on_error->('Socket.IO handshake failed') |
37
|
|
|
|
|
|
|
unless '200' eq $headers->{Status} and defined $body; |
38
|
0
|
|
|
|
|
|
my ($sid, $heartbeat) = split ':', $body, 3; |
39
|
0
|
|
|
|
|
|
AE::log debug => "Socket.IO handshake succeeded: $sid"; |
40
|
|
|
|
|
|
|
|
41
|
0
|
|
|
|
|
|
my $timer; |
42
|
|
|
|
|
|
|
$handle = AnyEvent::Handle->new( |
43
|
|
|
|
|
|
|
connect => [ $uri->host, $uri->port ], |
44
|
|
|
|
|
|
|
tls => $secure ? 'connect' : undef, |
45
|
|
|
|
|
|
|
on_error => sub { |
46
|
0
|
|
|
|
|
|
my ($handle, $fatal, $msg) = @_; |
47
|
0
|
|
|
|
|
|
$handle->destroy; |
48
|
0
|
|
|
|
|
|
undef $timer; |
49
|
0
|
0
|
|
|
|
|
$!{EPIPE} == $! ? $on_disconnect->() : $on_error->($msg); |
50
|
|
|
|
|
|
|
}, |
51
|
0
|
0
|
|
|
|
|
); |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
# WebSocket handshake. |
54
|
0
|
|
|
|
|
|
$uri->path("/socket.io/1/websocket/$sid"); |
55
|
0
|
0
|
|
|
|
|
$uri->scheme($secure ? 'wss' : 'ws'); |
56
|
0
|
|
|
|
|
|
AE::log debug => "Making WebSocket handshake to $uri"; |
57
|
0
|
|
|
|
|
|
my $wsh = Protocol::WebSocket::Handshake::Client->new(url => "$uri"); |
58
|
0
|
|
|
|
|
|
$handle->push_write($wsh->to_string); |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
$handle->push_read(sub { |
61
|
0
|
0
|
|
|
|
|
$wsh->parse($handle->rbuf) |
62
|
|
|
|
|
|
|
or return 1, $on_error->($wsh->error); |
63
|
0
|
0
|
|
|
|
|
if ($wsh->is_done) { |
64
|
0
|
|
|
|
|
|
AE::log debug => 'WebSocket handshake succeeded'; |
65
|
0
|
|
|
|
|
|
return 1; |
66
|
|
|
|
|
|
|
} |
67
|
0
|
|
|
|
|
|
}); |
68
|
|
|
|
|
|
|
|
69
|
0
|
|
|
|
|
|
my $frame = Protocol::WebSocket::Frame->new; |
70
|
|
|
|
|
|
|
my $send_message = sub { |
71
|
0
|
|
|
|
|
|
$handle->push_write($frame->new(@_)->to_bytes); |
72
|
0
|
|
|
|
|
|
}; |
73
|
|
|
|
|
|
|
my $send_heartbeat = sub { |
74
|
0
|
|
|
|
|
|
AE::log debug => 'Sending heartbeat'; |
75
|
0
|
|
|
|
|
|
$send_message->('2::'); |
76
|
0
|
|
|
|
|
|
}; |
77
|
|
|
|
|
|
|
|
78
|
0
|
0
|
|
|
|
|
$timer = AE::timer $heartbeat - 2, $heartbeat - 2, $send_heartbeat |
79
|
|
|
|
|
|
|
if $heartbeat; |
80
|
|
|
|
|
|
|
|
81
|
0
|
|
|
|
|
|
my $json = JSON->new; |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
$handle->push_read(sub { |
84
|
0
|
|
|
|
|
|
$frame->append($handle->rbuf); |
85
|
0
|
|
|
|
|
|
while (defined(my $msg = $frame->next)) { |
86
|
0
|
|
|
|
|
|
my ($type, $id, $endpoint, $data) = split ':', $msg, 4; |
87
|
0
|
0
|
0
|
|
|
|
if ('4' eq $type and '/mtgox' eq $endpoint) { |
|
|
0
|
0
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
88
|
0
|
0
|
|
|
|
|
if (my $scalar = eval { $json->decode($data) }) { |
|
0
|
|
|
|
|
|
|
89
|
0
|
|
|
|
|
|
$on_message->($scalar); |
90
|
|
|
|
|
|
|
} |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
# Respond to heartbeats only if a heartbeat timeout wasn't |
93
|
|
|
|
|
|
|
# given in the handshake. |
94
|
|
|
|
|
|
|
elsif ('2' eq $type and not $heartbeat) { |
95
|
0
|
|
|
|
|
|
$send_heartbeat->(); |
96
|
|
|
|
|
|
|
} |
97
|
0
|
|
|
|
|
|
elsif ('1::' eq $msg) { $send_message->('1::/mtgox') } |
98
|
0
|
|
|
|
|
|
elsif ('0' eq $type) { return 1, $on_disconnect->() } |
99
|
0
|
|
|
|
|
|
elsif ('7' eq $type) { return 1, $on_error->($data) } |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
# Unhandled message types: |
102
|
|
|
|
|
|
|
# 3: message |
103
|
|
|
|
|
|
|
# 5: event |
104
|
|
|
|
|
|
|
# 6: ack |
105
|
|
|
|
|
|
|
# 8: noop |
106
|
|
|
|
|
|
|
} |
107
|
0
|
|
|
|
|
|
}); |
108
|
0
|
|
|
|
|
|
}; |
109
|
|
|
|
|
|
|
|
110
|
0
|
0
|
|
|
|
|
return unless defined wantarray; |
111
|
0
|
|
|
0
|
|
|
return guard { $handle->destroy }; |
|
0
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
} |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
1; |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
__END__ |