App-htmlcat

 view release on metacpan or  search on metacpan

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

        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}/";
        };
    } else {
        push @{ $runner->{options} }, server_ready => sub {
            my ($args) = @_;
            my $host  = $args->{host} || 'localhost';
            my $proto = $args->{proto} || 'http';
            print STDERR "$0: $proto://$host:$args->{port}/\n";
        };
    }

    $runner->run;
}

# from Test::TCP
sub _empty_port {
    my $port = $ENV{HTTPCAT_PORT} || 45192 + int(rand() * 1000);

    while ($port++ < 60000) {
        my $remote = IO::Socket::INET->new(
            Proto    => 'tcp',
            PeerAddr => '127.0.0.1',
            PeerPort => $port,
        );

        if ($remote) {
            close $remote;
        } else {
            return $port;
        }
    }

    die 'Could not find empty port';
}
__DATA__

@@ html
<!DOCTYPE html>
<html>
<head>



( run in 1.613 second using v1.01-cache-2.11-cpan-39bf76dae61 )