Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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.
<?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
You can’t perform that action at this time.