File Coverage

blib/lib/CGI/Application/Server.pm
Criterion Covered Total %
statement 28 30 93.3
branch n/a
condition n/a
subroutine 10 10 100.0
pod n/a
total 38 40 95.0


line stmt bran cond sub pod time code
1 9     9   1354136 use strict;
  9         24  
  9         406  
2 9     9   55 use warnings;
  9         19  
  9         621  
3             package CGI::Application::Server;
4             {
5             $CGI::Application::Server::VERSION = '0.063';
6             }
7             # ABSTRACT: a simple HTTP server for developing with CGI::Application
8              
9 9     9   78 use Carp 0.01 qw( confess );
  9         349  
  9         703  
10 9     9   19868 use CGI qw( param );
  9         190119  
  9         66  
11 9     9   1203 use Scalar::Util 1.18 qw( blessed reftype );
  9         239  
  9         842  
12 9     9   4750 use HTTP::Response;
  9         163107  
  9         295  
13 9     9   68 use HTTP::Status;
  9         22  
  9         3650  
14              
15 9     9   66 use base qw( HTTP::Server::Simple::CGI );
  9         16  
  9         10190  
16 9     9   210071 use HTTP::Server::Simple 0.18;
  9         263  
  9         209  
17 9     9   12976 use HTTP::Server::Simple::Static 0.02;
  0            
  0            
18              
19             # HTTP::Server::Simple methods
20              
21             sub new {
22             my $class = shift;
23             my $self = $class->SUPER::new(@_);
24             $self->{entry_points} = {};
25             $self->{document_root} = '.';
26             return $self;
27             }
28              
29             # accessors
30              
31             sub document_root {
32             my ($self, $document_root) = @_;
33             if (defined $document_root) {
34             (-d $document_root)
35             || confess "The server root ($document_root) is not found";
36             $self->{document_root} = $document_root;
37             }
38             $self->{document_root};
39             }
40              
41             sub entry_points {
42             my ($self, $entry_points) = @_;
43             if (defined $entry_points) {
44             (reftype($entry_points) && reftype($entry_points) eq 'HASH')
45             || confess "The entry points map must be a HASH reference, not $entry_points";
46             $self->{entry_points} = $entry_points;
47             }
48             $self->{entry_points};
49             }
50              
51             # check request
52              
53             sub is_valid_entry_point {
54             my ($self, $uri) = @_;
55              
56             # Remove all parameters
57             $uri =~ s/\?.*//;
58              
59             while ( $uri ) {
60             # Check to see if this is an exact match
61             if (exists $self->{entry_points}{$uri}) {
62             return ($uri, $self->{entry_points}{$uri});
63             }
64              
65             # Remove the rightmost path element
66             $uri =~ s/\/[^\/]*$//;
67             }
68              
69             # Check to see if there's an entry for '/'
70             if (exists $self->{entry_points}{'/'}) {
71             return ($uri, $self->{entry_points}{'/'});
72             }
73              
74             # Didn't find anything. Oh, well.
75             return;
76             }
77              
78             sub handle_request {
79             my ($self, $cgi) = @_;
80             if (my ($path, $target) = $self->is_valid_entry_point($ENV{REQUEST_URI})) {
81             # warn "$ENV{REQUEST_URI} ($target)\n";
82             # warn "\t$_ => " . param( $_ ) . "\n" for param();
83              
84             local $ENV{CGI_APP_RETURN_ONLY} = 1;
85             (local $ENV{PATH_INFO} = $ENV{PATH_INFO}) =~ s/\A\Q$path//;
86              
87             if (-d $target && -x $target) {
88             return $self->serve_static($cgi, $target);
89             }
90             elsif ($target->isa('CGI::Application::Dispatch')) {
91             return $self->_serve_response($target->dispatch);
92             } elsif ($target->isa('CGI::Application')) {
93             if (!defined blessed $target) {
94             return $self->_serve_response($target->new->run);
95             } else {
96             $target->query($cgi);
97             return $self->_serve_response($target->run);
98             }
99             }
100             else {
101             confess "Target must be a CGI::Application or CGI::Application::Dispatch subclass or the name of a directory that exists and is readable.\n";
102             }
103             } else {
104             return $self->serve_static($cgi, $self->document_root);
105             }
106             }
107              
108             sub _serve_response {
109             my ( $self, $stdout ) = @_;
110              
111             my $response = $self->_build_response( $stdout );
112             print $response->as_string();
113              
114             return 1; # Like ...Simple::Static::serve_static does
115             }
116              
117             # Shamelessly stolen from HTTP::Request::AsCGI by chansen
118             sub _build_response {
119             my ( $self, $stdout ) = @_;
120              
121             $stdout =~ s{(.*?\x0d?\x0a\x0d?\x0a)}{}xsm;
122             my $headers = $1;
123              
124             unless ( defined $headers ) {
125             $headers = "HTTP/1.1 500 Internal Server Error\x0d\x0a";
126             }
127              
128             unless ( $headers =~ /^HTTP/ ) {
129             $headers = "HTTP/1.1 200 OK\x0d\x0a" . $headers;
130             }
131              
132             my $response = HTTP::Response->parse($headers);
133             $response->date( time() ) unless $response->date;
134              
135             my $message = $response->message;
136             my $status = $response->header('Status');
137              
138             $response->header( Connection => 'close' );
139              
140             if ( $message && $message =~ /^(.+)\x0d$/ ) {
141             $response->message($1);
142             }
143              
144             if ( $status && $status =~ /^(\d\d\d)\s?(.+)?$/ ) {
145              
146             my $code = $1;
147             $message = $2 || HTTP::Status::status_message($code);
148              
149             $response->code($code);
150             $response->message($message);
151             }
152              
153             my $length = length $stdout;
154              
155             if ( $response->code == 500 && !$length ) {
156              
157             $response->content( $response->error_as_HTML );
158             $response->content_type('text/html');
159              
160             return $response;
161             }
162              
163             $response->add_content($stdout);
164             $response->content_length($length);
165              
166             return $response;
167             }
168              
169              
170             1;
171              
172             __END__