File Coverage

lib/App/Context/HTTP.pm
Criterion Covered Total %
statement 40 229 17.4
branch 10 138 7.2
condition 0 38 0.0
subroutine 9 19 47.3
pod 6 10 60.0
total 65 434 14.9


line stmt bran cond sub pod time code
1              
2             #############################################################################
3             ## $Id: HTTP.pm 13887 2010-04-06 13:36:42Z spadkins $
4             #############################################################################
5              
6             package App::Context::HTTP;
7             $VERSION = (q$Revision: 13887 $ =~ /(\d[\d\.]*)/)[0]; # VERSION numbers generated by svn
8              
9 1     1   5 use App;
  1         1  
  1         22  
10 1     1   974 use App::Context;
  1         4  
  1         58  
11              
12             @ISA = ( "App::Context" );
13              
14 1     1   662 use App::UserAgent;
  1         2  
  1         28  
15 1     1   6 use Time::HiRes qw(gettimeofday tv_interval);
  1         1  
  1         10  
16 1     1   184 use Date::Format;
  1         2  
  1         51  
17              
18 1     1   4 use strict;
  1         1  
  1         7057  
19              
20             =head1 NAME
21              
22             App::Context::HTTP - context in which we are currently running
23              
24             =head1 SYNOPSIS
25              
26             # ... official way to get a Context object ...
27             use App;
28             $context = App->context();
29             $config = $context->config(); # get the configuration
30             $config->dispatch_events(); # dispatch events
31              
32             # ... alternative way (used internally) ...
33             use App::Context::HTTP;
34             $context = App::Context::HTTP->new();
35              
36             =cut
37              
38             #############################################################################
39             # DESCRIPTION
40             #############################################################################
41              
42             =head1 DESCRIPTION
43              
44             A Context class models the environment (aka "context)
45             in which the current process is running.
46             For the App::Context::HTTP class, this models any of the
47             web application runtime environments which employ the HTTP protocol
48             and produce HTML pages as output. This includes CGI, mod_perl, FastCGI,
49             etc. The difference between these environments is not in the Context
50             but in the implementation of the Request and Response objects.
51              
52             =cut
53              
54             #############################################################################
55             # PROTECTED METHODS
56             #############################################################################
57              
58             =head1 Protected Methods:
59              
60             The following methods are intended to be called by subclasses of the
61             current class.
62              
63             =cut
64              
65             #############################################################################
66             # _init()
67             #############################################################################
68              
69             =head2 _init()
70              
71             The _init() method is called from within the standard Context constructor.
72              
73             The _init() method sets debug flags.
74              
75             * Signature: $context->_init($args)
76             * Param: $args hash{string} [in]
77             * Return: void
78             * Throws: App::Exception
79             * Since: 0.01
80              
81             Sample Usage:
82              
83             $context->_init($args);
84              
85             =cut
86              
87             sub _init {
88 1 50   1   4 &App::sub_entry if ($App::trace);
89 1         2 my ($self, $args) = @_;
90 1 50       3 $args = {} if (!defined $args);
91              
92 1         3 eval {
93 1         12 $self->{user_agent} = App::UserAgent->new($self);
94             };
95 1 50       3 $self->add_message("Context::HTTP::_init(): $@") if ($@);
96              
97 1 50       5 &App::sub_exit() if ($App::trace);
98             }
99              
100             sub _default_session_class {
101 0 0   0   0 &App::sub_entry if ($App::trace);
102 0         0 my $session_class = "App::Session::HTMLHidden";
103 0 0       0 &App::sub_exit($session_class) if ($App::trace);
104 0         0 return($session_class);
105             }
106              
107             #############################################################################
108             # PROTECTED METHODS
109             #############################################################################
110              
111             =head1 Protected Methods
112              
113             These methods are considered protected because no class is ever supposed
114             to call them. They may however be called by the context-specific drivers.
115              
116             =cut
117              
118             sub dispatch_events_begin {
119 0 0   0 0 0 &App::sub_entry if ($App::trace);
120 0         0 my ($self) = @_;
121 0         0 my $events = $self->{events};
122 0         0 my $request = $self->request();
123              
124 0         0 my $session_id = $request->get_session_id();
125 0         0 my $session = $self->session($session_id);
126 0         0 $self->set_current_session($session);
127              
128 0         0 my $request_events = $request->get_events();
129 0 0 0     0 if ($request_events && $#$request_events > -1) {
130 0         0 push(@$events, @$request_events);
131             }
132 0         0 $self->init_profiler_log();
133              
134 0 0       0 &App::sub_exit() if ($App::trace);
135             }
136              
137             sub dispatch_events {
138 0 0   0 1 0 &App::sub_entry if ($App::trace);
139 0         0 my ($self) = @_;
140              
141 0         0 my ($content_length);
142 0         0 my $content_description = "Unknown";
143              
144 0         0 $self->dispatch_events_begin();
145 0         0 my $events = $self->{events};
146              
147 0         0 my $options = $self->{options};
148 0   0     0 my $app = $options->{app} || "app";
149 0         0 my $profiler = $options->{"app.Context.profiler"};
150 0         0 my ($app_scope, $app_scope_id_type, $app_scope_id, $content_name);
151              
152 0         0 eval {
153 0         0 my $user = $self->user();
154 0         0 my $authorization = $self->authorization();
155 0         0 my ($event, $service_type, $service_name, $method, $args, $return_results, $return_event_results, $event_results);
156 0         0 my $results = "";
157             # my $display_current_widget = 1;
158              
159 0 0       0 if ($#$events > -1) {
160 0 0       0 if ($profiler) {
161 0         0 $self->profile_start("event");
162             }
163 0         0 while ($#$events > -1) {
164 0         0 $event = shift(@$events);
165 0         0 ($service_type, $service_name, $method, $args, $return_event_results) = @$event;
166 0 0       0 if ($authorization->is_authorized("/App/$service_type/$service_name/$method", $user)) {
167 0         0 $event_results = $self->call($service_type, $service_name, $method, $args);
168 0 0       0 if ($return_event_results) {
169 0         0 $results = $event_results;
170 0         0 $return_results = 1;
171             }
172 0         0 $user = $self->user();
173             }
174             }
175 0 0       0 if ($profiler) {
176 0 0       0 my $args_str = (ref($args) eq "ARRAY") ? join(",", @$args) : $args;
177 0         0 $app_scope = "$service_type($service_name).$method($args_str)";
178 0         0 $self->profile_stop("event");
179             }
180             }
181 0         0 $service_type = $self->so_get("default","ctype","SessionObject");
182 0         0 $service_name = $self->so_get("default","cname");
183              
184 0 0       0 if ($authorization->is_authorized("/App/$service_type/$service_name", $user)) {
185             # do nothing
186             }
187             else {
188 0 0       0 if ($self->session_object_exists("login_${app}")) {
189 0         0 $service_name = "login_${app}";
190             }
191             else {
192 0         0 $service_name = "login";
193             }
194             }
195              
196 0 0       0 $results = $self->service($service_type, $service_name) if (!$return_results);
197              
198 0         0 my $response = $self->response();
199 0         0 my $ref = ref($results);
200 0 0 0     0 if (!$ref || $ref eq "ARRAY" || $ref eq "HASH") {
    0 0        
201 0         0 $app_scope = "results [$ref]";
202 0 0       0 if ($profiler) {
203 0         0 $self->update_profiler_log($app_scope, $service_name, $app_scope_id_type, $app_scope_id);
204             }
205 0         0 $response->content($results);
206             }
207             elsif ($results->isa("App::Service")) {
208 0         0 ($app_scope, $app_scope_id_type, $app_scope_id, $content_name) = $results->content_description();
209 0   0     0 $content_name ||= $service_name;
210 0 0       0 if ($profiler) {
211 0         0 $self->update_profiler_log($app_scope, $content_name, $app_scope_id_type, $app_scope_id);
212             }
213 0         0 $response->content($results->content());
214 0         0 $response->content_type($results->content_type());
215             }
216             else {
217 0         0 $app_scope = "$service_type($service_name).internals()";
218 0 0       0 if ($profiler) {
219 0         0 $self->update_profiler_log($app_scope, $service_name, $app_scope_id_type, $app_scope_id);
220             }
221 0         0 $response->content($results->internals());
222             }
223              
224 0 0       0 if ($profiler) {
225 0         0 $self->profile_start("xfer", 1);
226             }
227 0         0 $content_length = $self->send_response();
228              
229 0 0       0 if ($profiler) {
230 0         0 $self->{profile_state}{app_scope} = $app_scope;
231 0         0 $self->{profile_state}{content_length} = $content_length;
232             }
233             };
234 0 0       0 if ($@) {
235 0         0 $content_length = $self->send_error($@);
236 0 0       0 if ($profiler) {
237 0         0 $self->{profile_state}{app_scope} = "ERROR [$app_scope]: $@";
238 0         0 $self->{profile_state}{content_length} = $content_length;
239             }
240             }
241              
242 0 0       0 if ($self->{options}{debug_context}) {
243 0         0 print STDERR $self->dump();
244             }
245              
246 0         0 $self->dispatch_events_finish();
247 0 0       0 &App::sub_exit() if ($App::trace);
248             }
249              
250             sub dispatch_events_finish {
251 0 0   0 0 0 &App::sub_entry if ($App::trace);
252 0         0 my ($self) = @_;
253 0         0 $self->restore_default_session();
254 0         0 $self->shutdown(); # assume we won't be doing anything else (this can be overridden)
255 0 0       0 &App::sub_exit() if ($App::trace);
256             }
257              
258             sub send_error {
259 0 0   0 0 0 &App::sub_entry if ($App::trace);
260 0         0 my ($self, $errmsg) = @_;
261 0         0 my $str = <
262             Content-type: text/plain
263              
264             -----------------------------------------------------------------------------
265             AN ERROR OCCURRED in App::Context::HTTP->dispatch_events()
266             -----------------------------------------------------------------------------
267             $errmsg
268              
269             -----------------------------------------------------------------------------
270             Additional messages from earlier stages may be relevant if they exist below.
271             -----------------------------------------------------------------------------
272             $self->{messages}
273             EOF
274 0         0 my $content_length = length($str);
275 0         0 print $str;
276 0 0       0 &App::sub_exit($content_length) if ($App::trace);
277 0         0 return($content_length);
278             }
279              
280             #############################################################################
281             # request()
282             #############################################################################
283              
284             =head2 request()
285              
286             * Signature: $context->request()
287             * Param: void
288             * Return: void
289             * Throws: App::Exception
290             * Since: 0.01
291              
292             Sample Usage:
293              
294             $context->request();
295              
296             The request() method gets the current Request being handled in the Context.
297              
298             =cut
299              
300             sub request {
301 1 50   1 1 5 &App::sub_entry if ($App::trace);
302 1         3 my $self = shift;
303              
304 1 50       5 if (! defined $self->{request}) {
305              
306             #################################################################
307             # REQUEST
308             #################################################################
309              
310 1         14 my $request_class = $self->get_option("request_class");
311 1 50       97 if (!$request_class) {
312 1         5 my $gateway = $ENV{GATEWAY_INTERFACE};
313             # TODO: need to distinguish between PerlRun, Registry, libapreq, other
314 1 50       7 if ($ENV{MOD_PERL}) { # mod_perl: Registry
    50          
315 0         0 $request_class = "App::Request::CGI";
316             }
317             elsif ($ENV{HTTP_USER_AGENT}) { # running as CGI script?
318 0         0 $request_class = "App::Request::CGI";
319             }
320             else {
321 1         4 $request_class = "App::Request::CGI";
322             }
323             }
324              
325 1         1 eval {
326 1         11 $self->{request} = App->new($request_class, "new", $self, $self->{options});
327             };
328             # ignore the failure to find a request. no request is currently available. method will return undef.
329             }
330              
331 1 50       5 &App::sub_exit($self->{request}) if ($App::trace);
332 1         6 return $self->{request};
333             }
334              
335             #############################################################################
336             # send_response()
337             #############################################################################
338              
339             =head2 send_response()
340              
341             * Signature: $context->send_response()
342             * Param: void
343             * Return: void
344             * Throws: App::Exception
345             * Since: 0.01
346              
347             Sample Usage:
348              
349             $context->send_response();
350              
351             =cut
352              
353             sub send_response {
354 0 0   0 1   &App::sub_entry if ($App::trace);
355 0           my $self = shift;
356              
357 0           my ($serializer, $response, $content, $content_type, $content_length, $headers);
358 0           $response = $self->response();
359 0           $content = $response->content();
360              
361             # NOTE: $content will be a scalar if HTML is being returned
362 0 0         if (ref($content)) {
363 0           my $request = $self->request();
364 0           my $returntype = $request->get_returntype();
365 0           $serializer = $self->serializer($returntype);
366 0           $content = $serializer->serialize($content);
367 0           $content_type = $serializer->serialized_content_type();
368             }
369              
370 0 0         $content_type = $response->content_type() if (!$content_type);
371 0 0         $content_type = "text/plain" if (!$content_type);
372 0           $headers = "Content-type: $content_type\n";
373              
374 0 0         if (defined $self->{headers}) {
375 0           $headers .= $self->{headers};
376 0           delete $self->{headers}
377             }
378              
379 0 0         if ($self->{options}{gzip}) {
380 0           my $user_agent = $self->user_agent();
381 0           my $gzip_ok = $user_agent->supports("http.header.accept-encoding.x-gzip");
382              
383 0 0         if ($gzip_ok) {
384 0           $headers .= "Content-encoding: gzip\n";
385 1     1   5225 use Compress::Zlib;
  1         101837  
  1         1219  
386 0           $content = Compress::Zlib::memGzip($content);
387             }
388             }
389 0           $content_length = length($content);
390              
391 0 0         if ($self->{messages}) {
392 0           my $msg = $self->{messages};
393 0           $self->{messages} = "";
394 0           $msg =~ s/
/\n/g;
395 0           print "Content-type: text/plain\n\n", $msg, "\n";
396             }
397             else {
398 0           print $headers, "\n", $content;
399             }
400 0 0         &App::sub_exit($content_length) if ($App::trace);
401 0           return($content_length);
402             }
403              
404             #############################################################################
405             # set_header()
406             #############################################################################
407              
408             =head2 set_header()
409              
410             * Signature: $context->set_header()
411             * Param: void
412             * Return: void
413             * Throws: App::Exception
414             * Since: 0.01
415              
416             Sample Usage:
417              
418             $context->set_header();
419              
420             =cut
421              
422             sub set_header {
423 0 0   0 1   &App::sub_entry if ($App::trace);
424 0           my ($self, $header) = @_;
425 0 0         if ($self->{headers}) {
426 0           $self->{headers} .= $header;
427             }
428             else {
429 0           $self->{headers} = $header;
430             }
431 0 0         &App::sub_exit() if ($App::trace);
432             }
433              
434             #############################################################################
435             # user_agent()
436             #############################################################################
437              
438             =head2 user_agent()
439              
440             The user_agent() method returns a UserAgent objects which is primarily
441             useful to see what capabilities the user agent (browser) supports.
442              
443             * Signature: $user_agent = $context->user_agent();
444             * Param: void
445             * Return: $user_agent App::UserAgent
446             * Throws:
447             * Since: 0.01
448              
449             Sample Usage:
450              
451             $user_agent = $context->user_agent();
452              
453             =cut
454              
455             sub user_agent {
456 0 0   0 1   &App::sub_entry if ($App::trace);
457 0           my $self = shift;
458 0           my $user_agent = $self->{user_agent};
459 0 0         &App::sub_exit($user_agent) if ($App::trace);
460 0           return($user_agent);
461             }
462              
463             #############################################################################
464             # PUBLIC METHODS
465             #############################################################################
466              
467             =head1 Public Methods:
468              
469             =cut
470              
471             #############################################################################
472             # user()
473             #############################################################################
474              
475             =head2 user()
476              
477             The user() method returns the username of the authenticated user.
478             The special name, "guest", refers to the unauthenticated (anonymous) user.
479              
480             * Signature: $username = $self->user();
481             * Param: void
482             * Return: string
483             * Throws:
484             * Since: 0.01
485              
486             Sample Usage:
487              
488             $username = $context->user();
489              
490             In a request/response environment, this turns out to be a convenience
491             method which gets the authenticated user from the current Request object.
492              
493             =cut
494              
495             sub user {
496 0 0   0 1   &App::sub_entry if ($App::trace);
497 0           my $self = shift;
498 0   0       my $user = $self->{effective_user} || $self->{user};
499              
500 0 0         if (!$user) {
501 0           my $options = $self->{options};
502 0           my ($effective_user);
503 0           my $authenticated = 0;
504 0 0         if ($options->{app_auth_required}) {
505             # Bypass Basic Authentication, /../..?u=username&p=password
506 0           my $password = $self->so_get("default","p");
507 0           $user = $self->so_get("default","u");
508              
509 0 0 0       if (defined $password && defined $user) {
510 0           my $authentication = $self->authentication();
511 0 0         if ( $authentication->validate_password($user, $password) ) {
512 0           $authenticated = 1;
513 0           $effective_user = $self->so_get("default","eu");
514             }
515             }
516             }
517             else {
518 0           $user = $self->request()->user();
519              
520 0           my $p_pass = $self->so_get("default","p");
521 0           my $u_user = $self->so_get("default","u");
522 0 0 0       if (defined $p_pass && defined $u_user) {
523 0           my $authentication = $self->authentication();
524 0 0         if ( $authentication->validate_password($u_user, $p_pass) ) {
525 0           $authenticated = 1;
526 0           $user = $self->so_get("default","u");
527 0           $effective_user = $self->so_get("default","u");
528             }
529             else {
530 0           $user = 'guest';
531 0           $effective_user = 'guest';
532             }
533             }
534              
535 0           $authenticated = 1;
536 0           $effective_user = $self->so_get("default","u");
537             }
538              
539 0 0         $user = "guest" if (!$authenticated);
540 0           $ENV{REMOTE_USER} = $user;
541 0           $self->{user} = $user;
542 0 0 0       if ($user && $authenticated) {
543 0           my $switchable_users = $self->get_option("switchable_users");
544 0 0 0       if ($switchable_users && $switchable_users =~ /\b$user\b/) {
545             # check more carefully ...
546 0 0 0       if ($switchable_users eq $user ||
      0        
      0        
547             $switchable_users =~ /:$user:/ ||
548             $switchable_users =~ /^$user:/ ||
549             $switchable_users =~ /:$user$/) {
550 0 0         if ($effective_user) {
551 0           $user = $effective_user;
552 0           $self->{effective_user} = $effective_user;
553             }
554             }
555             }
556             }
557 0           $self->so_set("default", "user", $user);
558             }
559              
560 0 0         &App::sub_exit($user) if ($App::trace);
561 0           return $user;
562             }
563              
564             sub set_user {
565 0 0   0 0   &App::sub_entry if ($App::trace);
566 0           my ($self, $user) = @_;
567 0           $self->{user} = $user;
568 0           delete $self->{effective_user};
569 0 0         &App::sub_exit() if ($App::trace);
570             }
571              
572             1;
573