PHP 8.5 deprecates curl_close() (no-op since 8.0) and Libs\Proxy fataled on class load because SOURCE_URL was evaluated in a static property initializer. Stamps http with per-component core data for load-verified clients (aria2, proxy, compreFace).
176 lines
5.3 KiB
PHP
176 lines
5.3 KiB
PHP
<?php
|
|
|
|
namespace Libs;
|
|
|
|
/**
|
|
* PHP Proxy - DuckBrain
|
|
*
|
|
* Proxy is a library to create a PHP proxy.
|
|
* It allows controlling the headers that will be returned to the client,
|
|
* in addition to placing filters on the resulting HTML code.
|
|
*
|
|
* @author KJ
|
|
* @website https://kj2.me
|
|
* @licence MIT
|
|
*/
|
|
class Proxy
|
|
{
|
|
public static $proxied_headers = [
|
|
'Set-Cookie',
|
|
'Content-Type',
|
|
'Cookie',
|
|
'Location',
|
|
];
|
|
public static $proxy_base_url_canonical = '/';
|
|
public static ?string $destination_host = null;
|
|
protected static $filters = [];
|
|
|
|
/**
|
|
* Host destino efectivo: el asignado explicitamente o la constante
|
|
* SOURCE_URL del proyecto si existe. Se resuelve tarde para que la
|
|
* simple carga de la clase no falle sin configuracion.
|
|
*
|
|
* @return string
|
|
*/
|
|
protected static function destinationHost(): string
|
|
{
|
|
if (static::$destination_host !== null) {
|
|
return static::$destination_host;
|
|
}
|
|
|
|
return defined('SOURCE_URL') ? (string) SOURCE_URL : '';
|
|
}
|
|
|
|
/**
|
|
* Adds a callable (AKA filter) that will receive the content
|
|
* and should return a string.
|
|
*
|
|
* @param callable $cb
|
|
* Function that will modify the content.
|
|
*
|
|
* @return void
|
|
*/
|
|
public static function add_filter(callable $cb): void
|
|
{
|
|
static::$filters[] = $cb;
|
|
}
|
|
|
|
/**
|
|
* Applies the filters configured with add_filter.
|
|
*
|
|
* @param string $content
|
|
* Content on which the filters will be applied.
|
|
*
|
|
* @return mixed
|
|
* It would be correct to return a string, but
|
|
* since the filters might not return correctly,
|
|
* to avoid a design error, we specify that it returns mixed.
|
|
*/
|
|
protected static function apply_filters(string $content): mixed
|
|
{
|
|
foreach (static::$filters as $filter) {
|
|
$content = call_user_func($filter, $content);
|
|
}
|
|
return $content;
|
|
}
|
|
|
|
/**
|
|
* Starts the proxy process.
|
|
*
|
|
* @param string|null $proxy_request_uri
|
|
* The address to which the proxy will be applied.
|
|
*
|
|
* @return void
|
|
*/
|
|
public static function run(?string $proxy_request_uri = null): void
|
|
{
|
|
// Get the URI if not defined
|
|
if (is_null($proxy_request_uri)) {
|
|
$proxy_request_uri = substr($_SERVER['REQUEST_URI'], strlen(static::$proxy_base_url_canonical));
|
|
} elseif (substr($proxy_request_uri, 0, 1) == '/') {
|
|
$proxy_request_uri = substr($proxy_request_uri, 1);
|
|
}
|
|
|
|
// Generate the final URL
|
|
$proxy_request_url = static::destinationHost() . $proxy_request_uri;
|
|
|
|
// cURL initialization
|
|
$ch = curl_init();
|
|
|
|
curl_setopt($ch, CURLOPT_URL, $proxy_request_url);
|
|
curl_setopt($ch, CURLOPT_AUTOREFERER, 1);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
|
curl_setopt($ch, CURLOPT_HEADER, 1);
|
|
curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
|
|
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
|
|
|
|
// Capture client cookies
|
|
$hdrs = [];
|
|
if (isset($_SERVER['HTTP_COOKIE'])) {
|
|
$hdrs[] = "Cookie: " . $_SERVER['HTTP_COOKIE'];
|
|
}
|
|
|
|
// Capture client browser (User-Agent)
|
|
if (isset($_SERVER['HTTP_USER_AGENT'])) {
|
|
$hdrs[] = "User-Agent: " . $_SERVER['HTTP_USER_AGENT'];
|
|
}
|
|
|
|
// Add captured headers
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, $hdrs);
|
|
|
|
// Capture all client headers
|
|
$hdrs = [];
|
|
foreach (\getallheaders() as $name => $value) {
|
|
$hdrs[] = "$name: $value";
|
|
}
|
|
|
|
// Add captured headers to the request
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, $hdrs);
|
|
|
|
// Add POST parameters
|
|
if (sizeof($_POST) > 0) {
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($_POST));
|
|
}
|
|
|
|
// Execute the request
|
|
$res = curl_exec($ch);
|
|
|
|
// Parse the response
|
|
list($headers, $body) = explode("\r\n\r\n", $res, 2);
|
|
$headers = str_replace(static::destinationHost(), '/', $headers);
|
|
|
|
$headers = explode("\r\n", $headers);
|
|
$hs = [];
|
|
|
|
foreach ($headers as $header) {
|
|
if (false !== strpos($header, ': ')) {
|
|
list($h, $v) = explode(': ', $header);
|
|
$hs[$h][] = $v;
|
|
} else {
|
|
$header1 = $header;
|
|
}
|
|
}
|
|
|
|
// Define headers in the client
|
|
list($proto, $code, $text) = explode(' ', $header1);
|
|
header($_SERVER['SERVER_PROTOCOL'] . ' ' . $code . ' ' . $text);
|
|
|
|
foreach (static::$proxied_headers as $hname) {
|
|
if (isset($hs[$hname])) {
|
|
foreach ($hs[$hname] as $v) {
|
|
if ($hname === 'Set-Cookie') {
|
|
header($hname . ": " . $v, false);
|
|
} else {
|
|
header($hname . ": " . $v);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
die(static::apply_filters($body));
|
|
}
|
|
}
|