Apologies in advance for the tangential answer, but since I've done something very similar using Mojolicious I thought I'd show you how I did it there. I'll take a look at your code and see if I can figure out what's wrong with it in the meantime.
The controller:
package TailStream::Controller::Tail;
use Mojo::Base 'Mojolicious::Controller';
use Mojo::Util qw(url_unescape);
sub stream_tail {
my $self = shift;
my $encoded_filename = $self->stash('filename');
$self->inactivity_timeout(3600);
my $filename = url_unescape $encoded_filename;
die "Couldn't open $filename" unless -e -r -f $filename;
my $pid = open my $fh, '-|', 'tail', '-n', '+1', '-F', $filename;
die "Couldn't spawn: $!" unless defined $pid;
my $stream = Mojo::IOLoop::Stream->new($fh);
my $sid = Mojo::IOLoop->stream($stream);
$self->on( finish => sub { Mojo::IOLoop->remove($sid) } );
$stream->on( read => sub { $self->send( { text => pop } ) } );
$stream->on( close => sub { kill KILL => $pid; close $fh } );
$stream->timeout(0);
$stream->start;
}
1;
And the application:
package TailStream;
use Mojo::Base 'Mojolicious';
sub startup {
my $self = shift;
my $r = $self->routes;
$r->websocket('/tail_ws/*filename')->to('tail#stream_tail');
}
1;
πάντων χρημάτων μέτρον έστιν άνθρωπος.