File Coverage

blib/lib/Plack/Middleware/XSLT.pm
Criterion Covered Total %
statement 22 24 91.6
branch n/a
condition n/a
subroutine 8 8 100.0
pod n/a
total 30 32 93.7


line stmt bran cond sub pod time code
1             package Plack::Middleware::XSLT;
2             {
3             $Plack::Middleware::XSLT::VERSION = '0.30';
4             }
5 3     3   46707 use strict;
  3         6  
  3         111  
6 3     3   12 use warnings;
  3         3  
  3         90  
7              
8             # ABSTRACT: XSLT transformations with Plack
9              
10 3     3   1312 use parent 'Plack::Middleware';
  3         811  
  3         15  
11              
12 3     3   36380 use File::Spec;
  3         8  
  3         56  
13 3     3   12 use Plack::Util;
  3         4  
  3         63  
14 3     3   13 use Plack::Util::Accessor qw(cache path parser_options);
  3         3  
  3         12  
15 3     3   1564 use Try::Tiny;
  3         3422  
  3         159  
16 3     3   730 use XML::LibXML 1.62;
  0            
  0            
17             use XML::LibXSLT 1.62;
18              
19             my ($parser, $xslt);
20              
21             sub call {
22             my ($self, $env) = @_;
23              
24             my $res = $self->app->($env);
25              
26             Plack::Util::response_cb($res, sub {
27             my $res = shift;
28              
29             my $xsl_file = $env->{'xslt.style'};
30             return if !defined($xsl_file) || $xsl_file eq '';
31              
32             if (!$xslt) {
33             if ($self->cache) {
34             require XML::LibXSLT::Cache;
35             $xslt = XML::LibXSLT::Cache->new;
36             }
37             else {
38             $xslt = XML::LibXSLT->new;
39             }
40             }
41              
42             my $path = $self->path;
43             $xsl_file = File::Spec->catfile($path, $xsl_file)
44             if defined($path) && !File::Spec->file_name_is_absolute($xsl_file);
45              
46             my $stylesheet = $xslt->parse_stylesheet_file($xsl_file);
47             my $media_type = $stylesheet->media_type();
48             my $encoding = $stylesheet->output_encoding();
49              
50             my $headers = Plack::Util::headers($res->[1]);
51             $headers->remove('Content-Encoding');
52             $headers->remove('Transfer-Encoding');
53             $headers->set('Content-Type', "$media_type; charset=$encoding");
54              
55             if ($res->[2]) {
56             my ($output, $error) = $self->_xform($stylesheet, $res->[2]);
57              
58             if (defined($error)) {
59             # Try to convert error to HTTP response.
60              
61             my ($status, $message);
62              
63             for my $line (split(/\n/, $error)) {
64             if ($line =~ /^(\d\d\d)(?:\s+(.*))?\z/) {
65             $status = $1;
66             $message = defined($2) ? $2 : '';
67             last;
68             }
69             }
70              
71             die($error) if !defined($status);
72              
73             $res->[0] = $status;
74             $headers->set('Content-Type', 'text/plain');
75             $headers->set('Content-Length', length($message));
76             $res->[2] = [ $message ];
77             }
78             else {
79             $headers->set('Content-Length', length($output));
80             $res->[2] = [ $output ];
81             }
82             }
83             else {
84             # PSGI streaming
85              
86             my ($done, @chunks);
87              
88             return sub {
89             my $chunk = shift;
90              
91             return undef if $done;
92              
93             if (defined($chunk)) {
94             push(@chunks, $chunk);
95             return '';
96             }
97             else {
98             $done = 1;
99             my ($output, $error) =
100             $self->_xform($stylesheet, \@chunks);
101             die($error) if defined($error);
102             return $output;
103             }
104             }
105             }
106             });
107             }
108              
109             sub _xform {
110             my ($self, $stylesheet, $body) = @_;
111              
112             if (!$parser) {
113             my $options = $self->parser_options;
114             $parser = $options
115             ? XML::LibXML->new($options)
116             : XML::LibXML->new;
117             }
118              
119             my ($doc, $output, $error);
120              
121             if (ref($body) eq 'ARRAY') {
122             $doc = $parser->parse_string(join('', @$body));
123             }
124             else {
125             $doc = $parser->parse_fh($body);
126             }
127              
128             my $result = try {
129             $stylesheet->transform($doc) or die("XSLT transform failed: $!");
130             }
131             catch {
132             $error = defined($_) ? $_ : 'Unknown error';
133             undef;
134             };
135              
136             $output = $stylesheet->output_as_bytes($result)
137             if $result;
138              
139             return ($output, $error);
140             }
141              
142             sub _cache_hits {
143             my $self = shift;
144              
145             return $xslt->cache_hits
146             if $xslt && $xslt->isa('XML::LibXSLT::Cache');
147              
148             return 0;
149             }
150              
151             1;
152              
153             __END__