File Coverage

blib/lib/Catalyst/TraitFor/Request/ProxyBase.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package Catalyst::TraitFor::Request::ProxyBase;
2 1     1   1712 use Moose::Role;
  0            
  0            
3             use URI ();
4             use namespace::autoclean;
5              
6             our $VERSION = '0.000005';
7             $VERSION = eval $VERSION;
8              
9             requires qw/
10             base
11             secure
12             /;
13              
14             sub _with_scheme { return $_[0] =~ m/^https?/; }
15              
16             around 'base' => sub {
17             my ($orig, $self, @args) = @_;
18              
19             my $isset = $self->meta->find_attribute_by_name('base')->has_value($self);
20              
21             if ( $isset && @args == 0 ) {
22             return $self->$orig(@args);
23             }
24             else {
25             if (my $base = $self->header('X-Request-Base')) {
26             if (_with_scheme($base)) {
27             $base .= '/' unless $base =~ m|/$|;
28             @args = (URI->new($base));
29             }
30             else {
31             my $proxy_base = $self->$orig(@args)->clone();
32             $proxy_base->path( $base . $proxy_base->path() );
33             @args = ( $proxy_base );
34             }
35             }
36             }
37             $self->$orig(@args);
38             };
39              
40             around 'uri' => sub {
41             my ($orig, $self, @args) = @_;
42              
43             my $isset = $self->meta->find_attribute_by_name('uri')->has_value($self);
44             if ( $isset && @args == 0 ) {
45             return $self->$orig(@args);
46             }
47              
48             my $uri = $self->$orig(@args)->clone;
49              
50             if ( my $base = $self->header('X-Request-Base') ) {
51             if (_with_scheme($base)) {
52             my $proxy_uri = URI->new( $base );
53              
54             my $proxy_path = $proxy_uri->path;
55             my $orig_path = $uri->path;
56              
57             $proxy_path =~ s{/$}{} if $orig_path =~ m{^/};
58              
59             $uri->scheme( $proxy_uri->scheme );
60             $uri->path( $proxy_path . $orig_path );
61             }
62             else {
63             $uri->path( $base . $uri->path() );
64             }
65             }
66              
67             return $self->$orig( ($uri) );
68             };
69              
70             around 'secure' => sub {
71             my ($orig, $self, @args) = @_;
72             if (my $base = $self->header('X-Request-Base')) {
73             if (_with_scheme($base)) {
74             return URI->new($base)->scheme eq 'http' ? 0 : 1;
75             }
76             }
77             $self->$orig(@args);
78             };
79              
80             1;
81              
82             __END__
83              
84             =head1 NAME
85              
86             Catalyst::TraitFor::Request::ProxyBase - Replace request base with value passed by HTTP proxy
87              
88             =head1 SYNOPSIS
89              
90             package MyApp;
91             use Moose;
92             use namespace::autoclean;
93              
94             use Catalyst;
95             use CatalystX::RoleApplicator;
96              
97             extends 'Catalyst';
98              
99             __PACKAGE__->apply_request_class_roles(qw/
100             Catalyst::TraitFor::Request::ProxyBase
101             /);
102              
103             __PACKAGE__->setup;
104              
105             =head1 DESCRIPTION
106              
107             This module is a L<Moose::Role> which allows you more flexibility in your
108             application's deployment configurations when deployed behind a proxy.
109              
110             The problem is that there is no standard way for a proxy to tell a backend
111             server what the original URI for the request was, or if the request was
112             initially SSL. (Yes, I do know about C<< X-Forwarded-Host >>, but they don't
113             do enough)
114              
115             This creates an issue for someone wanting to deploy the same cluster of
116             application servers behind various URI endpoints.
117              
118             Using this module, the request base (C<< $c->req->base >>)
119             is replaced with the contents of the C<< X-Request-Base >> header,
120             which is expected to be a full URI, for example:
121              
122             http://example.com
123             https://example.com
124             http://other.example.com:81/foo/bar/yourapp
125              
126             This value will then be used as the base for uris constructed by
127             C<< $c->uri_for >>.
128              
129             In addition the request uri (C<< $c->req->uri >>) will reflect the scheme and path specifed in the header.
130              
131             =head1 REQUIRED METHODS
132              
133             =over
134              
135             =item base
136              
137             =item secure
138              
139             =back
140              
141             =head1 WRAPPED METHODS
142              
143             =over
144              
145             =item base
146              
147             =item secure
148              
149             =back
150              
151             =head1 APACHE SETUP
152              
153             On the frontend Proxy Apache, you would want to enable a Virtualhost config
154             somewhat like this. The backend apache config stays unchanged.
155              
156             <Virtualhost *:80>
157             ProxyRequests Off
158              
159             <Location /preview>
160             # You must have mod_headers enabled for that
161             # RequestHeader set X-Request-Base /preview
162             RequestHeader set X-Request-Base http://www.example.com/preview
163             </Location>
164              
165             ProxyPass /preview http://my.vpn.host/
166             ProxyPassReverse /preview http://my.vpn.host/
167             </Virtualhost>
168              
169             =head1 BUGS
170              
171             Probably. Patches welcome, please fork from:
172              
173             http://github.com/bobtfish/catalyst-traitfor-request-proxybase
174              
175             and send a pull request.
176              
177             =head1 AUTHORS
178              
179             Tomas Doran (t0m) C<< <bobtfish@bobtfish.net> >>
180              
181             =head1 CONTRIBUTORS
182              
183             Klaus Ita (koki) C<< <klaus@worstofall.com> >>
184              
185             =head1 COPYRIGHT
186              
187             This module is Copyright (c) 2009 Tomas Doran and is licensed under the same
188             terms as perl itself.
189              
190             =cut
191