Author: sparky
Date: Fri Sep 11 18:53:39 2009
New Revision: 10565

Added:
   toys/rsget.pl/RSGet/HTTPRequest.pm
   toys/rsget.pl/RSGet/HTTPServer.pm
Removed:
   toys/rsget.pl/RSGet/MicroHTTP.pm
Log:
- rewritten and vastly improved http interface


Added: toys/rsget.pl/RSGet/HTTPRequest.pm
==============================================================================
--- (empty file)
+++ toys/rsget.pl/RSGet/HTTPRequest.pm  Fri Sep 11 18:53:39 2009
@@ -0,0 +1,601 @@
+package RSGet::HTTPRequest;
+
+use strict;
+use warnings;
+use IO::Socket;
+use RSGet::Line;
+use RSGet::Tools;
+use RSGet::ListManager;
+
+our %handlers = (
+       "main.js" => \&putfile,
+       "main.css" => \&putfile,
+       "" => \&main_page,
+       "update" => \&main_update,
+       "log" => \&log,
+       add => \&add,
+       add_update => \&add_update,
+);
+
+my %lastid;
+
+sub xhtml_start
+{
+       my $js = shift;
+       return 
+               '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" 
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd";>' . "\n"
+               . '<html xmlns="http://www.w3.org/1999/xhtml"; xml:lang="en">'
+               . '<head>'
+                       . '<title>rsget.pl</title>'
+                       . '<link rel="stylesheet" type="text/css" 
href="/main.css" media="screen" />'
+                       . ($js ? qq#<script type="text/javascript" 
src="/$js"></script># : '')
+               . '</head>'
+               . '<body>'
+               ;
+
+}
+
+sub xhtml_end
+{
+       # no whitespaces here, or .lastChild won't work
+       return "</body></html>";
+}
+
+
+sub putfile
+{
+       my ( $file, $post, $headers ) = @_;
+
+       if ( $file =~ m{^main\.(js|css)$} ) {
+               $headers->{Content_Type} = sprintf "text/%s; charset=utf-8", 
($1 eq "js" ? "javascript" : "css");
+
+               local $/ = undef;
+               open F_IN, "data/$file";
+               $_ = <F_IN>;
+               close F_IN;
+
+               return $_;
+       }
+
+}
+
+sub main_page
+{
+       my ( $file, $post, $headers ) = @_;
+       my $r = xhtml_start( "main.js" );
+
+       $r .= f_status();
+       $r .= f_active();
+       $r .= f_log( 6 );
+       $r .= f_dllist();
+       $r .= f_addform();
+       $r .= '<script type="text/javascript">init_main();</script>';
+       $r .= xhtml_end();
+
+       return $r;
+}
+
+sub main_update
+{
+       my ( $file, $post, $headers ) = @_;
+       my $r = xhtml_start();
+
+       $r .= f_status();
+
+       command( $post->{exec} ) if $post->{exec};
+
+       my $data = {};
+       my $nowactive = scalar keys %RSGet::Line::active;
+       if ( $nowactive or not exists $post->{active} or $post->{active} != 
$nowactive ) {
+               $r .= f_active();
+               $data->{active} = $nowactive;
+       }
+       if ( not $post->{dead} or $RSGet::Line::dead_change != $post->{dead} ) {
+               $r .= f_log( 6 );
+               $data->{dead} = $RSGet::Line::dead_change;
+       }
+       if ( not $post->{dllist} or $post->{dllist} != 
$RSGet::FileList::listmtime ) {
+               $r .= f_dllist();
+               $data->{dllist} = $RSGet::FileList::listmtime;
+       }
+       $r .= '<script type="text/javascript" id="update">/*<![CDATA[/**/';
+       $r .= 'var update = ' . scalar_to_js( $data ) . ';';
+       $r .= '//]]></script>';
+       $r .= xhtml_end();
+
+       return $r;
+}
+
+
+sub log
+{
+       my ( $file, $post, $headers ) = @_;
+       my $r = xhtml_start( );
+       $r .= f_log();
+       $r .= xhtml_end();
+
+       return $r;
+}
+
+sub f_status
+{
+       my $r = '<fieldset id="f_status"><legend>rsget.pl</legend><ul>';
+       foreach my $name ( sort keys %RSGet::Line::status ) {
+               my $value = $RSGet::Line::status{ $name };
+               next unless $value;
+               $r .= qq#<li>$name: $value</li>#;
+       }
+
+       $r .= '</ul></fieldset>';
+       return $r;
+}
+
+sub f_active
+{
+       $lastid{act} = {};
+       my $r = '<fieldset id="f_active"><legend>active</legend><ul>';
+       foreach my $key ( sort { $a <=> $b } keys %RSGet::Line::active ) {
+               my $line = $RSGet::Line::active{ $key };
+
+               $r .= act_info( $line );
+               #$r .= qq#<li><span>$name</span>$value</li>\n#;
+       }
+
+       $r .= '</ul></fieldset>';
+       return $r;
+}
+
+sub act_info
+{
+       my $act = shift;
+       my ( $logo, $line, $o ) = @$act;
+
+       my %wait_to_color = (
+               restart => "orange",
+               multi => "red",
+               problem => "red",
+               wait => "blue",
+       );
+       my $color = $o->{wait} ? $wait_to_color{ $o->{wait} } : "green";
+
+       my $uri = $o->{uri};
+       my $uriid = makeid( "act", $uri, $uri );
+       my $name = sgml( $o->{name} );
+       my $size = bignum( $o->{size} );
+       $logo =~ s/ $//;
+
+       my $prog = "";
+       $prog = qq#<div style="width: $o->{prog}"></div># if $o->{prog};
+       $line =~ s/^\Q$o->{name}\E//;
+       $line =~ s/^.*?:\s+//;
+       $line = sgml( $line );
+
+       return qq#<li id="$uriid" class="active $color">#
+               . qq#<span class="logo">$logo</span>#
+               . qq#<div class="href"><a href="$uri">$uri</a></div>#
+               . qq#<div class="info"><span class="size">$size 
bytes</span>$name</div>#
+               . qq#<div class="progress">$prog<span>$line</span></div>#
+               . '</li>';
+}
+
+
+sub f_dllist
+{
+       my $r = '<fieldset id="f_dllist"><legend>download list</legend>';
+
+       my %cmd_to_color = (
+               DONE => "blue",
+               GET => "green",
+               STOP => "red",
+               ADD => "orange",
+       );
+
+       $lastid{file} = {};
+       $lastid{uri} = {};
+       $r .= '<ul class="flist">';
+       foreach my $l ( @RSGet::FileList::actual ) {
+               unless ( ref $l ) {
+                       $r .= '<li class="comment">' . href( $l ) . '</li>';
+                       next;
+               }
+               my ( $cmd, $g, $uris ) = @$l{ qw(cmd globals uris) };
+               my @tools;
+               if ( $cmd eq "GET" ) {
+                       push @tools, "STOP", "!REMOVE";
+               } elsif ( $cmd eq "STOP" ) {
+                       push @tools, "START", "REMOVE";
+               } elsif ( $cmd eq "DONE" ) {
+                       push @tools, "RESTART", "REMOVE";
+               }
+
+               my $color = $cmd_to_color{ $cmd };
+               my $fileid = makeid( "file", $g->{fname} || (keys %$uris)[0], 
$uris );
+
+               $r .= qq#<li id="$fileid" class="file $color">#;
+               my $size = $g->{fsize} ? bignum( $g->{fsize} ) : "?";
+               my $fname = $g->{fname} ? sgml( $g->{fname} ) : "???";
+               $r .= qq#<div class="info"><span class="cmd">$cmd</span><span 
class="size">$size bytes</span>$fname</div>#;
+
+               $r .= '<div class="tools">' . (join " | ", map 
"<span>$_</span>", @tools) . '</div>';
+               $r .= '</li>';
+
+               foreach my $uri ( sort keys %$uris ) {
+                       $r .= file_info( "uri", $uri, @{$uris->{$uri}} );
+               }
+
+       }
+
+       $r .= '</ul>';
+
+       $r .= '</fieldset>';
+       return $r;
+}
+
+sub file_info
+{
+       my ( $id_type, $uri, $getter, $o, $tools ) = @_;
+
+       my $bestname = $o->{name} || $o->{iname}
+               || $o->{aname} || $o->{ainame};
+       $bestname = sgml( $bestname || "???" );
+
+       my $bestsize = $o->{size} ? bignum( $o->{size} ) : sgml( $o->{asize} || 
"?" );
+       my $uriid = makeid( $id_type, $uri, $uri );
+
+       my $color = "blue";
+       $color = "green" if $o->{size} or $o->{asize};
+       $color = "red" if $o->{error};
+       $color = "orange" if exists $RSGet::Dispatch::downloading{ $uri };
+
+       $uri = sgml( $uri );
+
+       my $errormsg = "";
+       my @tools;
+       if ( $o->{error} ) {
+               push @tools, "CLEAN ERROR", "REMOVE";
+               $errormsg = qq#<div class="error">ERROR: # . sgml( $o->{error} 
) . qq#</div>#;
+       } else {
+               push @tools, "DISABLE", ( $id_type eq "uri" ? "!REMOVE" : 
"REMOVE" );
+       }
+       @tools = @$tools if $tools;
+
+
+       return qq#<li id="$uriid" class="uri $color">#
+               . qq#<span class="logo">[$getter->{short}]</span>#
+               . qq#<div class="href"><a href="$uri">$uri</a></div>#
+               . qq#<div class="info"><span 
class="size">$bestsize</span>$bestname</div>#
+               . $errormsg
+               . '<div class="tools">' . (join " | ", map "<span>$_</span>", 
@tools) . '</div>'
+               . '</li>';
+}
+
+sub f_log
+{
+       my $max = shift;
+       my $start = 0;
+       $start = $#RSGet::Line::dead - $max if $max;
+
+       my $r = " " x ( 200 * ( $max || $#RSGet::Line::dead ) ); # allocate 
some memory
+       $r = '<fieldset id="log"><legend>log</legend><ul>';
+       
+       for ( my $i = $#RSGet::Line::dead; $i >= $start; $i-- ) {
+               my $line = $RSGet::Line::dead[ $i ];
+               my $class = '';
+               $class = ' class="blue"' if $line =~ /PARTIAL/;
+               $class = ' class="green"' if $line =~ /DONE/;
+               $class = ' class="orange"' if $line =~ /^\[\S+\] WARNING/;
+               $class = ' class="red"' if $line =~ /ERROR/;
+               $r .= qq#<li$class># . href( $line ) . '</li>';
+       }
+
+       $r .= '<li class="comment"><a href="/log">Show more</a></li>' if $max;
+       $r .= '</ul></fieldset>';
+}
+
+sub sgml
+{
+       local $_ = shift;
+       s/&/&amp;/g;
+       s/</&lt;/g;
+       s/>/&gt;/g;
+       s#\0#<small>(???)</small>#g;
+       return $_;
+}
+
+sub href
+{
+       local $_ = sgml( shift );
+       s{(^|\s|#)(http://\S*)}{$1<a href="$2">$2</a>}g;
+       return $_;
+}
+
+sub makeid
+{
+       my $pre = shift;
+       my $id = shift;
+       my $data = shift;
+       
+       $id =~ s/[^a-zA-Z0-9]+/_/g;
+
+       my $idgrp = $lastid{$pre};
+       if ( exists $idgrp->{ $id } ) {
+               my $i = 1;
+               ++$i while exists $idgrp->{ "${id}_$i" };
+               $id .= "_" . $i;
+       }
+       $idgrp->{ $id } = $data;
+
+       return "${pre}_$id";
+}
+
+sub command
+{
+       my $exec = shift;
+       unless ( $exec =~ s/^(.*?):(.*?)_// ) {
+               warn "Invalid command: $exec\n";
+               return;
+       }
+       my $cmd = $1;
+       my $grp = $2;
+
+       my $idgrp = $lastid{$grp};
+       my $data = $idgrp->{ $exec };
+       unless ( $data ) {
+               warn "Invalid ID: $cmd, $grp, $exec\n";
+               return undef;
+       }
+
+       if ( $grp eq "file" ) {
+               my @save;
+               if ( $cmd eq "STOP" ) {
+                       @save = qw(cmd STOP);
+               } elsif ( $cmd eq "START" or $cmd eq "RESTART" ) {
+                       @save = qw(cmd GET);
+               } elsif ( $cmd =~ /^!?REMOVE$/ ) {
+                       @save = qw(delete 1);
+               } else {
+                       warn "Invalid command: $cmd, $grp, $exec\n";
+                       return;
+               }
+               foreach my $uri ( sort keys %$data ) {
+                       RSGet::FileList::save( $uri, @save );
+               }
+       } elsif ( $grp eq "uri" ) {
+               my @save;
+               if ( $cmd eq "CLEAN ERROR" ) {
+                       @save = ( options => { error => undef } );
+               } elsif ( $cmd eq "DISABLE" ) {
+                       @save = ( options => { error => "disabled" } );
+               } elsif ( $cmd =~ /^!?REMOVE$/ ) {
+                       @save = qw(delete 1);
+               } else {
+                       warn "Invalid command: $cmd, $grp, $exec\n";
+                       return;
+               }
+               RSGet::FileList::save( $data, @save );
+       } else {
+               warn "Invalid command group: $cmd, $grp, $exec\n";
+               return;
+       }
+       RSGet::FileList::update();
+}
+
+
+sub scalar_to_js
+{
+       local $_ = shift;
+
+       if ( my $ref = ref $_ ) {
+               my $obj;
+               if ( $ref eq "HASH" ) {
+                       my @js;
+                       foreach my $key ( sort keys %$_ ) {
+                               my $val = $_->{$key};
+                               push @js, "'$key': " . scalar_to_js( $val );
+                       }
+                       $obj = sprintf "{ %s }", join ", ", @js;
+               } elsif ( $ref eq "ARRAY" ) {
+                       my @js;
+                       foreach my $val ( @$_ ) {
+                               push @js, scalar_to_js( $val );
+                       }
+                       $obj = sprintf "[ %s ]", join ", ", @js;
+               } else {
+                       warn "Unsupported ref: $ref\n";
+               }
+               return $obj;
+       }
+
+       if ( not defined $_ ) {
+               return "null";
+       } elsif ( /^(0|-?[1-9]\d*)(\.\d+)?$/ ) {
+               return $_;
+       } else {
+               s/\\/\\\\/g;
+               s/"/\\"/g;
+               return '"'. $_ .'"';
+       }
+}
+
+sub f_addform
+{
+       my $id = shift;
+       return '<form action="/add" method="POST"' . ( $id ? '>' : ' 
target="_blank">' )
+               . '<fieldset id="add"><legend>Add links to the list</legend>'
+               . ( $id ? qq#<input type="hidden" name="id" value="$id" /># : 
'' )
+               . '<textarea cols="100" rows="8" name="links"></textarea>'
+               . '<input type="submit" value="OK" />'
+               . '</fieldset>'
+               . '</form>';
+}
+
+sub f_addcomment
+{
+       my $id = shift;
+       return '<form action="/add" method="POST">'
+               . '<fieldset id="add"><legend>Add comment (i.e. passwords) to 
the list</legend>'
+               . qq#<input type="hidden" name="id" value="$id" />#
+               . '<textarea cols="100" rows="4" name="comment"></textarea>'
+               . '<input type="submit" value="OK" />'
+               . '</fieldset>'
+               . '</form>';
+}
+
+
+sub f_addlist
+{
+       my $list = shift;
+
+       my $r = '<fieldset id="f_addlist"><legend>Add list</legend>'
+               . '<ul class="flist">';
+       my $uri_id = "adduri_" . $list->{id};
+       $lastid{ $uri_id } = {};
+
+       my $comment = $list->{comment};
+       foreach my $l ( @$comment ) {
+               $r .= '<li class="comment">' . href( $l ) . '</li>';
+       }
+
+       my $lines = $list->{lines};
+       foreach my $l ( @$lines ) {
+               unless ( ref $l ) {
+                       $r .= '<li class="comment">' . href( $l ) . '</li>';
+                       next;
+               }
+
+               $r .= qq#<li class="file green">#;
+               $r .= qq#<div class="info"><span 
class="cmd">$l->{cmd}</span></div>#;
+               $r .= '</li>';
+
+               my $uris = $l->{uris};
+               foreach my $uri ( sort keys %$uris ) {
+                       $r .= file_info( $uri_id, $uri, @{$uris->{$uri}} );
+               }
+       }
+
+       $r .= '</ul>'
+               . '</fieldset>';
+
+       return $r;
+}
+
+sub add
+{
+       my ( $file, $post, $headers ) = @_;
+       my $r = xhtml_start( "main.js" );
+
+
+       my $list;
+       $list = RSGet::ListManager::add_list( $post->{links}, $post->{id} )
+               if $post->{links};
+       $list = RSGet::ListManager::add_list_comment( $post->{comment}, 
$post->{id} )
+               if $post->{comment};
+
+       $r .= '<fieldset id="f_listask"></fieldset>';
+       $r .= f_addlist( $list );
+       $r .= f_addcomment( $list->{id} );
+       $r .= f_addform( $list->{id} );
+       $r .= qq#<script type="text/javascript">init_add( "$list->{id}" 
);</script>#;
+       $r .= xhtml_end();
<<diff output has been trimmed to 500 lines, 102 line(s) remained.>>

Added: toys/rsget.pl/RSGet/HTTPServer.pm
==============================================================================
--- (empty file)
+++ toys/rsget.pl/RSGet/HTTPServer.pm   Fri Sep 11 18:53:39 2009
@@ -0,0 +1,123 @@
+package RSGet::HTTPServer;
+
+use strict;
+use warnings;
+use IO::Socket;
+use RSGet::Tools;
+use RSGet::HTTPRequest;
+
+sub new
+{
+       my $class = shift;
+       my $port = shift;
+       my $socket = IO::Socket::INET->new(
+               Proto => 'tcp',
+               LocalPort => $port,
+               Listen => SOMAXCONN,
+               Reuse => 1,
+               Blocking => 0,
+       ) || return undef;
+
+       my $self = \$socket;
+       return bless $self, $class;
+}
+
+sub perform
+{
+       my $self = shift;
+       my $socket = $$self;
+
+       for ( my $i = 0; $i < 5; $i++ ) {
+               my $client = $socket->accept() or last;
+               last unless request( $client );
+       }
+}
+
+sub request
+{
+       my $client = shift;
+       my $request;
+       my @headers;
+       my $post = "";
+       my $OK = 0;
+       eval {
+               local $SIG{ALRM} = sub { die "HTTP: Frozen !\n"; };
+               alarm 2;
+               $request = <$client>;
+
+               my $len = 0;
+               while ( $_ = <$client> ) {
+                       if ( /^\s*$/ ) {
+                               $OK = 1;
+                               last;
+                       }
+                       push @headers, $_;
+                       $len = $1 if /^Content-Length:\s*(\d+)/i;
+               }
+
+               $client->read( $post, $len ) if $len;
+               $OK++;
+       };
+       alarm 0;
+       if ( $@ ) {
+               warn "HTTP error: $...@\n" unless $@ eq "HTTP: Frozen !\n";
+               close $client;
+               return undef;
+       }
+       unless ( $OK == 2 ) {
+               warn "Some HTTP problem\n";
+               close $client;
+               return undef;
+       }
+       
+       my( $method, $file, $ignore ) = split /\s+/, $request;
+
+       my %post;
+       if ( uc $method eq "POST" and length $post ) {
+               foreach ( split /&/, $post ) {
+                       s/^(.*?)=//;
+                       my $key = $1;
+                       tr/+/ /;
+                       s/%(..)/chr hex $1/eg;
+                       $post{ $key } = $_;
+               }
+       }
+
+       $file =~ s#^/+##;
+       my $print;
+       if ( my $func = $RSGet::HTTPRequest::handlers{$file} ) {
+               $print = "HTTP/1.1 200 OK\r\n";
+               my $headers = { Content_Type => "text/xml; charset=utf-8" };
+               my $data = &$func( $file, \%post, $headers );
+
+               $headers->{Content_Length} ||= length $data;
+               while ( my ( $k, $v ) = each %$headers ) {
+                       ( my $key = $k ) =~ s/_/-/g;
+                       $print .= "$key: $v\r\n";
+               }
+               $print .= "\r\n";
+
+               $print .= $data;
+       } else {
+               $print = "HTTP/1.1 404 Not found\r\n";
+               $print .= "\r\n";
+       }
+
+       my $kid = fork();
+       unless ( $kid ) {
+               # XXX: this is stupid, but I don't know what
+               # else to do if $client is closed already
+               print $client $print;
+               close $client;
+
+               # don't exit if we didn't actually fork
+               exit 0 if defined $kid;
+       };
+
+       close $client;
+       return 1;
+}
+
+1;
+
+# vim: ts=4:sw=4
_______________________________________________
pld-cvs-commit mailing list
[email protected]
http://lists.pld-linux.org/mailman/listinfo/pld-cvs-commit

Reply via email to