Added custom server host ability

This commit is contained in:
Marcel Pociot
2021-06-01 21:19:12 +02:00
parent 5e54d0a80f
commit 44b100b340
13 changed files with 116 additions and 44 deletions

View File

@@ -6,6 +6,7 @@ use App\Contracts\ConnectionManager as ConnectionManagerContract;
use App\Contracts\StatisticsCollector;
use App\Contracts\SubdomainGenerator;
use App\Http\QueryParameters;
use App\Server\Configuration;
use App\Server\Exceptions\NoFreePortAvailable;
use Ratchet\ConnectionInterface;
use React\EventLoop\LoopInterface;
@@ -48,7 +49,7 @@ class ConnectionManager implements ConnectionManagerContract
});
}
public function storeConnection(string $host, ?string $subdomain, ConnectionInterface $connection): ControlConnection
public function storeConnection(string $host, ?string $subdomain, ?string $serverHost, ConnectionInterface $connection): ControlConnection
{
$clientId = (string) uniqid();
@@ -59,6 +60,7 @@ class ConnectionManager implements ConnectionManagerContract
$host,
$subdomain ?? $this->subdomainGenerator->generateSubdomain(),
$clientId,
$serverHost,
$this->getAuthTokenFromConnection($connection)
);
@@ -152,10 +154,10 @@ class ConnectionManager implements ConnectionManagerContract
}
}
public function findControlConnectionForSubdomain($subdomain): ?ControlConnection
public function findControlConnectionForSubdomainAndServerHost($subdomain, $serverHost): ?ControlConnection
{
return collect($this->connections)->last(function ($connection) use ($subdomain) {
return $connection->subdomain == $subdomain;
return collect($this->connections)->last(function ($connection) use ($subdomain, $serverHost) {
return $connection->subdomain == $subdomain && $connection->serverHost === $serverHost;
});
}

View File

@@ -12,19 +12,21 @@ class ControlConnection
/** @var ConnectionInterface */
public $socket;
public $host;
public $serverHost;
public $authToken;
public $subdomain;
public $client_id;
public $proxies = [];
protected $shared_at;
public function __construct(ConnectionInterface $socket, string $host, string $subdomain, string $clientId, string $authToken = '')
public function __construct(ConnectionInterface $socket, string $host, string $subdomain, string $clientId, string $serverHost, string $authToken = '')
{
$this->socket = $socket;
$this->host = $host;
$this->subdomain = $subdomain;
$this->client_id = $clientId;
$this->authToken = $authToken;
$this->serverHost = $serverHost;
$this->shared_at = now()->toDateTimeString();
}
@@ -61,6 +63,7 @@ class ControlConnection
return [
'type' => 'http',
'host' => $this->host,
'server_host' => $this->serverHost,
'client_id' => $this->client_id,
'auth_token' => $this->authToken,
'subdomain' => $this->subdomain,

View File

@@ -6,6 +6,7 @@ use App\Contracts\ConnectionManager;
use App\Contracts\SubdomainRepository;
use App\Contracts\UserRepository;
use App\Http\QueryParameters;
use App\Server\Configuration;
use App\Server\Exceptions\NoFreePortAvailable;
use Illuminate\Support\Arr;
use Ratchet\ConnectionInterface;
@@ -26,11 +27,15 @@ class ControlMessageController implements MessageComponentInterface
/** @var SubdomainRepository */
protected $subdomainRepository;
public function __construct(ConnectionManager $connectionManager, UserRepository $userRepository, SubdomainRepository $subdomainRepository)
/** @var Configuration */
protected $configuration;
public function __construct(ConnectionManager $connectionManager, UserRepository $userRepository, SubdomainRepository $subdomainRepository, Configuration $configuration)
{
$this->connectionManager = $connectionManager;
$this->userRepository = $userRepository;
$this->subdomainRepository = $subdomainRepository;
$this->configuration = $configuration;
}
/**
@@ -93,6 +98,9 @@ class ControlMessageController implements MessageComponentInterface
if (! isset($data->type)) {
$data->type = 'http';
}
if (! isset($data->server_host) || is_null($data->server_host)) {
$data->server_host = $this->configuration->hostname();
}
$this->verifyAuthToken($connection)
->then(function ($user) use ($connection) {
@@ -139,14 +147,14 @@ class ControlMessageController implements MessageComponentInterface
protected function handleHttpConnection(ConnectionInterface $connection, $data, $user = null)
{
$this->hasValidSubdomain($connection, $data->subdomain, $user)->then(function ($subdomain) use ($data, $connection) {
$this->hasValidSubdomain($connection, $data->subdomain, $user, $data->server_host)->then(function ($subdomain) use ($data, $connection) {
if ($subdomain === false) {
return;
}
$data->subdomain = $subdomain;
$connectionInfo = $this->connectionManager->storeConnection($data->host, $data->subdomain, $connection);
$connectionInfo = $this->connectionManager->storeConnection($data->host, $data->subdomain, $data->server_host, $connection);
$this->connectionManager->limitConnectionLength($connectionInfo, config('expose.admin.maximum_connection_length'));
@@ -155,6 +163,7 @@ class ControlMessageController implements MessageComponentInterface
'data' => [
'message' => config('expose.admin.messages.message_of_the_day'),
'subdomain' => $connectionInfo->subdomain,
'server_host' => $connectionInfo->serverHost,
'client_id' => $connectionInfo->client_id,
],
]));
@@ -250,7 +259,7 @@ class ControlMessageController implements MessageComponentInterface
return $deferred->promise();
}
protected function hasValidSubdomain(ConnectionInterface $connection, ?string $subdomain, ?array $user): PromiseInterface
protected function hasValidSubdomain(ConnectionInterface $connection, ?string $subdomain, ?array $user, string $serverHost): PromiseInterface
{
/**
* Check if the user can specify a custom subdomain in the first place.
@@ -271,7 +280,7 @@ class ControlMessageController implements MessageComponentInterface
*/
if (! is_null($subdomain)) {
return $this->subdomainRepository->getSubdomainByName($subdomain)
->then(function ($foundSubdomain) use ($connection, $subdomain, $user) {
->then(function ($foundSubdomain) use ($connection, $subdomain, $user, $serverHost) {
if (! is_null($foundSubdomain) && ! is_null($user) && $foundSubdomain['user_id'] !== $user['id']) {
$message = config('expose.admin.messages.subdomain_reserved');
$message = str_replace(':subdomain', $subdomain, $message);
@@ -287,7 +296,7 @@ class ControlMessageController implements MessageComponentInterface
return \React\Promise\resolve(false);
}
$controlConnection = $this->connectionManager->findControlConnectionForSubdomain($subdomain);
$controlConnection = $this->connectionManager->findControlConnectionForSubdomainAndServerHost($subdomain, $serverHost);
if (! is_null($controlConnection) || $subdomain === config('expose.admin.subdomain') || in_array($subdomain, config('expose.admin.reserved_subdomains', []))) {
$message = config('expose.admin.messages.subdomain_taken');

View File

@@ -41,6 +41,7 @@ class TunnelMessageController extends Controller
public function handle(Request $request, ConnectionInterface $httpConnection)
{
$subdomain = $this->detectSubdomain($request);
$serverHost = $this->detectServerHost($request);
if (is_null($subdomain)) {
$httpConnection->send(
@@ -51,7 +52,7 @@ class TunnelMessageController extends Controller
return;
}
$controlConnection = $this->connectionManager->findControlConnectionForSubdomain($subdomain);
$controlConnection = $this->connectionManager->findControlConnectionForSubdomainAndServerHost($subdomain, $serverHost);
if (is_null($controlConnection)) {
$httpConnection->send(
@@ -69,11 +70,16 @@ class TunnelMessageController extends Controller
protected function detectSubdomain(Request $request): ?string
{
$subdomain = Str::before($request->getHost(), '.'.$this->configuration->hostname());
$subdomain = Str::before($request->getHost(), '.');
return $subdomain === $request->getHost() ? null : $subdomain;
}
protected function detectServerHost(Request $request): ?string
{
return Str::after($request->getHost(), '.');
}
protected function sendRequestToClient(Request $request, ControlConnection $controlConnection, ConnectionInterface $httpConnection)
{
$request = $this->prepareRequest($request, $controlConnection);