PHP,如何使用标题和正文重定向/转发HTTP请求?

时间:2014-03-16 13:38:34

标签: php curl

我有一个PHP页面,main.php位于服务器1上。

我在服务器2上有一个PHP页面main.php(相同的页面,不同的代码)。

main.php是一个WebService。

我想将服务器1的完整HTTP请求转发给服务器2, 这样当用户向main.php(服务器1)发送HTTP请求时,它将从服务器2上的main.php获得响应。

我希望对服务器2的请求与服务器1的原始请求完全相同。

我通过以下方式获取Http Request数据:

$some_param  = $_REQUEST['param']
$body =file_get_contents('php://input');

让我说我有

$server1_url = "11111";
$server2_url = "22222";

这样做的动机是,我有一个生产服务器和一个登台服务器,我想将一些流量引导到新服务器以测试登台服务器上的新功能。

如何使用所有数据重定向请求或“克隆”完整请求,将其发送到新服务器并返回新响应?

感谢您的帮助!

p.s我尝试使用php curl,但我不明白它是如何工作的,我也找到了各种答案,但是没有人提出请求参数和身体。

再次感谢!

3 个答案:

答案 0 :(得分:18)

If you have access to the Apache server config you can create a virtualhost with the following settings:

ProxyPreserveHost Off
ProxyPass / http://remotesite.domain.tld/
ProxyPassReverse / http://remotesite.domain.tld/
ProxyPassReverseCookieDomain remotesite.domain.tld proxysite.tld

You'll need to enable mod_proxy and mod_proxy_http for this. Substitute remotesite.domain.tld to the site you forward to, and proxysite.tld to the forwarder.

If you don't have access to the server config files, you can still do in php, by manually setting up curl and forward everything.

<?php

error_reporting(E_ALL);
ini_set('display_errors', 1);

/* Set it true for debugging. */
$logHeaders = FALSE;

/* Site to forward requests to.  */
$site = 'http://remotesite.domain.tld/';

/* Domains to use when rewriting some headers. */
$remoteDomain = 'remotesite.domain.tld';
$proxyDomain = 'proxysite.tld';

$request = $_SERVER['REQUEST_URI'];

$ch = curl_init();

/* If there was a POST request, then forward that as well.*/
if ($_SERVER['REQUEST_METHOD'] == 'POST')
{
    curl_setopt($ch, CURLOPT_POST, TRUE);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $_POST);
}
curl_setopt($ch, CURLOPT_URL, $site . $request);
curl_setopt($ch, CURLOPT_HEADER, TRUE);

$headers = getallheaders();

/* Translate some headers to make the remote party think we actually browsing that site. */
$extraHeaders = array();
if (isset($headers['Referer'])) 
{
    $extraHeaders[] = 'Referer: '. str_replace($proxyDomain, $remoteDomain, $headers['Referer']);
}
if (isset($headers['Origin'])) 
{
    $extraHeaders[] = 'Origin: '. str_replace($proxyDomain, $remoteDomain, $headers['Origin']);
}

/* Forward cookie as it came.  */
curl_setopt($ch, CURLOPT_HTTPHEADER, $extraHeaders);
if (isset($headers['Cookie']))
{
    curl_setopt($ch, CURLOPT_COOKIE, $headers['Cookie']);
}
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

if ($logHeaders)
{
    $f = fopen("headers.txt", "a");
    curl_setopt($ch, CURLOPT_VERBOSE, TRUE);
    curl_setopt($ch, CURLOPT_STDERR, $f);
}

curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$response = curl_exec($ch);

$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$headers = substr($response, 0, $header_size);
$body = substr($response, $header_size);

$headerArray = explode(PHP_EOL, $headers);

/* Process response headers. */
foreach($headerArray as $header)
{
    $colonPos = strpos($header, ':');
    if ($colonPos !== FALSE) 
    {
        $headerName = substr($header, 0, $colonPos);

        /* Ignore content headers, let the webserver decide how to deal with the content. */
        if (trim($headerName) == 'Content-Encoding') continue;
        if (trim($headerName) == 'Content-Length') continue;
        if (trim($headerName) == 'Transfer-Encoding') continue;
        if (trim($headerName) == 'Location') continue;
        /* -- */
        /* Change cookie domain for the proxy */
        if (trim($headerName) == 'Set-Cookie')
        {
            $header = str_replace('domain='.$remoteDomain, 'domain='.$proxyDomain, $header);
        }
        /* -- */

    }
    header($header, FALSE);
}

echo $body;

if ($logHeaders)
{
    fclose($f);
}
curl_close($ch);

?>

EDIT:

And of course the script must be in the root directory of a (sub)domain. And you should have a .htaccess that rewrites everything to it:

RewriteEngine On
RewriteRule .* index.php

答案 1 :(得分:4)

这是我找到的解决方案(可能会更好)

 public static function getResponse ($url,$headers,$body)
    {
        $params = '?' . http_build_query($headers);

        $redirect_url = $url . $params;

        $ch = curl_init($redirect_url);

        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        $response = curl_exec($ch);

        if (!isset($response))
            return null;
        return $response;
    }

答案 2 :(得分:0)

我使用了Rehmat和Calmarius的代码并进行了一些更改,所以现在它可以处理多个具有相同名称的字段,例如

