| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | ## @file | 
| 2 |  |  |  |  |  |  | # Perl based proxy used to replace mod_proxy | 
| 3 |  |  |  |  |  |  |  | 
| 4 |  |  |  |  |  |  | ## @class | 
| 5 |  |  |  |  |  |  | # Perl based proxy used to replace mod_proxy | 
| 6 |  |  |  |  |  |  | package Lemonldap::NG::Handler::Proxy; | 
| 7 |  |  |  |  |  |  |  | 
| 8 | 1 |  |  | 1 |  | 581 | use strict; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 40 |  | 
| 9 |  |  |  |  |  |  |  | 
| 10 | 1 |  |  | 1 |  | 405 | use Lemonldap::NG::Handler::Main qw(:apache :headers :tsv); | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 11 |  |  |  |  |  |  | use LWP::UserAgent; | 
| 12 |  |  |  |  |  |  | use Lemonldap::NG::Handler::Main::Headers; | 
| 13 |  |  |  |  |  |  | use Lemonldap::NG::Handler::Main::Logger; | 
| 14 |  |  |  |  |  |  |  | 
| 15 |  |  |  |  |  |  | our $VERSION = '1.2.0'; | 
| 16 |  |  |  |  |  |  |  | 
| 17 |  |  |  |  |  |  | ########################################## | 
| 18 |  |  |  |  |  |  | # COMPATIBILITY WITH APACHE AND APACHE 2 # | 
| 19 |  |  |  |  |  |  | ########################################## | 
| 20 |  |  |  |  |  |  |  | 
| 21 |  |  |  |  |  |  | BEGIN { | 
| 22 |  |  |  |  |  |  | if ( MP() == 2 ) { | 
| 23 |  |  |  |  |  |  | Apache2::compat->import(); | 
| 24 |  |  |  |  |  |  | } | 
| 25 |  |  |  |  |  |  | *handler = ( MP() == 2 ) ? \&handler_mp2 : \&handler_mp1; | 
| 26 |  |  |  |  |  |  | } | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | ## @cmethod int handler_mp1() | 
| 29 |  |  |  |  |  |  | # Launch run() when used under mod_perl version 1 | 
| 30 |  |  |  |  |  |  | # @return Apache constant | 
| 31 |  |  |  |  |  |  | sub handler_mp1 ($$) { shift->run(@_); } | 
| 32 |  |  |  |  |  |  |  | 
| 33 |  |  |  |  |  |  | ## @cmethod int handler_mp2() | 
| 34 |  |  |  |  |  |  | # Launch run() when used under mod_perl version 2 | 
| 35 |  |  |  |  |  |  | # @return Apache constant | 
| 36 |  |  |  |  |  |  | sub handler_mp2 : method { | 
| 37 |  |  |  |  |  |  | shift->run(@_); | 
| 38 |  |  |  |  |  |  | } | 
| 39 |  |  |  |  |  |  |  | 
| 40 |  |  |  |  |  |  | *lmLog = *Lemonldap::NG::Handler::Main::lmLog; | 
| 41 |  |  |  |  |  |  |  | 
| 42 |  |  |  |  |  |  | ######## | 
| 43 |  |  |  |  |  |  | # MAIN # | 
| 44 |  |  |  |  |  |  | ######## | 
| 45 |  |  |  |  |  |  |  | 
| 46 |  |  |  |  |  |  | # Shared variables | 
| 47 |  |  |  |  |  |  | our $r; | 
| 48 |  |  |  |  |  |  | our $base; | 
| 49 |  |  |  |  |  |  | our $headers_set; | 
| 50 |  |  |  |  |  |  | our $UA = new LWP::UserAgent; | 
| 51 |  |  |  |  |  |  | our $class; | 
| 52 |  |  |  |  |  |  |  | 
| 53 |  |  |  |  |  |  | # IMPORTANT: LWP does not have to execute any redirection itself. This has to | 
| 54 |  |  |  |  |  |  | # be done by the client itself, else cookies and other information may | 
| 55 |  |  |  |  |  |  | # disappear. | 
| 56 |  |  |  |  |  |  | $UA->requests_redirectable( [] ); | 
| 57 |  |  |  |  |  |  |  | 
| 58 |  |  |  |  |  |  | ## @cmethod int run(Apache2::RequestRec r) | 
| 59 |  |  |  |  |  |  | # Main proxy method. | 
| 60 |  |  |  |  |  |  | # Called for Apache response (PerlResponseHandler). | 
| 61 |  |  |  |  |  |  | # @return Apache constant | 
| 62 |  |  |  |  |  |  | sub run($$) { | 
| 63 |  |  |  |  |  |  | ( $class, $r ) = splice @_; | 
| 64 |  |  |  |  |  |  | my $url = $r->uri; | 
| 65 |  |  |  |  |  |  | $url .= "?" . $r->args if ( $r->args ); | 
| 66 |  |  |  |  |  |  |  | 
| 67 |  |  |  |  |  |  | # Uncomment this if you have lost of session problem with SAP. | 
| 68 |  |  |  |  |  |  | # I don't know why cookie value and URL parameter differs but it causes | 
| 69 |  |  |  |  |  |  | # this problem. By removing URL parameters, all works fine. SAP bug ? | 
| 70 |  |  |  |  |  |  |  | 
| 71 |  |  |  |  |  |  | # $url =~ s/sap-wd-cltwndid=[^\&]+//g; | 
| 72 |  |  |  |  |  |  | return DECLINED unless ( $base = $r->dir_config('LmProxyPass') ); | 
| 73 |  |  |  |  |  |  | my $request = new HTTP::Request( $r->method, $base . $url ); | 
| 74 |  |  |  |  |  |  |  | 
| 75 |  |  |  |  |  |  | # Scan Apache request headers to generate LWP request headers | 
| 76 |  |  |  |  |  |  | $r->headers_in->do( | 
| 77 |  |  |  |  |  |  | sub { | 
| 78 |  |  |  |  |  |  | return 1 if ( $_[1] =~ /^$/ ); | 
| 79 |  |  |  |  |  |  | $request->header(@_) unless ( $_[0] =~ /^(Host|Referer)$/i ); | 
| 80 |  |  |  |  |  |  | Lemonldap::NG::Handler::Main::Logger->lmLog( | 
| 81 |  |  |  |  |  |  | "$class: header pushed to the server: " . $_[0] . ": " . $_[1], | 
| 82 |  |  |  |  |  |  | 'debug' | 
| 83 |  |  |  |  |  |  | ); | 
| 84 |  |  |  |  |  |  | 1; | 
| 85 |  |  |  |  |  |  | } | 
| 86 |  |  |  |  |  |  | ); | 
| 87 |  |  |  |  |  |  | $base =~ s/https?:\/\/([^\/]+).*$/$1/; | 
| 88 |  |  |  |  |  |  | $request->header( Host => $base ); | 
| 89 |  |  |  |  |  |  |  | 
| 90 |  |  |  |  |  |  | # copy POST data, if any | 
| 91 |  |  |  |  |  |  | if ( $r->method eq "POST" ) { | 
| 92 |  |  |  |  |  |  | my $len = $r->headers_in->{'Content-Length'}; | 
| 93 |  |  |  |  |  |  | my $buf; | 
| 94 |  |  |  |  |  |  | if ($len) { | 
| 95 |  |  |  |  |  |  | $r->read( $buf, $len ); | 
| 96 |  |  |  |  |  |  | $request->content($buf); | 
| 97 |  |  |  |  |  |  | } | 
| 98 |  |  |  |  |  |  | } | 
| 99 |  |  |  |  |  |  | $headers_set = 0; | 
| 100 |  |  |  |  |  |  |  | 
| 101 |  |  |  |  |  |  | # For performance, we use a callback. See LWP::UserAgent for more | 
| 102 |  |  |  |  |  |  | my $response = $UA->request( $request, \&cb_content ); | 
| 103 |  |  |  |  |  |  | if ( $response->code != 200 ) { | 
| 104 |  |  |  |  |  |  | $class->headers($response) unless ($headers_set); | 
| 105 |  |  |  |  |  |  | $r->print( $response->content ); | 
| 106 |  |  |  |  |  |  | } | 
| 107 |  |  |  |  |  |  | return OK; | 
| 108 |  |  |  |  |  |  | } | 
| 109 |  |  |  |  |  |  |  | 
| 110 |  |  |  |  |  |  | ## @fn void cb_content(string chunk) | 
| 111 |  |  |  |  |  |  | # Send datas received from remote server to the client. | 
| 112 |  |  |  |  |  |  | # @param $chunk part of datas returned by HTTP server | 
| 113 |  |  |  |  |  |  | sub cb_content { | 
| 114 |  |  |  |  |  |  | my $chunk = shift; | 
| 115 |  |  |  |  |  |  | unless ($headers_set) { | 
| 116 |  |  |  |  |  |  | $class->headers(shift); | 
| 117 |  |  |  |  |  |  | $headers_set = 1; | 
| 118 |  |  |  |  |  |  | } | 
| 119 |  |  |  |  |  |  | $r->print($chunk); | 
| 120 |  |  |  |  |  |  | } | 
| 121 |  |  |  |  |  |  |  | 
| 122 |  |  |  |  |  |  | ## @cmethod void headers(HTTP::Request response) | 
| 123 |  |  |  |  |  |  | # Send headers received from remote server to the client. | 
| 124 |  |  |  |  |  |  | # Replace "Location" header. | 
| 125 |  |  |  |  |  |  | # @param $response current HTTP response | 
| 126 |  |  |  |  |  |  | sub headers { | 
| 127 |  |  |  |  |  |  | $class = shift; | 
| 128 |  |  |  |  |  |  | my $response = shift; | 
| 129 |  |  |  |  |  |  | my $tmp      = $response->header('Content-Type'); | 
| 130 |  |  |  |  |  |  | $r->content_type($tmp) if ($tmp); | 
| 131 |  |  |  |  |  |  | $r->status( $response->code ); | 
| 132 |  |  |  |  |  |  | $r->status_line( join ' ', $response->code, $response->message ); | 
| 133 |  |  |  |  |  |  |  | 
| 134 |  |  |  |  |  |  | # Scan LWP response headers to generate Apache response headers | 
| 135 |  |  |  |  |  |  | my ( $location_old, $location_new ) = split /[;,]+/, | 
| 136 |  |  |  |  |  |  | $r->dir_config('LmLocationToReplace'); | 
| 137 |  |  |  |  |  |  | my ( $cookieDomain_old, $cookieDomain_new ) = split /[;,]+/, | 
| 138 |  |  |  |  |  |  | $r->dir_config('LmCookieDomainToReplace'); | 
| 139 |  |  |  |  |  |  |  | 
| 140 |  |  |  |  |  |  | $response->scan( | 
| 141 |  |  |  |  |  |  | sub { | 
| 142 |  |  |  |  |  |  |  | 
| 143 |  |  |  |  |  |  | # Replace Location headers | 
| 144 |  |  |  |  |  |  | $_[1] =~ s#$location_old#$location_new#o | 
| 145 |  |  |  |  |  |  | if ( $location_old and $location_new and $_[0] =~ /Location/i ); | 
| 146 |  |  |  |  |  |  |  | 
| 147 |  |  |  |  |  |  | # Replace Set-Cookie headers | 
| 148 |  |  |  |  |  |  | $_[1] =~ s#$cookieDomain_old#$cookieDomain_new#o | 
| 149 |  |  |  |  |  |  | if (  $cookieDomain_old | 
| 150 |  |  |  |  |  |  | and $cookieDomain_new | 
| 151 |  |  |  |  |  |  | and $_[0] =~ /Set-Cookie/i ); | 
| 152 |  |  |  |  |  |  |  | 
| 153 |  |  |  |  |  |  | Lemonldap::NG::Handler::Main::Headers->lmSetErrHeaderOut( $r, @_ ); | 
| 154 |  |  |  |  |  |  |  | 
| 155 |  |  |  |  |  |  | Lemonldap::NG::Handler::Main::Logger->lmLog( | 
| 156 |  |  |  |  |  |  | "$class: header pushed to the client: " . $_[0] . ": " . $_[1], | 
| 157 |  |  |  |  |  |  | 'debug' | 
| 158 |  |  |  |  |  |  | ); | 
| 159 |  |  |  |  |  |  | 1; | 
| 160 |  |  |  |  |  |  | } | 
| 161 |  |  |  |  |  |  | ); | 
| 162 |  |  |  |  |  |  | $headers_set = 1; | 
| 163 |  |  |  |  |  |  | } | 
| 164 |  |  |  |  |  |  |  | 
| 165 |  |  |  |  |  |  | 1; | 
| 166 |  |  |  |  |  |  |  | 
| 167 |  |  |  |  |  |  | __END__ |