| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package App::htmlcat; |
|
2
|
1
|
|
|
1
|
|
7
|
use strict; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
58
|
|
|
3
|
1
|
|
|
1
|
|
8
|
use warnings; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
42
|
|
|
4
|
1
|
|
|
1
|
|
30
|
use 5.008_001; |
|
|
1
|
|
|
|
|
5
|
|
|
|
1
|
|
|
|
|
48
|
|
|
5
|
1
|
|
|
1
|
|
1578
|
use AnyEvent::Handle; |
|
|
1
|
|
|
|
|
31986
|
|
|
|
1
|
|
|
|
|
39
|
|
|
6
|
1
|
|
|
1
|
|
1182
|
use HTML::FromANSI::Tiny; |
|
|
1
|
|
|
|
|
1416
|
|
|
|
1
|
|
|
|
|
9
|
|
|
7
|
1
|
|
|
1
|
|
1098
|
use HTML::Entities; |
|
|
1
|
|
|
|
|
8581
|
|
|
|
1
|
|
|
|
|
87
|
|
|
8
|
1
|
|
|
1
|
|
905
|
use Data::Section::Simple qw(get_data_section); |
|
|
1
|
|
|
|
|
486
|
|
|
|
1
|
|
|
|
|
131
|
|
|
9
|
1
|
|
|
1
|
|
1925
|
use IO::Socket::INET; |
|
|
1
|
|
|
|
|
19916
|
|
|
|
1
|
|
|
|
|
9
|
|
|
10
|
1
|
|
|
1
|
|
1446
|
use Plack::Runner; |
|
|
1
|
|
|
|
|
11402
|
|
|
|
1
|
|
|
|
|
35
|
|
|
11
|
1
|
|
|
1
|
|
1150
|
use Encode; |
|
|
1
|
|
|
|
|
10559
|
|
|
|
1
|
|
|
|
|
1082
|
|
|
12
|
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
our $VERSION = '0.01'; |
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
sub new { |
|
16
|
0
|
|
|
0
|
1
|
|
my ($class, @args) = @_; |
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
my $self = bless { |
|
19
|
|
|
|
|
|
|
args => \@args, |
|
20
|
|
|
|
|
|
|
clients => {}, |
|
21
|
|
|
|
|
|
|
ansi => HTML::FromANSI::Tiny->new( |
|
22
|
|
|
|
|
|
|
auto_reverse => 1, |
|
23
|
|
|
|
|
|
|
no_plain_tags => 1, |
|
24
|
0
|
|
|
0
|
|
|
html_encode => sub { encode_entities($_[0], q("&<>)) }, |
|
25
|
0
|
|
|
|
|
|
), |
|
26
|
|
|
|
|
|
|
}, $class; |
|
27
|
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
$self->{in} = AnyEvent::Handle->new( |
|
29
|
|
|
|
|
|
|
fh => \*STDIN, |
|
30
|
|
|
|
|
|
|
on_eof => sub { |
|
31
|
0
|
|
|
0
|
|
|
my ($handle) = @_; |
|
32
|
0
|
|
|
|
|
|
exit 0; |
|
33
|
|
|
|
|
|
|
}, |
|
34
|
|
|
|
|
|
|
on_error => sub { |
|
35
|
0
|
|
|
0
|
|
|
my ($handle, $fatal, $message) = @_; |
|
36
|
0
|
|
|
|
|
|
warn "stdin: $message\n"; |
|
37
|
0
|
|
|
|
|
|
$self->_broadcast($_[0]{rbuf}); |
|
38
|
0
|
|
|
|
|
|
exit 1; |
|
39
|
|
|
|
|
|
|
} |
|
40
|
0
|
|
|
|
|
|
); |
|
41
|
|
|
|
|
|
|
|
|
42
|
0
|
|
|
|
|
|
return $self; |
|
43
|
|
|
|
|
|
|
} |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
sub _on_read_cb { |
|
46
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
return sub { |
|
49
|
0
|
|
|
0
|
|
|
my ($handle) = @_; |
|
50
|
0
|
|
|
|
|
|
$self->_broadcast($handle->rbuf); |
|
51
|
0
|
|
|
|
|
|
$handle->rbuf = ''; |
|
52
|
0
|
|
|
|
|
|
}; |
|
53
|
|
|
|
|
|
|
} |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
sub _broadcast { |
|
56
|
0
|
|
|
0
|
|
|
my ($self, $data) = @_; |
|
57
|
|
|
|
|
|
|
|
|
58
|
0
|
|
|
|
|
|
open my $fh, '<', \$data; |
|
59
|
0
|
|
|
|
|
|
while (defined (my $line = <$fh>)) { |
|
60
|
0
|
|
|
|
|
|
$line = decode_utf8 $line; |
|
61
|
0
|
|
|
|
|
|
foreach my $client (values %{ $self->{clients} }){ |
|
|
0
|
|
|
|
|
|
|
|
62
|
0
|
|
|
|
|
|
$self->_push_line($client->{handle}, $line); |
|
63
|
|
|
|
|
|
|
} |
|
64
|
|
|
|
|
|
|
} |
|
65
|
|
|
|
|
|
|
} |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
sub _push_line { |
|
68
|
0
|
|
|
0
|
|
|
my ($self, $handle, $line) = @_; |
|
69
|
0
|
|
|
|
|
|
$handle->push_write("data:" . Encode::encode("utf-8", scalar $self->{ansi}->html($line) ) ); |
|
70
|
0
|
|
|
|
|
|
$handle->push_write("\n"); |
|
71
|
|
|
|
|
|
|
} |
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
sub as_psgi { |
|
74
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
75
|
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
return sub { |
|
77
|
0
|
|
|
0
|
|
|
my $env = shift; |
|
78
|
|
|
|
|
|
|
|
|
79
|
0
|
0
|
|
|
|
|
$env->{'psgi.streaming'} or die 'psgi.streaming not supported'; |
|
80
|
|
|
|
|
|
|
|
|
81
|
0
|
0
|
|
|
|
|
if ($env->{PATH_INFO} eq '/stream') { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
return sub { |
|
83
|
0
|
|
|
|
|
|
my $respond = shift; |
|
84
|
|
|
|
|
|
|
|
|
85
|
0
|
|
|
|
|
|
my $remote_addr = $env->{REMOTE_ADDR}; |
|
86
|
|
|
|
|
|
|
|
|
87
|
0
|
|
|
|
|
|
my $writer = $respond->([ |
|
88
|
|
|
|
|
|
|
200, [ |
|
89
|
|
|
|
|
|
|
'Content-Type' => 'text/event-stream; charset=utf-8', |
|
90
|
|
|
|
|
|
|
'Cache-Control' => 'no-cache' |
|
91
|
|
|
|
|
|
|
] |
|
92
|
|
|
|
|
|
|
]); |
|
93
|
|
|
|
|
|
|
|
|
94
|
0
|
|
|
|
|
|
my $io = $env->{'psgix.io'}; |
|
95
|
|
|
|
|
|
|
my $handle = AnyEvent::Handle->new( |
|
96
|
|
|
|
|
|
|
fh => $io, |
|
97
|
|
|
|
|
|
|
on_error => sub { |
|
98
|
0
|
|
|
|
|
|
my ($handle, $fatal, $message) = @_; |
|
99
|
0
|
|
|
|
|
|
warn "client [$remote_addr]: $message\n"; |
|
100
|
0
|
|
|
|
|
|
delete $self->{clients}->{ 0+$io }; |
|
101
|
0
|
0
|
|
|
|
|
if (keys %{$self->{clients}} == 0) { |
|
|
0
|
|
|
|
|
|
|
|
102
|
0
|
|
|
|
|
|
$self->{in}->on_read(); |
|
103
|
|
|
|
|
|
|
} |
|
104
|
|
|
|
|
|
|
} |
|
105
|
0
|
|
|
|
|
|
); |
|
106
|
|
|
|
|
|
|
|
|
107
|
0
|
|
|
|
|
|
$self->{clients}->{ 0+$io } = { |
|
108
|
|
|
|
|
|
|
handle => $handle, |
|
109
|
|
|
|
|
|
|
writer => $writer, # keep reference |
|
110
|
|
|
|
|
|
|
}; |
|
111
|
0
|
|
|
|
|
|
$self->{in}->on_read($self->_on_read_cb); |
|
112
|
0
|
|
|
|
|
|
}; |
|
113
|
|
|
|
|
|
|
} elsif ($env->{PATH_INFO} eq '/css') { |
|
114
|
0
|
|
|
|
|
|
return [ 200, [ 'Content-Type' => 'text/css' ], [ $self->{ansi}->css ] ]; |
|
115
|
|
|
|
|
|
|
} elsif ($env->{PATH_INFO} eq '/js') { |
|
116
|
0
|
|
|
|
|
|
return [ 200, [ 'Content-Type' => 'text/javascript' ], [ get_data_section('js') ] ]; |
|
117
|
|
|
|
|
|
|
} elsif ($env->{PATH_INFO} eq '/') { |
|
118
|
0
|
|
|
|
|
|
return [ 200, [ 'Content-Type' => 'text/html; charset=utf-8' ], [ get_data_section('html') ] ]; |
|
119
|
|
|
|
|
|
|
} else { |
|
120
|
0
|
|
|
|
|
|
return [ 404, [], [] ]; |
|
121
|
|
|
|
|
|
|
} |
|
122
|
0
|
|
|
|
|
|
}; |
|
123
|
|
|
|
|
|
|
} |
|
124
|
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
sub run { |
|
126
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
127
|
0
|
|
|
|
|
|
my $runner = Plack::Runner->new(app => $self->as_psgi); |
|
128
|
0
|
|
|
|
|
|
$runner->parse_options( |
|
129
|
|
|
|
|
|
|
'--env' => 'production', |
|
130
|
|
|
|
|
|
|
'--port' => _empty_port(), |
|
131
|
0
|
|
|
|
|
|
@{ $self->{args} } |
|
132
|
|
|
|
|
|
|
); |
|
133
|
|
|
|
|
|
|
|
|
134
|
0
|
0
|
|
|
|
|
if (my $exec = { @{$runner->{options}} }->{exec}) { |
|
|
0
|
|
|
|
|
|
|
|
135
|
0
|
|
|
|
|
|
push @{ $runner->{options} }, server_ready => sub { |
|
136
|
0
|
|
|
0
|
|
|
my ($args) = @_; |
|
137
|
0
|
|
0
|
|
|
|
my $host = $args->{host} || 'localhost'; |
|
138
|
0
|
|
0
|
|
|
|
my $proto = $args->{proto} || 'http'; |
|
139
|
0
|
|
|
|
|
|
system "$exec $proto://$host:$args->{port}/"; |
|
140
|
0
|
|
|
|
|
|
}; |
|
141
|
|
|
|
|
|
|
} else { |
|
142
|
0
|
|
|
|
|
|
push @{ $runner->{options} }, server_ready => sub { |
|
143
|
0
|
|
|
0
|
|
|
my ($args) = @_; |
|
144
|
0
|
|
0
|
|
|
|
my $host = $args->{host} || 'localhost'; |
|
145
|
0
|
|
0
|
|
|
|
my $proto = $args->{proto} || 'http'; |
|
146
|
0
|
|
|
|
|
|
print STDERR "$0: $proto://$host:$args->{port}/\n"; |
|
147
|
0
|
|
|
|
|
|
}; |
|
148
|
|
|
|
|
|
|
} |
|
149
|
|
|
|
|
|
|
|
|
150
|
0
|
|
|
|
|
|
$runner->run; |
|
151
|
|
|
|
|
|
|
} |
|
152
|
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
# from Test::TCP |
|
154
|
|
|
|
|
|
|
sub _empty_port { |
|
155
|
0
|
|
0
|
0
|
|
|
my $port = $ENV{HTTPCAT_PORT} || 45192 + int(rand() * 1000); |
|
156
|
|
|
|
|
|
|
|
|
157
|
0
|
|
|
|
|
|
while ($port++ < 60000) { |
|
158
|
0
|
|
|
|
|
|
my $remote = IO::Socket::INET->new( |
|
159
|
|
|
|
|
|
|
Proto => 'tcp', |
|
160
|
|
|
|
|
|
|
PeerAddr => '127.0.0.1', |
|
161
|
|
|
|
|
|
|
PeerPort => $port, |
|
162
|
|
|
|
|
|
|
); |
|
163
|
|
|
|
|
|
|
|
|
164
|
0
|
0
|
|
|
|
|
if ($remote) { |
|
165
|
0
|
|
|
|
|
|
close $remote; |
|
166
|
|
|
|
|
|
|
} else { |
|
167
|
0
|
|
|
|
|
|
return $port; |
|
168
|
|
|
|
|
|
|
} |
|
169
|
|
|
|
|
|
|
} |
|
170
|
|
|
|
|
|
|
|
|
171
|
0
|
|
|
|
|
|
die 'Could not find empty port'; |
|
172
|
|
|
|
|
|
|
} |
|
173
|
|
|
|
|
|
|
__DATA__ |