<input type="text" name="example[]">
<input type="text" name="example[]">
<input type="text" name="example[]">

,也可以上传文件,包括使用相同字段名称的多个文件。

去了:

<?php
error_reporting(E_ALL);
ini_set('display_errors', '1');

class proxy {
    private $logHeaders     = false;
    
    /* Site to forward requests to.  */
    private $site           = 'http://redirectToSite';
    /* Domains to use when rewriting some headers. */
    private $remoteDomain   = 'redirectToSite';
    private $proxyDomain    = 'yourproxydomain.com';

    public function __construct() {
        $request = $_SERVER['REQUEST_URI'];
        $ch = curl_init();

        /* If there was a POST request, then forward that as well.*/
        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
            $post   = $this->sanitizePostFields($_POST);
            $files  = $this->sanitizeFiles($_FILES);
            if ($files) {
                $post = array_merge($post, $files);
            }
            
            curl_setopt($ch, CURLOPT_POST, TRUE);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
            /*
            // this is enough if not uploading files
            curl_setopt(
                $ch,
                CURLOPT_POSTFIELDS,
                http_build_query($_POST)
            );
            */
        }
        
        curl_setopt($ch, CURLOPT_URL, $this->site . $request);
        curl_setopt($ch, CURLOPT_HEADER, TRUE);

        $headers = getallheaders();

        /*
            Translate some headers to make the remote party think we
            actually browsing that site.
        */
        $extraHeaders = array();
        if (isset($headers['Referer'])) {
            $extraHeaders[] = 'Referer: '. str_replace(
                $this->proxyDomain,
                $this->remoteDomain,
                $headers['Referer']
            );
        }
        if (isset($headers['Origin'])) {
            $extraHeaders[] = 'Origin: '. str_replace(
                $this->proxyDomain,
                $this->remoteDomain,
                $headers['Origin']
            );
        }

        /*
            Forward cookie as it came.
        */
        curl_setopt($ch, CURLOPT_HTTPHEADER, $extraHeaders);
        if (isset($headers['Cookie'])) {
            curl_setopt($ch, CURLOPT_COOKIE, $headers['Cookie']);
        }
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

        if ($this->logHeaders) {
            $f = fopen("headers.txt", "a");
            curl_setopt($ch, CURLOPT_VERBOSE, TRUE);
            curl_setopt($ch, CURLOPT_STDERR, $f);
        }

        //curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        $response = curl_exec($ch);

        $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
        $headers = substr($response, 0, $header_size);
        $body = substr($response, $header_size);

        $headerArray = explode(PHP_EOL, $headers);

        /* Process response headers. */
        foreach($headerArray as $header) {
            $colonPos = strpos($header, ':');
            if ($colonPos !== FALSE) {
                $headerName = substr($header, 0, $colonPos);

                /*
                    Ignore content headers, let the webserver decide how to
                    deal with the content.
                */
                if (trim($headerName) == 'Content-Encoding') continue;
                if (trim($headerName) == 'Content-Length') continue;
                if (trim($headerName) == 'Transfer-Encoding') continue;
                
                //if (trim($headerName) == 'Location') continue;
                
                /* -- */
                /* Change cookie domain for the proxy */
                if (trim($headerName) == 'Set-Cookie') {
                    $header = str_replace(
                        'domain='.$this->remoteDomain,
                        'domain='.$this->proxyDomain,
                        $header
                    );
                }
                /* -- */
                
                if (trim($headerName) == 'Location') {
                    $header = str_replace(
                        $this->remoteDomain,
                        $this->proxyDomain,
                        $header
                    );
                }
            }
            
            header($header, FALSE);
        }

        echo $body;

        if ($this->logHeaders) {
            fclose($f);
        }
        
        curl_close($ch);
    }

    private function sanitizePostFields($post, $fieldName = '') {
        if (empty($post)) { return false; }
        if (!is_array($post)) { return false; }
        
        $result = [];
        
        foreach ($post as $k => $v) {
            if (is_string($v)) {
                $result[($fieldName ? "{$fieldName}[{$k}]" : $k)] = $v;
            }
            elseif (is_array($v)) {
                $result = array_merge(
                    $result,
                    $this->sanitizePostFields($v, $k)
                );
            }
        }
        
        return $result;
    }

    private function sanitizeFiles($files) {
        if (empty($files)) { return false; }
        if (!is_array($files)) { return false; }
        
        $result = [];
        
        foreach ($files as $k => $v) {
            if (empty($v['name'])) { continue; }
            
            if (is_array($v['name'])) {
                // more than one file using the same name field[]
                $nFields = count($v['name']);
                for ($i = 0; $i < $nFields; $i++) {
                    if (empty($v['tmp_name'][$i])) { continue; }
                    $curl_file_upload = new CURLFile(
                        $v['tmp_name'][$i],
                        $v['type'][$i],
                        $v['name'][$i]
                    );
                    $result["{$k}[{$i}]"] = $curl_file_upload;
                }
            }
            else {
                $curl_file_upload = new CURLFile(
                    $v['tmp_name'],
                    $v['type'],
                    $v['name']
                );
                $result[$k] = $curl_file_upload;
            }
        }
        
        return $result;
    }
    
}

$proxy = new proxy();