ASP4

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

    - ->is_subrequest means that a subrequest context will not clobber ->current.

2012-02-24    1.083
  - A wild Meta.json appeared in 1.082!

2012-02-24    1.082
  - Response->Redirect after Response->TrapInclude was causing the redirect to fail.
  - This release introduces a hack to fix it, by writing a meta tag to the client.

2012-02-13    1.081
  - Updated logging of errors so that it outputs something interesting, instead
    of a blank line.
  - Running under mod_perl should now correctly support full RESTful interfaces.

2012-02-12    1.080
  - Added support for multiple external "routes.json" files.

2012-02-07    1.079
  - Errors output to the stderr are now derived directly from $@ not from any
    parsed version of it.

2012-02-02    1.078
  - Fixed installation problem that came up in v1.075 
    (compilation root was missing leading forward slash on non-windows systems).

2012-02-01    1.077
  - Loath to add a mime-types-all-knowing dependency, we have a small list of
    common mime-types (html, css, js, etc).
  - Added mime for html and svg.

Changes  view on Meta::CPAN


2011-12-10    1.068
  - ASP4 is now hosted on github. https://github.com/jdrago999/ASP4

2011-12-01    1.067
  - ASP4::GlobalASA is completely deprecated.
  - ASP4::ErrorHandler contained a bug (cannot find method 'context').  Thanks
    to ray.baksh++ for discovering it.

2011-11-16    1.066
  - Fixed a POD error in ASP4::ErrorHandler::Remote.
  - ASP4::ErrorHandler::Remote now correctly clones the error object before POSTing it.

2011-11-15    1.065
  - Documented asp4-prep and asp4-deploy.  These are deployment tools for ASP4 apps.
  - Other POD updates here and there.

2011-11-15    1.064
  - 1.063 was broken - please upgrade.

2011-11-15    1.063
  - Stealth-mode ASP4::ErrorHandler::Remote will send your error messages to a
    remote server via http.
  - Added ASP4::Error
  - Refactored ASP4::ErrorHandler to be more easily sub-classable.
  - GlobalASA is now officially removed.

2011-11-13    1.062
  - The httpd.conf produced by asphelper had an incorrect DocumentRoot.  Fixed now.

2011-11-07    1.061
  - asphelper now creates new ASP4 apps using the proper structure.

Changes  view on Meta::CPAN

  - TODO: Add POD for asp4-prep and asp4-deploy.  This is delayed until it's proven
    that this is the correct way for onesie-twosie deployments.

2011-10-04    1.057
  - Renamed @AppRoot@ macro to @ProjectRoot@ to avoid confusion with $Config->web->application_root
  - Adjusted package declarations in some "stealth-mode" pm files because they have no POD yet.

2011-10-03    1.056
  - Updated asphelper to include directives that disable mod_deflate and Apache2::Reload
    for ASP4 websites. RayBaksh++
  - This fixes the dreaded "This website uses an invalid form of compression" error
    that you may have gotten after trying to $Response->Status(404) within an asp script.
  - Added in-memory mock sessionstate handler for faster testing and easier installation.

2011-09-22    1.055
  - Giving credit where credit is due :-)
  - Erikdj++
  - Added *experimental* memcached session storage backend.

2011-09-20    1.054
  - Added @AppRoot@ macro for asp4-config.json.  It is 1 folder "up" from @ServerRoot@.

Changes  view on Meta::CPAN

2011-07-09    v1.049
[Bug Fixes]
  - v1.048 broke session cookies.
  - Upgrade to v1.049 (quick).

2011-07-07    v1.048
[Bug Fixes]
  - <% $Response->Status(404); return $Response->End; %> DID NOT WORK.
    Instead it continued processing other ContentPlaceHolders.  Now we check
    to see if $Response->End was called before we process anything else.
  - Still getting some "Content encoding error" messages from FF/Chrome/MSIE but
    we're almost there.

2011-05-19    v1.047
[Bug Fixes]
  - $Response->Expires("30M") wasn't documented.  Now it is.
  - $Response->Expires wasn't working properly.  Now it is. (Always ended up with pre-epoch expiration times).

2011-05-03    v1.046
[Bug Fixes]
  - $Response->Redirect(...) wasn't returning '301' - now it does.

2011-05-03    v1.045
[Bug Fixes]
  - Actually it turned out that setting $Session->is_read_only(1) *DID* prevent
    $Session->save() from working.  This is now fixed to match the documentation.

2011-05-01    v1.044

[Bug Fixes]
  - ASP4::ModPerl now does the Right Thing when a non-200 response is encountered.
    - 500 response does not result in an "encoding error" in firefox.
    - 200 (or 0 response) does the right thing.
    - non-200 (and non-500) response does the right thing (eg: 401)
  - ASP4::SessionStateManager now checks $s->is_changed *before* checking $s->{__lastMod} date
    before deciding whether is should persist its changes in ->save().

[New Features]
  - $Session->is_read_only(1) is new.  Setting it to a true value (eg: 1) will prevent
    the default behavior of calling $Session->save() at the end of each successful request.

2011-04-08    v1.043
  - Documentation overhaul.

2011-03-23    v1.042
  - Fixed sporadic error in master pages that looks like this:
    Can't call method "Write" on an undefined value at /tmp/PAGE_CACHE/BStat/_masters_global_asp.pm line 1.
  - Apparently $s->init_asp_objects($context) was not getting called before the 
    master page's run() method was called, resulting in a call to $Response->Write(...)
    before $Response had been initialized.

2010-11-11    v1.041
  - ASP4::UserAgent calls all cleanup handlers registered via $Server->RegisterCleanup(sub { }, @args)
    at the end of each request, not when the ASP4::Mock::Pool object's DESTROY method is called.
    This fixes a condition which caused conflict when a Class::DBI::Lite ORM is
    used and the ASP4 application is executed via the `asp4` helper script.

2010-10-25    v1.040
  - 1.039 introduced a bug that could cause session-id conflicts in the asp_sessions table.
  - This release fixes that bug.

2010-10-25    v1.039
  - Session expiration now happens exclusively on the server, not as the 
    result of an expiring session cookie.

2010-10-21    v1.038
  - Another stab at getting http response codes right for errors.

2010-09-25    v1.037
  - Added a couple tweaks here and there to make ASP4 run on Windows a little easier:
    * $Config->web->page_cache_root now does the Right Thing on linux & win32.
    * $Config->web->page_cache_root is automatically created if it does not exist.

2010-09-21    v1.036
  - Added ASP4::StaticHandler to process requests for static files - like images, css, etc.

2010-09-17    v1.035

Changes  view on Meta::CPAN

  - In an environment with multiple VirtualHosts running ASP4 web applications,
    ASP4::HandlerResolver's %HandlerCache and %FileTimes hashes were shared between
    all VirtualHosts.  This means that if you had 2 web apps (Foo and Bar) then
    "/index.asp" on "Foo" might get handled by "Bar::_index_asp" or vice versa.

2010-02-08    v1.009
  ! Upgrade Recommended !
  - ASP4::ModPerl sets $ENV{DOCUMENT_ROOT} = $r->document_root before doing 
    anything else.
  - The scaffold website output by 'asphelper' had some minor bugs:
      * email was sometimes referred to as email_address
      * The error message for the 'message' field was displaying the wrong error.

2010-02-07    v1.008
  - Multi-value form parameters (eg 3 checkboxes with the same name) will now
    *correctly* appear as an arrayref in $Form, instead of 3 values joined with
    a null byte.

2010-01-31    v1.007
  - $FileUpload->SaveAs("/path/to/file.txt") will now create "/path" and "/path/to"
    before writing "/path/to/file.txt".

README.markdown  view on Meta::CPAN

The `$Session` object is a simple blessed hashref and should be used like a hashref.

Examples:

### Set a session variable

    $Session->{foo} = "bar";

    $Session->{thing} = {
      banana  => "yellow",
      cherry  => "red",
      peach   => "pink,
    };

### Get a session variable

    my $foo = $Session->{foo};

### $Session->save()

Called automatically at the end of every successful request, causes any changes

README.markdown  view on Meta::CPAN

    

    <asp:Content PlaceHolderID="main_content">
    <%
      # Sticky forms work like this:
      if( my $args = $Session->{__lastArgs} ) {
        map { $Form->{$_} = $args->{$_} } keys %$args;
      }
      

      # Our validation errors:
      my $errors = $Session->{validation_errors} || { };
      $::err = sub {
        my $field = shift;
        my $error = $errors->{$field} or return;
        %><span class="field_error"><%= $Server->HTMLEncode( $error ) %></span><%
      };
    %>
    <form id="register_form" method="post" action="/handlers/myapp.register">
      <p>
        <label>Email:</label>
        <input type="text" name="email" value="<%= $Server->HTMLEncode( $Form->{email} ) %>" />
        <% $::err->("email"); %>
      </p>
      <p>
        <label>Password:</label>
        <input type="password" name="password" />
        <% $::err->("password"); %>
      </p>
      <p>
        <label>Confirm Password:</label>
        <input type="password" name="password2" />
        <% $::err->("password2"); %>
      </p>
      <p>
        <input type="submit" value="Register Now" />
      </p>
    </form>
    </asp:Content>

The form submits to the URL `/handlers/app.register` which means `handlers/app/register.pm`

File: `handlers/app/register.pm`

