Skip to content

Commit

Permalink
RequestFactory: correctly detects scheme and port if the server is be…
Browse files Browse the repository at this point in the history
…hind a trusted proxy [Closes #81][Closes #4]
  • Loading branch information
Jakub Chabek authored and dg committed Feb 8, 2016
1 parent e0a1de7 commit dadd667
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 13 deletions.
35 changes: 22 additions & 13 deletions src/Http/RequestFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -185,24 +185,33 @@ public function createHttpRequest()
}
}

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

$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 && 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(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0]);
}

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

// 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
85 changes: 85 additions & 0 deletions tests/Http/RequestFactory.port.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 port detection
*/
class RequestFactoryPortTest extends Tester\TestCase
{

/**
* @dataProvider providerCreateHttpRequest
*/
public function testCreateHttpRequest($expectedPort, array $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
*/
public function testCreateHttpRequestWithTrustedProxy($expectedPort, array $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();
79 changes: 79 additions & 0 deletions tests/Http/RequestFactory.scheme.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?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
*/
public function testCreateHttpRequest($expectedScheme, array $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
*/
public function testCreateHttpRequestWithTrustedProxy($expectedScheme, array $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();

0 comments on commit dadd667

Please sign in to comment.