簡體   English   中英

PHP,如何使用標頭和正文重定向/轉發 HTTP 請求?

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

我有一個 PHP 頁面 main.php,它位於服務器 1 上。

我在服務器 2 上有一個 PHP 頁面 main.php(相同的頁面,不同的代碼)。

main.php 是一個 WebService。

我想將向服務器 1 發出的完整 HTTP 請求轉發到服務器 2,以便當用戶向 main.php(服務器 1)發送 HTTP 請求時,它會從服務器 2 上的 main.php 獲得響應。

我希望對服務器 2 的請求與對服務器 1 的原始請求完全相同。

我通過以下方式獲取 Http 請求數據:

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

讓我們說我有

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

這樣做的動機是,我有一個生產服務器和一個登台服務器,我想將一些流量引導到新服務器以測試登台服務器上的新功能。

如何使用所有數據重定向請求或“克隆”完整請求,將其發送到新服務器並返回新響應?

謝謝你的幫助!

ps 我嘗試使用 php curl,但我不明白它是如何工作的,我也找到了各種答案,但沒有轉發請求參數和正文。

再次感謝!

如果您有權訪問 Apache 服務器配置,則可以使用以下設置創建虛擬主機:

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

您需要為此啟用 mod_proxy 和 mod_proxy_http。 將 remotesite.domain.tld 替換為您轉發到的站點,將 proxysite.tld 替換為轉發器。

如果您無權訪問服務器配置文件,您仍然可以通過 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);

?>

編輯:

當然,腳本必須位於(子)域的根目錄中。 你應該有一個 .htaccess 來重寫所有內容:

RewriteEngine On
RewriteRule .* index.php

這是我找到的解決方案(可能有更好的)

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

我使用了 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();

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM