File Coverage

blib/lib/Mojolicious/Plugin/MountPSGI/Proxy.pm
Criterion Covered Total %
statement 68 68 100.0
branch 16 20 80.0
condition n/a
subroutine 9 9 100.0
pod 1 1 100.0
total 94 98 95.9


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::MountPSGI::Proxy;
2 6     6   42 use Mojo::Base 'Mojolicious';
  6         12  
  6         33  
3 6     6   3186 use Plack::Util;
  6         14982  
  6         5225  
4              
5             has app => sub { Plack::Util::load_psgi shift->script };
6             has 'script';
7             has 'rewrite';
8              
9             sub handler {
10 8     8 1 105978 my ($self, $c) = @_;
11 8         41 my $plack_env = _mojo_req_to_psgi_env($c->req, $self->rewrite);
12 8         1822 $plack_env->{'MOJO.CONTROLLER'} = $c;
13 8         42 my $plack_res = Plack::Util::run_app $self->app, $plack_env;
14              
15             # simple (array reference) response
16 8 100       7291 if (ref $plack_res eq 'ARRAY') {
17 5         21 my ($mojo_res, undef) = _psgi_res_to_mojo_res($plack_res);
18 5         23 $c->tx->res($mojo_res);
19 5         68 $c->rendered;
20 5         1507 return;
21             }
22              
23             # PSGI responses must be ARRAY or CODE
24 3 50       18 die 'PSGI response not understood'
25             unless ref $plack_res eq 'CODE';
26              
27             # delayed (code reference) response
28             my $responder = sub {
29 3     3   16 my $plack_res = shift;
30 3         11 my ($mojo_res, $streaming) = _psgi_res_to_mojo_res($plack_res);
31 3         14 $c->tx->res($mojo_res);
32              
33 3 100       37 return $c->rendered unless $streaming;
34              
35             # streaming response, possibly chunked
36 2         19 my $chunked = $mojo_res->content->is_chunked;
37 2 100       42 my $write = $chunked ? sub { $c->write_chunk(@_) } : sub { $c->write(@_) };
  2         41  
  2         39  
38 2         6 $write->(); # finalize header response
39             return Plack::Util::inline_object(
40             write => $write,
41 2         166 close => sub { $c->finish(@_) }
42 2         647 );
43 3         16 };
44 3         11 $plack_res->($responder);
45             }
46              
47             sub _mojo_req_to_psgi_env {
48 8     8   109 my $mojo_req = shift;
49 8         22 my $rewrite = shift;
50 8         85 my $url = $mojo_req->url;
51 8         71 my $base = $url->base;
52 8         78 my $body = $mojo_req->body;
53 6 50   6   58 open my $input, '<', \$body or die "Cannot open handle to scalar reference: $!";
  6         10  
  6         45  
  8         612  
54              
55 8         4760 my %headers = %{$mojo_req->headers->to_hash};
  8         52  
56 8         626 for my $key (keys %headers) {
57 34         60 my $value = $headers{$key};
58 34         59 delete $headers{$key};
59 34         84 $key =~ s{-}{_}g;
60 34         108 $headers{'HTTP_'. uc $key} = $value;
61             }
62              
63             # certain headers get their own psgi slot
64 8         25 for my $key (qw/CONTENT_LENGTH CONTENT_TYPE/) {
65 16 100       57 next unless exists $headers{"HTTP_$key"};
66 9         43 $headers{$key} = delete $headers{"HTTP_$key"};
67             }
68              
69 8         36 my $path = $url->path->to_string;
70 8         563 my $script = '';
71 8 100       38 if ($rewrite) {
72 1 50       18 $script = $rewrite if $path =~ s/\Q$rewrite//;
73 1 50       6 $path = "/$path" unless $path =~ m[^/];
74             }
75              
76             return {
77 8         203 %ENV,
78             %headers,
79             'SERVER_PROTOCOL' => 'HTTP/'. $mojo_req->version,
80             'SERVER_NAME' => $base->host,
81             'SERVER_PORT' => $base->port,
82             'REQUEST_METHOD' => $mojo_req->method,
83             'SCRIPT_NAME' => $script,
84             'PATH_INFO' => $path,
85             'REQUEST_URI' => $url->to_string,
86             'QUERY_STRING' => $url->query->to_string,
87             'psgi.url_scheme' => $base->scheme,
88             'psgi.multithread' => Plack::Util::FALSE,
89             'psgi.version' => [1,1],
90             'psgi.errors' => *STDERR,
91             'psgi.input' => $input,
92             'psgi.multithread' => Plack::Util::FALSE,
93             'psgi.multiprocess' => Plack::Util::TRUE,
94             'psgi.run_once' => Plack::Util::FALSE,
95             'psgi.streaming' => Plack::Util::TRUE,
96             'psgi.nonblocking' => Plack::Util::FALSE,
97             };
98             }
99              
100             sub _psgi_res_to_mojo_res {
101 8     8   18 my $psgi_res = shift;
102 8         69 my $mojo_res = Mojo::Message::Response->new;
103 8         78 $mojo_res->code($psgi_res->[0]);
104              
105 8         69 my $headers = $mojo_res->headers;
106 8     10   443 Plack::Util::header_iter $psgi_res->[1] => sub { $headers->header(@_) };
  10         186  
107 8         237 $headers->remove('Content-Length'); # should be set by mojolicious later
108              
109 8         43 my $streaming = 0;
110 8 100       30 if (@$psgi_res == 3) {
111 6         17 my $asset = $mojo_res->content->asset;
112 6     6   133 Plack::Util::foreach($psgi_res->[2], sub {$asset->add_chunk($_[0])});
  6         93  
113             } else {
114 2         5 $streaming = 1;
115             }
116              
117 8         170 return ($mojo_res, $streaming);
118             }
119              
120             1;
121