Skip to content

Instantly share code, notes, and snippets.

@meitinger
Last active October 27, 2021 14:00
Show Gist options
  • Save meitinger/cc208a04dde8037118796c4891b67413 to your computer and use it in GitHub Desktop.
Save meitinger/cc208a04dde8037118796c4891b67413 to your computer and use it in GitHub Desktop.
A simple but fast and functional PHP reverse proxy.
<?php
/* Copyright (C) 2019-2021, Manuel Meitinger
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//
// INIT
//
// be strict and define the proper end of line for HTTP
error_reporting(E_ALL);
define('EOL', "\r\n");
// disable any default charset (otherwise always gets appended)
ini_set('default_charset', '');
// compute the actual url
$host = 'example.org';
$ip = '1.2.3.4';
$port = 443;
$url = "https://$host:$port".$_SERVER['REQUEST_URI'];
//
// HELPERS
//
function buildPostDataForFields($delimiter, $name, $content)
{
// handle array content specially
if (is_array($content))
{
$data = '';
foreach ($content as $key => $value)
{
$data .= buildPostDataForFields($delimiter, $name . '[' . $key . ']', $value);
}
return $data;
}
// return the field post data section
return '--' . $delimiter . EOL
. 'Content-Disposition: form-data; name="' . $name . '"' . EOL
. EOL
. $content . EOL;
}
function buildPostDataForFiles($delimiter, $name, $error, $fileName, $fileType, $filePath)
{
// handle array content specially
if (is_array($error))
{
$data = '';
foreach ($error as $key => $value)
{
$data .= buildPostDataForFiles($delimiter, $name . '[' . $key . ']', $value, $fileName[$key], $fileType[$key], $filePath[$key]);
}
return $data;
}
// ensure the file was uploaded properly
if ($error != UPLOAD_ERR_OK)
{
return '';
}
// create the file's post data section
return '--' . $delimiter . EOL
. 'Content-Disposition: form-data; name="' . $name . '"; filename="' . $fileName . '"' . EOL
. 'Content-Transfer-Encoding: ' . $fileType . EOL
. EOL
. file_get_contents($filePath) . EOL;
}
function handleReturnHeader($curl, $header)
{
// either set the response code or return the header
if (preg_match('/^HTTP\/\d+\.\d+ (\d{3})/', $header, $code))
{
http_response_code($code[1]);
}
else
{
header($header);
}
return strlen($header);
}
//
// MAIN
//
// initialize cURL
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RESOLVE, array("$host:$port:$ip"));
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, FALSE);
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $_SERVER['REQUEST_METHOD']);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, FALSE);
curl_setopt($curl, CURLOPT_HEADERFUNCTION, 'handleReturnHeader');
// build the headers
$headers = array();
foreach($_SERVER as $key => $value)
{
// forward all HTTP headers except the content length, which may change
if (preg_match('/^HTTP_/', $key) && $key != 'HTTP_CONTENT_LENGTH')
{
// transform the key names to proper HTTP headers
array_push($headers, str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($key, 5))))) . ': ' . $value);
}
}
// append any post data
if (!empty($_SERVER['CONTENT_TYPE']))
{
if (preg_match('/^multipart\/form-data/', strtolower($_SERVER['CONTENT_TYPE'])))
{
// create a unique delimiter
$delimiter = '-------------' . uniqid();
// build the post data for fields and files
$postData = '';
foreach ($_POST as $name => $content)
{
$postData .= buildPostDataForFields($delimiter, $name, $content);
}
foreach ($_FILES as $name => $content)
{
$postData .= buildPostDataForFiles($delimiter, $name, $content['error'], $content['name'], $content['type'], $content['tmp_name']);
}
$postData .= '--' . $delimiter . '--' . EOL;
// set the content type to multipart with the new delimiter
array_push($headers, 'Content-Type: multipart/form-data; boundary=' . $delimiter);
}
else
{
// simply forward the content and its type
$postData = file_get_contents('php://input');
array_push($headers, 'Content-Type: ' . $_SERVER['CONTENT_TYPE']);
}
// instruct curl to post data if there is any
if ($postData)
{
curl_setopt($curl, CURLOPT_POST, TRUE);
curl_setopt($curl, CURLOPT_SAFE_UPLOAD, TRUE);
curl_setopt($curl, CURLOPT_POSTFIELDS, $postData);
}
}
// set the headers and execute the query
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
curl_exec($curl);
curl_close($curl);
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment