5

I'm having a lot of troubles with the proper configuration of Apache mod_proxy.so to work as desired...

The main idea, is to create a proxy on a local machine in a network which will have the ability to process a client request (client connected through this Apache prepared proxy) in PHP. And also, it will have the capacity to process the server responses on PHP too.

Those are the 2 functionalities, and they are independent from each other.

Let me present a little schema of what I need to achieve:

alt text

As you can see here, there're 2 ways: blue one and red one.

For the blue one, I basically connected a client (Machine B - cell phone) on my local network (home) and configured it to go through a proxy, which is the Machine A (personal computer) on the exactly same network.

So let's say (not DHCP):

Machine A: 192.168.1.40 --> Apache is running on this machine, and configured to listen port 80.

Machine B (cell phone): 192.168.1.75 --> configured to go through a proxy, which is IP 192.168.1.75 and port 80 (basically, Machine A).

After configuring Apache properly, which is basically to remove the "#" from httpd.conf on the lines for the mod_proxy.so (main worker), mod_proxy_connect.so (SSL, allowCONNECT, ...) and mod_proxy_http.so (needed for handle HTTP request/responses) and having in my case, lines like this:

# Implements a proxy/gateway for Apache.
Include "conf/extra/httpd-proxy.conf"

# Various default settings
Include "conf/extra/httpd-default.conf"

# Secure (SSL/TLS) connections
Include "conf/extra/httpd-ssl.conf"

Which gives me the ability to configure the file httpd-proxy.conf to prepare the forward proxy or the reverse proxy.

So I'm not sure, if what I need is a forward proxy or a reverse one.

For a forward proxy I've done this:

<IfModule proxy_module>
<IfModule proxy_http_module>

#
# FORWARD Proxy
#

#ProxyRequests Off
ProxyRequests On
ProxyVia On

<Proxy *>
    Order deny,allow
#   Allow from all
    Deny from all
    Allow from 192.168.1
</Proxy>

</IfModule>
</IfModule>

which basically passes all the packets normally to the server and back to the client. I can trace it perfectly (and testing that works) looking at the "access.log" from Apache. Any request I make with the cell phone, appears then on the Apache log. So it works.

But here comes the problem:

  • I need to process those client requests. And I need to do it, in PHP.

I have read a lot about this. I've read in detail the official site from Apache about mod_proxy. And I've searched a lot on forums, but without luck.

So I thought about a first approximation:

1) Forward proxy in Apache, passes all the packets and it's not possible to process them. This seems to be true, so, what about a reverse proxy?

So I envisioned something like:

ProxyRequests Off

<Proxy *>
Order deny,allow
Allow from all
</Proxy>

ProxyPass http://www.google.com http://www.yahoo.com
ProxyPassReverse http://www.google.com http://www.yahoo.com 

which is just a test, but this should cause on my cell phone that when trying to navigate to Google, I should be going to Yahoo, isn't it? But not. It doesn't work.

So you really see, that ALL the examples on Apache reverse proxy, goes like:

ProxyPass /foo http://foo.example.com/bar
ProxyPassReverse /foo http://foo.example.com/bar

which means, that any kind of request in a local context, will be solved on a remote location.

But what I needed is the inverse! It's that when asking for a remote site on my phone, I solve this request on my local server (the Apache one) to process it with a PHP module.

So, if it's a forward proxy, I need to pass through PHP first. If it's a reverse proxy, I need to change the "going" direction to my local server one to process first on PHP.

Then comes in mind second option:

2) I've seen something like:

<Proxy http://example.com/foo/*>
SetOutputFilter INCLUDES
</Proxy>

And I started to search for SetOutputFilter, SetInputFilter, AddOutputFilter and AddInputFilter.

But I don't really know how can I use it.

Seems to be good, or a solution to me, because with something like this, I should be able to add an Input filter to process on PHP the client requests and send back to the client what I programmed/want (not the remote server response) which is the BLUE path on schema, and I should have the ability to add an Output filter which seems to give me the ability to process the remote server response before sending it to the client, which should be the RED path on the schema.

Red path, it's just to read server responses and play with them. But nothing more. The Blue path, is the important one. Because I will send to the client whatever I want after processing the requests.

Sorry for this amazingly big post, but I needed to explain it as well as I can.

I hope someone will understand my problem, and will help me to solve it!

