ASP4::HTTPContext - Provides access to the intrinsic objects for an HTTP request.


ASP4 documentation Contained in the ASP4 distribution.

Index


Code Index:

NAME

Top

ASP4::HTTPContext - Provides access to the intrinsic objects for an HTTP request.

SYNOPSIS

Top

  use ASP4::HTTPContext;

  my $context = ASP4::HTTPContext->current;

  # Intrinsics:
  my $request   = $context->request;
  my $response  = $context->response;
  my $session   = $context->session;
  my $server    = $context->server;
  my $config    = $context->config;
  my $stash     = $context->stash;

  # Advanced:
  my $cgi = $context->cgi;
  my $r = $context->r;

DESCRIPTION

Top

The HTTPContext itself is the root of all request-processing in an ASP4 web application.

There is only one ASP4::HTTPContext instance throughout the lifetime of a request.

PROPERTIES

Top

current

Returns the ASP4::HTTPContext object in use for the current HTTP request.

request

Returns the ASP4::Request for the HTTP request.

response

Returns the ASP4::Response for the HTTP request.

server

Returns the ASP4::Server for the HTTP request.

session

Returns the ASP4::SessionStateManager for the HTTP request.

stash

Returns the current stash hash in use for the HTTP request.

config

Returns the current ASP4::Config for the HTTP request.

cgi

Provided Just In Case - returns the CGI object for the HTTP request.

r

Provided Just In Case - returns the Apache2::RequestRec for the HTTP request.

NOTE: Under ASP4::API (eg: in a unit test) $r will be an instance of ASP4::Mock::RequestRec instead.

BUGS

Top

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

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

HOMEPAGE

Top

Please visit the ASP4 homepage at http://0x31337.org/code/ to see examples of ASP4 in action.


ASP4 documentation Contained in the ASP4 distribution.

package ASP4::HTTPContext;

use strict;
use warnings 'all';
use HTTP::Date ();
use HTTP::Headers ();
use ASP4::ConfigLoader;
use ASP4::Request;
use ASP4::Response;
use ASP4::Server;
use ASP4::OutBuffer;
use ASP4::SessionStateManager::NonPersisted;

use vars '$_instance';

sub new
{
  my ($class) = @_;
  
  my $s = bless {
    config => ASP4::ConfigLoader->load,
    buffer => [ ASP4::OutBuffer->new ],
    stash  => { },
    headers_out => HTTP::Headers->new(),
  }, $class;
  $s->config->_init_inc();
  
  my $web = $s->config->web;
  $s->config->load_class( $web->handler_resolver );
  $s->config->load_class( $web->handler_runner );
  $s->config->load_class( $s->config->data_connections->session->manager );
  $s->config->load_class( $web->filter_resolver );
  
  return $_instance = $s;
}# end new()


sub setup_request
{
  my ($s, $r, $cgi) = @_;
  
  $ENV{DOCUMENT_ROOT} = $r->document_root;
  $s->{r} = $r;
  $s->{cgi} = $cgi;
  
  # Must instantiate $_instance before creating the other objects:
  $s->{request}   ||= ASP4::Request->new();
  $s->{response}  ||= ASP4::Response->new();
  $s->{server}    ||= ASP4::Server->new();
  
  my $do_session_onstart;
  if( $s->do_disable_session_state )
  {
    $s->{session} ||= ASP4::SessionStateManager::NonPersisted->new( $s->r );
  }
  else
  {
    $s->{session}   ||= $s->config->data_connections->session->manager->new( $s->r );
    $do_session_onstart++;
  }# end if()
  
  $s->{global_asa} = $s->resolve_global_asa_class( );
  {
    no warnings 'uninitialized';
    $s->{global_asa}->init_asp_objects( $s );
    if( $do_session_onstart )
    {
      unless( $s->session->{__started} )
      {
        $s->handle_phase( $s->global_asa->can('Session_OnStart') );
        $s->session->{__started} = 1;
      }# end unless()
    }# end if()
  }
  
  return $_instance;
}# end setup_request()


# Intrinsics:
sub current   { $_instance || shift->new }
sub request   { shift->{request} }
sub response  { shift->{response} }
sub server    { shift->{server} }
sub session   { shift->{session} }
sub config    { shift->{config} }
sub stash     { shift->{stash} }

# More advanced:
sub cgi         { shift->{cgi} }
sub r           { shift->{r} }
sub global_asa  { shift->{global_asa} }
sub handler     { shift->{handler} }
sub headers_out { shift->{headers_out} }
sub content_type  { my $s = shift; $s->r->content_type( @_ ) }
sub status        { my $s = shift; $s->r->status( @_ ) }
sub did_send_headers  { shift->{did_send_headers} }
sub did_end {
  my $s = shift;
  @_ ? $s->{did_end} = shift : $s->{did_end};
}

sub rprint {
  my ($s,$str) = @_;
  $s->buffer->add( $str );
}

sub rflush {
  my $s = shift;
  $s->send_headers;
  $s->r->print( $s->buffer->data );
  $s->r->rflush;
  $s->rclear;
}

sub rclear {
  my $s = shift;
  $s->buffer->clear;
}

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;
  $s->rflush;
  push @{$s->{buffer}}, ASP4::OutBuffer->new;
}
sub purge_buffer  { shift( @{shift->{buffer}} ) }


sub execute
{
  my ($s, $args, $is_include) = @_;
  
  unless( $is_include )
  {
    # Set up and execute any matching request filters:
    my $resolver = $s->config->web->filter_resolver;
    foreach my $filter ( $resolver->new()->resolve_request_filters( $s->r->uri ) )
    {
      $s->config->load_class( $filter->class );
      $filter->class->init_asp_objects( $s );
      my $IS_FILTER = 1;
      my $res = $s->handle_phase(sub{ $filter->class->new()->run( $s ) }, $IS_FILTER);
      if( $s->did_end || ( defined($res) && $res != -1 ) )
      {
        return $res;
      }# end if()
    }# end foreach()
    
    my $start_res = $s->handle_phase( $s->global_asa->can('Script_OnStart') );
    return $start_res if $s->did_end || defined( $start_res );
  }# 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;
    }
    else
    {
      return;
    }# 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;
  
  my $error = "$@";
  $s->response->Status( 500 );
  no strict 'refs';

  $s->response->Clear;
  my ($main, $title, $file, $line) = $error =~ m/^((.*?)\s(?:at|in)\s(.*?)\sline\s(\d+))/;
  $s->stash->{error} = {
    title       => $title,
    file        => $file,
    line        => $line,
    stacktrace  => $error,
  };
  warn "[Error: @{[ HTTP::Date::time2iso() ]}] @{[ $main || $error ]}\n";
  
  $s->config->load_class( $s->config->errors->error_handler );
  my $error_handler = $s->config->errors->error_handler->new();
  $error_handler->init_asp_objects( $s );
  eval { $error_handler->run( $s ) };
  confess $@ if $@;
  
  return $s->end_request;
}# end handle_error()


sub end_request
{
  my $s = shift;
  
  $s->handle_phase( $s->global_asa->can('Script_OnEnd') )
    unless $s->{did_end};
  
  $s->response->End;
  $s->session->save unless $s->session->is_read_only;
  my $res = $s->response->Status =~ m/^200/ ? 0 : $s->response->Status;
  
  return $res;
}# end end_request()


sub resolve_global_asa_class
{
  my $s = shift;
  
  my $file = $s->config->web->www_root . '/GlobalASA.pm';
  my $class;
  if( -f $file )
  {
    $class = $s->config->web->application_name . '::GlobalASA';
    eval { require $file };
    confess $@ if $@;
  }
  else
  {
    $class = 'ASP4::GlobalASA';
    $s->config->load_class( $class );
  }# end if()
  
  return $class;
}# end resolve_global_asa_class()


sub do_disable_session_state
{
  my ($s) = @_;
  
  my ($uri) = split /\?/, $s->r->uri;
  my ($yes) = grep { $_->disable_session } grep {
    if( my $pattern = $_->uri_match )
    {
      $uri =~ m/$pattern/
    }
    else
    {
      $uri eq $_->uri_equals;
    }# end if()
  } $s->config->web->disable_persistence;
  
  return $yes;
}# end do_disable_session_state()


sub DESTROY
{
  my $s = shift;
  undef(%$s);
}# end DESTROY()

1;# return true: