Skip to content

Commit

Permalink
NEW Director::host() to determine host name of site
Browse files Browse the repository at this point in the history
  • Loading branch information
dhensby committed Jan 31, 2017
1 parent 6e096f6 commit 0a9ec3f
Show file tree
Hide file tree
Showing 10 changed files with 113 additions and 138 deletions.
15 changes: 15 additions & 0 deletions cli-script.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,21 @@
die();
}

// We update the $_SERVER variable to contain data consistent with the rest of the application.
$_SERVER = array_merge(array(
'SERVER_PROTOCOL' => 'HTTP/1.1',
'HTTP_ACCEPT' => 'text/plain;q=0.5',
'HTTP_ACCEPT_LANGUAGE' => '*;q=0.5',
'HTTP_ACCEPT_ENCODING' => '',
'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1;q=0.5',
'SERVER_SIGNATURE' => 'Command-line PHP/' . phpversion(),
'SERVER_SOFTWARE' => 'PHP/' . phpversion(),
'SERVER_ADDR' => '127.0.0.1',
'REMOTE_ADDR' => '127.0.0.1',
'REQUEST_METHOD' => 'GET',
'HTTP_USER_AGENT' => 'CLI',
), $_SERVER);

/**
* Identify the cli-script.php file and change to its container directory, so that require_once() works
*/
Expand Down
1 change: 1 addition & 0 deletions docs/en/00_Getting_Started/03_Environment_Management.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,4 @@ SilverStripe core environment variables are listed here, though you're free to d
| `SS_ALLOWED_HOSTS` | A comma deliminated list of hostnames the site is allowed to respond to |
| `SS_MANIFESTCACHE` | The manifest cache to use (defaults to file based caching) |
| `SS_IGNORE_DOT_ENV` | If set the .env file will be ignored. This is good for live to mitigate any performance implications of loading the .env file |
| `SS_HOST` | The hostname to use when it isn't determinable by other means (eg: for CLI commands) |
1 change: 1 addition & 0 deletions docs/en/04_Changelogs/4.0.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -1352,3 +1352,4 @@ logic early in the bootstrap, this is best placed in the `_config.php` files.
* Removed support for _ss_environment.php in favour of .env and first class environment variables
* Environment variables now can be set in `.env` file placed in webroot or one level above
* Environment variables will be read from the environment as well
* `$_FILE_TO_URL_MAPPING` has been removed and replaced with using `Director.alternate_host` or `SS_HOST` env var
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,7 @@ Example `.env`:
SS_DEFAULT_ADMIN_PASSWORD="password"

# Basic CLI hostname
global $_FILE_TO_URL_MAPPING;
$_FILE_TO_URL_MAPPING[__DIR__] = "http://localhost";
SS_HOST="localhost";


You will also need to be assigned the following permissions. Contact one of the SS staff from
Expand Down
3 changes: 2 additions & 1 deletion main.php
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,9 @@
header($_SERVER['SERVER_PROTOCOL'] . " 500 Server Error");
die('SilverStripe Framework requires a $databaseConfig defined.');
}
$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME'];
$s = (isset($_SERVER['SSL']) || (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off')) ? 's' : '';
$installURL = "http$s://" . $_SERVER['HTTP_HOST'] . BASE_URL . '/install.php';
$installURL = "http$s://" . $host . BASE_URL . '/install.php';

// The above dirname() will equate to "\" on Windows when installing directly from http://localhost (not using
// a sub-directory), this really messes things up in some browsers. Let's get rid of the backslashes
Expand Down
110 changes: 75 additions & 35 deletions src/Control/Director.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@ class Director implements TemplateGlobalProvider
*/
private static $alternate_protocol;

/**
* @config
*
* @var string
*/
private static $alternate_host;

/**
* @config
*
Expand Down Expand Up @@ -139,6 +146,15 @@ class Director implements TemplateGlobalProvider
*/
public static function direct($url, DataModel $model)
{
// check allowed hosts
if (getenv('SS_ALLOWED_HOSTS') && !Director::is_cli()) {
$all_allowed_hosts = explode(',', getenv('SS_ALLOWED_HOSTS'));
if (!in_array(static::host(), $all_allowed_hosts)) {
throw new HTTPResponse_Exception('Invalid Host', 400);
}
}


// Validate $_FILES array before merging it with $_POST
foreach ($_FILES as $k => $v) {
if (is_array($v['tmp_name'])) {
Expand Down Expand Up @@ -552,38 +568,70 @@ public static function absoluteURL($url, $relativeParent = self::BASE)
}

/**
* Returns the domain part of the URL 'http://www.mysite.com'. Returns FALSE is this environment
* variable isn't set.
* A helper to determine the current hostname used to access the site.
* The following are used to determine the host (in order)
* - Director.alternate_host
* - Director.alternate_base_url (if it contains a domain name)
* - Trusted proxy headers
* - HTTP Host header
* - SS_HOST env var
* - SERVER_NAME
* - gethostname()
*
* @return bool|string
* @return string
*/
public static function protocolAndHost()
public static function host()
{
$alternate = Config::inst()->get('SilverStripe\\Control\\Director', 'alternate_base_url');
if ($alternate) {
if (preg_match('/^(http[^:]*:\/\/[^\/]+)(\/|$)/', $alternate, $matches)) {
return $matches[1];
$headerOverride = false;
if (TRUSTED_PROXY) {
$headers = (getenv('SS_TRUSTED_PROXY_HOST_HEADER')) ? array(getenv('SS_TRUSTED_PROXY_HOST_HEADER')) : null;
if (!$headers) {
// Backwards compatible defaults
$headers = array('HTTP_X_FORWARDED_HOST');
}
foreach ($headers as $header) {
if (!empty($_SERVER[$header])) {
// Get the first host, in case there's multiple separated through commas
$headerOverride = strtok($_SERVER[$header], ',');
break;
}
}
}

if (isset($_SERVER['HTTP_HOST'])) {
return Director::protocol() . $_SERVER['HTTP_HOST'];
} else {
global $_FILE_TO_URL_MAPPING;
if (Director::is_cli() && isset($_FILE_TO_URL_MAPPING)) {
$errorSuggestion = ' You probably want to define ' .
'an entry in $_FILE_TO_URL_MAPPING that covers "' . Director::baseFolder() . '"';
} elseif (Director::is_cli()) {
$errorSuggestion = ' You probably want to define $_FILE_TO_URL_MAPPING in ' .
'your _ss_environment.php as instructed on the "sake" page of the doc.silverstripe.com wiki';
} else {
$errorSuggestion = "";
if ($host = static::config()->get('alternate_host')) {
return $host;
}

if ($baseURL = static::config()->get('alternate_base_url')) {
if (preg_match('/^(http[^:]*:\/\/[^\/]+)(\/|$)/', $baseURL, $matches)) {
return parse_url($baseURL, PHP_URL_HOST);
}
}

user_error("Director::protocolAndHost() lacks sufficient information - HTTP_HOST not set."
. $errorSuggestion, E_USER_WARNING);
return false;
if ($headerOverride) {
return $headerOverride;
}

if (isset($_SERVER['HTTP_HOST'])) {
return $_SERVER['HTTP_HOST'];
}

if ($host = getenv('SS_HOST')) {
return $host;
}

return isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : gethostname();
}

/**
* Returns the domain part of the URL 'http://www.mysite.com'. Returns FALSE is this environment
* variable isn't set.
*
* @return bool|string
*/
public static function protocolAndHost()
{
return static::protocol() . static::host();
}

/**
Expand Down Expand Up @@ -950,7 +998,7 @@ public static function absoluteBaseURLWithAuth()
$login = "$_SERVER[PHP_AUTH_USER]:$_SERVER[PHP_AUTH_PW]@";
}

return Director::protocol() . $login . $_SERVER['HTTP_HOST'] . Director::baseURL();
return Director::protocol() . $login . static::host() . Director::baseURL();
}

/**
Expand Down Expand Up @@ -1052,7 +1100,7 @@ public static function forceSSL($patterns = null, $secureDomain = null)
*/
public static function forceWWW()
{
if (!Director::isDev() && !Director::isTest() && strpos($_SERVER['HTTP_HOST'], 'www') !== 0) {
if (!Director::isDev() && !Director::isTest() && strpos(static::host(), 'www') !== 0) {
$destURL = str_replace(
Director::protocol(),
Director::protocol() . 'www.',
Expand Down Expand Up @@ -1191,11 +1239,7 @@ public static function isDev()

// Check if we are running on one of the test servers
$devServers = (array)Config::inst()->get('SilverStripe\\Control\\Director', 'dev_servers');
if (isset($_SERVER['HTTP_HOST']) && in_array($_SERVER['HTTP_HOST'], $devServers)) {
return true;
}

return false;
return in_array(static::host(), $devServers);
}

/**
Expand Down Expand Up @@ -1223,11 +1267,7 @@ public static function isTest()

// Check if we are running on one of the test servers
$testServers = (array)Config::inst()->get('SilverStripe\\Control\\Director', 'test_servers');
if (isset($_SERVER['HTTP_HOST']) && in_array($_SERVER['HTTP_HOST'], $testServers)) {
return true;
}

return false;
return in_array(static::host(), $testServers);
}

/**
Expand Down
74 changes: 0 additions & 74 deletions src/Core/Constants.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,80 +61,6 @@
define('TRUSTED_PROXY', $trusted);
}

/**
* A blank HTTP_HOST value is used to detect command-line execution.
* We update the $_SERVER variable to contain data consistent with the rest of the application.
*/
if (!isset($_SERVER['HTTP_HOST'])) {
// HTTP_HOST, REQUEST_PORT, SCRIPT_NAME, and PHP_SELF
global $_FILE_TO_URL_MAPPING;
if (isset($_FILE_TO_URL_MAPPING)) {
$fullPath = $testPath = realpath($_SERVER['SCRIPT_FILENAME']);
while ($testPath && $testPath != '/' && !preg_match('/^[A-Z]:\\\\$/', $testPath)) {
if (isset($_FILE_TO_URL_MAPPING[$testPath])) {
$url = $_FILE_TO_URL_MAPPING[$testPath]
. str_replace(DIRECTORY_SEPARATOR, '/', substr($fullPath, strlen($testPath)));

$components = parse_url($url);
$_SERVER['HTTP_HOST'] = $components['host'];
if (!empty($components['port'])) {
$_SERVER['HTTP_HOST'] .= ':' . $components['port'];
}
$_SERVER['SCRIPT_NAME'] = $_SERVER['PHP_SELF'] = $components['path'];
if (!empty($components['port'])) {
$_SERVER['REQUEST_PORT'] = $components['port'];
}
break;
}
$testPath = dirname($testPath);
}
}

// Everything else
$serverDefaults = array(
'SERVER_PROTOCOL' => 'HTTP/1.1',
'HTTP_ACCEPT' => 'text/plain;q=0.5',
'HTTP_ACCEPT_LANGUAGE' => '*;q=0.5',
'HTTP_ACCEPT_ENCODING' => '',
'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1;q=0.5',
'SERVER_SIGNATURE' => 'Command-line PHP/' . phpversion(),
'SERVER_SOFTWARE' => 'PHP/' . phpversion(),
'SERVER_ADDR' => '127.0.0.1',
'REMOTE_ADDR' => '127.0.0.1',
'REQUEST_METHOD' => 'GET',
'HTTP_USER_AGENT' => 'CLI',
);

$_SERVER = array_merge($serverDefaults, $_SERVER);

/**
* If we have an HTTP_HOST value, then we're being called from the webserver and there are some things that
* need checking
*/
} else {

/**
* Fix HTTP_HOST from reverse proxies
*/
$trustedProxyHeader = (defined('SS_TRUSTED_PROXY_HOST_HEADER'))
? SS_TRUSTED_PROXY_HOST_HEADER
: 'HTTP_X_FORWARDED_HOST';

if (TRUSTED_PROXY && !empty($_SERVER[$trustedProxyHeader])) {
// Get the first host, in case there's multiple separated through commas
$_SERVER['HTTP_HOST'] = strtok($_SERVER[$trustedProxyHeader], ',');
}
}

// Filter by configured allowed hosts
if (defined('SS_ALLOWED_HOSTS') && php_sapi_name() !== "cli") {
$all_allowed_hosts = explode(',', SS_ALLOWED_HOSTS);
if (!isset($_SERVER['HTTP_HOST']) || !in_array($_SERVER['HTTP_HOST'], $all_allowed_hosts)) {
header('HTTP/1.1 400 Invalid Host', true, 400);
die();
}
}

/**
* Define system paths
*/
Expand Down
3 changes: 2 additions & 1 deletion src/Core/Startup/ParameterConfirmationToken.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace SilverStripe\Core\Startup;

use SilverStripe\Control\Director;
use SilverStripe\Security\RandomGenerator;

/**
Expand Down Expand Up @@ -203,7 +204,7 @@ protected function currentAbsoluteURL()

$parts = array_filter(array(
// What's our host
$_SERVER['HTTP_HOST'],
Director::host(),
// SilverStripe base
self::$alternateBaseURL !== null ? self::$alternateBaseURL : BASE_URL,
// And URL including base script (eg: if it's index.php/page/url/)
Expand Down
25 changes: 0 additions & 25 deletions src/Dev/DevelopmentAdmin.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,31 +64,6 @@ protected function init()
return;
}

// check for valid url mapping
// lacking this information can cause really nasty bugs,
// e.g. when running Director::test() from a FunctionalTest instance
global $_FILE_TO_URL_MAPPING;
if (Director::is_cli()) {
if (isset($_FILE_TO_URL_MAPPING)) {
$testPath = BASE_PATH;
$matched = false;
while ($testPath && $testPath != "/" && !preg_match('/^[A-Z]:\\\\$/', $testPath)) {
if (isset($_FILE_TO_URL_MAPPING[$testPath])) {
$matched = true;
break;
}
$testPath = dirname($testPath);
}
if (!$matched) {
echo 'Warning: You probably want to define '.
'an entry in $_FILE_TO_URL_MAPPING that covers "' . Director::baseFolder() . '"' . "\n";
}
} else {
echo 'Warning: You probably want to define $_FILE_TO_URL_MAPPING in '.
'your _ss_environment.php as instructed on the "sake" page of the doc.silverstripe.org wiki'."\n";
}
}

// Backwards compat: Default to "draft" stage, which is important
// for tasks like dev/build which call DataObject->requireDefaultRecords(),
// but also for other administrative tasks which have assumptions about the default stage.
Expand Down
16 changes: 16 additions & 0 deletions tests/bootstrap/cli.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,22 @@
$_SERVER = array();
}

// We update the $_SERVER variable to contain data consistent with the rest of the application.
$_SERVER = array_merge(array(
'SERVER_PROTOCOL' => 'HTTP/1.1',
'HTTP_ACCEPT' => 'text/plain;q=0.5',
'HTTP_ACCEPT_LANGUAGE' => '*;q=0.5',
'HTTP_ACCEPT_ENCODING' => '',
'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1;q=0.5',
'SERVER_SIGNATURE' => 'Command-line PHP/' . phpversion(),
'SERVER_SOFTWARE' => 'PHP/' . phpversion(),
'SERVER_NAME' => 'localhost',
'SERVER_ADDR' => '127.0.0.1',
'REMOTE_ADDR' => '127.0.0.1',
'REQUEST_METHOD' => 'GET',
'HTTP_USER_AGENT' => 'CLI',
), $_SERVER);

$frameworkPath = dirname(dirname(__FILE__));
$frameworkDir = basename($frameworkPath);

Expand Down

0 comments on commit 0a9ec3f

Please sign in to comment.