Skip to content
Snippets Groups Projects
Commit 1e973034 authored by Tim van Dijen's avatar Tim van Dijen
Browse files

Use BuiltInServer from test-framework

parent 04fd7ddd
No related branches found
No related tags found
No related merge requests found
<?php
declare(strict_types=1);
namespace SimpleSAML\Test;
use SimpleSAML\Utils\System;
/**
* An extremely simple class to start and stop PHP's built-in server, with the possibility to specify the document
* root and the "router" file to run for every request.
*
* @author Jaime Pérez Crespo <jaime.perez@uninett.no>
* @package SimpleSAMLphp
*/
class BuiltInServer
{
/**
* The PID of the running server.
*
* @var int
*/
protected $pid = 0;
/**
* The address (host:port) where the server is listening for connections after being started.
*
* @var string
*/
protected $address = 'example.org';
/**
* The name of a "router" file to run for every request performed to this server.
*
* @var string
*/
protected $router = '';
/**
* The document root of the server.
*
* @var string
*/
protected $docroot;
/**
* BuiltInServer constructor.
*
* @param string|null $router The name of a "router" file to run first for every request performed to this server.
* @param string|null $docroot The document root to use when starting the server.
*
* @see http://php.net/manual/en/features.commandline.webserver.php
*/
public function __construct($router = null, $docroot = null)
{
if (!is_null($router)) {
$this->setRouter($router);
}
if (!is_null($docroot)) {
$this->docroot = $docroot;
} else {
$this->docroot = dirname(dirname(__FILE__)) . '/www/';
}
// Rationalize docroot
$this->docroot = str_replace('\\', '/', $this->docroot);
$this->docroot = rtrim($this->docroot, '/');
}
/**
* Start the built-in server in a random port.
*
* This method will wait up to 5 seconds for the server to start. When it returns an address, it is guaranteed that
* the server has started and is listening for connections. If it returns the default value on the other hand,
* there will be no guarantee that the server started properly.
*
* @return string The address where the server is listening for connections, or false if the server failed to start
* for some reason.
*
* @todo This method should be resilient to clashes in the randomly-picked port number.
*/
public function start()
{
$port = mt_rand(1025, 65535);
$this->address = 'localhost:' . $port;
if (System::getOS() === System::WINDOWS) {
$command = sprintf(
'powershell $proc = start-process php -ArgumentList (\'-S %s\', \'-t %s\', \'%s\') '
. '-Passthru; Write-output $proc.Id;',
$this->address,
$this->docroot,
$this->router
);
} else {
$command = sprintf(
'php -S %s -t %s %s >> /dev/null 2>&1 & echo $!',
$this->address,
$this->docroot,
$this->router
);
}
// execute the command and store the process ID
$output = [];
exec($command, $output);
$this->pid = intval($output[0]);
// wait until it's listening for connections to avoid race conditions
$start = microtime(true);
do {
$sock = @fsockopen('localhost', $port, $errno, $errstr, 10);
if ($sock === false) {
// set a 5 secs timeout waiting for the server to start
if (microtime(true) > $start + 5) {
$this->pid = 0; // signal failure
break;
}
}
} while ($sock === false);
if ($sock !== false) {
fclose($sock);
}
return $this->address;
}
/**
* Stop the built-in server.
* @return void
*/
public function stop()
{
if ($this->pid === 0) {
return;
} elseif (System::getOS() === System::WINDOWS) {
exec('taskkill /PID ' . $this->pid);
} else {
exec('kill ' . $this->pid);
}
$this->pid = 0;
}
/**
* Get the PID of the running server.
*
* @return int The PID of the server, or 0 if the server was not started.
*/
public function getPid()
{
return $this->pid;
}
/**
* Get the name of the "router" file.
*
* @return string The name of the "router" file.
*/
public function getRouter()
{
return $this->router;
}
/**
* Set the "router" file.
*
* @param string $router The name of a "router" file to use when starting the server.
* @return void
*/
public function setRouter($router)
{
$file = dirname(dirname(__FILE__)) . '/tests/routers/' . $router . '.php';
if (!file_exists($file)) {
throw new \InvalidArgumentException('Unknown router "' . $router . '".');
}
$this->router = $file;
}
/**
* This function performs an HTTP GET request to the built-in server.
*
* @param string $query The query to perform.
* @param array $parameters An array (can be empty) with parameters for the requested URI.
* @param array $curlopts An array (can be empty) with options for cURL.
*
* @return array The response obtained from the built-in server.
*/
public function get($query, $parameters, $curlopts = [])
{
$ch = curl_init();
$url = 'http://' . $this->address . $query;
$url .= (!empty($parameters)) ? '?' . http_build_query($parameters) : '';
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_HEADER => 1,
]);
curl_setopt_array($ch, $curlopts);
/** @psalm-var array|false $resp RETURNTRANSFER was set to true */
$resp = curl_exec($ch);
if ($resp === false) {
throw new \Exception("Unable to contact: " . $url);
}
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
list($header, $body) = explode("\r\n\r\n", $resp, 2);
$raw_headers = explode("\r\n", $header);
array_shift($raw_headers);
$headers = [];
foreach ($raw_headers as $header) {
list($name, $value) = explode(':', $header, 2);
$headers[trim($name)] = trim($value);
}
curl_close($ch);
return [
'code' => $code,
'headers' => $headers,
'body' => $body,
];
}
}
<?php
declare(strict_types=1);
/*
* This "router" (a script that's executed for every request received by PHP's built-in web server) will look
* for a file in the system's temporary directory, with the PID of the current process as its name, and the
* '.lock' extension. If the file exists, it will try to include it and preload SimpleSAMLphp's configuration with
* the $config array defined in that file.
* This is useful to configure SimpleSAMLphp dynamically when running inside the built-in server, so that
* we can test different configurations without the need to keep a structure of files.
*
* In order to use it:
*
* 1. Create an array with the SimpleSAMLphp configuration you would like to use.
* 2. Start the built-in server passing "configLoader" as the first parameter to the constructor:
* $server = new BuiltInServer('configLoader');
* $addr = $server->start();
* 3. Get the PID of the server once it has started:
* $pid = $server->getPid();
* 4. Build the path to the file that this script will use:
* $file = sys_get_temp_dir().'/'.$pid.'.lock';
* 5. Dump the configuration array to the file:
* file_put_contents("<?php\n\$config = ".var_export($config, true).";\n");
* 6. Make a request to the server:
* $server->get($query, $parameters);
* 7. Remove the temporary file when done:
* unlink($file);
*/
include_once(sys_get_temp_dir() . '/' . getmypid() . '.lock');
// load SimpleSAMLphp's autoloader
require_once(dirname(__FILE__) . '/../../vendor/autoload.php');
// initialize configuration
if (isset($config)) {
\SimpleSAML\Configuration::loadFromArray($config, '[ARRAY]', 'simplesaml');
}
// let the script proceed
// see: http://php.net/manual/en/features.commandline.webserver.php
return false;
......@@ -5,7 +5,7 @@ declare(strict_types=1);
namespace SimpleSAML\Test\Web;
use PHPUnit\Framework\TestCase;
use SimpleSAML\Test\BuiltInServer;
use SimpleSAML\TestUtils\BuiltInServer;
/**
* Simple test for the www/index.php script.
......@@ -18,7 +18,7 @@ use SimpleSAML\Test\BuiltInServer;
class IndexTest extends TestCase
{
/**
* @var \SimpleSAML\Test\BuiltInServer
* @var \SimpleSAML\TestUtils\BuiltInServer
*/
protected $server;
......@@ -57,7 +57,7 @@ class IndexTest extends TestCase
* @param array $config
* @return void
*/
protected function updateConfig(array $config)
protected function updateConfig(array $config): void
{
@unlink($this->shared_file);
$config = "<?php\n\$config = " . var_export($config, true) . ";\n";
......@@ -69,7 +69,7 @@ class IndexTest extends TestCase
* A simple test to make sure the index.php file redirects appropriately to the right URL.
* @return void
*/
public function testRedirection()
public function testRedirection(): void
{
// test most basic redirection
$this->updateConfig([
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment