Last active
December 13, 2022 17:52
-
-
Save ariankordi/e987c6c47500ce8583ed6afc869953fb to your computer and use it in GitHub Desktop.
Reverse proxy to a UNIX socket with PHP, for when you are running Go web apps on cPanel servers or something like that LOL (complete with header and multipart POST form parsing!) Doesn't work with WebSocket though.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// This works best on Apache, and if you have enable_post_data_reading off. | |
// Set "php_flag enable_post_data_reading off" in your htaccess. It'll avoid having to parse multipart forms. | |
// enter the sock to connect to here | |
const SOCK_TO_CONNECT_TO = './run.sock'; | |
// Close the session right now, because it might make everything faster, and we don't know how long the response will last for. | |
session_write_close(); | |
$sock = @stream_socket_client('unix://' . SOCK_TO_CONNECT_TO, $errno, $errstr); | |
if(!$sock) { | |
http_response_code(502); | |
header('Content-Type: text/plain'); | |
exit('errno ' . $errno . ': ' . $errstr); | |
} | |
// make raw http request into $request | |
$request = $_SERVER['REQUEST_METHOD'] . ' ' . $_SERVER['REQUEST_URI'] . ' ' . $_SERVER['SERVER_PROTOCOL'] . "\r\n"; | |
$request .= "Connection: close\r\n"; | |
$request .= 'X-Forwarded-For: ' . $_SERVER['REMOTE_ADDR'] . "\r\n"; | |
$request .= 'X-Forwarded-Proto: ' . (!empty($_SERVER['HTTPS']) ? 'https' : 'http') . "\r\n"; | |
fwrite($sock, $request); | |
if(function_exists('getallheaders')) { | |
// getallheaders exists with apache but not some servers | |
foreach(getallheaders() as $name => $value) { | |
if($name === 'Connection' || $name === 'X-Https') { | |
continue; | |
} | |
$header = $name . ': ' . $value . "\r\n"; | |
fwrite($sock, $header); | |
} | |
} else { | |
// polyfill if there is no getallheaders | |
foreach($_SERVER as $name => $value) { | |
if(substr($name, 0, 5) === 'HTTP_') { | |
// strip http_ part, replace underscores with dashes | |
$name = str_replace('_', '-', substr($name, 5)); | |
if($name === 'CONNECTION') { | |
continue; | |
} | |
$header = $name . ': ' . $value . "\r\n"; | |
fwrite($sock, $header); | |
} | |
} | |
} | |
fwrite($sock, "\r\n"); | |
// read POST data | |
$input = fopen('php://input', 'r'); | |
while(!feof($input)) { | |
$data = fgets($input, 1024); | |
if(empty($data) && (!empty($_POST) || !empty($_FILES))) { | |
// polyfill for if enable_post_data_reading is on, start parsing $_POST to a "raw" post data equivalent | |
// see what content type is there (there should be a content type at this point, if php is parsing post data) | |
// actually, i found out that only multipart/form-data should be parsed here? | |
// also see if it is long enough to have a boundary | |
if(substr($_SERVER['HTTP_CONTENT_TYPE'], 0, 19) === 'multipart/form-data' && strlen($_SERVER['HTTP_CONTENT_TYPE']) > 30) { | |
$boundary = '--' . substr($_SERVER['HTTP_CONTENT_TYPE'], 30); | |
foreach($_POST as $name => $value) { | |
// key/values... | |
$part = $boundary . "\r\n" . 'Content-Disposition: form-data; name="' . $name . '"' . "\r\n\r\n" . $value . "\r\n"; | |
fwrite($sock, $part); | |
} | |
// files | |
$out = fopen('php://stdout', 'w'); | |
foreach($_FILES as $name => $value) { | |
$part = $boundary . "\r\n" . 'Content-Disposition: form-data; name="' . $name . '"; filename="' . $value['name'] . '"' . "\r\nContent-Type: " . $value['type'] . "\r\n\r\n"; | |
fwrite($sock, $part); | |
$file = fopen($value['tmp_name'], 'r'); | |
// $file is sometimes falsey but i can't reproduce it | |
if($file) { | |
while(!feof($file)) { | |
$data = fgets($file, 1024); | |
fwrite($sock, $data); | |
} | |
} | |
fwrite($sock, "\r\n"); | |
fclose($file); | |
} | |
$part = $boundary . '--'; | |
fwrite($sock, $part); | |
} | |
break; | |
} | |
fwrite($sock, $data); | |
} | |
fclose($input); | |
fwrite($sock, "\r\n"); | |
// start parsing response | |
$is_done_with_headers = false; | |
while(!feof($sock)) { | |
if(!$is_done_with_headers) { | |
$header = fgets($sock, 1024); | |
if($header === "\r\n") { | |
$is_done_with_headers = true; | |
continue; | |
} | |
header($header); | |
continue; | |
} | |
echo fgets($sock, 1024); | |
} | |
fclose($sock); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Brilliant!