Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 26 additions & 16 deletions src/Http/RequestFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,11 @@ public function createHttpRequest()
{
// DETECTS URI, base path and script path of the request.
$url = new UrlScript;
$url->setScheme(!empty($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'off') ? 'https' : 'http');
$url->setScheme(!empty($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'off') !== 0 ? 'https' : 'http');
$url->setUser(isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : '');
$url->setPassword(isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : '');

// host & port
// host and 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)
) {
Expand All @@ -78,7 +78,7 @@ public function createHttpRequest()
}
}

// path & query
// path and query
$requestUrl = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/';
$requestUrl = Strings::replace($requestUrl, $this->urlFilters['url']);
$tmp = explode('?', $requestUrl, 2);
Expand Down Expand Up @@ -185,24 +185,34 @@ public function createHttpRequest()
}
}

$remoteAddr = !empty($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : NULL;
$remoteHost = !empty($_SERVER['REMOTE_HOST']) ? $_SERVER['REMOTE_HOST'] : NULL;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why change isset to !empty? Isset worked OK for years, is faster to execute, can be in PHP 7 rewritten with ?? and does not consider 0 as empty.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because I don't think that having an empty host or address makes sense. Is that wrong assumption?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having invalid value like '0' or '1' here makes no sense either and it's not checked anyway, maybe it could be.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's true but I guess that we don't want to check webserver settings there. I just though that this is a little bit better (to ignore empty header rather the somehow try to use it).


$remoteAddr = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : NULL;
$remoteHost = isset($_SERVER['REMOTE_HOST']) ? $_SERVER['REMOTE_HOST'] : NULL;
// use real client address and host if trusted proxy is used
$usingTrustedProxy = $remoteAddr !== NULL
&& (bool) array_filter($this->proxies, function ($proxy) use ($remoteAddr) {
return Helpers::ipMatch($remoteAddr, $proxy);
});

// proxy
foreach ($this->proxies as $proxy) {
if (Helpers::ipMatch($remoteAddr, $proxy)) {
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$remoteAddr = trim(current(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])));
}
if (isset($_SERVER['HTTP_X_FORWARDED_HOST'])) {
$remoteHost = trim(current(explode(',', $_SERVER['HTTP_X_FORWARDED_HOST'])));
}
break;
if ($usingTrustedProxy) {
if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
$url->setScheme(strcasecmp($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') === 0 ? 'https' : 'http');
}

if (!empty($_SERVER['HTTP_X_FORWARDED_PORT'])) {
$url->setPort((int) $_SERVER['HTTP_X_FORWARDED_PORT']);
}
}

if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$remoteAddr = trim(current(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])));
}

if (!empty($_SERVER['HTTP_X_FORWARDED_HOST'])) {
$remoteHost = trim(current(explode(',', $_SERVER['HTTP_X_FORWARDED_HOST'])));
}
}

// method, eg. GET, PUT, ...
$method = isset($_SERVER['REQUEST_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'])
Expand Down
91 changes: 91 additions & 0 deletions tests/Http/RequestFactory.port.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?php

use Tester\Assert;

require __DIR__ . '/../bootstrap.php';

