Gungho::Component::Core - Gungho Core Methods


Gungho documentation Contained in the Gungho distribution.

Index


Code Index:

NAME

Top

Gungho::Component::Core - Gungho Core Methods

METHODS

Top

new($config)

This method has been deprecated. Use run() instead.

run

Starts the Gungho process. It requires either the name of a config filename or a hashref.

has_feature($name)

Returns true if Gungho supports some feature $name

setup()

Sets up the Gungho environment, including calling the various setup_* methods to configure the provider, engine, handler, etc.

setup_components()

setup_engine()

setup_handler()

setup_log()

setup_provider()

setup_plugins()

Sets up the various components.

register_hook($hook_name => $coderef[, $hook_name => $coderef])

Is deprecated. Use register_event instead.

register_event($event, $observer)

Registers an observer that gets notified when $event happens. The $observer argument can be either an object implementing notify(), or a subroutine reference.

unregister_event($event, $observer)

Unregisters an observer from the specified event

run_hook($hook_name, @args)

Is deprecated. Use notify() instead.

notify($event, @args)

Notifies observers of an event.

has_requests

Delegates to provider's has_requests

get_requests

Delegates to provider's get_requests

handle_response

Delegates to handler's handle_response

handle_dns_response

Delegates to engine's send_request upon successful DNS response

dispatch_requests

Calls provider->dispatch

prepare_request($req)

Given a request, preps it before sending it to the engine

prepare_response($req)

Given a response, preps it before sending it to handle_response()

send_request

Delegates to engine's send_request

pushback_request

Push back a request

load_config($config)

Loads the config from $config via Config::Any.

load_gungho_module($name, $prefix)

Loads a Gungho component. Compliments the module name with 'Gungho::$prefix::', unless the name is prefixed with a '+'. In that case, no transformation is performed, and the module name is used as-is.

request_is_allowed($req)

Returns true if the given request is allowed to be fetched (this has nothing to do with authentication and such, and is purely internal)

shutdown($reason)

Shuts down Gungho. Call this if you want to tell the entire system to stop. This method in turn calls stop methods on the Engine, Provider, and Handler objects


Gungho documentation Contained in the Gungho distribution.

# $Id: /mirror/gungho/lib/Gungho/Component/Core.pm 31304 2007-11-29T11:56:44.884140Z lestrrat  $
#
# Copyright (c) 2007 Daisuke Maki <daisuke@endeworks.jp>
# All rights reserved.

package Gungho::Component::Core;
use strict;
use warnings;
use base qw(Gungho::Component);
use Carp ();
use Config::Any;
use Class::Inspector;
use Event::Notify;
use UNIVERSAL::isa;
use UNIVERSAL::require;
use HTTP::Status qw(status_message);

use Gungho::Exception;
use Gungho::Request;
use Gungho::Response;
use Gungho::Util;

__PACKAGE__->mk_classdata('notify_hub');

sub setup
{
    my $c = shift;

    $c->notify_hub( Event::Notify->new );
    $c->setup_log();
    $c->setup_provider();
    $c->setup_handler();
    $c->setup_engine();
    $c->setup_plugins();

    $c;
}

sub setup_log
{
    my $c = shift;

    my $log_config = { %{$c->config->{log} || { logs => [] }} };
    my $module     = delete $log_config->{module} || 'Simple';
    my $pkg        = $c->load_gungho_module($module, 'Log');
    my $log        = $pkg->new(config => $log_config->{config} || $log_config);

    $log->setup($c);
    $c->log($log);
}

sub setup_provider
{
    my $c = shift;

    my $config = $c->config->{provider};

    my $ref = ref $config;
    if (! $config || ! defined $ref) {
        Carp::croak("Gungho requires a provider");
    }

    if ($ref eq 'CODE') {
        # Smells like an inlined provider
        my $code = $config;
        $config = {
            module => "Inline",
            config => {
                callback => $code
            }
        }
    } elsif ( $ref ne 'HASH') {
        Carp::croak("Gungho requires a provider");
    }

    my $pkg = $c->load_gungho_module($config->{module}, 'Provider');
    $pkg->isa('Gungho::Provider') or die "$pkg is not a Gungho::Provider subclass";
    my $obj = $pkg->new(config => $config->{config} || {} );
    $obj->setup( $c );
    $c->provider( $obj );
}

sub setup_engine
{
    my $c = shift;

    my $config = $c->config->{engine} || {
        module => 'POE',
    };
    if (! $config || ref $config ne 'HASH') {
        Carp::croak("Gungho requires a engine");
    }

    my $pkg = $c->load_gungho_module($config->{module}, 'Engine');
    $pkg->isa('Gungho::Engine') or die "$pkg is not a Gungho::Engine subclass";
    my $obj = $pkg->new( config => $config->{config} || {} );
    $obj->setup( $c );
    $c->engine( $obj );
}

sub setup_handler
{
    my $c = shift;

    my $config = $c->config->{handler} || {
        module => 'Null',
        config => {}
    };
    my $ref = ref $config;
    if (! $config || ! defined $ref) {
        Carp::croak("Gungho requires a handler");
    }

    if ($ref eq 'CODE') {
        # Smells like an inlined handler
        my $code = $config;
        $config = {
            module => "Inline",
            config => {
                callback => $code
            }
        }
    } elsif ( $ref ne 'HASH') {
        Carp::croak("Gungho requires a handler");
    }

    my $pkg = $c->load_gungho_module($config->{module}, 'Handler');
    $pkg->isa('Gungho::Handler') or die "$pkg is not a Gungho::Handler subclass";
    my $obj = $pkg->new( config => $config->{config} || {});
    $obj->setup( $c );
    $c->handler( $obj );
}

sub setup_plugins
{
    my $c = shift;

    my $plugins = $c->config->{plugins} || [];
    foreach my $plugin (@$plugins) {
        my $pkg = $c->load_gungho_module($plugin->{module}, 'Plugin');
        my $obj = $pkg->new( config => $plugin->{config} || {});
        $obj->setup($c);
    }
}

sub has_feature
{
    my ($c, $name) = @_;
    return exists $c->features()->{$name};
}

sub load_gungho_module
{
    my ($c, $pkg, $prefix) = @_;
    return Gungho::Util::load_module( 
        $pkg,
        $prefix ? "Gungho::${prefix}" : "Gunho"
    );
    Class::Inspector->loaded($pkg) or $pkg->require or die;
    return $pkg;
}

sub dispatch_requests
{
    my $c = shift;
    if ($c->is_running) {
        $c->provider->dispatch($c, @_);
        $c->notify('dispatch.dispatch_requests');
    }
}

sub prepare_request
{
    my $c = shift;
    my $req  = shift;
    $c->notify('dispatch.prepare_request', $req);
    return $req;
}

sub prepare_response
{
    my ($c, $res) = @_;

    {
        my $old = $res;
        $res = Gungho::Response->new(
            $res->code,
            $res->message,
            $res->headers,
            $res->content
        );
        $res->request( $old->request );
    }
    return $res;
}

sub send_request
{
    my $c = shift;
    my $request = shift;
    $request = $c->prepare_request($request);
    return $c->engine->send_request($c, $request);
}

sub pushback_request
{
    my ($c, $request) = @_;
    $c->provider->pushback_request( $c, $request );
}

sub request_is_allowed { 1 }

sub handle_response
{
    my $c = shift;
    my ($req, $res) = @_;

    my $e;
    eval {
        $c->maybe::next::method($req, $res);
    };
    if ($e = Gungho::Exception->caught('Gungho::Exception::HandleResponse::Handled')) {
        return;
    } elsif ($e = Gungho::Exception->caught()) {
        die $e;
    }
    $c->handler->handle_response($c, $req, $res);
}

sub handle_dns_response
{
    my ($c, $request, $answer, $dns_response) = @_;

    my $host = $request->uri->host;
    my $addr = $answer->address;
    $request->header(Host => $host);
    $request->notes(original_host => $host);
    $request->notes(resolved_ip   => $addr);
    eval {
        $c->send_request($request);
    };
    if (my $e = $@) {
        if ($e->isa('Gungho::Exception::RequestThrottled')) {
            # This request was throttled. Attempt to do it later
            $c->provider->pushback_request($c, $request);
        } else {
            die $e;
        }
    }

    return 1;
}

# Utility method to create an error HTTP response.
# Stolen from PoCo::Client::HTTP::Request
sub _http_error
{
    my ($self, $code, $message, $request) = @_;

    my $nl = "\n";
    my $r = Gungho::Response->new($code);
    my $http_msg = status_message($code);
    my $m = (
      "<html>$nl"
      . "<HEAD><TITLE>Error: $http_msg</TITLE></HEAD>$nl"
      . "<BODY>$nl"
      . "<H1>Error: $http_msg</H1>$nl"
      . "$message$nl"
      . "</BODY>$nl"
      . "</HTML>$nl"
    );

    $r->content($m);
    $r->request($request);
    return $r;
}

sub register_event
{
    my $c = shift;
    $c->notify_hub->register_event(@_);
}
*register_hook = \&register_event;

sub unregister_event
{
    my $c = shift;
    $c->notify_hub->unregister_event(@_);
}

sub notify
{
    my ($c, $event, @args) = @_;
    $c->notify_hub->notify($event, $c, @args);
}
*run_hook = \&notify;

sub shutdown
{
    my ($c, $reason) = @_;

    $reason ||= 'unknown reason';
    $c->log->notice("Gungho received a shutdown request!: '$reason'");
    $c->is_running(0);

    # Tell everybody to shutdown
    $c->provider->stop($reason);
    $c->handler->stop($reason);
    $c->engine->stop($reason);
}

1;

__END__