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   65 use Mojo::Base 'Mojolicious';
  7         17  
  7         48  
3 7     7   5543 use Plack::Util;
  7         23796  
  7         10020  
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 202542 my ($self, $c) = @_;
16 11         166 local $ENV{PLACK_ENV} = $self->mode;
17              
18 11         315 my $plack_env = _mojo_req_to_psgi_env($c, $self->rewrite);
19 11         3429 $plack_env->{'MOJO.CONTROLLER'} = $c;
20 11         62 my $plack_res = Plack::Util::run_app $self->app, $plack_env;
21              
22             # simple (array reference) response
23 11 100       67240 if (ref $plack_res eq 'ARRAY') {
24 8         40 my ($mojo_res, undef) = _psgi_res_to_mojo_res($plack_res);
25 8         49 $c->tx->res($mojo_res);
26 8         139 $c->rendered;
27 8         2203 return;
28             }
29              
30             # PSGI responses must be ARRAY or CODE
31 3 50       16 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   22 my $plack_res = shift;
38 3         12 my ($mojo_res, $streaming) = _psgi_res_to_mojo_res($plack_res);
39 3         18 $c->tx->res($mojo_res);
40              
41 3 100       52 return $c->rendered unless $streaming;
42              
43             # streaming response, possibly chunked
44 2         8 my $chunked = $mojo_res->content->is_chunked;
45 2 100       47 my $write = $chunked ? sub { $c->write_chunk(@_) } : sub { $c->write(@_) };
  2         54  
  2         43  
46 2         8 $write->(); # finalize header response
47             return Plack::Util::inline_object(
48             write => $write,
49 2         174 close => sub { $c->finish(@_) }
50 2         624 );
51 3         18 };
52 3         13 $plack_res->($responder);
53             }
54              
55             sub _mojo_req_to_psgi_env {
56 11     11   82 my $c = shift;
57 11         34 my $rewrite = shift;
58 11         54 my $mojo_tx = $c->tx;
59 11         88 my $mojo_req = $c->req;
60 11         139 my $url = $mojo_req->url;
61 11         92 my $base = $url->base;
62 11         87 my $content = $mojo_req->content;
63 11         78 my $body;
64 11 100       75 if ($content->is_multipart) {
65 2         21 $content = $content->clone;
66 2         203 my $offset = 0;
67 2         6 while (1) {
68 13         35 my $chunk = $content->get_body_chunk($offset);
69 13 50       866 next unless defined $chunk;
70 13         22 my $len = length $chunk;
71 13 100       28 last unless $len;
72 11         18 $offset += $len;
73 11         23 $body .= $chunk;
74             }
75             } else {
76 9         121 $body = $mojo_req->body;
77             }
78 7 50   7   92 open my $input, '<', \$body or die "Cannot open handle to scalar reference: $!";
  7         17  
  7         65  
  11         1067  
79              
80 11         8237 my %headers = %{$mojo_req->headers->to_hash};
  11         76  
81 11         1124 for my $key (keys %headers) {
82 49         104 my $value = $headers{$key};
83 49         100 delete $headers{$key};
84 49         150 $key =~ s{-}{_}g;
85 49         174 $headers{'HTTP_'. uc $key} = $value;
86             }
87              
88             # certain headers get their own psgi slot
89 11         39 for my $key (qw/CONTENT_LENGTH CONTENT_TYPE/) {
90 22 100       170 next unless exists $headers{"HTTP_$key"};
91 15         79 $headers{$key} = delete $headers{"HTTP_$key"};
92             }
93              
94 11         68 my $path = $url->path->to_string;
95 11         1028 my $script = '';
96 11 100       46 if ($rewrite) {
97 1 50       19 $script = $rewrite if $path =~ s/\Q$rewrite//;
98 1 50       9 $path = "/$path" unless $path =~ m[^/];
99             }
100              
101             return {
102 11         221 %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   29 my $psgi_res = shift;
130 11         127 my $mojo_res = Mojo::Message::Response->new;
131 11         141 $mojo_res->code($psgi_res->[0]);
132              
133 11         115 my $headers = $mojo_res->headers;
134 11     15   715 Plack::Util::header_iter $psgi_res->[1] => sub { $headers->header(@_) };
  15         310  
135 11         493 $headers->remove('Content-Length'); # should be set by mojolicious later
136              
137 11         78 my $streaming = 0;
138 11 100       49 if (@$psgi_res == 3) {
139 9         34 my $asset = $mojo_res->content->asset;
140 9     9   254 Plack::Util::foreach($psgi_res->[2], sub {$asset->add_chunk($_[0])});
  9         144  
141             } else {
142 2         5 $streaming = 1;
143             }
144              
145 11         320 return ($mojo_res, $streaming);
146             }
147              
148             1;
149