'/update' => \&Update, #### sub Update { my $appeasy = shift; # your CGI::AppEasy object .... return qq(

update successful

) # or whatever } ##
## $appeasy->response->code( 404 ); # or whatever #### '/help' => \$help_text, #### '/' => \$default_page, #### =pod Version: 20100504.2 Brief example: use CGI::AppEasy; CGI::AppEasy->new( '/' => \&cmd, )->serve; sub cmd { my( $easy ) = @_; $easy->response->code( 404 ); "

Command ".$easy->path_info." not found

" } =cut package CGI::AppEasy; use HTTP::Daemon; use HTTP::Status; use URI; use URI::QueryParam; use Data::Dumper; use strict; use warnings; sub new { my( $pkg, %config ) = @_; bless \%config, $pkg } # read-only access to these contained objects: sub request { $_[0]{'request'} } sub response { $_[0]{'response'} } sub daemon { $_[0]{'daemon'} } sub uri { $_[0]{'uri'} } sub server_name { $_[0]{'server_name'} } sub server_port { $_[0]{'server_port'} } sub remote_port { $_[0]{'remote_port'} } sub remote_host { $_[0]{'remote_host'} } sub remote_addr { $_[0]{'remote_addr'} } # delegation calls: sub path_info { $_[0]->uri->path } sub query { $_[0]->uri->query } sub request_method { $_[0]->request->method } sub protocol { $_[0]->request->protocol } sub content_type { $_[0]->request->content_type } # via Header sub content_length { $_[0]->request->content_length } # via Header sub content_encoding { $_[0]->request->content_encoding } # via Header sub content_language { $_[0]->request->content_language } # via Header sub referer { $_[0]->request->referer } # via Header sub user_agent { $_[0]->request->user_agent } # via Header # note that http('host') returns the server hostname, not the requester's hostname sub host { $_[0]->request->header('Host') } sub Accept { $_[0]->request->header('Accept') } sub accept_charset { $_[0]->request->header('Accept-Charset') } sub accept_encoding { $_[0]->request->header('Accept-Encoding') } sub accept_language { $_[0]->request->header('Accept-Language') } sub keep_alive { $_[0]->request->header('Keep-Alive') } sub return { my( $self, $content ) = @_; $self->{content_set}=1; $self->response->content($content); $content } sub end { my $self = shift; $self->{'serving'} = 0; $self } sub serve { my $self = shift; my %config = ( LocalPort => 8080, Blocking => 1, # default from IO::Socket::INET Timeout => 5, %$self, @_ ); $self->{'daemon'} = my $d = HTTP::Daemon->new( LocalPort => $config{'LocalPort'}||8080, ) or die; warn "contact ", $d->url, "\n"; $self->{'serving'} = 1; my($c,$peer_addr); while ( $self->{'serving'} and ($c,$peer_addr) = $d->accept and $c ) # if timeout, $c will be undef # how to handle this case? # more to the point - when does this actually happen? # my rudimentary attempts to trigger it failed. { { my($port,$iaddr) = sockaddr_in( $peer_addr ); $self->{'remote_port'} = $port; $self->{'remote_host'} = gethostbyaddr($iaddr,AF_INET); $self->{'remote_addr'} = inet_ntoa($iaddr); } while ( $self->{'serving'} and my $r = $c->get_request ) { warn "\nRequest:\n".$r->as_string; $self->{'request'} = $r; $self->{'uri'} = URI->new( $r->url, $d->url ); ( $self->{'server_name'}, $self->{'server_port'} ) = $r->header('Host') =~ /(.*):(.*)/; my $params_hr = $self->uri->query_form_hash; # not currently used here $self->{'response'} = HTTP::Response->new( RC_OK, undef, [ # headers 'Content-type' => 'text/html', ], ); # XXX currently, only GET method is supported $r->method eq 'GET' or $c->send_error( RC_METHOD_NOT_ALLOWED ), next; # if the path matches a defined cmd handler exactly, just go with that. # otherwise, try to find a matching prefix among the defined handlers. my $path = $self->uri->path; my $cmd = $config{$path} ? $path : (sort { ($a =~ m,/,g) <=> ($b =~ m,/,g) or length($b) <=> length($a) } grep { $path =~ /^\Q$_\E\b/ } keys %config)[0]; defined $cmd and $cmd eq '/' && $cmd ne $path and undef $cmd; # but don't accept '/' as a matching prefix. if ( $cmd ) { if ( ref($config{$cmd}) eq 'CODE' ) { $self->{'content_set'}=0; my $ret = $config{$cmd}->( $self ); $self->return($ret) unless $self->{'content_set'}; # ignore returned value if the handler has called ->return()! } elsif ( ref($config{$cmd}) eq 'SCALAR' ) # text { $self->return( ${ $config{$cmd} } ); } else { $self->response->code( RC_NOT_FOUND ); $self->return( <Error:

Command '$cmd' not recognized at this time!

EOF } } else { $self->response->code( RC_NOT_FOUND ); $self->return( <Error:

Command path '$path' not recognized at this time!

EOF } $c->send_response( $self->response ); } $c->close; undef $c; } } sub continue_link { my $context = shift; my $text = shift; my $cmd = '/'; @_ % 2 and $cmd = shift; my %args = @_; my $uri = new URI; $uri->path( $cmd ); $uri->query_form( %args ); qq($text) } sub continue_formstart { my $context = shift; my $cmd = '/'; @_ % 2 and $cmd = shift; my %args = @_; join '', qq(
), map( qq(), keys %args ), } sub continue_button # a form containing a single button, plus optionally some hidden args { my $context = shift; my $text = shift; join '', $context->continue_formstart( @_ ), # cmd (optionally), and args qq(), '
' } use Exporter; our @ISA=qw(Exporter); our @EXPORT_OK=qw( %LINK %FORMSTART %BUTTON ); { package Tie::EmbedFunc; sub TIEHASH { bless $_[1], $_[0] } sub FETCH { join $", $_[0]->( split $; => $_[1] => -1 ); } } tie our %LINK, 'Tie::EmbedFunc', sub { __PACKAGE__->continue_link(@_) }; tie our %FORMSTART, 'Tie::EmbedFunc', sub { __PACKAGE__->continue_formstart(@_) }; tie our %BUTTON, 'Tie::EmbedFunc', sub { __PACKAGE__->continue_button(@_) }; =pod if you say use CGI::AppEasy qw( $BUTTON ); then you do this in your code: $html = "... $BUTTON{'Recalc','/recalc',foo=>42} ..."; of course, you don't have to pollute your own namespace if you don't want to: use CGI::AppEasy; $html = "... $CGI::AppEasy::BUTTON{'Recalc','/recalc',foo=>42} ..."; You can even use your own variable if you wish: use CGI::AppEasy; our %Button; *Button = \%CGI::AppEasy::BUTTON; $html = "... $Button{'Recalc','/recalc',foo=>42} ..."; =cut __PACKAGE__