File Coverage

blib/lib/HTTP/Proxy/GreaseMonkey/Redirector.pm
Criterion Covered Total %
statement 27 86 31.4
branch 0 18 0.0
condition 0 9 0.0
subroutine 9 19 47.3
pod 3 3 100.0
total 39 135 28.8


line stmt bran cond sub pod time code
1             package HTTP::Proxy::GreaseMonkey::Redirector;
2              
3 1     1   5 use warnings;
  1         1  
  1         32  
4 1     1   5 use strict;
  1         1  
  1         24  
5 1     1   5 use Carp;
  1         3  
  1         60  
6 1     1   1172 use JSON;
  1         22996  
  1         6  
7 1     1   175 use HTTP::Response;
  1         3  
  1         23  
8 1     1   5 use HTML::Tiny;
  1         2  
  1         26  
9 1     1   943 use YAML qw( DumpFile LoadFile );
  1         9333  
  1         98  
10 1     1   982 use LockFile::Simple qw( lock unlock );
  1         6878  
  1         79  
11              
12 1     1   10 use base qw( HTTP::Proxy::HeaderFilter );
  1         3  
  1         1283  
13              
14             =head1 NAME
15              
16             HTTP::Proxy::GreaseMonkey::Redirector - Proxy cross-site requests
17              
18             =head1 VERSION
19              
20             This document describes HTTP::Proxy::GreaseMonkey::Redirector version 0.05
21              
22             =cut
23              
24             our $VERSION = '0.05';
25              
26             =head1 SYNOPSIS
27            
28             =head1 DESCRIPTION
29              
30             =head1 INTERFACE
31              
32             =head2 C<< passthru >>
33              
34             Set the passthru key.
35              
36             =cut
37              
38             sub passthru {
39 0     0 1   my $self = shift;
40 0           my $key = quotemeta shift;
41 0           $self->{passthru} = qr{ ^/ $key
42             / ( [-a-z0-9]+ (?: \. [-a-z0-9]+ )+ )
43             (/.*) $}xi;
44 0           $self->{internal} = qr{ ^/ $key
45             / \$ internal \$ $ }xi;
46             }
47              
48             =head2 C<< filter >>
49              
50             Filter the request headers.
51              
52             =cut
53              
54             sub filter {
55 0     0 1   my ( $self, $headers, $message ) = @_;
56              
57 0   0       my $passthru = $self->{passthru} || return;
58              
59 0           my $uri = $message->uri;
60 0           my $path = $uri->path;
61              
62             # print "$path, $self->{internal}\n";
63              
64 0 0         if ( $path =~ $self->{internal} ) {
    0          
65 0           $self->proxy->response(
66             $self->_despatch_internal(
67             $headers, $message, $uri->query
68             )
69             );
70             }
71             elsif ( $path =~ $passthru ) {
72             # Redirect
73 0           my $real_uri = $uri->scheme . '://' . $1 . $2;
74 0 0         if ( my $query = $uri->query ) {
75 0           $real_uri = join '?', $real_uri, $query;
76             }
77 0           $message->uri( $real_uri );
78 0           $headers->header( host => $1 );
79             }
80             }
81              
82             =head2 C<< state_file >>
83              
84             Set the name of the file that will be used to store state.
85              
86             =cut
87              
88             sub state_file {
89 0     0 1   my $self = shift;
90 0 0         $self->{state_file} = shift if @_;
91 0 0         croak "No state_file defined" unless defined $self->{state_file};
92 0           return $self->{state_file};
93             }
94              
95             sub _load_state {
96 0     0     my $file = shift->state_file;
97 0 0         return -f $file ? LoadFile( $file ) : {};
98             }
99              
100             sub _save_state {
101 0     0     DumpFile( shift->state_file, @_ );
102             }
103              
104             sub _locked {
105 0     0     my ( $self, $func ) = @_;
106 0           my $file = $self->state_file;
107 0           lock( $file );
108 0           my @res = $func->();
109 0           unlock( $file );
110 0           return @res;
111             }
112              
113             sub _despatch_internal {
114 0     0     my ( $self, $headers, $message, $query ) = @_;
115 0           my $result = eval {
116             # JSON == YAML, right?
117             my %handler = (
118             setValue => sub {
119 0     0     my ( $args, $name, $val ) = @_;
120             $self->_locked(
121             sub {
122 0           my $state = $self->_load_state;
123 0           $state->{ $args->{ns} }->{ $args->{n} }->{$name}
124             = $val;
125 0           $self->_save_state( $state );
126             }
127 0           );
128 0           return 1;
129             },
130             getValue => sub {
131 0     0     my ( $args, $name, $dflt ) = @_;
132             my ( $state )
133 0           = $self->_locked( sub { $self->_load_state } );
  0            
134 0           my $val
135             = $state->{ $args->{ns} }->{ $args->{n} }->{$name};
136 0 0         return defined $val ? $val : $dflt;
137             },
138             log => sub {
139 0     0     my ( $args, @argv ) = @_;
140 0           print join( ': ', $args->{n}, join( '', @argv ) ), "\n";
141 0           return 1;
142             },
143 0           );
144              
145 0   0       my $h = $self->{_html} ||= HTML::Tiny->new;
146 0           my $qs = $h->url_decode( $query );
147 0           my $args = from_json( $qs );
148              
149 0   0       my $method = delete $args->{m}
150             || die "Missing 'm' arg";
151 0   0       my $code = $handler{$method}
152             || die "No method $method";
153              
154 0 0         my @arguments = @{ delete $args->{a} || [] };
  0            
155              
156 0           my $result = $code->( $args, @arguments );
157              
158 0           return HTTP::Response->new(
159             200, 'OK',
160             [ 'content_type' => 'application/json' ],
161             to_json( [$result] )
162             );
163             };
164              
165 0 0         if ( $@ ) {
166 0           ( my $err = $@ ) =~ s/\s+/ /g;
167 0           print "Error: $err\n";
168 0           return HTTP::Response->new( 500, $err );
169             }
170              
171 0           return $result;
172             }
173              
174             1;
175              
176             __END__