PHP。按原样将请求转发到另一台服务器
PHP. Forward request to another server as is
服务器 A 正在向服务器 B 发送消息。我需要在它们之间插入带有 PHP 脚本的服务器 C。
所以我希望它像这样工作:
服务器 A -> 服务器 C(对数据做一些处理并进一步转发请求)-> 服务器 B。
我需要服务器 B 接收从服务器 A 发送的完全相同的请求。我不需要服务器 C 作为代理,只需按原样发送请求,仅此而已
我该怎么做?
我试过类似的方法,但没有成功:
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'Server B URL');
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_VERBOSE, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_FRESH_CONNECT, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLINFO_HEADER_OUT, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $_POST);
$response = curl_exec($ch);
服务器 B 返回 200,但什么也没做。我无权访问它,所以我不知道是什么阻止了请求。
尝试更改此行
curl_setopt($ch, CURLOPT_POSTFIELDS, $_POST);
至:
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($_POST));
当您发出常规 cURL 请求时,您不能直接使用数组($_POST
是一个数组)作为 CURLOPT_POSTFIELDS
的值。您必须将该数组转换为 postdata 字符串,这正是 http_build_query()
所做的。它转换例如
["name" => "my first name", "email" => "my@email.com"]
进入:
name=my%20first%20name&email=my@email.com
确保 curl 准确复制任何请求到最小的细节?这不是 curl 的工作,而是 socket_* 的工作。我敢打赌有人会说这也不是 PHP 的真正工作,但 PHP 肯定能够以单线程方式做到这一点。使用套接字 api 接受来自服务器 A 的连接,读取请求,进行处理,将收到的请求转发给服务器 B,读取来自服务器 B 的响应,并将该响应发送回服务器 A。
示例(可以称之为 ServerC.php):
<?php
declare(strict_types = 1);
if(php_sapi_name() !== 'cli'){die('this script must run in cli mode.');}
$port = 9999;
$targetIP = gethostbyname ( 'example.org' );
$targetPort = 80;
assert ( false !== filter_var ( $targetIP, FILTER_VALIDATE_IP ) );
var_dump ( $targetIP, $targetPort );
y ( ($listen = socket_create ( AF_INET, SOCK_STREAM, SOL_TCP )) );
register_shutdown_function ( function () use (&$listen) {
socket_close ( $listen );
} );
y ( socket_bind ( $listen, '0.0.0.0', $port ) );
y ( socket_listen ( $listen ) );
y ( socket_set_block ( $listen ) );
echo 'listening for connections... ', PHP_EOL;
while ( false !== ($newconn = socket_accept ( $listen )) ) {
echo 'new connection from ';
socket_getpeername ( $newconn, $peername, $peerport );
echo $peername . ':' . $peerport . PHP_EOL;
y ( socket_set_block ( $newconn ) );
$data = '';
echo 'reading request... ';
$data = socket_read_all ( $newconn, 2 );
var_dump ( $data );
echo 'done.' . PHP_EOL;
// client didnt send any bytes in a while, assume we got the whole request...
{
// do whatever processing you need to do between requests in here.
}
{
// now to forward the request.
y ( ($targetSock = socket_create ( AF_INET, SOCK_STREAM, SOL_TCP )) );
y ( socket_set_block ( $targetSock ) );
y ( socket_connect ( $targetSock, $targetIP, $targetPort ) );
echo 'connected to target. forwarding request... ';
socket_write_all ( $targetSock, $data );
echo 'done.', PHP_EOL;
unset ( $data, $newdata );
$data = '';
echo 'reading response... ';
$data = socket_read_all ( $targetSock, 2 );
var_dump ( $data );
echo 'done.', PHP_EOL;
socket_close ( $targetSock );
}
echo 'sending response back to client... ';
socket_write_all ( $newconn, $data );
echo 'done.', PHP_EOL;
socket_close ( $newconn );
}
function y($in) {
if (! $in) {
$str = hhb_return_var_dump ( socket_last_error (), socket_strerror ( socket_last_error () ) );
throw new \Exception ( $str );
}
return $in;
}
function n($in) {
if (! ! $in) {
throw new \Exception ();
}
return $in;
}
function hhb_return_var_dump(): string // works like var_dump, but returns a string instead of printing it.
{
$args = func_get_args ();
ob_start ();
call_user_func_array ( 'var_dump', $args );
return ob_get_clean ();
}
function socket_write_all($sock, string $data) {
$len = strlen ( $data );
while ( $len > 0 ) {
$written = socket_write ( $sock, $data );
if ($written === false) {
throw new RuntimeException ( 'socket_write failed. errno: ' . socket_last_error ( $sock ) . '. error: ' . socket_strerror ( socket_last_error ( $sock ) ) );
}
$len -= $written;
$data = substr ( $data, $written );
}
return; // all data written
}
function socket_read_all($sock, int $sleep_sec = 2): string {
$ret = '';
while ( true ) {
sleep ( $sleep_sec ); // ...
$buf = '';
$read = socket_recv ( $sock, $buf, 9999, MSG_DONTWAIT );
if ($read === false || $read < 1) {
return $ret;
}
$ret .= $buf;
}
}
(这个脚本很慢,一次只处理 1 个请求(并发请求由 OS 为 socket_accept 缓冲),并且是同步的。但它可以被优化和制作与 socket_select & co 完全异步,如果值得优化的话)
服务器 A 正在向服务器 B 发送消息。我需要在它们之间插入带有 PHP 脚本的服务器 C。
所以我希望它像这样工作:
服务器 A -> 服务器 C(对数据做一些处理并进一步转发请求)-> 服务器 B。
我需要服务器 B 接收从服务器 A 发送的完全相同的请求。我不需要服务器 C 作为代理,只需按原样发送请求,仅此而已
我该怎么做?
我试过类似的方法,但没有成功:
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'Server B URL');
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_VERBOSE, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_FRESH_CONNECT, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLINFO_HEADER_OUT, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $_POST);
$response = curl_exec($ch);
服务器 B 返回 200,但什么也没做。我无权访问它,所以我不知道是什么阻止了请求。
尝试更改此行
curl_setopt($ch, CURLOPT_POSTFIELDS, $_POST);
至:
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($_POST));
当您发出常规 cURL 请求时,您不能直接使用数组($_POST
是一个数组)作为 CURLOPT_POSTFIELDS
的值。您必须将该数组转换为 postdata 字符串,这正是 http_build_query()
所做的。它转换例如
["name" => "my first name", "email" => "my@email.com"]
进入:
name=my%20first%20name&email=my@email.com
确保 curl 准确复制任何请求到最小的细节?这不是 curl 的工作,而是 socket_* 的工作。我敢打赌有人会说这也不是 PHP 的真正工作,但 PHP 肯定能够以单线程方式做到这一点。使用套接字 api 接受来自服务器 A 的连接,读取请求,进行处理,将收到的请求转发给服务器 B,读取来自服务器 B 的响应,并将该响应发送回服务器 A。
示例(可以称之为 ServerC.php):
<?php
declare(strict_types = 1);
if(php_sapi_name() !== 'cli'){die('this script must run in cli mode.');}
$port = 9999;
$targetIP = gethostbyname ( 'example.org' );
$targetPort = 80;
assert ( false !== filter_var ( $targetIP, FILTER_VALIDATE_IP ) );
var_dump ( $targetIP, $targetPort );
y ( ($listen = socket_create ( AF_INET, SOCK_STREAM, SOL_TCP )) );
register_shutdown_function ( function () use (&$listen) {
socket_close ( $listen );
} );
y ( socket_bind ( $listen, '0.0.0.0', $port ) );
y ( socket_listen ( $listen ) );
y ( socket_set_block ( $listen ) );
echo 'listening for connections... ', PHP_EOL;
while ( false !== ($newconn = socket_accept ( $listen )) ) {
echo 'new connection from ';
socket_getpeername ( $newconn, $peername, $peerport );
echo $peername . ':' . $peerport . PHP_EOL;
y ( socket_set_block ( $newconn ) );
$data = '';
echo 'reading request... ';
$data = socket_read_all ( $newconn, 2 );
var_dump ( $data );
echo 'done.' . PHP_EOL;
// client didnt send any bytes in a while, assume we got the whole request...
{
// do whatever processing you need to do between requests in here.
}
{
// now to forward the request.
y ( ($targetSock = socket_create ( AF_INET, SOCK_STREAM, SOL_TCP )) );
y ( socket_set_block ( $targetSock ) );
y ( socket_connect ( $targetSock, $targetIP, $targetPort ) );
echo 'connected to target. forwarding request... ';
socket_write_all ( $targetSock, $data );
echo 'done.', PHP_EOL;
unset ( $data, $newdata );
$data = '';
echo 'reading response... ';
$data = socket_read_all ( $targetSock, 2 );
var_dump ( $data );
echo 'done.', PHP_EOL;
socket_close ( $targetSock );
}
echo 'sending response back to client... ';
socket_write_all ( $newconn, $data );
echo 'done.', PHP_EOL;
socket_close ( $newconn );
}
function y($in) {
if (! $in) {
$str = hhb_return_var_dump ( socket_last_error (), socket_strerror ( socket_last_error () ) );
throw new \Exception ( $str );
}
return $in;
}
function n($in) {
if (! ! $in) {
throw new \Exception ();
}
return $in;
}
function hhb_return_var_dump(): string // works like var_dump, but returns a string instead of printing it.
{
$args = func_get_args ();
ob_start ();
call_user_func_array ( 'var_dump', $args );
return ob_get_clean ();
}
function socket_write_all($sock, string $data) {
$len = strlen ( $data );
while ( $len > 0 ) {
$written = socket_write ( $sock, $data );
if ($written === false) {
throw new RuntimeException ( 'socket_write failed. errno: ' . socket_last_error ( $sock ) . '. error: ' . socket_strerror ( socket_last_error ( $sock ) ) );
}
$len -= $written;
$data = substr ( $data, $written );
}
return; // all data written
}
function socket_read_all($sock, int $sleep_sec = 2): string {
$ret = '';
while ( true ) {
sleep ( $sleep_sec ); // ...
$buf = '';
$read = socket_recv ( $sock, $buf, 9999, MSG_DONTWAIT );
if ($read === false || $read < 1) {
return $ret;
}
$ret .= $buf;
}
}
(这个脚本很慢,一次只处理 1 个请求(并发请求由 OS 为 socket_accept 缓冲),并且是同步的。但它可以被优化和制作与 socket_select & co 完全异步,如果值得优化的话)