Skip to content

Commit

Permalink
Some renaming and improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
jrabausch committed Apr 20, 2021
1 parent f2b35bd commit 3c4063e
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 70 deletions.
8 changes: 6 additions & 2 deletions bin/proxy
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,9 @@ if (php_sapi_name() !== 'cli') {
require __DIR__ . '/../src/Proxy.php';

$proxy = new Proxy();
$proxy->setUrl($argv[1]);
$proxy->execute();

if (isset($argv[1])) {
$proxy->setUrl($argv[1]);
}

$proxy->execute(true);
172 changes: 107 additions & 65 deletions src/Proxy.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,53 +15,55 @@ class Proxy
protected $method = 'GET';

/** @var string */
protected $data = '';
protected $body = '';

/** @var array<string, string> */
protected $header = [
protected $headers = [
'user-agent' => 'SempertonProxy/1.0.0 (+https://github.com/semperton/proxy)'
];

/** @var array<string, string> */
protected $responseHeader = [];
protected $responseHeaders = [];

/** @var bool */
protected $return = false;
protected $echo = false;

/** @var bool */
protected $isHttp2 = false;
protected $isHttp1 = false;

/**
* @param array<string, string> $header
* @param array<string, string> $headers
*/
public function __construct(string $url = '', string $method = 'GET', string $data = '', array $header = [])
public function __construct(string $url = '', string $method = 'GET', string $body = '', array $headers = [])
{
$this->setUrl($url);
$this->setMethod($method);
$this->setData($data);
$this->setHeader($header);

if (
php_sapi_name() === 'cli' ||
(isset($_SERVER['SERVER_PROTOCOL']) && stripos($_SERVER['SERVER_PROTOCOL'], 'http/2') !== false)
) {
$this->isHttp2 = true;
}
$this->setBody($body);
$this->setHeaders($headers);

$this->isHttp1 = self::getServerHttpVersion() === 1;
}

public static function createFromGlobals(): Proxy
{
if (php_sapi_name() === 'cli') {
return new self();
$proxy = new self();

if (isset($_SERVER['REQUEST_METHOD'])) {
$proxy->setMethod((string)$_SERVER['REQUEST_METHOD']);
}

$url = substr($_SERVER['REQUEST_URI'], strlen($_SERVER['SCRIPT_NAME']) + 1);
$method = $_SERVER['REQUEST_METHOD'];
$data = @file_get_contents('php://input');
$header = getallheaders();
if (in_array($proxy->getMethod(), ['POST', 'PUT', 'PATCH'])) {
$data = file_get_contents('php://input');
$proxy->setBody($data);
}

$headers = function_exists('getallheaders') ? getallheaders() : self::getServerHeaders();
$proxy->setHeaders($headers);

$proxy = new self($url, $method, $data, $header);
$proxy->removeHeader('host');
if (isset($_SERVER['REQUEST_URI'], $_SERVER['SCRIPT_NAME'])) {
$url = substr((string)$_SERVER['REQUEST_URI'], strlen((string)$_SERVER['SCRIPT_NAME']) + 1);
$proxy->setUrl($url)->removeHeader('host');
}

return $proxy;
}
Expand Down Expand Up @@ -96,28 +98,28 @@ public function getMethod(): string
return $this->method;
}

public function setData(string $data): self
public function setBody(string $data): self
{
$this->data = $data;
$this->body = $data;

return $this;
}

public function getData(): string
public function getBody(): string
{
return $this->data;
return $this->body;
}

/**
* @param array<string, string> $header
* @param array<string, string> $headers
*/
public function setHeader(array $header): self
public function setHeaders(array $headers): self
{
foreach ($header as $key => $val) {
foreach ($headers as $key => $val) {

$key = strtolower($key);

$this->header[$key] = $val;
$this->headers[$key] = $val;
}

return $this;
Expand All @@ -127,15 +129,15 @@ public function getHeader(string $name): ?string
{
$name = strtolower($name);

return isset($this->header[$name]) ? $this->header[$name] : null;
return isset($this->headers[$name]) ? $this->headers[$name] : null;
}

/**
* @return array<string, string>
*/
public function getAllHeaders(): array
{
return $this->header;
return $this->headers;
}

/**
Expand All @@ -152,7 +154,7 @@ public function removeHeader($header): self

$key = strtolower($key);

unset($this->header[$key]);
unset($this->headers[$key]);
}

return $this;
Expand All @@ -161,18 +163,17 @@ public function removeHeader($header): self
/**
* @return array|void
*/
public function execute(bool $return = false)
public function execute(bool $echo = false)
{
$this->return = $return;
$this->responseHeader = [];
$this->echo = $echo;
$this->responseHeaders = [];

$ch = curl_init($this->url);

curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $this->method);

if (in_array($this->method, ['POST', 'PUT', 'PATCH'])) {

curl_setopt($ch, CURLOPT_POSTFIELDS, $this->data);
curl_setopt($ch, CURLOPT_POSTFIELDS, $this->body);
}

curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
Expand All @@ -187,12 +188,11 @@ public function execute(bool $return = false)

curl_setopt($ch, CURLOPT_HEADERFUNCTION, [$this, 'onCurlHeader']);

if (!$this->return) {
if ($this->echo) {

curl_setopt($ch, CURLOPT_WRITEFUNCTION, [$this, 'onCurlWrite']);

if (!$this->isHttp2) {

if ($this->isHttp1) {
header('Transfer-Encoding: chunked');
}
}
Expand All @@ -202,21 +202,15 @@ public function execute(bool $return = false)

curl_close($ch);

if (!$this->return) {

if (!$this->isHttp2) {

echo "0\r\n\r\n";
flush();
}

die();
if ($this->echo && $this->isHttp1) {
echo "0\r\n\r\n";
flush();
}

return [

'info' => $responseInfo,
'header' => $this->responseHeader,
'header' => $this->responseHeaders,
'body' => $responseBody
];
}
Expand All @@ -228,10 +222,10 @@ protected function onCurlWrite($ch, string $data): int
{
$length = strlen($data);

if ($this->isHttp2) {
echo $data;
} else {
if ($this->isHttp1) {
echo dechex($length) . "\r\n$data\r\n";
} else {
echo $data;
}

flush();
Expand All @@ -246,23 +240,21 @@ protected function onCurlHeader($ch, string $header): int
// we follow redirects, so we need to reset the headers...
if (stripos($header, 'http') === 0) {

$this->responseHeader = [];
$this->responseHeaders = [];
} else {

if ($this->return) {

if ($this->echo) {
header($header);
} else {
$col = strpos($header, ':');

if ($col) { // not false and > 0

$key = strtolower(substr($header, 0, $col));
$val = substr($header, $col + 1);

$this->responseHeader[trim($key)] = trim($val);
$this->responseHeaders[trim($key)] = trim($val);
}
} else {

header($header);
}
}

Expand All @@ -276,17 +268,67 @@ protected function getHeaderArray(): array
{
$header = [];

foreach ($this->header as $key => $val) {
foreach ($this->headers as $key => $val) {

$header[] = $key . ':' . $val;
}

return $header;
}

protected static function getServerHttpVersion(): int
{
if (isset($_SERVER['SERVER_PROTOCOL'])) {

$proto = (string)$_SERVER['SERVER_PROTOCOL'];
$pos = stripos($proto, 'http/');

if ($pos !== false) {
return (int)substr($proto, $pos + 1, 1);
}
}

return 0;
}

/**
* laminas-diactoros/src/functions/marshal_headers_from_sapi.php
* @return array<string, string>
*/
protected static function getServerHeaders(): array
{
$headers = [];
foreach ($_SERVER as $key => $value) {

$key = (string)$key;

if (0 === strpos($key, 'REDIRECT_')) {
$key = substr($key, 9);

if (array_key_exists($key, $_SERVER)) {
continue;
}
}

if ($value && 0 === strpos($key, 'HTTP_')) {
$name = strtr(strtolower(substr($key, 5)), '_', '-');
$headers[$name] = (string)$value;
continue;
}

if ($value && 0 === strpos($key, 'CONTENT_')) {
$name = 'content-' . strtolower(substr($key, 8));
$headers[$name] = (string)$value;
continue;
}
}

return $headers;
}
}

// allow standalone usage
if (get_included_files()[0] === __FILE__) {

Proxy::createFromGlobals()->execute();
Proxy::createFromGlobals()->execute(true);
}
6 changes: 3 additions & 3 deletions tests/ProxyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,23 @@ final class ProxyTest extends TestCase
public function testHttp(): void
{
$proxy = new Proxy('http://httpbin.org/get');
$response = $proxy->execute(true);
$response = $proxy->execute();

$this->assertEquals('HTTP', $response['info']['scheme']);
}

public function testHttps(): void
{
$proxy = new Proxy('https://httpbin.org/get');
$response = $proxy->execute(true);
$response = $proxy->execute();

$this->assertEquals('HTTPS', $response['info']['scheme']);
}

public function testEncoding(): void
{
$proxy = new Proxy('https://httpbin.org/gzip');
$response = $proxy->execute(true);
$response = $proxy->execute();

$this->assertEquals('gzip', $response['header']['content-encoding']);

Expand Down

0 comments on commit 3c4063e

Please sign in to comment.