File Coverage

blib/lib/Plack/Middleware/CustomErrorDocument.pm
Criterion Covered Total %
statement 53 55 96.3
branch 14 20 70.0
condition 3 3 100.0
subroutine 9 9 100.0
pod 1 1 100.0
total 80 88 90.9


line stmt bran cond sub pod time code
1             package Plack::Middleware::CustomErrorDocument;
2              
3             # ABSTRACT: dynamically select error documents based on HTTP status code
4              
5             our $VERSION = '0.004'; # VERSION
6              
7 3     3   57623 use strict;
  3         8  
  3         110  
8 3     3   18 use warnings;
  3         5  
  3         103  
9 3     3   16 use parent qw(Plack::Middleware);
  3         5  
  3         26  
10 3     3   1758 use Plack::MIME;
  3         1711  
  3         81  
11 3     3   18 use Plack::Util;
  3         6  
  3         67  
12 3     3   16 use Plack::Util::Accessor qw( subrequest );
  3         6  
  3         28  
13              
14 3     3   1753 use HTTP::Status qw(is_error);
  3         7823  
  3         1783  
15              
16             sub call {
17 14     14 1 146696 my $self = shift;
18 14         25 my $env = shift;
19              
20 14         66 my $r = $self->app->($env);
21              
22             $self->response_cb(
23             $r,
24             sub {
25 14     14   272 my $r = shift;
26 14 100 100     52 unless ( is_error( $r->[0] ) && exists $self->{ $r->[0] } ) {
27 8         86 return;
28             }
29              
30 6 100       86 my $path
31             = ref $self->{ $r->[0] }
32             ? $self->{ $r->[0] }->($env)
33             : $self->{ $r->[0] };
34              
35 6 50       93 return unless $path;
36              
37 6 100       23 if ( $self->subrequest ) {
38 1         13 for my $key ( keys %$env ) {
39 22 100       72 unless ( $key =~ /^psgi/ ) {
40 13         58 $env->{ 'psgix.errordocument.' . $key }
41             = $env->{$key};
42             }
43             }
44              
45             # TODO: What if SCRIPT_NAME is not empty?
46 1         5 $env->{REQUEST_METHOD} = 'GET';
47 1         2 $env->{REQUEST_URI} = $path;
48 1         2 $env->{PATH_INFO} = $path;
49 1         3 $env->{QUERY_STRING} = '';
50 1         4 delete $env->{CONTENT_LENGTH};
51              
52 1         5 my $sub_r = $self->app->($env);
53 1 50       691 if ( $sub_r->[0] == 200 ) {
54 1         2 $r->[1] = $sub_r->[1];
55 1         9 $r->[2] = $sub_r->[2];
56             }
57              
58             # TODO: allow 302 here?
59              
60             } else {
61              
62 5         45 my $h = Plack::Util::headers( $r->[1] );
63 5         157 $h->remove('Content-Length');
64              
65 5         185 $h->set( 'Content-Type', Plack::MIME->mime_type($path) );
66              
67 5 50       528 open my $fh, "<", $path or die "$path: $!";
68 5 100       17 if ( $r->[2] ) {
69 4         47 $r->[2] = $fh;
70             } else {
71 1         2 my $done;
72             return sub {
73 2 50       82 unless ($done) {
74 2         47 return join '', <$fh>;
75             }
76 0           $done = 1;
77 0 0         return defined $_[0] ? '' : undef;
78 1         23 };
79             }
80             }
81             }
82 14         734 );
83             }
84              
85             1;
86              
87              
88              
89             =pod
90              
91             =head1 NAME
92              
93             Plack::Middleware::CustomErrorDocument - dynamically select error documents based on HTTP status code
94              
95             =head1 VERSION
96              
97             version 0.004
98              
99             =head1 SYNOPSIS
100              
101             # in app.psgi
102              
103             $app = Plack::Middleware::CustomErrorDocument->wrap(
104             $app,
105             # dynamic $path (set according to $env):
106             404 => sub {
107             my $env = shift;
108             ...
109             return $path;
110             },
111             # use static path
112             500 => 'path/to/error/doc',
113             );
114              
115             # or with Plack::Builder:
116             builder {
117             enable "Plack::Middleware::CustomErrorDocument",
118             404 => sub {
119             ...;
120             };
121             $app;
122             };
123              
124             # subrequests are possible, as with Plack::Middleware::ErrorDocument
125             # (but untested and unrecommended)
126             $app = Plack::Middleware::CustomErrorDocument->wrap(
127             $app,
128             404 => sub {
129             my $env = shift;
130             ...
131             return $path;
132             },
133             subrequest => 1,
134             );
135              
136             =head1 DESCRIPTION
137              
138             Dynamically select an appropriate error document for an HTTP status error code.
139             Pass in a subroutine coderef, which should take C<$env> as the sole argument,
140             and return the destination file path as a string.
141              
142             An example use would be to return a 'missing' image file for image requests that
143             result in a 404 status (and a standard 404 HTML page for all others).
144              
145             =head1 SEE ALSO
146              
147             =over 4
148              
149             =item *
150              
151             L
152              
153             =back
154              
155             =for :stopwords cpan testmatrix url annocpan anno bugtracker rt cpants kwalitee diff irc mailto metadata placeholders metacpan
156              
157             =head1 SUPPORT
158              
159             =head2 Bugs / Feature Requests
160              
161             Please report any bugs or feature requests through the issue tracker
162             at L.
163             You will be notified automatically of any progress on your issue.
164              
165             =head2 Source Code
166              
167             This is open source software. The code repository is available for
168             public review and contribution under the terms of the license.
169              
170             L
171              
172             git clone https://github.com/mjemmeson/Plack-Middleware-CustomErrorDocument.git
173              
174             =head1 AUTHOR
175              
176             Michael Jemmeson
177              
178             =head1 COPYRIGHT AND LICENSE
179              
180             This software is copyright (c) 2012 by Foxtons Ltd.
181              
182             This is free software; you can redistribute it and/or modify it under
183             the same terms as the Perl 5 programming language system itself.
184              
185             =cut
186              
187              
188             __END__