| 1 | #!/usr/bin/perl |
|---|
| 2 | |
|---|
| 3 | BEGIN { |
|---|
| 4 | unless ($ENV{DISPLAY}) { |
|---|
| 5 | die q"can't find DISPLAY ($ENV{DISPLAY})"; |
|---|
| 6 | } |
|---|
| 7 | } |
|---|
| 8 | |
|---|
| 9 | use strict; |
|---|
| 10 | use warnings; |
|---|
| 11 | use Pod::Usage qw(pod2usage); |
|---|
| 12 | use Getopt::Long qw(GetOptions); |
|---|
| 13 | use Class::Inspector; |
|---|
| 14 | use Socket qw(INADDR_LOOPBACK); |
|---|
| 15 | use IO::Poll; |
|---|
| 16 | use HTTP::Response; |
|---|
| 17 | use HTTP::Status; |
|---|
| 18 | use Mozilla::Screenshot; |
|---|
| 19 | |
|---|
| 20 | use POE qw( |
|---|
| 21 | Sugar::Args |
|---|
| 22 | Wheel::SocketFactory |
|---|
| 23 | Wheel::ReadWrite |
|---|
| 24 | Wheel::Run |
|---|
| 25 | Filter::Stream |
|---|
| 26 | Filter::HTTPD |
|---|
| 27 | Component::TSTP |
|---|
| 28 | ); |
|---|
| 29 | |
|---|
| 30 | GetOptions( |
|---|
| 31 | 'h|help' => sub { pod2usage 1 }, |
|---|
| 32 | 'p|port=i' => \ (my $port = 3000), |
|---|
| 33 | 'x|proxy=s' => \ my $proxy, |
|---|
| 34 | ) or pod2usage 1; |
|---|
| 35 | |
|---|
| 36 | $ENV{HTTP_PROXY} = $proxy if $proxy; |
|---|
| 37 | |
|---|
| 38 | POE::Component::TSTP->create; |
|---|
| 39 | |
|---|
| 40 | POE::Session->create( |
|---|
| 41 | inline_states => { |
|---|
| 42 | _start => sub { sweet_args->kernel->yield('server_start') }, |
|---|
| 43 | }, |
|---|
| 44 | package_states => [ main => Class::Inspector->methods('main') ], |
|---|
| 45 | heap => { port => $port }, |
|---|
| 46 | ); |
|---|
| 47 | |
|---|
| 48 | POE::Kernel->sig($_ => sub { POE::Kernel->stop }) for qw( INT TERM HUP ); |
|---|
| 49 | POE::Kernel->run; |
|---|
| 50 | exit; |
|---|
| 51 | |
|---|
| 52 | sub server_start { |
|---|
| 53 | my $poe = sweet_args; |
|---|
| 54 | |
|---|
| 55 | $poe->heap->{server} = POE::Wheel::SocketFactory->new( |
|---|
| 56 | BindAddress => INADDR_LOOPBACK, |
|---|
| 57 | BindPort => $poe->heap->{port}, |
|---|
| 58 | SuccessEvent => 'server_accept', |
|---|
| 59 | FailureEvent => 'server_error', |
|---|
| 60 | Reuse => 'on', |
|---|
| 61 | ); |
|---|
| 62 | |
|---|
| 63 | $poe->heap->{ua} = Mozilla::Screenshot->new({ env_proxy => $ENV{HTTP_PROXY} ? 1 : 0 }); |
|---|
| 64 | } |
|---|
| 65 | |
|---|
| 66 | sub server_accept { |
|---|
| 67 | my $poe = sweet_args; |
|---|
| 68 | |
|---|
| 69 | my $connection = POE::Wheel::ReadWrite->new( |
|---|
| 70 | Handle => $poe->args->[0], |
|---|
| 71 | InputEvent => 'client_input', |
|---|
| 72 | FlushedEvent => 'client_flush', |
|---|
| 73 | ErrorEvent => 'client_error', |
|---|
| 74 | Filter => POE::Filter::HTTPD->new, |
|---|
| 75 | ); |
|---|
| 76 | |
|---|
| 77 | $poe->heap->{connection}->{$connection->ID} = $connection; |
|---|
| 78 | } |
|---|
| 79 | |
|---|
| 80 | sub server_error { delete sweet_args->heap->{server} } |
|---|
| 81 | |
|---|
| 82 | sub client_input { |
|---|
| 83 | my $poe = sweet_args; |
|---|
| 84 | my $req = $poe->args->[0]; |
|---|
| 85 | |
|---|
| 86 | my $res = HTTP::Response->new; |
|---|
| 87 | $res->protocol('HTTP/1.0'); |
|---|
| 88 | $res->request($req); |
|---|
| 89 | $res->date(time); |
|---|
| 90 | $res->header(Connection => 'close'); |
|---|
| 91 | |
|---|
| 92 | if (my $uri = $req->header('Content-Location')) { |
|---|
| 93 | my $image; |
|---|
| 94 | eval { $image = $poe->heap->{ua}->fetch($uri)->save_to_buffer('png') }; |
|---|
| 95 | $@ ? $res->code(RC_INTERNAL_SERVER_ERROR) : do { |
|---|
| 96 | $res->code(RC_OK); |
|---|
| 97 | $res->content_type('image/png'); |
|---|
| 98 | $res->content_length(length $image); |
|---|
| 99 | $res->content($image); |
|---|
| 100 | }; |
|---|
| 101 | } |
|---|
| 102 | else { |
|---|
| 103 | $res->code(RC_BAD_REQUEST); |
|---|
| 104 | } |
|---|
| 105 | |
|---|
| 106 | $poe->heap->{connection}->{$poe->args->[1]}->put($res); |
|---|
| 107 | } |
|---|
| 108 | |
|---|
| 109 | sub client_flush { |
|---|
| 110 | my $poe = sweet_args; |
|---|
| 111 | $poe->kernel->yield(client_close => $poe->args->[0]); |
|---|
| 112 | } |
|---|
| 113 | |
|---|
| 114 | sub client_close { |
|---|
| 115 | my $poe = sweet_args; |
|---|
| 116 | delete $poe->heap->{connection}->{$poe->args->[0]}; |
|---|
| 117 | } |
|---|
| 118 | |
|---|
| 119 | sub client_error { |
|---|
| 120 | my $poe = sweet_args; |
|---|
| 121 | $poe->kernel->yield(client_close => $poe->args->[3]); |
|---|
| 122 | } |
|---|
| 123 | |
|---|
| 124 | __END__ |
|---|
| 125 | |
|---|
| 126 | =head1 NAME |
|---|
| 127 | |
|---|
| 128 | screenshot-httpd-poe.pl - sample screenshot httpd with POE |
|---|
| 129 | |
|---|
| 130 | =head1 SYNOPSIS |
|---|
| 131 | |
|---|
| 132 | screenshot-httpd-poe.pl [options] |
|---|
| 133 | |
|---|
| 134 | Options: |
|---|
| 135 | -h --help display this help and exits |
|---|
| 136 | -p --port port (defaults to 3000) |
|---|
| 137 | -x --proxy proxy (defaults to $ENV{HTTP_PROXY}) |
|---|
| 138 | |
|---|
| 139 | =cut |
|---|