PHP proxy 使用 fsockopen

使用socket 來實作 proxy 功能

<?php
$targetHost = "192.168.123.123;        //要轉送的目的HOST(另一台伺服器)
$targetPort = 80;
$targetPath = "/target.php";            //要轉發的目的路徑

// 取得原始請求的方法
$method = $_SERVER['REQUEST_METHOD'];

// 取得原始請求的所有參數
$params = http_build_query($_REQUEST);

// 建立原始請求頭
$requestHeaders = "$method $targetPath HTTP/1.1\r\n";
$requestHeaders .= "Host: $targetHost\r\n";
$requestHeaders .= "Content-Length: " . strlen($params) . "\r\n";
$requestHeaders .= "Content-Type: application/x-www-form-urlencoded\r\n";
$requestHeaders .= "Connection: close\r\n\r\n";

// 開啟與目標伺服器的連接
$socket = fsockopen($targetHost, $targetPort, $errno, $errstr, 30);

if (!$socket) {
    header('HTTP/1.1 500 Internal Server Error');
    echo "Error: $errstr ($errno)";
} else {
    fwrite($socket, $requestHeaders . $params);     // 傳送原始請求
    $isFlush = false;
    $byteArray = unpack('C*', 'abce');  // 初始化4個byte的array
    while (!feof($socket)) {
        $byte = fread($socket, 1);      // 從目標伺服器讀取回應並即時傳送給客戶端
        if(!$isFlush){
            array_shift($byteArray);    // 迭代控管用的array,為了過濾\r\n\r\n
            array_push($byteArray, tobyte($byte));
            if(byteArray2String($byteArray) == "\r\n\r\n"){     //過濾掉header,後續的才傳送
                $isFlush = true;
            }
        }else{
            echo $byte;     //這邊才是內容
            flush();
        }
    }
    fclose($socket);        // 關閉連線
}

function byteArray2String($byteArray) {
    $chars = array_map("chr", $byteArray);
    return join($chars);
}

function tobyte($str){
    $tmpAry = unpack('C*', $str);
    return $tmpAry[1];
}

?>

注意:

1. php 預設沒有byte 型態,所以要放入byte array 的字串必須轉型

2. fread 讀取出來的其實是字串

3. unpack這個function 會return 一個index 從1 開始的 array

Caution
If you do not name an element, numeric indices starting from 1 are used. Be aware that if you have more than one unnamed element, some data is overwritten because the numbering restarts from 1 for each element.

參考資料:

https://www.php.net/manual/en/function.unpack.php

發佈留言