File Coverage

blib/lib/Mojolicious/Command/proxy.pm
Criterion Covered Total %
statement 27 37 72.9
branch 2 8 25.0
condition 2 4 50.0
subroutine 6 7 85.7
pod 2 2 100.0
total 39 58 67.2


line stmt bran cond sub pod time code
1             package Mojolicious::Command::proxy;
2 1     1   949 use Mojo::Base 'Mojolicious::Command';
  1         193682  
  1         7  
3 1     1   53426 use Mojo::Util qw(getopt url_escape);
  1         2  
  1         47  
4 1     1   502 use Mojo::URL;
  1         8023  
  1         8  
5 1     1   576 use Mojolicious::Routes;
  1         15849  
  1         12  
6              
7             our $VERSION = '0.004';
8              
9             has description => 'Proxy web requests elsewhere';
10             has usage => sub { shift->extract_usage . "\n" };
11              
12             sub run {
13 0     0 1 0 my ($self, @args) = @_;
14 0         0 getopt \@args, [qw(no_permute pass_through)],
15             'f|from=s' => \my $from;
16 0   0     0 $from ||= '';
17 0         0 my $to = shift @args;
18 0 0       0 die $self->usage . "No to" if !$to;
19 0 0       0 die $self->usage . "from must be blank or start '/', then something"
20             if $from !~ m#^(?:$|/.)#;
21 0         0 my $app = $self->app;
22 0 0       0 $app->routes(Mojolicious::Routes->new) if ref $app eq 'Mojo::HelloWorld';
23 0         0 $self->proxy($app, $from, $to);
24 0         0 $app->start(@args);
25             }
26              
27             sub proxy {
28 2     2 1 1016 my ($self, $app, $from, $to) = @_;
29 2   100     16 $from ||= '/';
30             $app->routes->any("$from*path" => { path => "" } => sub {
31 6     6   101905 my ($c) = @_;
32 6         25 my $req = $c->req;
33 6         78 my $path = $c->stash('path');
34 6         76 $path = url_escape $path, '^A-Za-z0-9\-._~/'; # route in unescapes, escaping normal stuff except "/" seems reasonable
35 6         455 my $query = $c->req->url->query->to_string;
36 6         362 $path = join '?', $path, grep length, $query;
37 6         22 $c->app->log->debug("Proxying '$path'");
38 6 100       111 $path = '/' . $path if $from eq '/'; # weird special behaviour by router
39 6         17 my $onward_url = $to . $path;
40 6         79 my $onward_tx = $app->ua->build_tx($req->method => $onward_url);
41 6         1184 $onward_tx->req->content($req->content); # headers and body
42 6         116 $c->proxy->start_p($onward_tx);
43 2         10 }, 'proxy');
44             }
45              
46             1;
47              
48             =encoding utf8
49              
50             =head1 NAME
51              
52             Mojolicious::Command::proxy - Proxy web requests elsewhere
53              
54             =begin markdown
55              
56             # PROJECT STATUS
57              
58             | OS | Build status |
59             |:-------:|--------------:|
60             | Linux | [![Build Status](https://travis-ci.org/mohawk2/Mojolicious-Command-proxy.svg?branch=master)](https://travis-ci.org/mohawk2/Mojolicious-Command-proxy) |
61              
62             [![CPAN version](https://badge.fury.io/pl/Mojolicious-Command-proxy.svg)](https://metacpan.org/pod/Mojolicious::Command::proxy)
63              
64             =end markdown
65              
66             =head1 SYNOPSIS
67              
68             Usage: APPLICATION proxy [--from route_prefix] to_url
69              
70             mojo proxy http://example.com/subdir daemon -l http://*:3000
71             mojo proxy -f /proxy http://example.com/subdir get /proxy/hi
72              
73             Options:
74             -f, --from Proxying route prefix
75              
76             =head1 DESCRIPTION
77              
78             L is a command line interface for
79             making an app that proxies some or all incoming requests elsewhere.
80             Having done so, it then passes the rest of its arguments to the app's
81             C method, as illustrated in the synopsis above.
82              
83             One major reason for this is to be able to point your browser at
84             e.g. C (see first example in synopsis). This relaxes
85             restrictions on e.g. Service Workers and push notifications, which
86             normally demand TLS, so you can test functionality even if your real
87             development server is running elsewhere.
88              
89             =head1 ATTRIBUTES
90              
91             =head2 description
92              
93             $str = $self->description;
94              
95             =head2 usage
96              
97             $str = $self->usage;
98              
99             =head1 METHODS
100              
101             =head2 run
102              
103             $get->run(@ARGV);
104              
105             Run this command. It will add a L route as below. If not supplied,
106             the C<$from> will be empty-string.
107              
108             Command-line arguments will only be parsed at the start of the
109             command-line. This allows you to pass option through to e.g. C.
110              
111             As a special case, if the C attribute is exactly a
112             L app, it will replace its C attribute with an
113             empty one first, since the C route clashes with the proxy route,
114             being also a match-everything wildcard route. This makes the C
115             invocation function as expected.
116              
117             =head2 proxy
118              
119             Mojolicious::Command::proxy->proxy($app, $from_prefix, $to_prefix);
120              
121             Add a route to the given app, with the given prefix, named C. It
122             will transparently proxy all matching requests to the give C<$to>,
123             with all the same headers both ways.
124              
125             It operates by simply appending everything after the C<$from_prefix>,
126             which I be an empty string (which is treated the same as solitary
127             C, doing what you'd expect), to the C<$to_prefix>. E.g.:
128              
129             $cmd->proxy($app, '', '/subdir'); # /2 -> /subdir/2, / -> /subdir/ i.e. all
130             $cmd->proxy($app, '/proxy', '/subdir'); # /proxy/2 -> /subdir/2
131              
132             C<$to> can be a path as well as a full URL, so you can also use this to
133             route internally. However, the author can see no good reason to do this
134             outside of testing.
135              
136             It uses Lstart_p> but
137             adds the full header-proxying behaviour.
138              
139             =head1 AUTHOR
140              
141             Ed J
142              
143             =head1 COPYRIGHT AND LICENSE
144              
145             This is free software; you can redistribute it and/or modify it under
146             the same terms as the Perl 5 programming language system itself.
147              
148             =cut