File Coverage

blib/lib/Mojolicious/Plugin/MountPSGI/Proxy.pm
Criterion Covered Total %
statement 83 83 100.0
branch 21 26 80.7
condition n/a
subroutine 9 9 100.0
pod 1 1 100.0
total 114 119 95.8


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::MountPSGI::Proxy;
2 7     7   49 use Mojo::Base 'Mojolicious';
  7         13  
  7         34  
3 7     7   3868 use Plack::Util;
  7         17375  
  7         6900  
4              
5             has app => sub {
6             my $self = shift;
7             local $ENV{PLACK_ENV} = $self->mode;
8             Plack::Util::load_psgi $self->script;
9             };
10             has mode => sub { $ENV{PLACK_ENV} || 'development' };
11             has 'script';
12             has 'rewrite';
13              
14             sub handler {
15 11     11 1 125554 my ($self, $c) = @_;
16 11         120 local $ENV{PLACK_ENV} = $self->mode;
17              
18 11         173 my $plack_env = _mojo_req_to_psgi_env($c, $self->rewrite);
19 11         2676 $plack_env->{'MOJO.CONTROLLER'} = $c;
20 11         48 my $plack_res = Plack::Util::run_app $self->app, $plack_env;
21              
22             # simple (array reference) response
23 11 100       59372 if (ref $plack_res eq 'ARRAY') {
24 8         37 my ($mojo_res, undef) = _psgi_res_to_mojo_res($plack_res);
25 8         36 $c->tx->res($mojo_res);
26 8         110 $c->rendered;
27 8         1389 return;
28             }
29              
30             # PSGI responses must be ARRAY or CODE
31 3 50       13 die 'PSGI response not understood'
32             unless ref $plack_res eq 'CODE';
33              
34             #TODO do something with $self->mode in delayed response
35             # delayed (code reference) response
36             my $responder = sub {
37 3     3   17 my $plack_res = shift;
38 3         9 my ($mojo_res, $streaming) = _psgi_res_to_mojo_res($plack_res);
39 3         11 $c->tx->res($mojo_res);
40              
41 3 100       36 return $c->rendered unless $streaming;
42              
43             # streaming response, possibly chunked
44 2         5 my $chunked = $mojo_res->content->is_chunked;
45 2 100       33 my $write = $chunked ? sub { $c->write_chunk(@_) } : sub { $c->write(@_) };
  2         40  
  2         33  
46 2         6 $write->(); # finalize header response
47             return Plack::Util::inline_object(
48             write => $write,
49 2         167 close => sub { $c->finish(@_) }
50 2         418 );
51 3         12 };
52 3         9 $plack_res->($responder);
53             }
54              
55             sub _mojo_req_to_psgi_env {
56 11     11   67 my $c = shift;
57 11         24 my $rewrite = shift;
58 11         29 my $mojo_tx = $c->tx;
59 11         58 my $mojo_req = $c->req;
60 11         127 my $url = $mojo_req->url;
61 11         61 my $base = $url->base;
62 11         61 my $content = $mojo_req->content;
63 11         63 my $body;
64 11 100       49 if ($content->is_multipart) {
65 2         18 $content = $content->clone;
66 2         357 my $offset = 0;
67 2         5 while (1) {
68 13         32 my $chunk = $content->get_body_chunk($offset);
69 13 50       822 next unless defined $chunk;
70 13         22 my $len = length $chunk;
71 13 100       28 last unless $len;
72 11         15 $offset += $len;
73 11         26 $body .= $chunk;
74             }
75             } else {
76 9         88 $body = $mojo_req->body;
77             }
78 7 50   7   56 open my $input, '<', \$body or die "Cannot open handle to scalar reference: $!";
  7         12  
  7         54  
  11         588  
79              
80 11         5724 my %headers = %{$mojo_req->headers->to_hash};
  11         47  
81 11         813 for my $key (keys %headers) {
82 49         91 my $value = $headers{$key};
83 49         77 delete $headers{$key};
84 49         132 $key =~ s{-}{_}g;
85 49         144 $headers{'HTTP_'. uc $key} = $value;
86             }
87              
88             # certain headers get their own psgi slot
89 11         31 for my $key (qw/CONTENT_LENGTH CONTENT_TYPE/) {
90 22 100       75 next unless exists $headers{"HTTP_$key"};
91 15         60 $headers{$key} = delete $headers{"HTTP_$key"};
92             }
93              
94 11         43 my $path = $url->path->to_string;
95 11         735 my $script = '';
96 11 100       47 if ($rewrite) {
97 1 50       16 $script = $rewrite if $path =~ s/\Q$rewrite//;
98 1 50       6 $path = "/$path" unless $path =~ m[^/];
99             }
100              
101             return {
102 11         170 %ENV,
103             %headers,
104             'REMOTE_ADDR' => $mojo_tx->remote_address,
105             'REMOTE_HOST' => $mojo_tx->remote_address,
106             'REMOTE_PORT' => $mojo_tx->remote_port,
107             'SERVER_PROTOCOL' => 'HTTP/'. $mojo_req->version,
108             'SERVER_NAME' => $base->host,
109             'SERVER_PORT' => $base->port,
110             'REQUEST_METHOD' => $mojo_req->method,
111             'SCRIPT_NAME' => $script,
112             'PATH_INFO' => $path,
113             'REQUEST_URI' => $url->to_string,
114             'QUERY_STRING' => $url->query->to_string,
115             'psgi.url_scheme' => $base->scheme,
116             'psgi.multithread' => Plack::Util::FALSE,
117             'psgi.version' => [1,1],
118             'psgi.errors' => *STDERR,
119             'psgi.input' => $input,
120             'psgi.multithread' => Plack::Util::FALSE,
121             'psgi.multiprocess' => Plack::Util::TRUE,
122             'psgi.run_once' => Plack::Util::FALSE,
123             'psgi.streaming' => Plack::Util::TRUE,
124             'psgi.nonblocking' => Plack::Util::FALSE,
125             };
126             }
127              
128             sub _psgi_res_to_mojo_res {
129 11     11   24 my $psgi_res = shift;
130 11         81 my $mojo_res = Mojo::Message::Response->new;
131 11         102 $mojo_res->code($psgi_res->[0]);
132              
133 11         88 my $headers = $mojo_res->headers;
134 11     15   514 Plack::Util::header_iter $psgi_res->[1] => sub { $headers->header(@_) };
  15         237  
135 11         340 $headers->remove('Content-Length'); # should be set by mojolicious later
136              
137 11         64 my $streaming = 0;
138 11 100       51 if (@$psgi_res == 3) {
139 9         27 my $asset = $mojo_res->content->asset;
140 9     9   200 Plack::Util::foreach($psgi_res->[2], sub {$asset->add_chunk($_[0])});
  9         136  
141             } else {
142 2         5 $streaming = 1;
143             }
144              
145 11         248 return ($mojo_res, $streaming);
146             }
147              
148             1;
149