4
  • How is this a programming question?
    – netcoder
    Jan 14, 2011 at 20:29
  • It's not a "programming" question itself, but it's pretty related! I need to know how to achive Apache to handle requests and responses via PHP on demand, on certain domain (i.e: YouTube.com)
    – Lightworker
    Jan 14, 2011 at 21:40
  • @Lightworker - I'm migrating this to ServerFault.com, it's another site in the trilogy and much better venue specifically with experts in this area. Your question is programming related yes, but it's much more on topic at ServerFault, where it'll find a quicker/better answer. Jan 15, 2011 at 12:09
  • I need to achive Apache work as a proxy, and when a client request - connected through the Apache proxy - comes from YouTube (i.e.), send this request to a PHP module to process an specific answer and send it to the requesting client. :\ Jan 15, 2011 at 17:57

2 Answers 2

3

@Sarek is right, but the question is about how to handle proxy requests with PHP (from Apache's mod_proxy) not if Apache is the right tool for that.

To use PHP with Apache as proxy, I use (it require mod_proxy and mod_rewrite) in httpd.conf:

# Forward proxy server
<VirtualHost *:8080>
    ProxyRequests On
    ProxyVia On
    
    <Proxy *>
        Order deny,allow
        Deny from all
        Allow from 192.168
        
        RewriteEngine On
        RewriteCond %{REQUEST_URI} !/pac.php
        RewriteRule ^ /endpoint.php [L]
    </Proxy>
</VirtualHost>

Then in /pac.php (which define the PAC file content):

<?php header('Content-Type: application/x-javascript-config') ?>
function FindProxyForURL(url, host)
{ 
    return "PROXY <?php echo $_SERVER['SERVER_ADDR'] ?>:<?php echo $_SERVER['SERVER_PORT'] ?>; DIRECT";
}
?>

It used for proxy configuration on machine B (proxy client). Use http://192.168.1.40:8080/pac.php. With that, the client will always use the proxy for any domains/ip (like 127.0.0.1, localhost, *.local, etc.). Note: IE and .Net Apps still need to write only for localhost and 127.0.0.1 the FQDN trailing dot: http://localhost. and http://127.0.0.1..

Finally in /endpoint.php:

<?php
// Don't handle domain existance
$url = $_SERVER['REQUEST_URI'];
$url_parts = parse_url($url);
// Some security checks (no local file...)
if(false === $url_parts || empty($url_parts['scheme']) || !in_array($url_parts['scheme'], array('http', 'https'))){
    die();
}

$headers_raw = '';
foreach ($_SERVER as $name => $value)
{
    if (substr($name, 0, 5) == 'HTTP_')
    {
        $name = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))));
        $headers_raw .= $name . ': ' . $value . "\r\n";
    } else if ($name == "CONTENT_TYPE") {
        $headers_raw .= 'Content-Type: ' . $value . "\r\n";
    } else if ($name == "CONTENT_LENGTH") {
        $headers_raw .= 'Content-Length: ' . $value . "\r\n";
    }
}

// http://php.net/manual/en/context.http.php
$context = stream_context_create(array(
    'http' => array(
        'method' => $_SERVER['REQUEST_METHOD'],
        'header' => $headers_raw,
        'ignore_errors' => true,
        'content' => file_get_contents('php://input')
    )
));
$content = file_get_contents($url, false, $context);

$content_type = 'application/octet-stream';// "text/html"
$content_type_raw = $content_type;// "text/html; charset=UTF-8"
foreach($http_response_header as $response_header){
    if('Content-Type:' == substr($response_header, 0, 13)){
        $content_type_raw = substr($response_header, 14);
        $content_type = strstr($content_type_raw, ';', true);
        header($response_header);
    }
    elseif('Content-Encoding:' == substr($response_header, 0, 17) && 'gzip' == substr($response_header, 18, 4))
    {
        //Now lets uncompress the compressed data
        $content = gzinflate(substr($content, 10, -8));
    }
    elseif('Content-Length:' == substr($response_header, 0, 15))
    {
        //Skip it
    }
    else{
        header($response_header);
    }
}

// Content transforms
//var_dump($url_parts);exit();
if('text/html' == $content_type){
    echo str_replace('cat', 'dog', $content);
}else{
    echo $content;
}
?>

It's an example, don't use it in production. It don't handle any invalid/timeout domains/ip.

With that you can rewrite the requested URL (the proxy can serve the content of http://google.com/search?q=dog for URL http://google.com/search?q=cat, a funny joke) and update the content (like remove ads, insert JS/CSS, etc.)

2

Okay, so first of all: Apache is the wrong tool!

Apache is a webserver, not a proxy server. Yes, it comes with a proxy module, but first and foremost it's a webserver.

Rather, you should look into a real proxy server like squid. And within squid you are looking for a feature called "Content Adaptation":

http://wiki.squid-cache.org/SquidFaq/ContentAdaptation

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .