###########################################################################
# basic Perlbal statistics gatherer
###########################################################################
package Perlbal::Plugin::Jslab;
use strict;
use warnings;
use GD;
use MIME::Base64;
our $im = new GD::Image(250,250);
our $white = $im->colorAllocate(255,255,255);
#$im->transparent($white);
our $black = $im->colorAllocate(0,0,0);
our @subs; # subscribers
sub send_event {
my $js = shift;
my $need_clean = 0;
my $now = time();
foreach my $s (@subs) {
if ($s->{closed}) {
$need_clean = 1;
next;
}
$s->{alive_time} = $now;
$s->write(sprintf("%8d$js", length($js)));
}
if ($need_clean) {
@subs = grep { ! $_->{closed} } @subs;
send_event("setCount(" . scalar @subs . ");");
}
}
# called when we're being added to a service
sub register {
my ($class, $svc) = @_;
Perlbal::Socket::register_callback(1, sub {
my $ct = @subs;
@subs = grep { ! $_->{closed} } @subs;
my $ct2 = @subs;
if ($ct != $ct2) {
send_event("setCount($ct2);");
}
return 1;
});
Perlbal::Socket::register_callback(5, sub {
send_event("1;");
return 5;
});
$svc->register_hook('Jslab', 'start_proxy_request', sub {
my Perlbal::ClientProxy $self = shift;
my Perlbal::HTTPHeaders $hds = $self->{req_headers};
return 0 unless $hds;
my $uri = $hds->request_uri;
return 0 unless $uri =~ m!^/jslab,(\w+)(?:\?(.*))?!;
my ($mode, $qs) = ($1, $2);
#print "MODE: $mode, QS: $qs\n";
my $send = sub {
my ($ctype, $bodyref) = @_;
my $res = $self->{res_headers} = Perlbal::HTTPHeaders->new_response(200);
$res->header("Content-Type", "text/plain");
$res->header('Content-Length', length($$bodyref));
$self->setup_keepalive($res);
$self->state('xfer_resp');
$self->tcp_cork(1); # cork writes to self
$self->write($res->to_string_ref);
unless ($self->{req_headers} && $self->{req_headers}->request_method eq 'HEAD') {
# don't write body for head requests
$self->write($bodyref);
}
$self->write(sub { $self->http_response_sent; });
};
if ($mode eq "sub") {
my $res = $self->{res_headers} = Perlbal::HTTPHeaders->new_response(200);
$res->header("Content-Type", "text/plain");
$res->header('Connection', 'close');
push @subs, $self;
$self->write($res->to_string_ref);
send_event("setCount(" . scalar @subs . ");");
return 1;
}
if ($mode eq "image") {
my $png = $im->png;
$send->("image/png", \$png);
return 1;
}
if ($mode eq "sendcmd") {
#print "sendcmd: $qs\n";
my $dirty_image = 0;
my $brush = 1;
my $js = ""; # response
my @pairs = split(/,/, $qs);
while (my $cmd = shift @pairs) {
if ($cmd eq "lb") {
$dirty_image = 1;
$brush = int shift @pairs;
my ($x1, $y1, $x2, $y2) = map { int($_) } splice(@pairs, 0, 4);
my $width = $brush - 1;
$width = 0 if $width < 0;
$width = 3 if $width > 3;
for (my $dx = -$width; $dx <= $width; $dx ++) {
for (my $dy = -$width; $dy <= $width; $dy ++) {
$im->line($x1+$dx, $y1+$dy, $x2+$dx, $y2+$dy, $black);
}
}
}
my $snapfile = time() . ".png";
my $snap = sub {
return 0 unless
open (S, ">/var/www/danga.com/htdocs/misc/jsdraw/archive/$snapfile");
print S $im->png;
close S;
};
if ($cmd eq "wipe") {
$dirty_image = 1;
$snap->();
$im->filledRectangle(0,0,250,250, $white);
$js .= "insChat(\"" . ejs("[SCREEN WIPE from " . $self->peer_ip_string . "]") . "\");\n";
}
if ($cmd eq "chat") {
my $chat = durl(splice(@pairs, 0, 1));
if ($chat ne "") {
$js .= "insChat(\"" . ejs($self->peer_ip_string . ": $chat") . "\");\n";
}
if ($chat eq "snap") {
$snap->();
}
}
}
if ($dirty_image) {
my $enc = encode_base64($im->png);
$enc =~ s/[\n\r]//g;
$js .= "document.getElementById('foo').src = \"data:image/png;base64,$enc\";\n";
}
send_event($js);
#$send->("text/plain", \$body);
$send->("text/plain", \ "OK");
return 1;
}
return 0;
});
return 1;
}
# called when we're no longer active on a service
sub unregister {
my ($class, $svc) = @_;
return 1;
}
# called when we are loaded
sub load {
return 1;
}
# called for a global unload
sub unload {
return 1;
}
sub durl
{
my ($a) = @_;
$a =~ tr/+/ /;
$a =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
return $a;
}
sub ejs
{
my $a = $_[0];
$a =~ s/[\"\'\\]/\\$&/g;
$a =~ s/\r?\n/\\n/gs;
$a =~ s/\r//;
return $a;
}
1;