App-htmlcat

 view release on metacpan or  search on metacpan

lib/App/htmlcat.pm  view on Meta::CPAN

        args    => \@args,
        clients => {},
        ansi    => HTML::FromANSI::Tiny->new(
            auto_reverse  => 1,
            no_plain_tags => 1,
            html_encode   => sub { encode_entities($_[0], q("&<>)) },
        ),
    }, $class;

    $self->{in} = AnyEvent::Handle->new(
        fh => \*STDIN,
        on_eof => sub {
            my ($handle) = @_;
            exit 0;
        },
        on_error => sub {
            my ($handle, $fatal, $message) = @_;
            warn "stdin: $message\n";
            $self->_broadcast($_[0]{rbuf});
            exit 1;
        }
    );

    return $self;
}

sub _on_read_cb {
    my $self = shift;

    return sub {
        my ($handle) = @_;
        $self->_broadcast($handle->rbuf);
        $handle->rbuf = '';
    };
}

sub _broadcast {
    my ($self, $data) = @_;

    open my $fh, '<', \$data;
    while (defined (my $line = <$fh>)) {
        $line = decode_utf8 $line;
        foreach my $client (values %{ $self->{clients} }){ 
            $self->_push_line($client->{handle}, $line);
        }
    }
}

sub _push_line {
    my ($self, $handle, $line) = @_;
    $handle->push_write("data:" . Encode::encode("utf-8", scalar $self->{ansi}->html($line) ) );
    $handle->push_write("\n");
}

sub as_psgi {
    my $self = shift;

    return sub {
        my $env = shift;

        $env->{'psgi.streaming'} or die 'psgi.streaming not supported';

        if ($env->{PATH_INFO} eq '/stream') {
            return sub {
                my $respond = shift;

                my $remote_addr = $env->{REMOTE_ADDR};

                my $writer = $respond->([
                    200, [
                        'Content-Type' => 'text/event-stream; charset=utf-8',
                        'Cache-Control' => 'no-cache'
                    ]
                ]);

                my $io = $env->{'psgix.io'};
                my $handle = AnyEvent::Handle->new(
                    fh => $io,
                    on_error => sub {
                        my ($handle, $fatal, $message) = @_;
                        warn "client [$remote_addr]: $message\n";
                        delete $self->{clients}->{ 0+$io };
                        if (keys %{$self->{clients}} == 0) {
                            $self->{in}->on_read();
                        }
                    }
                );

                $self->{clients}->{ 0+$io } = {
                    handle => $handle,
                    writer => $writer, # keep reference
                };
                $self->{in}->on_read($self->_on_read_cb);
            };
        } elsif ($env->{PATH_INFO} eq '/css') {
            return [ 200, [ 'Content-Type' => 'text/css' ], [ $self->{ansi}->css ] ];
        } elsif ($env->{PATH_INFO} eq '/js') {
            return [ 200, [ 'Content-Type' => 'text/javascript' ], [ get_data_section('js') ] ];
        } elsif ($env->{PATH_INFO} eq '/') {
            return [ 200, [ 'Content-Type' => 'text/html; charset=utf-8' ], [ get_data_section('html') ] ];
        } else {
            return [ 404, [], [] ];
        }
    };
}

sub run {
    my $self = shift;
    my $runner = Plack::Runner->new(app => $self->as_psgi);
    $runner->parse_options(
        '--env' => 'production',
        '--port' => _empty_port(),
        @{ $self->{args} }
    );

    if (my $exec = { @{$runner->{options}} }->{exec}) {
        push @{ $runner->{options} }, server_ready => sub {
            my ($args) = @_;
            my $host  = $args->{host} || 'localhost';
            my $proto = $args->{proto} || 'http';
            system "$exec $proto://$host:$args->{port}/";



( run in 1.361 second using v1.01-cache-2.11-cpan-140bd7fdf52 )