mirror of
https://github.com/masterking32/MasterHttpRelayVPN.git
synced 2026-05-18 07:34:35 +03:00
126 lines
3.4 KiB
PHP
126 lines
3.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/*
|
|
* MasterHttpRelayVPN - Simple PHP Relay
|
|
*
|
|
* Test relay endpoint:
|
|
* - Accepts the incoming HTTP request
|
|
* - Forwards the raw request body to the configured upstream URL
|
|
* - Returns the upstream response status/body to the caller
|
|
*
|
|
* Configuration:
|
|
* 1. Edit UPSTREAM_URL below
|
|
* 2. Or set environment variable RELAY_UPSTREAM_URL
|
|
*/
|
|
|
|
const UPSTREAM_URL = 'https://example.com/relay';
|
|
const CONNECT_TIMEOUT_SECONDS = 10;
|
|
const REQUEST_TIMEOUT_SECONDS = 30;
|
|
|
|
$upstreamUrl = getenv('RELAY_UPSTREAM_URL');
|
|
if (!is_string($upstreamUrl) || trim($upstreamUrl) === '') {
|
|
$upstreamUrl = UPSTREAM_URL;
|
|
}
|
|
|
|
if (trim($upstreamUrl) === '' || $upstreamUrl === 'https://example.com/relay') {
|
|
http_response_code(500);
|
|
header('Content-Type: text/plain; charset=utf-8');
|
|
echo "PHP relay is not configured. Set RELAY_UPSTREAM_URL or edit UPSTREAM_URL.\n";
|
|
exit;
|
|
}
|
|
|
|
$method = $_SERVER['REQUEST_METHOD'] ?? 'POST';
|
|
$rawBody = file_get_contents('php://input');
|
|
if ($rawBody === false) {
|
|
http_response_code(400);
|
|
header('Content-Type: text/plain; charset=utf-8');
|
|
echo "Failed to read request body.\n";
|
|
exit;
|
|
}
|
|
|
|
$incomingHeaders = function_exists('getallheaders') ? getallheaders() : [];
|
|
$forwardHeaders = [];
|
|
|
|
foreach ($incomingHeaders as $name => $value) {
|
|
$lower = strtolower((string) $name);
|
|
if (in_array($lower, ['host', 'content-length', 'connection'], true)) {
|
|
continue;
|
|
}
|
|
|
|
$forwardHeaders[] = $name . ': ' . $value;
|
|
}
|
|
|
|
if (!hasHeader($forwardHeaders, 'Content-Type')) {
|
|
$contentType = $_SERVER['CONTENT_TYPE'] ?? 'application/octet-stream';
|
|
$forwardHeaders[] = 'Content-Type: ' . $contentType;
|
|
}
|
|
|
|
$forwardHeaders[] = 'X-Relay-By: MasterHttpRelayVPN-PHP';
|
|
$forwardHeaders[] = 'X-Forwarded-Method: ' . $method;
|
|
|
|
$ch = curl_init($upstreamUrl);
|
|
if ($ch === false) {
|
|
http_response_code(500);
|
|
header('Content-Type: text/plain; charset=utf-8');
|
|
echo "Failed to initialize cURL.\n";
|
|
exit;
|
|
}
|
|
|
|
$responseHeaders = [];
|
|
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_CUSTOMREQUEST => $method,
|
|
CURLOPT_POSTFIELDS => $rawBody,
|
|
CURLOPT_HTTPHEADER => $forwardHeaders,
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_FOLLOWLOCATION => false,
|
|
CURLOPT_CONNECTTIMEOUT => CONNECT_TIMEOUT_SECONDS,
|
|
CURLOPT_TIMEOUT => REQUEST_TIMEOUT_SECONDS,
|
|
CURLOPT_HEADERFUNCTION => static function ($curl, string $headerLine) use (&$responseHeaders): int {
|
|
$trimmed = trim($headerLine);
|
|
if ($trimmed !== '') {
|
|
$responseHeaders[] = $trimmed;
|
|
}
|
|
return strlen($headerLine);
|
|
},
|
|
]);
|
|
|
|
$responseBody = curl_exec($ch);
|
|
$curlError = curl_error($ch);
|
|
$statusCode = (int) curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
|
|
$responseContentType = (string) curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
|
|
curl_close($ch);
|
|
|
|
if ($responseBody === false) {
|
|
http_response_code(502);
|
|
header('Content-Type: text/plain; charset=utf-8');
|
|
echo "Upstream relay failed: {$curlError}\n";
|
|
exit;
|
|
}
|
|
|
|
if ($statusCode > 0) {
|
|
http_response_code($statusCode);
|
|
}
|
|
|
|
if ($responseContentType !== '') {
|
|
header('Content-Type: ' . $responseContentType);
|
|
} else {
|
|
header('Content-Type: application/octet-stream');
|
|
}
|
|
|
|
echo $responseBody;
|
|
|
|
function hasHeader(array $headers, string $headerName): bool
|
|
{
|
|
$needle = strtolower($headerName) . ':';
|
|
foreach ($headers as $header) {
|
|
if (str_starts_with(strtolower($header), $needle)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|