Merge pull request #12606 from nupplaphil/bug/file_put
Fix config read/write locking
This commit is contained in:
commit
6c033c9bd1
8 changed files with 125 additions and 146 deletions
|
@ -126,7 +126,7 @@ if ($mode == 'status') {
|
||||||
|
|
||||||
unlink($pidfile);
|
unlink($pidfile);
|
||||||
|
|
||||||
DI::config()->set('system', 'worker_daemon_mode', false);
|
DI::keyValue()->set('worker_daemon_mode', false);
|
||||||
die("Daemon process $pid isn't running.\n");
|
die("Daemon process $pid isn't running.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -172,53 +172,32 @@ class BaseURL
|
||||||
*/
|
*/
|
||||||
public function save($hostname = null, $sslPolicy = null, $urlPath = null): bool
|
public function save($hostname = null, $sslPolicy = null, $urlPath = null): bool
|
||||||
{
|
{
|
||||||
$currHostname = $this->hostname;
|
$currUrl = $this->url;
|
||||||
$currSSLPolicy = $this->sslPolicy;
|
|
||||||
$currURLPath = $this->urlPath;
|
$configTransaction = $this->config->beginTransaction();
|
||||||
|
|
||||||
if (!empty($hostname) && $hostname !== $this->hostname) {
|
if (!empty($hostname) && $hostname !== $this->hostname) {
|
||||||
if ($this->config->set('config', 'hostname', $hostname)) {
|
$configTransaction->set('config', 'hostname', $hostname);
|
||||||
$this->hostname = $hostname;
|
$this->hostname = $hostname;
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($sslPolicy) && $sslPolicy !== $this->sslPolicy) {
|
if (isset($sslPolicy) && $sslPolicy !== $this->sslPolicy) {
|
||||||
if ($this->config->set('system', 'ssl_policy', $sslPolicy)) {
|
$configTransaction->set('system', 'ssl_policy', $sslPolicy);
|
||||||
$this->sslPolicy = $sslPolicy;
|
$this->sslPolicy = $sslPolicy;
|
||||||
} else {
|
|
||||||
$this->hostname = $currHostname;
|
|
||||||
$this->config->set('config', 'hostname', $this->hostname);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($urlPath) && $urlPath !== $this->urlPath) {
|
if (isset($urlPath) && $urlPath !== $this->urlPath) {
|
||||||
if ($this->config->set('system', 'urlpath', $urlPath)) {
|
$configTransaction->set('system', 'urlpath', $urlPath);
|
||||||
$this->urlPath = $urlPath;
|
$this->urlPath = $urlPath;
|
||||||
} else {
|
|
||||||
$this->hostname = $currHostname;
|
|
||||||
$this->sslPolicy = $currSSLPolicy;
|
|
||||||
$this->config->set('config', 'hostname', $this->hostname);
|
|
||||||
$this->config->set('system', 'ssl_policy', $this->sslPolicy);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->determineBaseUrl();
|
$this->determineBaseUrl();
|
||||||
if (!$this->config->set('system', 'url', $this->url)) {
|
if ($this->url !== $currUrl) {
|
||||||
$this->hostname = $currHostname;
|
$configTransaction->set('system', 'url', $this->url);
|
||||||
$this->sslPolicy = $currSSLPolicy;
|
|
||||||
$this->urlPath = $currURLPath;
|
|
||||||
$this->determineBaseUrl();
|
|
||||||
|
|
||||||
$this->config->set('config', 'hostname', $this->hostname);
|
|
||||||
$this->config->set('system', 'ssl_policy', $this->sslPolicy);
|
|
||||||
$this->config->set('system', 'urlpath', $this->urlPath);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$configTransaction->commit();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,8 +41,6 @@ interface ISetConfigValuesTransactionally
|
||||||
* @param mixed $value The value to store
|
* @param mixed $value The value to store
|
||||||
*
|
*
|
||||||
* @return static the current instance
|
* @return static the current instance
|
||||||
*
|
|
||||||
* @throws ConfigPersistenceException In case the persistence layer throws errors
|
|
||||||
*/
|
*/
|
||||||
public function set(string $cat, string $key, $value): self;
|
public function set(string $cat, string $key, $value): self;
|
||||||
|
|
||||||
|
@ -54,8 +52,6 @@ interface ISetConfigValuesTransactionally
|
||||||
*
|
*
|
||||||
* @return static the current instance
|
* @return static the current instance
|
||||||
*
|
*
|
||||||
* @throws ConfigPersistenceException In case the persistence layer throws errors
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public function delete(string $cat, string $key): self;
|
public function delete(string $cat, string $key): self;
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,8 @@ class ConfigTransaction implements ISetConfigValuesTransactionally
|
||||||
protected $cache;
|
protected $cache;
|
||||||
/** @var Cache */
|
/** @var Cache */
|
||||||
protected $delCache;
|
protected $delCache;
|
||||||
|
/** @var bool field to check if something is to save */
|
||||||
|
protected $changedConfig = false;
|
||||||
|
|
||||||
public function __construct(IManageConfigValues $config)
|
public function __construct(IManageConfigValues $config)
|
||||||
{
|
{
|
||||||
|
@ -70,6 +72,7 @@ class ConfigTransaction implements ISetConfigValuesTransactionally
|
||||||
public function set(string $cat, string $key, $value): ISetConfigValuesTransactionally
|
public function set(string $cat, string $key, $value): ISetConfigValuesTransactionally
|
||||||
{
|
{
|
||||||
$this->cache->set($cat, $key, $value, Cache::SOURCE_DATA);
|
$this->cache->set($cat, $key, $value, Cache::SOURCE_DATA);
|
||||||
|
$this->changedConfig = true;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
@ -80,6 +83,7 @@ class ConfigTransaction implements ISetConfigValuesTransactionally
|
||||||
{
|
{
|
||||||
$this->cache->delete($cat, $key);
|
$this->cache->delete($cat, $key);
|
||||||
$this->delCache->set($cat, $key, 'deleted');
|
$this->delCache->set($cat, $key, 'deleted');
|
||||||
|
$this->changedConfig = true;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
@ -87,6 +91,11 @@ class ConfigTransaction implements ISetConfigValuesTransactionally
|
||||||
/** {@inheritDoc} */
|
/** {@inheritDoc} */
|
||||||
public function commit(): void
|
public function commit(): void
|
||||||
{
|
{
|
||||||
|
// If nothing changed, just do nothing :)
|
||||||
|
if (!$this->changedConfig) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$newCache = $this->config->getCache()->merge($this->cache);
|
$newCache = $this->config->getCache()->merge($this->cache);
|
||||||
$newCache = $newCache->diff($this->delCache);
|
$newCache = $newCache->diff($this->delCache);
|
||||||
|
|
|
@ -173,7 +173,21 @@ class ConfigFileManager
|
||||||
$filename = $this->configDir . '/' . self::CONFIG_DATA_FILE;
|
$filename = $this->configDir . '/' . self::CONFIG_DATA_FILE;
|
||||||
|
|
||||||
if (file_exists($filename)) {
|
if (file_exists($filename)) {
|
||||||
$dataArray = include $filename;
|
|
||||||
|
$content = '<?php return [];';
|
||||||
|
|
||||||
|
$configStream = fopen($filename, 'r');
|
||||||
|
if (flock($configStream, LOCK_SH)) {
|
||||||
|
$content = fread($configStream, filesize($filename));
|
||||||
|
if (!$content) {
|
||||||
|
throw new ConfigFileException(sprintf('Couldn\'t read file %s', $filename));
|
||||||
|
}
|
||||||
|
flock($configStream, LOCK_UN);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose($configStream);
|
||||||
|
|
||||||
|
$dataArray = eval('?>' . $content);
|
||||||
|
|
||||||
if (!is_array($dataArray)) {
|
if (!is_array($dataArray)) {
|
||||||
throw new ConfigFileException(sprintf('Error loading config file %s', $filename));
|
throw new ConfigFileException(sprintf('Error loading config file %s', $filename));
|
||||||
|
@ -200,8 +214,12 @@ class ConfigFileManager
|
||||||
throw new ConfigFileException('config source cannot get encoded');
|
throw new ConfigFileException('config source cannot get encoded');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!file_put_contents($this->configDir . '/' . self::CONFIG_DATA_FILE, $encodedData)) {
|
$configStream = fopen($this->configDir . '/' . self::CONFIG_DATA_FILE, 'w');
|
||||||
throw new ConfigFileException(sprintf('Cannot save data to file %s/%s', $this->configDir, self::CONFIG_DATA_FILE));
|
|
||||||
|
if (flock($configStream, LOCK_EX)) {
|
||||||
|
fwrite($configStream, $encodedData);
|
||||||
|
fflush($configStream);
|
||||||
|
flock($configStream, LOCK_UN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -407,7 +407,7 @@ class ConfigFileManagerTest extends MockedTest
|
||||||
|
|
||||||
$configFileLoader->setupCache($configCache);
|
$configFileLoader->setupCache($configCache);
|
||||||
|
|
||||||
$specialChars = '!"§$%&/()(/&%$\'><?$a,;:[]}{}\\';
|
$specialChars = '!"§$%&/()(/&%$\'><?$a,;:[]}{}\\?¿¿ß';
|
||||||
|
|
||||||
// overwrite some data and save it back to the config file
|
// overwrite some data and save it back to the config file
|
||||||
$configCache->set('system', 'test', 'it', Cache::SOURCE_DATA);
|
$configCache->set('system', 'test', 'it', Cache::SOURCE_DATA);
|
||||||
|
|
|
@ -9,6 +9,7 @@ use Friendica\Core\Config\Util\ConfigFileManager;
|
||||||
use Friendica\Core\Config\ValueObject\Cache;
|
use Friendica\Core\Config\ValueObject\Cache;
|
||||||
use Friendica\Test\MockedTest;
|
use Friendica\Test\MockedTest;
|
||||||
use Friendica\Test\Util\VFSTrait;
|
use Friendica\Test\Util\VFSTrait;
|
||||||
|
use Mockery\Exception\InvalidCountException;
|
||||||
|
|
||||||
class ConfigTransactionTest extends MockedTest
|
class ConfigTransactionTest extends MockedTest
|
||||||
{
|
{
|
||||||
|
@ -108,4 +109,27 @@ class ConfigTransactionTest extends MockedTest
|
||||||
// the whole category should be gone
|
// the whole category should be gone
|
||||||
self::assertNull($tempData['delete'] ?? null);
|
self::assertNull($tempData['delete'] ?? null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test asserts that in empty transactions, no saveData is called, thus no config file writing was performed
|
||||||
|
*/
|
||||||
|
public function testNothingToDo()
|
||||||
|
{
|
||||||
|
$this->configFileManager = \Mockery::spy(ConfigFileManager::class);
|
||||||
|
|
||||||
|
$config = new Config($this->configFileManager, new Cache());
|
||||||
|
$configTransaction = new ConfigTransaction($config);
|
||||||
|
|
||||||
|
// commit empty transaction
|
||||||
|
$configTransaction->commit();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->configFileManager->shouldNotHaveReceived('saveData');
|
||||||
|
} catch (InvalidCountException $exception) {
|
||||||
|
self::fail($exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not failed, the test ends successfully :)
|
||||||
|
self::assertTrue(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,10 +23,25 @@ namespace Friendica\Test\src\Util;
|
||||||
|
|
||||||
use Friendica\App\BaseURL;
|
use Friendica\App\BaseURL;
|
||||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||||
|
use Friendica\Core\Config\Capability\ISetConfigValuesTransactionally;
|
||||||
|
use Friendica\Core\Config\Model\Config;
|
||||||
|
use Friendica\Core\Config\Model\ConfigTransaction;
|
||||||
|
use Friendica\Core\Config\Util\ConfigFileManager;
|
||||||
|
use Friendica\Core\Config\ValueObject\Cache;
|
||||||
use Friendica\Test\MockedTest;
|
use Friendica\Test\MockedTest;
|
||||||
|
use Friendica\Test\Util\VFSTrait;
|
||||||
|
|
||||||
class BaseURLTest extends MockedTest
|
class BaseURLTest extends MockedTest
|
||||||
{
|
{
|
||||||
|
use VFSTrait;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->setUpVfsDir();
|
||||||
|
}
|
||||||
|
|
||||||
public function dataDefault()
|
public function dataDefault()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
@ -231,6 +246,21 @@ class BaseURLTest extends MockedTest
|
||||||
public function dataSave()
|
public function dataSave()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
'no_change' => [
|
||||||
|
'input' => [
|
||||||
|
'hostname' => 'friendica.local',
|
||||||
|
'urlPath' => 'path',
|
||||||
|
'sslPolicy' => BaseURL::SSL_POLICY_FULL,
|
||||||
|
'url' => 'https://friendica.local/path',
|
||||||
|
'force_ssl' => true,
|
||||||
|
],
|
||||||
|
'save' => [
|
||||||
|
'hostname' => 'friendica.local',
|
||||||
|
'urlPath' => 'path',
|
||||||
|
'sslPolicy' => BaseURL::SSL_POLICY_FULL,
|
||||||
|
],
|
||||||
|
'url' => 'https://friendica.local/path',
|
||||||
|
],
|
||||||
'default' => [
|
'default' => [
|
||||||
'input' => [
|
'input' => [
|
||||||
'hostname' => 'friendica.old',
|
'hostname' => 'friendica.old',
|
||||||
|
@ -315,28 +345,20 @@ class BaseURLTest extends MockedTest
|
||||||
*/
|
*/
|
||||||
public function testSave($input, $save, $url)
|
public function testSave($input, $save, $url)
|
||||||
{
|
{
|
||||||
$configMock = \Mockery::mock(IManageConfigValues::class);
|
$configFileManager = new ConfigFileManager($this->root->url(), $this->root->url() . '/config/', $this->root->url() . '/static/');
|
||||||
$configMock->shouldReceive('get')->with('config', 'hostname')->andReturn($input['hostname']);
|
$config = new Config($configFileManager, new Cache([
|
||||||
$configMock->shouldReceive('get')->with('system', 'urlpath')->andReturn($input['urlPath']);
|
'config' => [
|
||||||
$configMock->shouldReceive('get')->with('system', 'ssl_policy')->andReturn($input['sslPolicy']);
|
'hostname' => $input['hostname'] ?? null,
|
||||||
$configMock->shouldReceive('get')->with('system', 'url')->andReturn($input['url']);
|
],
|
||||||
$configMock->shouldReceive('get')->with('system', 'force_ssl')->andReturn($input['force_ssl']);
|
'system' => [
|
||||||
|
'urlpath' => $input['urlPath'] ?? null,
|
||||||
|
'ssl_policy' => $input['sslPolicy'] ?? null,
|
||||||
|
'url' => $input['url'] ?? null,
|
||||||
|
'force_ssl' => $input['force_ssl'] ?? null,
|
||||||
|
],
|
||||||
|
]));
|
||||||
|
|
||||||
$baseUrl = new BaseURL($configMock, []);
|
$baseUrl = new BaseURL($config, []);
|
||||||
|
|
||||||
if (isset($save['hostname'])) {
|
|
||||||
$configMock->shouldReceive('set')->with('config', 'hostname', $save['hostname'])->andReturn(true)->once();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($save['urlPath'])) {
|
|
||||||
$configMock->shouldReceive('set')->with('system', 'urlpath', $save['urlPath'])->andReturn(true)->once();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($save['sslPolicy'])) {
|
|
||||||
$configMock->shouldReceive('set')->with('system', 'ssl_policy', $save['sslPolicy'])->andReturn(true)->once();
|
|
||||||
}
|
|
||||||
|
|
||||||
$configMock->shouldReceive('set')->with('system', 'url', $url)->andReturn(true)->once();
|
|
||||||
|
|
||||||
$baseUrl->save($save['hostname'], $save['sslPolicy'], $save['urlPath']);
|
$baseUrl->save($save['hostname'], $save['sslPolicy'], $save['urlPath']);
|
||||||
|
|
||||||
|
@ -353,28 +375,20 @@ class BaseURLTest extends MockedTest
|
||||||
*/
|
*/
|
||||||
public function testSaveByUrl($input, $save, $url)
|
public function testSaveByUrl($input, $save, $url)
|
||||||
{
|
{
|
||||||
$configMock = \Mockery::mock(IManageConfigValues::class);
|
$configFileManager = new ConfigFileManager($this->root->url(), $this->root->url() . '/config/', $this->root->url() . '/static/');
|
||||||
$configMock->shouldReceive('get')->with('config', 'hostname')->andReturn($input['hostname']);
|
$config = new Config($configFileManager, new Cache([
|
||||||
$configMock->shouldReceive('get')->with('system', 'urlpath')->andReturn($input['urlPath']);
|
'config' => [
|
||||||
$configMock->shouldReceive('get')->with('system', 'ssl_policy')->andReturn($input['sslPolicy']);
|
'hostname' => $input['hostname'] ?? null,
|
||||||
$configMock->shouldReceive('get')->with('system', 'url')->andReturn($input['url']);
|
],
|
||||||
$configMock->shouldReceive('get')->with('system', 'force_ssl')->andReturn($input['force_ssl']);
|
'system' => [
|
||||||
|
'urlpath' => $input['urlPath'] ?? null,
|
||||||
|
'ssl_policy' => $input['sslPolicy'] ?? null,
|
||||||
|
'url' => $input['url'] ?? null,
|
||||||
|
'force_ssl' => $input['force_ssl'] ?? null,
|
||||||
|
],
|
||||||
|
]));
|
||||||
|
|
||||||
$baseUrl = new BaseURL($configMock, []);
|
$baseUrl = new BaseURL($config, []);
|
||||||
|
|
||||||
if (isset($save['hostname'])) {
|
|
||||||
$configMock->shouldReceive('set')->with('config', 'hostname', $save['hostname'])->andReturn(true)->once();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($save['urlPath'])) {
|
|
||||||
$configMock->shouldReceive('set')->with('system', 'urlpath', $save['urlPath'])->andReturn(true)->once();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($save['sslPolicy'])) {
|
|
||||||
$configMock->shouldReceive('set')->with('system', 'ssl_policy', $save['sslPolicy'])->andReturn(true)->once();
|
|
||||||
}
|
|
||||||
|
|
||||||
$configMock->shouldReceive('set')->with('system', 'url', $url)->andReturn(true)->once();
|
|
||||||
|
|
||||||
$baseUrl->saveByURL($url);
|
$baseUrl->saveByURL($url);
|
||||||
|
|
||||||
|
@ -498,65 +512,4 @@ class BaseURLTest extends MockedTest
|
||||||
|
|
||||||
self::assertEquals($redirect, $baseUrl->checkRedirectHttps());
|
self::assertEquals($redirect, $baseUrl->checkRedirectHttps());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function dataWrongSave()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'wrongHostname' => [
|
|
||||||
'fail' => 'hostname',
|
|
||||||
],
|
|
||||||
'wrongSSLPolicy' => [
|
|
||||||
'fail' => 'sslPolicy',
|
|
||||||
],
|
|
||||||
'wrongURLPath' => [
|
|
||||||
'fail' => 'urlPath',
|
|
||||||
],
|
|
||||||
'wrongURL' => [
|
|
||||||
'fail' => 'url',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test the save() method with wrong parameters
|
|
||||||
* @dataProvider dataWrongSave
|
|
||||||
*/
|
|
||||||
public function testWrongSave($fail)
|
|
||||||
{
|
|
||||||
$configMock = \Mockery::mock(IManageConfigValues::class);
|
|
||||||
$configMock->shouldReceive('get')->with('config', 'hostname')->andReturn('friendica.local');
|
|
||||||
$configMock->shouldReceive('get')->with('system', 'urlpath')->andReturn('new/test');
|
|
||||||
$configMock->shouldReceive('get')->with('system', 'ssl_policy')->andReturn(BaseURL::DEFAULT_SSL_SCHEME);
|
|
||||||
$configMock->shouldReceive('get')->with('system', 'url')->andReturn('http://friendica.local/new/test');
|
|
||||||
|
|
||||||
switch ($fail) {
|
|
||||||
case 'hostname':
|
|
||||||
$configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any())->andReturn(false)->once();
|
|
||||||
break;
|
|
||||||
case 'sslPolicy':
|
|
||||||
$configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any())->andReturn(true)->twice();
|
|
||||||
$configMock->shouldReceive('set')->with('system', 'ssl_policy', \Mockery::any())->andReturn(false)->once();
|
|
||||||
break;
|
|
||||||
case 'urlPath':
|
|
||||||
$configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any())->andReturn(true)->twice();
|
|
||||||
$configMock->shouldReceive('set')->with('system', 'ssl_policy', \Mockery::any())->andReturn(true)->twice();
|
|
||||||
$configMock->shouldReceive('set')->with('system', 'urlpath', \Mockery::any())->andReturn(false)->once();
|
|
||||||
break;
|
|
||||||
case 'url':
|
|
||||||
$configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any())->andReturn(true)->twice();
|
|
||||||
$configMock->shouldReceive('set')->with('system', 'ssl_policy', \Mockery::any())->andReturn(true)->twice();
|
|
||||||
$configMock->shouldReceive('set')->with('system', 'urlpath', \Mockery::any())->andReturn(true)->twice();
|
|
||||||
$configMock->shouldReceive('set')->with('system', 'url', \Mockery::any())->andReturn(false)->once();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$baseUrl = new BaseURL($configMock, []);
|
|
||||||
self::assertFalse($baseUrl->save('test', 10, 'nope'));
|
|
||||||
|
|
||||||
// nothing should have changed because we never successfully saved anything
|
|
||||||
self::assertEquals('friendica.local', $baseUrl->getHostname());
|
|
||||||
self::assertEquals('new/test', $baseUrl->getUrlPath());
|
|
||||||
self::assertEquals(BaseURL::DEFAULT_SSL_SCHEME, $baseUrl->getSSLPolicy());
|
|
||||||
self::assertEquals('http://friendica.local/new/test', $baseUrl->get());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue