File Coverage

blib/lib/Gungho/Component/Core.pm
Criterion Covered Total %
statement 34 36 94.4
branch n/a
condition n/a
subroutine 12 12 100.0
pod n/a
total 46 48 95.8


line stmt bran cond sub pod time code
1             # $Id: /mirror/gungho/lib/Gungho/Component/Core.pm 31304 2007-11-29T11:56:44.884140Z lestrrat $
2             #
3             # Copyright (c) 2007 Daisuke Maki <daisuke@endeworks.jp>
4             # All rights reserved.
5              
6             package Gungho::Component::Core;
7 1     1   642 use strict;
  1         1  
  1         22  
8 1     1   3 use warnings;
  1         1  
  1         20  
9 1     1   3 use base qw(Gungho::Component);
  1         1  
  1         48  
10 1     1   3 use Carp ();
  1         1  
  1         14  
11 1     1   3 use Config::Any;
  1         0  
  1         13  
12 1     1   385 use Class::Inspector;
  1         2446  
  1         22  
13 1     1   396 use Event::Notify;
  1         422  
  1         20  
14 1     1   350 use UNIVERSAL::isa;
  1         656  
  1         3  
15 1     1   428 use UNIVERSAL::require;
  1         857  
  1         7  
16 1     1   383 use HTTP::Status qw(status_message);
  1         2530  
  1         85  
17              
18 1     1   290 use Gungho::Exception;
  1         1  
  1         6  
19 1     1   305 use Gungho::Request;
  0            
  0            
20             use Gungho::Response;
21             use Gungho::Util;
22              
23             __PACKAGE__->mk_classdata('notify_hub');
24              
25             sub setup
26             {
27             my $c = shift;
28              
29             $c->notify_hub( Event::Notify->new );
30             $c->setup_log();
31             $c->setup_provider();
32             $c->setup_handler();
33             $c->setup_engine();
34             $c->setup_plugins();
35              
36             $c;
37             }
38              
39             sub setup_log
40             {
41             my $c = shift;
42              
43             my $log_config = { %{$c->config->{log} || { logs => [] }} };
44             my $module = delete $log_config->{module} || 'Simple';
45             my $pkg = $c->load_gungho_module($module, 'Log');
46             my $log = $pkg->new(config => $log_config->{config} || $log_config);
47              
48             $log->setup($c);
49             $c->log($log);
50             }
51              
52             sub setup_provider
53             {
54             my $c = shift;
55              
56             my $config = $c->config->{provider};
57              
58             my $ref = ref $config;
59             if (! $config || ! defined $ref) {
60             Carp::croak("Gungho requires a provider");
61             }
62              
63             if ($ref eq 'CODE') {
64             # Smells like an inlined provider
65             my $code = $config;
66             $config = {
67             module => "Inline",
68             config => {
69             callback => $code
70             }
71             }
72             } elsif ( $ref ne 'HASH') {
73             Carp::croak("Gungho requires a provider");
74             }
75              
76             my $pkg = $c->load_gungho_module($config->{module}, 'Provider');
77             $pkg->isa('Gungho::Provider') or die "$pkg is not a Gungho::Provider subclass";
78             my $obj = $pkg->new(config => $config->{config} || {} );
79             $obj->setup( $c );
80             $c->provider( $obj );
81             }
82              
83             sub setup_engine
84             {
85             my $c = shift;
86              
87             my $config = $c->config->{engine} || {
88             module => 'POE',
89             };
90             if (! $config || ref $config ne 'HASH') {
91             Carp::croak("Gungho requires a engine");
92             }
93              
94             my $pkg = $c->load_gungho_module($config->{module}, 'Engine');
95             $pkg->isa('Gungho::Engine') or die "$pkg is not a Gungho::Engine subclass";
96             my $obj = $pkg->new( config => $config->{config} || {} );
97             $obj->setup( $c );
98             $c->engine( $obj );
99             }
100              
101             sub setup_handler
102             {
103             my $c = shift;
104              
105             my $config = $c->config->{handler} || {
106             module => 'Null',
107             config => {}
108             };
109             my $ref = ref $config;
110             if (! $config || ! defined $ref) {
111             Carp::croak("Gungho requires a handler");
112             }
113              
114             if ($ref eq 'CODE') {
115             # Smells like an inlined handler
116             my $code = $config;
117             $config = {
118             module => "Inline",
119             config => {
120             callback => $code
121             }
122             }
123             } elsif ( $ref ne 'HASH') {
124             Carp::croak("Gungho requires a handler");
125             }
126              
127             my $pkg = $c->load_gungho_module($config->{module}, 'Handler');
128             $pkg->isa('Gungho::Handler') or die "$pkg is not a Gungho::Handler subclass";
129             my $obj = $pkg->new( config => $config->{config} || {});
130             $obj->setup( $c );
131             $c->handler( $obj );
132             }
133              
134             sub setup_plugins
135             {
136             my $c = shift;
137              
138             my $plugins = $c->config->{plugins} || [];
139             foreach my $plugin (@$plugins) {
140             my $pkg = $c->load_gungho_module($plugin->{module}, 'Plugin');
141             my $obj = $pkg->new( config => $plugin->{config} || {});
142             $obj->setup($c);
143             }
144             }
145              
146             sub has_feature
147             {
148             my ($c, $name) = @_;
149             return exists $c->features()->{$name};
150             }
151              
152             sub load_gungho_module
153             {
154             my ($c, $pkg, $prefix) = @_;
155             return Gungho::Util::load_module(
156             $pkg,
157             $prefix ? "Gungho::${prefix}" : "Gunho"
158             );
159             Class::Inspector->loaded($pkg) or $pkg->require or die;
160             return $pkg;
161             }
162              
163             sub dispatch_requests
164             {
165             my $c = shift;
166             if ($c->is_running) {
167             $c->provider->dispatch($c, @_);
168             $c->notify('dispatch.dispatch_requests');
169             }
170             }
171              
172             sub prepare_request
173             {
174             my $c = shift;
175             my $req = shift;
176             $c->notify('dispatch.prepare_request', $req);
177             return $req;
178             }
179              
180             sub prepare_response
181             {
182             my ($c, $res) = @_;
183              
184             {
185             my $old = $res;
186             $res = Gungho::Response->new(
187             $res->code,
188             $res->message,
189             $res->headers,
190             $res->content
191             );
192             $res->request( $old->request );
193             }
194             return $res;
195             }
196              
197             sub send_request
198             {
199             my $c = shift;
200             my $request = shift;
201             $request = $c->prepare_request($request);
202             return $c->engine->send_request($c, $request);
203             }
204              
205             sub pushback_request
206             {
207             my ($c, $request) = @_;
208             $c->provider->pushback_request( $c, $request );
209             }
210              
211             sub request_is_allowed { 1 }
212              
213             sub handle_response
214             {
215             my $c = shift;
216             my ($req, $res) = @_;
217              
218             my $e;
219             eval {
220             $c->maybe::next::method($req, $res);
221             };
222             if ($e = Gungho::Exception->caught('Gungho::Exception::HandleResponse::Handled')) {
223             return;
224             } elsif ($e = Gungho::Exception->caught()) {
225             die $e;
226             }
227             $c->handler->handle_response($c, $req, $res);
228             }
229              
230             sub handle_dns_response
231             {
232             my ($c, $request, $answer, $dns_response) = @_;
233              
234             my $host = $request->uri->host;
235             my $addr = $answer->address;
236             $request->header(Host => $host);
237             $request->notes(original_host => $host);
238             $request->notes(resolved_ip => $addr);
239             eval {
240             $c->send_request($request);
241             };
242             if (my $e = $@) {
243             if ($e->isa('Gungho::Exception::RequestThrottled')) {
244             # This request was throttled. Attempt to do it later
245             $c->provider->pushback_request($c, $request);
246             } else {
247             die $e;
248             }
249             }
250              
251             return 1;
252             }
253              
254             # Utility method to create an error HTTP response.
255             # Stolen from PoCo::Client::HTTP::Request
256             sub _http_error
257             {
258             my ($self, $code, $message, $request) = @_;
259              
260             my $nl = "\n";
261             my $r = Gungho::Response->new($code);
262             my $http_msg = status_message($code);
263             my $m = (
264             "<html>$nl"
265             . "<HEAD><TITLE>Error: $http_msg</TITLE></HEAD>$nl"
266             . "<BODY>$nl"
267             . "<H1>Error: $http_msg</H1>$nl"
268             . "$message$nl"
269             . "</BODY>$nl"
270             . "</HTML>$nl"
271             );
272              
273             $r->content($m);
274             $r->request($request);
275             return $r;
276             }
277              
278             sub register_event
279             {
280             my $c = shift;
281             $c->notify_hub->register_event(@_);
282             }
283             *register_hook = \&register_event;
284              
285             sub unregister_event
286             {
287             my $c = shift;
288             $c->notify_hub->unregister_event(@_);
289             }
290              
291             sub notify
292             {
293             my ($c, $event, @args) = @_;
294             $c->notify_hub->notify($event, $c, @args);
295             }
296             *run_hook = \&notify;
297              
298             sub shutdown
299             {
300             my ($c, $reason) = @_;
301              
302             $reason ||= 'unknown reason';
303             $c->log->notice("Gungho received a shutdown request!: '$reason'");
304             $c->is_running(0);
305              
306             # Tell everybody to shutdown
307             $c->provider->stop($reason);
308             $c->handler->stop($reason);
309             $c->engine->stop($reason);
310             }
311              
312             1;
313              
314             __END__
315              
316             =head1 NAME
317              
318             Gungho::Component::Core - Gungho Core Methods
319              
320             =head1 METHODS
321              
322             =head2 new($config)
323              
324             This method has been deprecated. Use run() instead.
325              
326             =head2 run
327              
328             Starts the Gungho process. It requires either the name of a config filename
329             or a hashref.
330              
331             =head2 has_feature($name)
332              
333             Returns true if Gungho supports some feature $name
334              
335             =head2 setup()
336              
337             Sets up the Gungho environment, including calling the various setup_*
338             methods to configure the provider, engine, handler, etc.
339              
340             =head2 setup_components()
341              
342             =head2 setup_engine()
343              
344             =head2 setup_handler()
345              
346             =head2 setup_log()
347              
348             =head2 setup_provider()
349              
350             =head2 setup_plugins()
351              
352             Sets up the various components.
353              
354             =head2 register_hook($hook_name => $coderef[, $hook_name => $coderef])
355              
356             Is deprecated. Use register_event instead.
357              
358             =head2 register_event($event, $observer)
359              
360             Registers an observer that gets notified when $event happens. The $observer
361             argument can be either an object implementing notify(), or a subroutine
362             reference.
363              
364             =head2 unregister_event($event, $observer)
365              
366             Unregisters an observer from the specified event
367              
368             =head2 run_hook($hook_name, @args)
369              
370             Is deprecated. Use notify() instead.
371              
372             =head2 notify($event, @args)
373              
374             Notifies observers of an event.
375              
376             =head2 has_requests
377              
378             Delegates to provider's has_requests
379              
380             =head2 get_requests
381              
382             Delegates to provider's get_requests
383              
384             =head2 handle_response
385              
386             Delegates to handler's handle_response
387              
388             =head2 handle_dns_response
389              
390             Delegates to engine's send_request upon successful DNS response
391              
392             =head2 dispatch_requests
393              
394             Calls provider->dispatch
395              
396             =head2 prepare_request($req)
397              
398             Given a request, preps it before sending it to the engine
399              
400             =head2 prepare_response($req)
401              
402             Given a response, preps it before sending it to handle_response()
403              
404             =head2 send_request
405              
406             Delegates to engine's send_request
407              
408             =head2 pushback_request
409              
410             Push back a request
411              
412             =head2 load_config($config)
413              
414             Loads the config from $config via Config::Any.
415              
416             =head2 load_gungho_module($name, $prefix)
417              
418             Loads a Gungho component. Compliments the module name with 'Gungho::$prefix::',
419             unless the name is prefixed with a '+'. In that case, no transformation is
420             performed, and the module name is used as-is.
421              
422             =head2 request_is_allowed($req)
423              
424             Returns true if the given request is allowed to be fetched (this has nothing
425             to do with authentication and such, and is purely internal)
426              
427             =head2 shutdown($reason)
428              
429             Shuts down Gungho. Call this if you want to tell the entire system to stop.
430             This method in turn calls stop methods on the Engine, Provider, and Handler
431             objects
432              
433             =cut