/**
* Test of Nette\Http\RequestFactory port detection
*/
class RequestFactoryPortTest extends Tester\TestCase
{

/**
* @dataProvider providerCreateHttpRequest
*
* @param string
* @param array
*/
public function testCreateHttpRequest($expectedPort, $server)
{
$_SERVER = $server;

$factory = new Nette\Http\RequestFactory;
Assert::same($expectedPort, $factory->createHttpRequest()->getUrl()->getPort());
}

/**
* @return array
*/
public function providerCreateHttpRequest()
{
return [
[80, []],
[8080, ['HTTP_HOST' => 'localhost:8080']],
[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']],
[8080, ['SERVER_NAME' => 'localhost', 'SERVER_PORT' => '8080']],

[80, ['HTTP_X_FORWARDED_PORT' => '8080']],
[8080, ['HTTP_HOST' => 'localhost:8080', 'HTTP_X_FORWARDED_PORT' => '666']],
[8080, ['SERVER_NAME' => 'localhost:8080', 'HTTP_X_FORWARDED_PORT' => '666']],
[8080, ['HTTP_HOST' => 'localhost:8080', 'SERVER_PORT' => '80', 'HTTP_X_FORWARDED_PORT' => '666']],
[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']],
[8080, ['SERVER_NAME' => 'localhost', 'SERVER_PORT' => '8080', 'HTTP_X_FORWARDED_PORT' => '666']],
[44443, ['HTTPS' => 'on', 'SERVER_NAME' => 'localhost:44443', 'HTTP_X_FORWARDED_PORT' => '666']],
];
}

/**
* @dataProvider providerCreateHttpRequestWithTrustedProxy
*
* @param string
* @param array
*/
public function testCreateHttpRequestWithTrustedProxy($expectedPort, $server)
{
$_SERVER = array_merge(['REMOTE_ADDR' => '10.0.0.1'], $server);

$factory = new Nette\Http\RequestFactory;
$factory->setProxy(['10.0.0.1']);
Assert::same($expectedPort, $factory->createHttpRequest()->getUrl()->getPort());
}

/**
* @return array
*/
public function providerCreateHttpRequestWithTrustedProxy()
{
return [
[8080, ['HTTP_X_FORWARDED_PORT' => '8080']],
[8080, ['HTTP_HOST' => 'localhost:666', 'HTTP_X_FORWARDED_PORT' => '8080']],
[8080, ['SERVER_NAME' => 'localhost:666', 'HTTP_X_FORWARDED_PORT' => '8080']],
[8080, ['HTTP_HOST' => 'localhost:666', 'SERVER_PORT' => '80', 'HTTP_X_FORWARDED_PORT' => '8080']],
[8080, ['SERVER_NAME' => 'localhost:666', 'SERVER_PORT' => '80', 'HTTP_X_FORWARDED_PORT' => '8080']],
[8080, ['HTTP_HOST' => 'localhost', 'HTTP_X_FORWARDED_PORT' => '8080']],
[8080, ['SERVER_NAME' => 'localhost', 'HTTP_X_FORWARDED_PORT' => '8080']],
[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']],
];
}

}

$test = new RequestFactoryPortTest();
$test->run();
85 changes: 85 additions & 0 deletions tests/Http/RequestFactory.scheme.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php

use Tester\Assert;

require __DIR__ . '/../bootstrap.php';

/**
* Test of Nette\Http\RequestFactory schema detection
*/
class RequestFactorySchemeTest extends Tester\TestCase
{

/**
* @covers RequestFactory::getScheme
* @dataProvider providerCreateHttpRequest
*
* @param string
* @param array
*/
public function testCreateHttpRequest($expectedScheme, $server)
{
$_SERVER = $server;

$factory = new Nette\Http\RequestFactory;
Assert::same($expectedScheme, $factory->createHttpRequest()->getUrl()->getScheme());
}

/**
* @return array
*/
public function providerCreateHttpRequest()
{
return [
['http', []],
['http', ['HTTPS' => '']],
['http', ['HTTPS' => 'off']],
['http', ['HTTP_X_FORWARDED_PROTO' => 'https']],
['http', ['HTTP_X_FORWARDED_PORT' => '443']],
['http', ['HTTP_X_FORWARDED_PROTO' => 'https', 'HTTP_X_FORWARDED_PORT' => '443']],

['https', ['HTTPS' => 'on']],
['https', ['HTTPS' => 'anything']],
['https', ['HTTPS' => 'on', 'HTTP_X_FORWARDED_PROTO' => 'http']],
['https', ['HTTPS' => 'on', 'HTTP_X_FORWARDED_PORT' => '80']],
['https', ['HTTPS' => 'on', 'HTTP_X_FORWARDED_PROTO' => 'http', 'HTTP_X_FORWARDED_PORT' => '80']],
];
}

/**
* @covers RequestFactory::getScheme
* @dataProvider providerCreateHttpRequestWithTrustedProxy
*
* @param string
* @param array
*/
public function testCreateHttpRequestWithTrustedProxy($expectedScheme, $server)
{
$_SERVER = array_merge(['REMOTE_ADDR' => '10.0.0.1'], $server);

$factory = new Nette\Http\RequestFactory;
$factory->setProxy(['10.0.0.1']);
Assert::same($expectedScheme, $factory->createHttpRequest()->getUrl()->getScheme());
}

/**
* @return array
*/
public function providerCreateHttpRequestWithTrustedProxy()
{
return [
['http', ['HTTP_X_FORWARDED_PROTO' => 'http']],
['http', ['HTTPS' => 'on', 'HTTP_X_FORWARDED_PROTO' => 'http']],
['http', ['HTTPS' => 'on', 'HTTP_X_FORWARDED_PROTO' => 'something-unexpected']],
['http', ['HTTPS' => 'on', 'HTTP_X_FORWARDED_PROTO' => 'http', 'HTTP_X_FORWARDED_PORT' => '443']],

['https', ['HTTP_X_FORWARDED_PROTO' => 'https']],
['https', ['HTTPS' => 'off', 'HTTP_X_FORWARDED_PROTO' => 'https']],
['https', ['HTTPS' => 'off', 'HTTP_X_FORWARDED_PROTO' => 'https', 'HTTP_X_FORWARDED_PORT' => '80']],
];
}

}

$test = new RequestFactorySchemeTest();
$test->run();