Permalink
Browse files

RequestFactory: refactoring

  • Loading branch information...
dg committed Feb 11, 2019
1 parent 840de35 commit d0992eb73f34c0fdf8fb76058262e3222f959158
Showing with 152 additions and 97 deletions.
  1. +152 −97 src/Http/RequestFactory.php
@@ -62,11 +62,34 @@ public function setProxy($proxy)
*/
public function createHttpRequest(): Request
{
// DETECTS URI, base path and script path of the request.
$url = new UrlScript;
$this->identifyServer($url);
$this->identifyPathAndQuery($url);
$this->identifyScriptPath($url);
[$post, $cookies] = $this->identifyGetPostCookie($url);
[$remoteAddr, $remoteHost] = $this->identifyProxy($url);
return new Request(
$url,
null,
$post,
$this->identifyFiles(),
$cookies,
$this->identifyHeaders(),
$this->identifyMethod(),
$remoteAddr,
$remoteHost,
function (): string {
return file_get_contents('php://input');
}
);
}
private function identifyServer(Url $url): void
{
$url->setScheme(!empty($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'off') ? 'https' : 'http');
// host & port
if (
(isset($_SERVER[$tmp = 'HTTP_HOST']) || isset($_SERVER[$tmp = 'SERVER_NAME']))
&& preg_match('#^([a-z0-9_.-]+|\[[a-f0-9:]+\])(:\d+)?\z#i', $_SERVER[$tmp], $pair)
@@ -78,18 +101,26 @@ public function createHttpRequest(): Request
$url->setPort((int) $_SERVER['SERVER_PORT']);
}
}
}
// path & query
private function identifyPathAndQuery(Url $url): void
{
$requestUrl = $_SERVER['REQUEST_URI'] ?? '/';
$requestUrl = preg_replace('#^\w++://[^/]++#', '', $requestUrl);
$requestUrl = Strings::replace($requestUrl, $this->urlFilters['url']);
$tmp = explode('?', $requestUrl, 2);
$path = Url::unescape($tmp[0], '%/?#');
$path = Strings::fixEncoding(Strings::replace($path, $this->urlFilters['path']));
$url->setPath($path);
$url->setQuery($tmp[1] ?? '');
}
// detect script path
private function identifyScriptPath(Url $url): void
{
$path = $url->getPath();
$lpath = strtolower($path);
$script = isset($_SERVER['SCRIPT_NAME']) ? strtolower($_SERVER['SCRIPT_NAME']) : '';
if ($lpath !== $script) {
@@ -98,8 +129,11 @@ public function createHttpRequest(): Request
$path = $i ? substr($path, 0, strrpos($path, '/', $i - strlen($path) - 1) + 1) : '/';
}
$url->setScriptPath($path);
}
// GET, POST, COOKIE
private function identifyGetPostCookie(Url $url): array
{
$useFilter = (!in_array(ini_get('filter.default'), ['', 'unsafe_raw'], true) || ini_get('filter.default_flags'));
$query = $url->getQueryParameters();
@@ -126,26 +160,30 @@ public function createHttpRequest(): Request
}
unset($list, $key, $val, $k, $v);
}
$url->setQuery($query);
return [$post, $cookies];
}
// FILES and create FileUpload objects
private function identifyFiles(): array
{
$reChars = '#^[' . self::CHARS . ']*+\z#u';
$files = [];
$list = [];
if (!empty($_FILES)) {
foreach ($_FILES as $k => $v) {
if (
!is_array($v)
|| !isset($v['name'], $v['type'], $v['size'], $v['tmp_name'], $v['error'])
|| (!$this->binary && is_string($k) && (!preg_match($reChars, $k) || preg_last_error()))
) {
continue;
}
$v['@'] = &$files[$k];
$list[] = $v;
foreach ($_FILES ?? [] as $k => $v) {
if (
!is_array($v)
|| !isset($v['name'], $v['type'], $v['size'], $v['tmp_name'], $v['error'])
|| (!$this->binary && is_string($k) && (!preg_match($reChars, $k) || preg_last_error()))
) {
continue;
}
$v['@'] = &$files[$k];
$list[] = $v;
}
// create FileUpload objects
foreach ($list as &$v) {
if (!isset($v['name'])) {
continue;
@@ -174,23 +212,45 @@ public function createHttpRequest(): Request
];
}
}
return $files;
}
// HEADERS
private function identifyHeaders(): array
{
if (function_exists('apache_request_headers')) {
$headers = apache_request_headers();
} else {
$headers = [];
foreach ($_SERVER as $k => $v) {
if (strncmp($k, 'HTTP_', 5) == 0) {
$k = substr($k, 5);
} elseif (strncmp($k, 'CONTENT_', 8)) {
continue;
}
$headers[strtr($k, '_', '-')] = $v;
return apache_request_headers();
}
$headers = [];
foreach ($_SERVER as $k => $v) {
if (strncmp($k, 'HTTP_', 5) == 0) {
$k = substr($k, 5);
} elseif (strncmp($k, 'CONTENT_', 8)) {
continue;
}
$headers[strtr($k, '_', '-')] = $v;
}
return $headers;
}
private function identifyMethod(): ?string
{
$method = $_SERVER['REQUEST_METHOD'] ?? null;
if (
$method === 'POST'
&& isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])
&& preg_match('#^[A-Z]+\z#', $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])
) {
$method = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'];
}
return $method;
}
private function identifyProxy(Url $url): array
{
$remoteAddr = !empty($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null;
$remoteHost = !empty($_SERVER['REMOTE_HOST']) ? $_SERVER['REMOTE_HOST'] : null;
@@ -199,87 +259,82 @@ public function createHttpRequest(): Request
return Helpers::ipMatch($remoteAddr, $proxy);
});
if ($usingTrustedProxy) {
if (!empty($_SERVER['HTTP_FORWARDED'])) {
$forwardParams = preg_split('/[,;]/', $_SERVER['HTTP_FORWARDED']);
foreach ($forwardParams as $forwardParam) {
[$key, $value] = explode('=', $forwardParam, 2) + [1 => null];
$proxyParams[strtolower(trim($key))][] = trim($value, " \t\"");
}
empty($_SERVER['HTTP_FORWARDED'])
? $this->identifyNonstandardProxy($url, $remoteAddr, $remoteHost)
: $this->identifyForwardedProxy($url, $remoteAddr, $remoteHost);
}
if (isset($proxyParams['for'])) {
$address = $proxyParams['for'][0];
if (strpos($address, '[') === false) { //IPv4
$remoteAddr = explode(':', $address)[0];
} else { //IPv6
$remoteAddr = substr($address, 1, strpos($address, ']') - 1);
}
}
return [$remoteAddr, $remoteHost];
}
if (isset($proxyParams['host']) && count($proxyParams['host']) === 1) {
$host = $proxyParams['host'][0];
$startingDelimiterPosition = strpos($host, '[');
if ($startingDelimiterPosition === false) { //IPv4
$remoteHostArr = explode(':', $host);
$remoteHost = $remoteHostArr[0];
if (isset($remoteHostArr[1])) {
$url->setPort((int) $remoteHostArr[1]);
}
} else { //IPv6
$endingDelimiterPosition = strpos($host, ']');
$remoteHost = substr($host, strpos($host, '[') + 1, $endingDelimiterPosition - 1);
$remoteHostArr = explode(':', substr($host, $endingDelimiterPosition));
if (isset($remoteHostArr[1])) {
$url->setPort((int) $remoteHostArr[1]);
}
}
}
$scheme = (isset($proxyParams['proto']) && count($proxyParams['proto']) === 1) ? $proxyParams['proto'][0] : 'http';
$url->setScheme(strcasecmp($scheme, 'https') === 0 ? 'https' : 'http');
} else {
if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
$url->setScheme(strcasecmp($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') === 0 ? 'https' : 'http');
$url->setPort($url->getScheme() === 'https' ? 443 : 80);
}
private function identifyForwardedProxy(Url $url, &$remoteAddr, &$remoteHost): void
{
$forwardParams = preg_split('/[,;]/', $_SERVER['HTTP_FORWARDED']);
foreach ($forwardParams as $forwardParam) {
[$key, $value] = explode('=', $forwardParam, 2) + [1 => null];
$proxyParams[strtolower(trim($key))][] = trim($value, " \t\"");
}
if (!empty($_SERVER['HTTP_X_FORWARDED_PORT'])) {
$url->setPort((int) $_SERVER['HTTP_X_FORWARDED_PORT']);
}
if (isset($proxyParams['for'])) {
$address = $proxyParams['for'][0];
if (strpos($address, '[') === false) { //IPv4
$remoteAddr = explode(':', $address)[0];
} else { //IPv6
$remoteAddr = substr($address, 1, strpos($address, ']') - 1);
}
}
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$xForwardedForWithoutProxies = array_filter(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']), function (string $ip): bool {
return !array_filter($this->proxies, function (string $proxy) use ($ip): bool {
return filter_var(trim($ip), FILTER_VALIDATE_IP) !== false && Helpers::ipMatch(trim($ip), $proxy);
});
});
$remoteAddr = trim(end($xForwardedForWithoutProxies));
$xForwardedForRealIpKey = key($xForwardedForWithoutProxies);
if (isset($proxyParams['host']) && count($proxyParams['host']) === 1) {
$host = $proxyParams['host'][0];
$startingDelimiterPosition = strpos($host, '[');
if ($startingDelimiterPosition === false) { //IPv4
$remoteHostArr = explode(':', $host);
$remoteHost = $remoteHostArr[0];
if (isset($remoteHostArr[1])) {
$url->setPort((int) $remoteHostArr[1]);
}
if (isset($xForwardedForRealIpKey) && !empty($_SERVER['HTTP_X_FORWARDED_HOST'])) {
$xForwardedHost = explode(',', $_SERVER['HTTP_X_FORWARDED_HOST']);
if (isset($xForwardedHost[$xForwardedForRealIpKey])) {
$remoteHost = trim($xForwardedHost[$xForwardedForRealIpKey]);
}
} else { //IPv6
$endingDelimiterPosition = strpos($host, ']');
$remoteHost = substr($host, strpos($host, '[') + 1, $endingDelimiterPosition - 1);
$remoteHostArr = explode(':', substr($host, $endingDelimiterPosition));
if (isset($remoteHostArr[1])) {
$url->setPort((int) $remoteHostArr[1]);
}
}
}
// method, eg. GET, PUT, ...
$method = $_SERVER['REQUEST_METHOD'] ?? null;
if (
$method === 'POST'
&& isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])
&& preg_match('#^[A-Z]+\z#', $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])
) {
$method = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'];
$scheme = (isset($proxyParams['proto']) && count($proxyParams['proto']) === 1) ? $proxyParams['proto'][0] : 'http';
$url->setScheme(strcasecmp($scheme, 'https') === 0 ? 'https' : 'http');
}
private function identifyNonstandardProxy(Url $url, &$remoteAddr, &$remoteHost): void
{
if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
$url->setScheme(strcasecmp($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') === 0 ? 'https' : 'http');
$url->setPort($url->getScheme() === 'https' ? 443 : 80);
}
// raw body
$rawBodyCallback = function (): string {
return file_get_contents('php://input');
};
if (!empty($_SERVER['HTTP_X_FORWARDED_PORT'])) {
$url->setPort((int) $_SERVER['HTTP_X_FORWARDED_PORT']);
}
return new Request($url, null, $post, $files, $cookies, $headers, $method, $remoteAddr, $remoteHost, $rawBodyCallback);
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$xForwardedForWithoutProxies = array_filter(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']), function (string $ip): bool {
return !array_filter($this->proxies, function (string $proxy) use ($ip): bool {
return filter_var(trim($ip), FILTER_VALIDATE_IP) !== false && Helpers::ipMatch(trim($ip), $proxy);
});
});
$remoteAddr = trim(end($xForwardedForWithoutProxies));
$xForwardedForRealIpKey = key($xForwardedForWithoutProxies);
}
if (isset($xForwardedForRealIpKey) && !empty($_SERVER['HTTP_X_FORWARDED_HOST'])) {
$xForwardedHost = explode(',', $_SERVER['HTTP_X_FORWARDED_HOST']);
if (isset($xForwardedHost[$xForwardedForRealIpKey])) {
$remoteHost = trim($xForwardedHost[$xForwardedForRealIpKey]);
}
}
}
}

0 comments on commit d0992eb

Please sign in to comment.