README.markdown  view on Meta::CPAN

    use warnings 'all';
    use base 'ASP4::FormHandler';
    use vars __PACKAGE__->VARS; # Import $Response, $Form, $Session, etc
    use App::db::user;
    

    sub run {
      my ($self, $context) = @_;
      

    # If there is an error, return the user to the registration page:
    if( my $errors = $self->validate() ) {
      $Session->{validation_errors} = $errors;
      $Session->{__lastArgs} = $Form;
      $Session->save;
      return $Response->Redirect( $ENV{HTTP_REFERER} );
    }
    

    # Create the user:
    my $user = eval {
      App::db::user->do_transaction(sub {
        return App::db::user->create(
          email     => $Form->{email},
          password  => $Form->{password},
        );
      });
    };
    

    if( $@ ) {
      # There was an error:
      $Session->{validation_errors} = {email => "Server error.  Sorry!"};
      $Session->{__lastArgs} = $Form;
      $Session->save;
      return $Response->Redirect( $ENV{HTTP_REFERER} );
    }
    else {
      # No error - Sign them in:
      $Session->{user_id} = $user->id;
      $Session->{msg} = "Thank you for registering!";
      $Session->save;
      

        # Redirect to /profile.asp:
      return $Response->Redirect("/profile.asp");
      }# end if()
    }
    

    sub validate {
      my ($self) = @_;
      

    $self->trim_form;
    

    my $errors = { };
    no warnings 'uninitialized';
    

    # email:
    if( length($Form->{email}) ) {
      # Basic email validation:
      unless( $Form->{email} =~ m{[^@]+@[^@]+\.[^@]+} ) {
        $errors->{email} = "Invalid email address";
      }
    }
    else {
      $errors->{email} = "Required";
    }
    

    # password:
    unless( length($Form->{password} ) {
      $errors->{password} = "Required";
    }
    

    # password2:
    if( length($Form->{password2}) ) {
      if( length($Form->{password}) ) {
        unless( $Form->{password} eq $Form->{password2} ) {
          $errors->{password2} = "Passwords don't match";
        }
      }
    }
    else {
      $errors->{password2} = "Required";
    }
    

    # Bail out of we already have errors:
    return $errors if keys %$errors;
    

    # See if the user already exists:
    if( App::db::user->count_search( email => $Form->{email} ) ) {
      $errors->{email} = "Already in use";
    }
    

      # Errors or not?:
      keys %$errors ? return $errors : return;
    }
    

    1;# return true:

File: `htdocs/profile.asp`

    <%@ Page UseMasterPage="/masters/global.asp" %>
    

inc/Module/Install.pm  view on Meta::CPAN






# Whether or not inc::Module::Install is actually loaded, the
# $INC{inc/Module/Install.pm} is what will still get set as long as
# the caller loaded module this in the documented manner.
# If not set, the caller may NOT have loaded the bundled version, and thus
# they may not have a MI version that works with the Makefile.PL. This would
# result in false errors or unexpected behaviour. And we don't want that.
my $file = join( '/', 'inc', split /::/, __PACKAGE__ ) . '.pm';
unless ( $INC{$file} ) { die <<"END_DIE" }

Please invoke ${\__PACKAGE__} with:

	use inc::${\__PACKAGE__};

not:

	use ${\__PACKAGE__};

lib/ASP4.pm  view on Meta::CPAN

The C<$Session> object is a simple blessed hashref and should be used like a hashref.

Examples:

=head3 Set a session variable

  $Session->{foo} = "bar";

  $Session->{thing} = {
    banana  => "yellow",
    cherry  => "red",
    peach   => "pink,
  };

=head3 Get a session variable

  my $foo = $Session->{foo};

=head3 $Session->save()

Called automatically at the end of every successful request, causes any changes

lib/ASP4.pm  view on Meta::CPAN

  
  <asp:Content PlaceHolderID="headline">Register</asp:Content>
  
  <asp:Content PlaceHolderID="main_content">
  <%
    # Sticky forms work like this:
    if( my $args = $Session->{__lastArgs} ) {
      map { $Form->{$_} = $args->{$_} } keys %$args;
    }
    
    # Our validation errors:
    my $errors = $Session->{validation_errors} || { };
    $::err = sub {
      my $field = shift;
      my $error = $errors->{$field} or return;
      %><span class="field_error"><%= $Server->HTMLEncode( $error ) %></span><%
    };
  %>
  <form id="register_form" method="post" action="/handlers/myapp.register">
    <p>
      <label>Email:</label>
      <input type="text" name="email" value="<%= $Server->HTMLEncode( $Form->{email} ) %>" />
      <% $::err->("email"); %>
    </p>
    <p>
      <label>Password:</label>
      <input type="password" name="password" />
      <% $::err->("password"); %>
    </p>
    <p>
      <label>Confirm Password:</label>
      <input type="password" name="password2" />
      <% $::err->("password2"); %>
    </p>
    <p>
      <input type="submit" value="Register Now" />
    </p>
  </form>
  </asp:Content>

The form submits to the URL C</handlers/app.register> which means C<handlers/app/register.pm>

File: C<handlers/app/register.pm>

lib/ASP4.pm  view on Meta::CPAN

  
  use strict;
  use warnings 'all';
  use base 'ASP4::FormHandler';
  use vars __PACKAGE__->VARS; # Import $Response, $Form, $Session, etc
  use App::db::user;
  
  sub run {
    my ($self, $context) = @_;
    
    # If there is an error, return the user to the registration page:
    if( my $errors = $self->validate() ) {
      $Session->{validation_errors} = $errors;
      $Session->{__lastArgs} = $Form;
      $Session->save;
      return $Response->Redirect( $ENV{HTTP_REFERER} );
    }
    
    # Create the user:
    my $user = eval {
      App::db::user->do_transaction(sub {
        return App::db::user->create(
          email     => $Form->{email},
          password  => $Form->{password},
        );
      });
    };
    
    if( $@ ) {
      # There was an error:
      $Session->{validation_errors} = {email => "Server error.  Sorry!"};
      $Session->{__lastArgs} = $Form;
      $Session->save;
      return $Response->Redirect( $ENV{HTTP_REFERER} );
    }
    else {
      # No error - Sign them in:
      $Session->{user_id} = $user->id;
      $Session->{msg} = "Thank you for registering!";
      $Session->save;
      
      # Redirect to /profile.asp:
    return $Response->Redirect("/profile.asp");
    }# end if()
  }
  
  sub validate {
    my ($self) = @_;
    
    $self->trim_form;
    
    my $errors = { };
    no warnings 'uninitialized';
    
    # email:
    if( length($Form->{email}) ) {
      # Basic email validation:
      unless( $Form->{email} =~ m{[^@]+@[^@]+\.[^@]+} ) {
        $errors->{email} = "Invalid email address";
      }
    }
    else {
      $errors->{email} = "Required";
    }
    
    # password:
    unless( length($Form->{password} ) {
      $errors->{password} = "Required";
    }
    
    # password2:
    if( length($Form->{password2}) ) {
      if( length($Form->{password}) ) {
        unless( $Form->{password} eq $Form->{password2} ) {
          $errors->{password2} = "Passwords don't match";
        }
      }
    }
    else {
      $errors->{password2} = "Required";
    }
    
    # Bail out of we already have errors:
    return $errors if keys %$errors;
    
    # See if the user already exists:
    if( App::db::user->count_search( email => $Form->{email} ) ) {
      $errors->{email} = "Already in use";
    }
    
    # Errors or not?:
    keys %$errors ? return $errors : return;
  }
  
  1;# return true:

File: C<htdocs/profile.asp>

  <%@ Page UseMasterPage="/masters/global.asp" %>
  
  <asp:Content PlaceHolderID="meta_title">My Profile</asp:Content>
  

lib/ASP4/Config.pm  view on Meta::CPAN


ASP4::Config - Central configuration for ASP4

=head1 SYNOPSIS

  # Settings:
  $Config->system->settings->some_setting;
  $Config->system->settings->another_setting;
  
  # Error-handling:
  $Config->errors->error_handler;
  $Config->errors->mail_errors_to;
  $Config->errors->mail_errors_from;
  $Config->errors->smtp_server;
  
  # Web:
  $Config->web->application_name;
  $Config->web->application_root;
  $Config->web->project_root;
  $Config->web->www_root;
  $Config->web->handler_root;
  $Config->web->media_manager_upload_root;
  $Config->web->page_cache_root;
  

lib/ASP4/Config.pm  view on Meta::CPAN

      ],
      "env_vars": {
        "myvar":        "Some-Value",
        "another_var":  "Another Value"
      },
      "settings": {
        "foo": "bar",
        "baz": "bux"
      }
    },
    "errors": {
      "error_handler":    "ASP4::ErrorHandler",
      "mail_errors_to":   "you@yours.com",
      "mail_errors_from": "root@localhost",
      "smtp_server":      "localhost"
    },
    "web": {
      "application_name": "DefaultApp",
      "application_root": "@ServerRoot@",
      "www_root":         "@ServerRoot@/htdocs",
      "handler_root":     "@ServerRoot@/handlers",
      "page_cache_root":  "/tmp/PAGE_CACHE",
      "handler_resolver": "ASP4::HandlerResolver",
      "handler_runner":   "ASP4::HandlerRunner",

lib/ASP4/Error.pm  view on Meta::CPAN


use strict;
use warnings 'all';
use ASP4::HTTPContext;
use JSON::XS;


sub new
{
  my $class = shift;
  my ($err_str, %args);
  if( @_ )
  {
    if( @_ == 1 )
    {
      $err_str = shift;
    }
    else
    {
      %args = @_;
    }# end if()
  }
  elsif( $@ )
  {
    $err_str = $@;
  }# end if()
  
  my $context   = ASP4::HTTPContext->current;
  my $Config    = $context->config;
  my $Response  = $context->response;
  my $Session   = $context->session;
  my $Form      = $context->request->Form;
  
  my %session_data = %$Session;
  
  my $error;
  if( $err_str )
  {
    my ($main, $message, $file, $line) = $err_str =~ m/^((.*?)\s(?:at|in)\s(.*?)\sline\s(\d+))/;
    $error = {
      message     => $err_str,
      file        => $file,
      line        => $line,
      stacktrace  => $err_str,
    };
    
    if( $error->{file} =~ m{_asp\.pm$} )
    {
      $error->{file} = -f $ENV{SCRIPT_FILENAME} ? $ENV{SCRIPT_FILENAME} : $error->{file};
    }# end if()
  }
  else
  {
    $error = \%args;
  }# end if()
    
  # Get the line of code that's breaking:
  # And maybe a few before and after:
  my $code;
  if( -f $error->{file} )
  {
    if( open my $ifh, '<', $error->{file} )
    {
      my $max = 0;
      $max++ while <$ifh>;
      close($ifh);
      open $ifh, '<', $error->{file};
      
      my $padding = 10;
      my ($low, $high) = $class->_number_range($error->{line}, $max, $padding);

      my $line_number = 0;
      my @lines = ( );
      while( my $line = <$ifh> )
      {
        $line_number++;
        next unless $line_number >= $low;
        last if $line_number > $high;
        push @lines, "line $line_number: $line";
      }# end while()
      close($ifh);
      $code = join "", @lines;
    }# end if()
  }# end if()
  
  my %info = (
    # Defaults:
    domain        => eval { $Config->errors->domain } || $ENV{HTTP_HOST},
    request_uri   => $ENV{REQUEST_URI},
    file          => $error->{file},
    line          => $error->{line},
    message       => $error->{message},
    stacktrace    => $error->{stacktrace},
    code          => $code,
    form_data     => encode_json($Form) || "{}",
    session_data  => eval { encode_json(\%session_data) } || "{}",
    http_referer  => $ENV{HTTP_REFERER},
    user_agent    => $ENV{HTTP_USER_AGENT},
    http_code     => ($Response->Status =~ m{^(\d+)})[0],
    remote_addr   => $ENV{REMOTE_ADDR} || '127.0.0.1',
    # Allow overrides:
    %args
  );
  
  return bless \%info, $class;
}# end new()


sub domain        { $_[0]->{domain} }
sub request_uri   { $_[0]->{request_uri} }
sub file          { $_[0]->{file} }

lib/ASP4/Error.pm  view on Meta::CPAN

  my $high = $number + $padding <= $max ? $number + $padding : $max;
  return ($low, $high);
}# end _number_range()

1;# return true:

=pod

=head1 NAME

ASP4::Error - Representation of a server-side error

=head1 SYNOPSIS

  use ASP4::Error;
  
  # Pass in the $@ value after something dies or confesses:
  eval { die "Foo" };
  if( $@ ) {
    my $error = ASP4::Error->new( $@ )
  }
  
  # Pass in your own info:
  unless( $something ) {
    my $error = ASP4::Error->new(
      message => "If can, can.  If no can, no can!"
    );
  }
  
=head1 DESCRIPTION

ASP4 provides a simple means of dealing with errors.  It emails them, by default,
to an email address you specify.

Sometimes that is not convenient.  Maybe you want to do something special with
the error - like log it someplace special.

ASP4::Error is a simple representation of a server-side error.

=head1 PUBLIC READ-ONLY PROPERTIES

=head2 domain

C<<$Config->errors->domain>> or C<$ENV{HTTP_HOST}>

=head2 request_uri

C<$ENV{REQUEST_URI}>

=head2 file

The name of the file in which the error occurred.  This is gleaned from C<$@> unless C<file> is
passed to the constructor.

=head2 line

The line number within the file that the error occurred.  This is gleaned from C<$@> unless
C<line> is passed to the constructor.

=head2 code

A string.  Includes the 5 lines of code before and after the line of code where the error occurred.

=head2 message

Defaults to the first part of C<$@> unless otherwise specified.

=head2 stacktrace

A string - defaults to the value of C<$@>.

=head2 form_data

lib/ASP4/ErrorHandler.pm  view on Meta::CPAN

use base 'ASP4::HTTPHandler';
use vars __PACKAGE__->VARS;
use MIME::Base64;
use Data::Dumper;


sub run
{
  my ($s, $context) = @_;
  
  my $error = $Stash->{error};
  $s->print_error( $error );
  $s->send_error( $error );
}# end run()


sub print_error
{
  my ($s, $error) = @_;
  
  $Response->ContentType('text/html');

  if( $ENV{HTTP_HOST} eq 'localhost' )
  {
    $Response->Write( Dumper(\%$error) );
  }
  else
  {
    $Response->Write( $s->error_html( $error ) );
  }# end if()
  
  $Response->Flush;
}# end print_error()


sub send_error
{
  my ($s, $error) = @_;
  
  $Server->Mail(
    To                          => $Config->errors->mail_errors_to,
    From                        => $Config->errors->mail_errors_from,
    Subject                     => "ASP4: Error in @{[ $ENV{HTTP_HOST} ]}@{[ $ENV{REQUEST_URI} ]}",
    'content-type'              => 'text/html',
    'content-transfer-encoding' => 'base64',
    Message                     => encode_base64( $s->error_html($error) ),
    smtp                        => $Config->errors->smtp_server,
  );
}# end send_error()


sub error_html
{
  my ($s, $error) = @_;
  
  my $msg = <<"ERROR";
<!DOCTYPE html>
<html>
<head><title>500 Server Error</title>
<meta charset="utf-8" />
<style type="text/css">
HTML,BODY {
  background-color: #FFFFFF;
}

lib/ASP4/ErrorHandler.pm  view on Meta::CPAN

  width: 80px;
  font-weight: bold;
}
.info {
  float: left;
  color: #CC0000;
}
</style>
<body>
<h1>500 Server Error</h1>
<h2>@{[ $error->message ]}</h2>
<div><div class="label">URL:</div> <div class="info"><code>@{[ $ENV{HTTP_HOST} ]}@{[ $ENV{REQUEST_URI} ]}</code></div></div>
<div class="clear"></div>
<div><div class="label">File:</div> <div class="info"><code>@{[ $error->file ]}</code></div></div>
<div class="clear"></div>
<div><div class="label">Line:</div> <div class="info">@{[ $error->line ]}</div></div>
<div class="clear"></div>
<div><div class="label">Time:</div> <div class="info">@{[ HTTP::Date::time2iso() ]}</div></div>
<div class="clear"></div>
<h2>Stacktrace follows below:</h2>
<div class="code"><pre>@{[ $error->stacktrace ]}</pre></div>
<div class="clear"></div>
<h3>\%ENV</h3>
<div class="code"><pre>
HTTP_REFERER:     '@{[ $Server->HTMLEncode($ENV{HTTP_REFERER}||'NONE') ]}'
HTTP_COOKIE:      '@{[ $Server->HTMLEncode($ENV{HTTP_COOKIE}||'NONE') ]}'
HTTP_USER_AGENT:  '@{[ $Server->HTMLEncode($ENV{HTTP_USER_AGENT}||'NONE') ]}'
REMOTE_ADDR:      '@{[ $Server->HTMLEncode($ENV{REMOTE_ADDR}||'NONE') ]}'
</pre></div>
<h3>\$Form</h3>
<div class="code"><pre>@{[ Dumper($Form) ]}</pre></div>
<div class="clear"></div>
<div style="display: none;">
</body>
</html>
ERROR
  
  return $msg;
}# end error_html()


1;# return true:

=pod

=head1 NAME

ASP4::ErrorHandler - Default fatal error handler

=head1 SYNOPSIS

In your C<asp4-config.json>:

  ...
    "errors": {
      "error_handler":    "ASP4::ErrorHandler",
      "mail_errors_to":   "you@server.com",
      "mail_errors_from": "root@localhost",
      "smtp_server":      "localhost"
    },
  ...

=head1 DESCRIPTION

This class provides a default error handler which does the following:

1) Makes a simple HTML page and prints it to the browser, telling the user
that an error has just occurred.

2) Sends that same HTML to the email address specified in the config, using the
SMTP server also specified in the config.  The email subject will look something like:

  ASP4: Error in your-site.com/index.asp

=head1 SUBCLASSING

To subclass C<ASP4::ErrorHandler> you must do the following:

  package My::ErrorHandler;
  
  use strict;
  use warnings 'all';
  use base 'ASP4::ErrorHandler';
  use vars __PACKAGE__->VARS;
  
  sub run {
    my ($s, $context) = @_;
    
    my $error = $Stash->{error};
    
    # $error is an ASP4::Error object.
  
    # Do something here about the error.
    $s->print_error( $error );
    $s->send_error( $error );
  }
  
  1;# return true:

=head1 METHODS

=head2 error_html( $error )

Returns a string of html suitable for printing to the browser or emailing.

=head2 print_error( $error )

Prints the error html to the browser.

=head2 send_error( $error )

Sends the error html to the email address specified in the config, using C<<$Server->Mail(...)>>
and the smtp server specified in the config.

=head1 BUGS

