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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22437548/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 04:56:15  来源:igfitidea点击:

PHP, How to Redirect/Forward HTTP Request with header and body?

phpcurl

提问by user2721348

I have a PHP page, main.php which is on server 1.

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

I have a PHP page main.php (same page, different code) on server 2.

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

main.php is a WebService.

main.php 是一个 WebService。

I would like to forward the full HTTP request made to server 1, to server 2, so that when a user sends an HTTP request to main.php (server 1), it would get the response from main.php on server 2.

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

I would like the request made to server 2 to be exactly like the original request to server 1.

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

I take the Http Request data via:

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

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

and lets say i have

让我们说我有

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

The motivation for this is, i have a production server and a staging server, i would like to direct some traffic to the new server to test the new functionality on the staging server.

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

How do i redirect the request with all the data or "cloning" the full request, sending it to the new server and returning the new response?

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

Thanks for your help!

谢谢你的帮助!

p.s i tried using php curl, but i dont understand how it works, also i found all kinds of answers, but none forward the Requests params and the body.

ps 我尝试使用 php curl,但我不明白它是如何工作的,我也找到了各种答案,但没有转发请求参数和正文。

Again thanks!

再次感谢!

采纳答案by user2721348

this is the solution i have found (there might be better)

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

 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;
    }

回答by Calmarius

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

如果您有权访问 Apache 服务器配置,则可以使用以下设置创建虚拟主机:

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.

您需要为此启用 mod_proxy 和 mod_proxy_http。将 remotesite.domain.tld 替换为您转发到的站点,将 proxysite.tld 替换为转发器。

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 手动设置 curl 并转发所有内容。

<?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:

当然,脚本必须位于(子)域的根目录中。你应该有一个 .htaccess 来重写它的所有内容:

RewriteEngine On
RewriteRule .* index.php

回答by Trenbolon

I have used the code by Rehmat and Calmarius and made a few changes so now it handles multiple fields with same name like

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

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

and to upload files too, including multiple files that use the same field name.

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

here is goes:

这是:

<?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();