diff --git a/src/Bridges/HttpDI/SessionExtension.php b/src/Bridges/HttpDI/SessionExtension.php index f3d13b47..ed9fdf91 100644 --- a/src/Bridges/HttpDI/SessionExtension.php +++ b/src/Bridges/HttpDI/SessionExtension.php @@ -91,6 +91,10 @@ public function loadConfiguration() $options['autoStart'] = false; } + if ($config->readAndClose === null) { + unset($options['readAndClose']); + } + if (!empty($options)) { $session->addSetup('setOptions', [$options]); } diff --git a/src/Http/RequestFactory.php b/src/Http/RequestFactory.php index fe406289..75a8d5a3 100644 --- a/src/Http/RequestFactory.php +++ b/src/Http/RequestFactory.php @@ -10,6 +10,7 @@ namespace Nette\Http; use Nette; +use Nette\Utils\Arrays; use Nette\Utils\Strings; @@ -94,7 +95,7 @@ private function getServer(Url $url): void $url->setHost(rtrim(strtolower($pair[1]), '.')); if (isset($pair[2])) { $url->setPort((int) substr($pair[2], 1)); - } elseif (isset($_SERVER['SERVER_PORT'])) { + } elseif ($tmp === 'SERVER_NAME' && isset($_SERVER['SERVER_PORT'])) { $url->setPort((int) $_SERVER['SERVER_PORT']); } } @@ -145,7 +146,7 @@ private function getScriptPath(Url $url): string private function getGetPostCookie(Url $url): array { - $useFilter = (!in_array(ini_get('filter.default'), ['', 'unsafe_raw'], true) || ini_get('filter.default_flags')); + $useFilter = (!in_array((string) ini_get('filter.default'), ['', 'unsafe_raw'], true) || ini_get('filter.default_flags')); $query = $url->getQueryParameters(); $post = $useFilter @@ -280,25 +281,26 @@ private function getClient(Url $url): array $remoteAddr = !empty($_SERVER['REMOTE_ADDR']) ? trim($_SERVER['REMOTE_ADDR'], '[]') // workaround for PHP 7.3.0 : null; - $remoteHost = !empty($_SERVER['REMOTE_HOST']) - ? $_SERVER['REMOTE_HOST'] - : null; // use real client address and host if trusted proxy is used - $usingTrustedProxy = $remoteAddr && array_filter($this->proxies, function (string $proxy) use ($remoteAddr): bool { + $usingTrustedProxy = $remoteAddr && Arrays::some($this->proxies, function (string $proxy) use ($remoteAddr): bool { return Helpers::ipMatch($remoteAddr, $proxy); }); if ($usingTrustedProxy) { - empty($_SERVER['HTTP_FORWARDED']) - ? $this->useNonstandardProxy($url, $remoteAddr, $remoteHost) - : $this->useForwardedProxy($url, $remoteAddr, $remoteHost); + $remoteHost = null; + $remoteAddr = empty($_SERVER['HTTP_FORWARDED']) + ? $this->useNonstandardProxy($url) + : $this->useForwardedProxy($url); + + } else { + $remoteHost = !empty($_SERVER['REMOTE_HOST']) ? $_SERVER['REMOTE_HOST'] : null; } return [$remoteAddr, $remoteHost]; } - private function useForwardedProxy(Url $url, &$remoteAddr, &$remoteHost): void + private function useForwardedProxy(Url $url): ?string { $forwardParams = preg_split('/[,;]/', $_SERVER['HTTP_FORWARDED']); foreach ($forwardParams as $forwardParam) { @@ -313,35 +315,34 @@ private function useForwardedProxy(Url $url, &$remoteAddr, &$remoteHost): void : substr($address, 1, strpos($address, ']') - 1); // IPv6 } + if (isset($proxyParams['proto']) && count($proxyParams['proto']) === 1) { + $url->setScheme(strcasecmp($proxyParams['proto'][0], 'https') === 0 ? 'https' : 'http'); + $url->setPort($url->getScheme() === 'https' ? 443 : 80); + } + 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]; - $url->setHost($remoteHost); - if (isset($remoteHostArr[1])) { - $url->setPort((int) $remoteHostArr[1]); + $pair = explode(':', $host); + $url->setHost($pair[0]); + if (isset($pair[1])) { + $url->setPort((int) $pair[1]); } } else { //IPv6 $endingDelimiterPosition = strpos($host, ']'); - $remoteHost = substr($host, strpos($host, '[') + 1, $endingDelimiterPosition - 1); - $url->setHost($remoteHost); - $remoteHostArr = explode(':', substr($host, $endingDelimiterPosition)); - if (isset($remoteHostArr[1])) { - $url->setPort((int) $remoteHostArr[1]); + $url->setHost(substr($host, strpos($host, '[') + 1, $endingDelimiterPosition - 1)); + $pair = explode(':', substr($host, $endingDelimiterPosition)); + if (isset($pair[1])) { + $url->setPort((int) $pair[1]); } } } - - $scheme = (isset($proxyParams['proto']) && count($proxyParams['proto']) === 1) - ? $proxyParams['proto'][0] - : 'http'; - $url->setScheme(strcasecmp($scheme, 'https') === 0 ? 'https' : 'http'); + return $remoteAddr ?? null; } - private function useNonstandardProxy(Url $url, &$remoteAddr, &$remoteHost): void + private function useNonstandardProxy(Url $url): ?string { if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO'])) { $url->setScheme(strcasecmp($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') === 0 ? 'https' : 'http'); @@ -354,9 +355,10 @@ private function useNonstandardProxy(Url $url, &$remoteAddr, &$remoteHost): void 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); - }); + return filter_var(trim($ip), FILTER_VALIDATE_IP) === false || + !Arrays::some($this->proxies, function (string $proxy) use ($ip): bool { + return Helpers::ipMatch(trim($ip), $proxy); + }); }); if ($xForwardedForWithoutProxies) { $remoteAddr = trim(end($xForwardedForWithoutProxies)); @@ -367,10 +369,11 @@ private function useNonstandardProxy(Url $url, &$remoteAddr, &$remoteHost): void if (isset($xForwardedForRealIpKey) && !empty($_SERVER['HTTP_X_FORWARDED_HOST'])) { $xForwardedHost = explode(',', $_SERVER['HTTP_X_FORWARDED_HOST']); if (isset($xForwardedHost[$xForwardedForRealIpKey])) { - $remoteHost = trim($xForwardedHost[$xForwardedForRealIpKey]); - $url->setHost($remoteHost); + $url->setHost(trim($xForwardedHost[$xForwardedForRealIpKey])); } } + + return $remoteAddr ?? null; } diff --git a/src/Http/Session.php b/src/Http/Session.php index 87536242..a591f736 100644 --- a/src/Http/Session.php +++ b/src/Http/Session.php @@ -405,7 +405,7 @@ public function setOptions(array $options) $normalized[$normKey] = $value; } - if (array_key_exists('read_and_close', $normalized)) { + if (isset($normalized['read_and_close'])) { if (session_status() === PHP_SESSION_ACTIVE) { throw new Nette\InvalidStateException('Cannot configure "read_and_close" for already started session.'); } diff --git a/src/Http/Url.php b/src/Http/Url.php index a80e1ec6..584531a9 100644 --- a/src/Http/Url.php +++ b/src/Http/Url.php @@ -351,9 +351,11 @@ public function isEqual($url): bool ksort($query); $query2 = $this->query; ksort($query2); + $host = rtrim($url->host, '.'); + $host2 = rtrim($this->host, '.'); return $url->scheme === $this->scheme - && (!strcasecmp($url->host, $this->host) - || self::idnHostToUnicode($url->host) === self::idnHostToUnicode($this->host)) + && (!strcasecmp($host, $host2) + || self::idnHostToUnicode($host) === self::idnHostToUnicode($host2)) && $url->getPort() === $this->getPort() && $url->user === $this->user && $url->password === $this->password @@ -375,6 +377,7 @@ public function canonicalize() function (array $m): string { return rawurlencode($m[0]); }, self::unescape($this->path, '%/') ); + $this->host = rtrim($this->host, '.'); $this->host = self::idnHostToUnicode(strtolower($this->host)); return $this; } diff --git a/tests/Http/RequestFactory.port.phpt b/tests/Http/RequestFactory.port.phpt index 6223f189..9dbeaa65 100644 --- a/tests/Http/RequestFactory.port.phpt +++ b/tests/Http/RequestFactory.port.phpt @@ -31,7 +31,7 @@ class RequestFactoryPortTest extends Tester\TestCase [8080, ['SERVER_NAME' => 'localhost:8080']], [8080, ['HTTP_HOST' => 'localhost:8080', 'SERVER_PORT' => '666']], [8080, ['SERVER_NAME' => 'localhost:8080', 'SERVER_PORT' => '666']], - [8080, ['HTTP_HOST' => 'localhost', 'SERVER_PORT' => '8080']], + [80, ['HTTP_HOST' => 'localhost', 'SERVER_PORT' => '8080']], [8080, ['SERVER_NAME' => 'localhost', 'SERVER_PORT' => '8080']], [80, ['HTTP_X_FORWARDED_PORT' => '8080']], @@ -41,7 +41,7 @@ class RequestFactoryPortTest extends Tester\TestCase [8080, ['SERVER_NAME' => 'localhost:8080', 'SERVER_PORT' => '80', 'HTTP_X_FORWARDED_PORT' => '666']], [80, ['HTTP_HOST' => 'localhost', 'HTTP_X_FORWARDED_PORT' => '666']], [80, ['SERVER_NAME' => 'localhost', 'HTTP_X_FORWARDED_PORT' => '666']], - [8080, ['HTTP_HOST' => 'localhost', 'SERVER_PORT' => '8080', 'HTTP_X_FORWARDED_PORT' => '666']], + [80, ['HTTP_HOST' => 'localhost', 'SERVER_PORT' => '8080', 'HTTP_X_FORWARDED_PORT' => '666']], [8080, ['SERVER_NAME' => 'localhost', 'SERVER_PORT' => '8080', 'HTTP_X_FORWARDED_PORT' => '666']], [44443, ['HTTPS' => 'on', 'SERVER_NAME' => 'localhost:44443', 'HTTP_X_FORWARDED_PORT' => '666']], ]; @@ -74,6 +74,10 @@ class RequestFactoryPortTest extends Tester\TestCase [8080, ['HTTP_HOST' => 'localhost', 'SERVER_PORT' => '666', 'HTTP_X_FORWARDED_PORT' => '8080']], [8080, ['SERVER_NAME' => 'localhost', 'SERVER_PORT' => '666', 'HTTP_X_FORWARDED_PORT' => '8080']], [44443, ['HTTPS' => 'on', 'SERVER_NAME' => 'localhost:666', 'HTTP_X_FORWARDED_PORT' => '44443']], + [443, ['HTTP_FORWARDED' => 'for=192.168.1.1;host=example.com;proto=https']], + [44443, ['HTTP_FORWARDED' => 'for=192.168.1.1;host=example.com:44443;proto=https']], + [80, ['HTTP_FORWARDED' => 'for=192.168.1.1;host=example.com;proto=http']], + [8080, ['HTTP_FORWARDED' => 'for=192.168.1.1;host=example.com:8080;proto=http']], ]; } } diff --git a/tests/Http/RequestFactory.proxy.forwarded.phpt b/tests/Http/RequestFactory.proxy.forwarded.phpt index 0d8b81d4..c1a92659 100644 --- a/tests/Http/RequestFactory.proxy.forwarded.phpt +++ b/tests/Http/RequestFactory.proxy.forwarded.phpt @@ -25,7 +25,7 @@ test('', function () { $factory->setProxy('127.0.0.1/8'); Assert::same('23.75.45.200', $factory->fromGlobals()->getRemoteAddress()); - Assert::same('192.168.0.1', $factory->fromGlobals()->getRemoteHost()); + Assert::same('a23-75-45-200.deploy.static.akamaitechnologies.com', $factory->fromGlobals()->getRemoteHost()); $url = $factory->fromGlobals()->getUrl(); Assert::same('http', $url->getScheme()); @@ -43,7 +43,7 @@ test('', function () { $factory->setProxy('127.0.0.3'); Assert::same('23.75.45.200', $factory->fromGlobals()->getRemoteAddress()); - Assert::same('192.168.0.1', $factory->fromGlobals()->getRemoteHost()); + Assert::same('a23-75-45-200.deploy.static.akamaitechnologies.com', $factory->fromGlobals()->getRemoteHost()); $url = $factory->fromGlobals()->getUrl(); Assert::same(8080, $url->getPort()); @@ -62,7 +62,7 @@ test('', function () { $factory->setProxy('127.0.0.3'); Assert::same('2001:db8:cafe::17', $factory->fromGlobals()->getRemoteAddress()); - Assert::same('2001:db8:cafe::18', $factory->fromGlobals()->getRemoteHost()); + Assert::same('2001:db8:cafe::17', $factory->fromGlobals()->getRemoteHost()); $url = $factory->fromGlobals()->getUrl(); Assert::same('2001:db8:cafe::18', $url->getHost()); @@ -79,7 +79,7 @@ test('', function () { $factory->setProxy('127.0.0.3'); Assert::same('2001:db8:cafe::17', $factory->fromGlobals()->getRemoteAddress()); - Assert::same('2001:db8:cafe::18', $factory->fromGlobals()->getRemoteHost()); + Assert::same('2001:db8:cafe::17', $factory->fromGlobals()->getRemoteHost()); $url = $factory->fromGlobals()->getUrl(); Assert::same(47832, $url->getPort()); diff --git a/tests/Http/RequestFactory.proxy.x-forwarded.phpt b/tests/Http/RequestFactory.proxy.x-forwarded.phpt index a2efaeb2..d44753cf 100644 --- a/tests/Http/RequestFactory.proxy.x-forwarded.phpt +++ b/tests/Http/RequestFactory.proxy.x-forwarded.phpt @@ -27,7 +27,7 @@ test('', function () { $factory->setProxy('127.0.0.1/8'); Assert::same('23.75.45.200', $factory->fromGlobals()->getRemoteAddress()); - Assert::same('otherhost', $factory->fromGlobals()->getRemoteHost()); + Assert::same('a23-75-45-200.deploy.static.akamaitechnologies.com', $factory->fromGlobals()->getRemoteHost()); $url = $factory->fromGlobals()->getUrl(); Assert::same('otherhost', $url->getHost()); @@ -44,12 +44,11 @@ test('', function () { $factory = new RequestFactory; $factory->setProxy('10.0.0.0/24'); Assert::same('172.16.0.1', $factory->fromGlobals()->getRemoteAddress()); - Assert::same('real', $factory->fromGlobals()->getRemoteHost()); + Assert::same('172.16.0.1', $factory->fromGlobals()->getRemoteHost()); + Assert::same('real', $factory->fromGlobals()->getUrl()->getHost()); $factory->setProxy(['10.0.0.1', '10.0.0.2']); Assert::same('172.16.0.1', $factory->fromGlobals()->getRemoteAddress()); - Assert::same('real', $factory->fromGlobals()->getRemoteHost()); - - $url = $factory->fromGlobals()->getUrl(); - Assert::same('real', $url->getHost()); + Assert::same('172.16.0.1', $factory->fromGlobals()->getRemoteHost()); + Assert::same('real', $factory->fromGlobals()->getUrl()->getHost()); }); diff --git a/tests/Http/Url.isEqual().phpt b/tests/Http/Url.isEqual().phpt index 73d80323..73a51df6 100644 --- a/tests/Http/Url.isEqual().phpt +++ b/tests/Http/Url.isEqual().phpt @@ -56,3 +56,10 @@ Assert::true($url->isEqual('http://example.com')); $url = new Url('https://xn--tst-qla.de/'); Assert::true($url->isEqual('https://täst.de/')); + + +// dots at end +$url = new Url('https://google.com./abcd'); +Assert::true($url->isEqual('https://google.com/abcd')); +$url = new Url('https://xn--tst-qla.de./'); +Assert::true($url->isEqual('https://täst.de/'));