It's possible that some bugs have found their way into this release.

Use RT L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=ASP4> to submit bug reports.

=head1 HOMEPAGE

lib/ASP4/ErrorHandler/Remote.pm  view on Meta::CPAN

use JSON::XS;
use Data::Dumper;
require ASP4;

our $ua;

sub run
{
  my ($s, $context) = @_;
  
  my $error = $Stash->{error};
  
  $s->print_error( $error );
  $s->send_error($error);
}# end run()


sub send_error
{
  my ($s, $error) = @_;
  
  $ua ||= LWP::UserAgent->new();
  $ua->agent( ref($s) . " $ASP4::VERSION" );
  my %clone = %$error;
  my $req = POST $Config->errors->post_errors_to, \%clone;
  $ua->request( $req );
}# end send_error()

1;# return true:

=pod

=head1 NAME

ASP4::ErrorHandler::Remote - Send your errors someplace else via http.

=head1 SYNOPSIS

In your C<asp4-config.json>:

  ...
    "errors": {
      "error_handler":    "ASP4::ErrorHandler::Remote",
      "post_errors_to":   "http://errors.ohno.com/post/errors/here/"
    },
  ...

=head1 DESCRIPTION

This class provides a default error handler which does the following:

1) Makes a simple HTML page and prints it to the browser, telling the user
that an error has just occurred.

2) Sends an error notification to the web address specified in the config.

The data contained within the POST will match the public properties of L<ASP4::Error>, like this:

  $VAR1 = {
            'remote_addr' => '127.0.0.1',
            'request_uri' => '/',
            'user_agent' => 'test-useragent v2.0',
            'file' => '/home/john/Projects/myapp/www/htdocs/index.asp',
            'session_data' => '{}',
            'message' => 'A fake error has occurred',
            'http_code' => '500',
            'stacktrace' => 'A fake error has occurred at /tmp/PAGE_CACHE/TSR_WWW/__index_asp.pm line 2.
  ',
            'domain' => 'www.tsr.local',
            'form_data' => '{}',
            'http_referer' => '',
            'code' => 'line 1: <h1>Hello, world!</h1>
  line 2: <%
  line 3:   die "A fake error has occurred";
  line 4: %>
  ',
            'line' => '2'
  };


=head1 PUBLIC METHODS

=head2 send_error( $error )

Sends the error data to the web address specified in C<<$Config->errors->post_errors_to>>.

The field names and values will correspond to the properties of an C<ASP4::Error> object.

=head1 BUGS

It's possible that some bugs have found their way into this release.

Use RT L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=ASP4> to submit bug reports.

=head1 HOMEPAGE

lib/ASP4/HTTPContext.pm  view on Meta::CPAN

}

sub send_headers
{
  my $s = shift;
  return if $s->{did_send_headers};
  
  my $headers = $s->headers_out;
  while( my ($k,$v) = each(%$headers) )
  {
    $s->r->err_headers_out->{$k} = $v;
  }# end while()

  $s->r->rflush;
  $s->{did_send_headers} = 1;
}# end send_headers()

# Here be dragons:
sub buffer        { shift->{buffer}->[-1] }
sub add_buffer    {
  my $s = shift;

lib/ASP4/HTTPContext.pm  view on Meta::CPAN

    }# end foreach()
  }# end unless()
  
  eval {
    $s->{handler} = $s->config->web->handler_resolver->new()->resolve_request_handler( $s->r->uri );
  };
  
  if( $@ )
  {
    $s->server->{LastError} = $@;
    return $s->handle_error;
  }# end if()

  return $s->response->Status( 404 ) unless $s->{handler};
  
  eval {
    $s->config->load_class( $s->handler );
    $s->config->web->handler_runner->new()->run_handler( $s->handler, $args );
  };
  
  if( $@ )
  {
    $s->server->{LastError} = $@;
    return $s->handle_error;
  }# end if()
  
  $s->response->Flush;
  my $res = $s->end_request();
  
  $res = 0 if $res =~ m/^200/;
  return $res;
}# end execute()


sub handle_phase
{
  my ($s, $ref, $is_filter) = @_;
  
  my $res = eval { $ref->( ) };
  if( $@ )
  {
    $s->handle_error;
  }# end if()
  
  # Undef on success:
  if( $is_filter )
  {
    if( defined($res) && $res > -1 )
    {
      $s->response->Status( $res );
      return $res;
    }

lib/ASP4/HTTPContext.pm  view on Meta::CPAN

    }# end if()
  }
  else
  {
    return if (! defined($res)) || $res == -1;
    return $s->response->Status =~ m/^200/ ? undef : $s->response->Status;
  }# end if()
}# end handle_phase()


sub handle_error
{
  my $s = shift;
  
  $s->response->Status( 500 );
  $s->response->Clear();
  my $err_str = $@;
  my $error = $s->server->Error( $@ );
  warn "[Error: @{[ HTTP::Date::time2iso() ]}] $err_str\n";
  
  return $s->end_request;
}# end handle_error()


sub end_request
{
  my $s = shift;
  
  $s->response->End;
  my $res = $s->response->Status =~ m/^200/ ? 0 : $s->response->Status;
  
  return $res;

lib/ASP4/Mock/RequestRec.pm  view on Meta::CPAN

    headers_out   => { },
    uri           => $args{uri} || $ENV{REQUEST_URI},
    args          => $args{args} || $ENV{QUERY_STRING},
    pnotes        => { },
    method        => $args{method},
    pool          => ASP4::Mock::Pool->new(),
    connection    => ASP4::Mock::Connection->new(),
  }, $class;
  
  weaken($s->{connection});
  $s->{err_headers_out} = $s->{headers_out};
  $s->{filename}        = $s->document_root . $s->uri;
  
  return $s;
}# end new()


# Public read-write properties:
sub pnotes
{
  my $s = shift;

lib/ASP4/Mock/RequestRec.pm  view on Meta::CPAN

}# end status()


# Public read-only properties:
sub document_root   { shift->{document_root} }
sub method          { uc( shift->{method} ) }
sub pool            { shift->{pool} }
sub connection      { shift->{connection} }
sub headers_out     { shift->{headers_out} }
sub headers_in      { shift->{headers_in} }
sub err_headers_out { shift->{err_headers_out} }

sub buffer          { shift->{buffer} } # Not documented:


# Public methods:
sub print { my ($s,$str) = @_; $s->{buffer} .= $str; }
sub content_type
{
  my ($s, $type) = @_;
  return $s->headers_out->{'content-type'} unless $type;

lib/ASP4/Mock/RequestRec.pm  view on Meta::CPAN

=head2 connection( )

Returns the current L<ASP4::Mock::Connection> object.

  my $connection = $r->connection;

=head2 headers_out( )

Returns a hashref representing the outgoing headers.

=head2 err_headers_out( )

Returns a hashref representing the outgoing headers.

=head2 status( [$new_status] )

Sets or gets the status code for the response.  200 for "OK", 301 for "Moved" - 404 for "not found" etc.

=head2 content_type( [$new_content_type] )

Sets or gets the mime-header for the outgoing response.  Default is C<text/plain>.

lib/ASP4/RequestFilter.pm  view on Meta::CPAN

    my ($self, $context) = @_;
    
    if( $Session->{is_logged_in} )
    {
      # The user is logged in - we can ignore this request:
      return $Response->Declined;
    }
    else
    {
      # The user must authenticate first:
      $Session->{validation_errors} = { general => "You must log in first" };
      return $Response->Redirect("/login/");
    }# end if()
  }
  
  1;# return true:

Then, in your C<asp4-config.json>:

  {
    ...

lib/ASP4/Response.pm  view on Meta::CPAN


=item * path

Defaults to "C</>" - you can restrict the "path" that the cookie will apply to.

=item * domain

Defaults to whatever you set your config->data_connections->session->cookie_domain to
in your asp4-config.json.  Otherwise defaults to C<$ENV{HTTP_HOST}>.

You can override the defaults by passing in a domain, but the browser may not accept
other domains.  See L<http://www.ietf.org/rfc/rfc2109.txt> for details.

=back

=head2 Redirect( $url )

Causes the following HTTP header to be sent:

  Status: 301 Moved
  Location: $url

lib/ASP4/Server.pm  view on Meta::CPAN

  
  ASP4::HTTPContext->current->config->web->www_root . $path;
}# end MapPath()


sub Mail
{
  my $s = shift;
  
  Mail::Sendmail::sendmail( @_ );
  die $Mail::Sendmail::error if $Mail::Sendmail::error;
  return $Mail::Sendmail::log;
}# end Mail()


sub RegisterCleanup
{
  my ($s, $sub, @args) = @_;
  
  $s->context->r->pool->cleanup_register( $sub, \@args );
}# end RegisterCleanup()


sub Error
{
  my $s = shift;
  
  my $error = ref($_[0]) && $_[0]->isa('ASP4::Error') ? $_[0] : ASP4::Error->new( @_ );

  $s->context->stash->{error} = $error;
  $s->context->config->load_class( $s->context->config->errors->error_handler );
  my $error_handler = $s->context->config->errors->error_handler->new();
  $error_handler->init_asp_objects( $s->context );
  $error_handler->run( $s->context );
  return $error;
}# end Error()


1;# return true:

=pod

=head1 NAME

ASP4::Server - Utility Methods

lib/ASP4/Server.pm  view on Meta::CPAN


The supplied coderef will be executed with its arguments as the request enters
its Cleanup phase.

See L<http://perl.apache.org/docs/2.0/user/handlers/http.html#PerlCleanupHandler> for details.

=head2 Error( [%args] )

Calling C<<$Server->Error()>> without arguments will use the value of C<$@> and
generate a L<ASP4::Error> object from it, then pass it to the C<run(...)> method
of your C<<$Config->errors->error_handler>> for processing.

Please take a look at the documentation for L<ASP4::Error>, L<ASP4::ErrorHandler>
and L<ASP4::ErrorHandler::Remote> for details on how errors are handled.

=head1 BUGS

It's possible that some bugs have found their way into this release.

Use RT L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=ASP4> to submit bug reports.

=head1 HOMEPAGE

Please visit the ASP4 homepage at L<http://0x31337.org/code/> to see examples

lib/ASP4/UserAgent.pm  view on Meta::CPAN

  };
}# end upload()


sub submit_form
{
  my ($s, $form) = @_;
  
  chdir( $s->{cwd} );
  
  my $temp_referrer = $ENV{HTTP_REFERER};
  my $req = $form->click;
  my $referer = $ENV{HTTP_REFERER};
  %ENV = (
    %{ $s->{env} },
    HTTP_REFERER    => $referer || '',
    DOCUMENT_ROOT   => $s->config->web->www_root,
    REQUEST_METHOD  => uc( $req->method ),
    CONTENT_TYPE    => $form->enctype ? $form->enctype : 'application/x-www-form-urlencoded',
    HTTP_COOKIE     => $s->http_cookie,
    REQUEST_URI     => $form->action,

lib/ASP4/UserAgent.pm  view on Meta::CPAN

  # XXX: Sometimes this dies with 'HTTP::Message requires bytes' or similar:
  eval { $response->content( $s->context->r->buffer ) };
  if( $@ )
  {
    (my $ascii = $s->context->r->buffer) =~ s/[^[:ascii:]]//gs;
    $response->content( $ascii );
  }# end if()
  
  $response->header( 'Content-Type' => $s->context->response->{ContentType} );
  
  foreach my $header ( $s->context->response->Headers, $s->context->r->err_headers_out )
  {
    if( my ($k,$v) = each(%$header) )
    {
      $response->header( lc($k) => $v );
      if( lc($k) eq 'set-cookie' )
      {
        my @cookies = ( );
        if( ref($v) )
        {
          @cookies = @$v;

sbin/asp4-deploy  view on Meta::CPAN

  -d $target && chdir($target)
    or die "Invalid --target: '$target': $!\n";
}# end if()

if( -d 'latest' )
{
  `rm -f deploying`;
  
  # Copy over the config files:
  `tar -zxvf "$src" && ln -s "$id" deploying`;
  my @test_errors = ( );
  foreach( grep { $_ !~ m{latest/common$} } <latest/*> )
  {
    my ($folder) = $_ =~ m{latest/([^/]+)};
    `cp -rf latest/$folder/conf/* deploying/$folder/conf/`;
    chdir("deploying/$folder");
    unless( eval { runtests( <t/*/*.t> ) } ) #/
    {
      push @test_errors, $@;
    }# end unless()
  }# end foreach()
  chdir($start_cwd);
  
  if( @test_errors )
  {
    die "Tests failed:\n", join "\n", @test_errors;
  }# end if()
  
  `rm -rf latest`;
  `rm -rf deploying`;
  `ln -s "$id" latest`;
}
else
{
  `tar -zxvf "$src" && ln -s "$id" latest`;
  my @to_update = ( );

sbin/asp4-deploy  view on Meta::CPAN

  /var/www/myweb/deploying  ->  /var/www/myweb/MyWeb_2011-11-15_23.59.39

=item Step 3

Copies all your configuration files from 'C<latest/*/conf/*>' into 'C<deploying/*/conf/*>'

=item Step 4

Runs unit tests on all folders under deploying except for 'C<common>'.

If all tests pass, we continue.  Otherwise, we bail out with errors.

=item Step 5

Unlink the C<deploying> symbolic link and change C<latest> to point to the new directory.

=item Step 6

B<After a successful deployment, you should restart apache.>

=back

sbin/asphelper  view on Meta::CPAN

  $dbUser,
  $dbPass
);

my $drh = DBI->install_driver("mysql");
my $rc = $drh->func('createdb', $dbName, $dbHost, $dbUser, $dbPass, 'admin');

my $dbh = eval { DBI->connect( @DSN, {RaiseError => 1} ) };
if( $@ )
{
  (my $error = $@) =~ s/\sat\s\Q$0\E\s+line.*//;
  die "[ERROR]: $error\n";
}# end if()

# Setup folder structure:
(my $project_path = lc($appName)) =~ s{::}{_}sg;
make_path($project_path);
chdir($project_path);
my $cwd = cwd();
my $appFolder = join( '/',  split(/::/, $appName) );
make_path("common/lib/$appFolder/db");
make_path("common/sbin");

sbin/asphelper  view on Meta::CPAN

      "@ServerRoot@/lib",
      "@ProjectRoot@/common/lib"
    ],
    "load_modules": [
    ],
    "env_vars": {
    },
    "settings": {
    }
  },
  "errors": {
    "error_handler":    "ASP4::ErrorHandler",
    "mail_errors_to":   "%email%",
    "mail_errors_from": "root@localhost",
    "smtp_server":      "localhost"
  },
  "web": {
    "application_name": "%appName%",
    "application_root": "@ServerRoot@",
    "www_root":         "@ServerRoot@/htdocs",
    "handler_root":     "@ServerRoot@/handlers",
    "page_cache_root":  "/tmp/PAGE_CACHE",
    "handler_resolver": "ASP4::HandlerResolver",
    "handler_runner":   "ASP4::HandlerRunner",

sbin/asphelper  view on Meta::CPAN


sub generic_httpconf {
  <<'EOF';

# Load up some important modules:
PerlModule DBI
PerlModule DBD::mysql
PerlModule ASP4::ModPerl

# Apache2::Reload does not play well with ASP4.
# Uncomment the following line if you get Apache2::Reload errors:
#PerlSetVar ReloadAll Off

# Admin website:
<VirtualHost *:80>

  ServerName    %domain%
  DocumentRoot  %CWD%/www/htdocs
  
  # Set the directory index:
  DirectoryIndex index.asp

t/010-basic/090-everything.t  view on Meta::CPAN

# static 404:
{
  ok(
    my $res = $api->ua->get('/missing-file.txt'),
    "Requested /missing-file.txt"
  );
  ok(
    ! $res->is_success,
    "Not successful"
  );
  like $res->status_line, qr{^404}, "Status looks like a 404 error";
}


t/conf/asp4-config.json  view on Meta::CPAN

    
    "env_vars": {
      "myvar":        "Some-Value",
      "another_var":  "Another Value"
    },
    "settings": {
      "foo": "bar",
      "baz": "bux"
    }
  },
  "errors": {
    "error_handler":    "ASP4::ErrorHandler",
    "mail_errors_to":   "jdrago_999@yahoo.com",
    "mail_errors_from": "root@localhost",
    "smtp_server":      "localhost"
  },
  "web": {
    "application_name": "DefaultApp",
    "application_root": "@ServerRoot@",
    "www_root":         "@ServerRoot@/htdocs",
    "handler_root":     "@ServerRoot@/handlers",
    "handler_resolver": "ASP4::HandlerResolver",
    "handler_runner":   "ASP4::HandlerRunner",
    "filter_resolver":  "ASP4::FilterResolver",



( run in 1.636 second using v1.01-cache-2.11-cpan-49f99fa48dc )