This commit is contained in:
Marcel Pociot
2020-04-29 16:49:33 +02:00
parent c2084e6be6
commit 6cf206e0a2
35 changed files with 566 additions and 215 deletions

View File

@@ -2,14 +2,16 @@
namespace App\Client;
use App\Client\Http\Controllers\PushLogsToDashboardController;
use App\Client\Http\HttpClient;
use App\HttpServer\App;
use App\HttpServer\Controllers\AttachDataToLogController;
use App\HttpServer\Controllers\ClearLogsController;
use App\HttpServer\Controllers\DashboardController;
use App\HttpServer\Controllers\LogController;
use App\HttpServer\Controllers\ReplayLogController;
use App\HttpServer\Controllers\StoreLogController;
use App\Http\App;
use App\Client\Http\Controllers\AttachDataToLogController;
use App\Client\Http\Controllers\ClearLogsController;
use App\Client\Http\Controllers\DashboardController;
use App\Client\Http\Controllers\LogController;
use App\Client\Http\Controllers\ReplayLogController;
use App\Http\Controllers\StoreLogController;
use App\Http\RouteGenerator;
use App\WebSockets\Socket;
use Ratchet\WebSocket\WsServer;
use React\EventLoop\LoopInterface;
@@ -33,9 +35,13 @@ class Factory
/** @var App */
protected $app;
/** @var RouteGenerator */
protected $router;
public function __construct()
{
$this->loop = LoopFactory::create();
$this->router = new RouteGenerator();
}
public function setHost(string $host)
@@ -93,21 +99,18 @@ class Factory
protected function addRoutes()
{
$dashboardRoute = new Route('/', ['_controller' => app(DashboardController::class)], [], [], null, [], ['GET']);
$logRoute = new Route('/logs', ['_controller' => app(LogController::class)], [], [], null, [], ['GET']);
$storeLogRoute = new Route('/logs', ['_controller' => app(StoreLogController::class)], [], [], null, [], ['POST']);
$replayLogRoute = new Route('/replay/{log}', ['_controller' => app(ReplayLogController::class)], [], [], null, [], ['GET']);
$attachLogDataRoute = new Route('/logs/{request_id}/data', ['_controller' => app(AttachDataToLogController::class)], [], [], null, [], ['POST']);
$clearLogsRoute = new Route('/logs/clear', ['_controller' => app(ClearLogsController::class)], [], [], null, [], ['GET']);
$this->router->get('/', DashboardController::class);
$this->router->get('/logs', LogController::class);
$this->router->post('/logs', PushLogsToDashboardController::class);
$this->router->get('/replay/{log}', ReplayLogController::class);
$this->router->post('/logs/{request_id}/data', AttachDataToLogController::class);
$this->router->post('/logs/clear', ClearLogsController::class);
$this->app->route('/socket', new WsServer(new Socket()), ['*']);
$this->app->routes->add('dashboard', $dashboardRoute);
$this->app->routes->add('logs', $logRoute);
$this->app->routes->add('storeLogs', $storeLogRoute);
$this->app->routes->add('replayLog', $replayLogRoute);
$this->app->routes->add('attachLogData', $attachLogDataRoute);
$this->app->routes->add('clearLogs', $clearLogsRoute);
foreach ($this->router->getRoutes()->all() as $name => $route) {
$this->app->routes->add($name, $route);
}
}
protected function detectNextFreeDashboardPort($port = 4040): int
@@ -138,6 +141,11 @@ class Factory
return $this;
}
public function getApp(): App
{
return $this->app;
}
public function run()
{
$this->loop->run();

View File

@@ -0,0 +1,37 @@
<?php
namespace App\Client\Http\Controllers;
use App\Http\Controllers\PostController;
use GuzzleHttp\Psr7\Response;
use Illuminate\Http\Request;
use App\Logger\RequestLogger;
use Ratchet\ConnectionInterface;
use function GuzzleHttp\Psr7\str;
class AttachDataToLogController extends PostController
{
/** @var RequestLogger */
protected $requestLogger;
public function __construct(RequestLogger $requestLogger)
{
$this->requestLogger = $requestLogger;
}
public function handle(Request $request, ConnectionInterface $httpConnection)
{
$loggedRequest = $this->requestLogger->findLoggedRequest($request->get('request_id', ''));
if (! is_null($loggedRequest)) {
$loggedRequest->setAdditionalData((array)$request->get('data', []));
$this->requestLogger->pushLogs();
$httpConnection->send(str(new Response(200)));
return;
}
$httpConnection->send(str(new Response(404)));
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace App\Client\Http\Controllers;
use App\Client\Http\HttpClient;
use App\Http\Controllers\Controller;
use App\Http\QueryParameters;
use App\Logger\RequestLogger;
use GuzzleHttp\Psr7\Response;
use Ratchet\ConnectionInterface;
use function GuzzleHttp\Psr7\str;
use Psr\Http\Message\RequestInterface;
class ClearLogsController extends Controller
{
/** @var RequestLogger */
protected $requestLogger;
public function __construct(RequestLogger $requestLogger)
{
$this->requestLogger = $requestLogger;
}
public function onOpen(ConnectionInterface $connection, RequestInterface $request = null)
{
$this->requestLogger->clear();
$connection->send(
str(new Response(
200,
['Content-Type' => 'application/json'],
''
))
);
$connection->close();
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Client\Http\Controllers;
use App\Client\Client;
use App\Http\Controllers\Controller;
use GuzzleHttp\Psr7\Response;
use function GuzzleHttp\Psr7\str;
use Psr\Http\Message\RequestInterface;
use Ratchet\ConnectionInterface;
class DashboardController extends Controller
{
public function onOpen(ConnectionInterface $connection, RequestInterface $request = null)
{
$connection->send(
str(new Response(
200,
['Content-Type' => 'text/html'],
$this->getView('client.dashboard', [
'subdomains' => Client::$subdomains,
])
))
);
$connection->close();
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace App\Client\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Logger\RequestLogger;
use GuzzleHttp\Psr7\Response;
use Ratchet\ConnectionInterface;
use function GuzzleHttp\Psr7\str;
use Psr\Http\Message\RequestInterface;
class LogController extends Controller
{
/** @var RequestLogger */
protected $requestLogger;
public function __construct(RequestLogger $requestLogger)
{
$this->requestLogger = $requestLogger;
}
public function onOpen(ConnectionInterface $connection, RequestInterface $request = null)
{
$connection->send(
str(new Response(
200,
['Content-Type' => 'application/json'],
json_encode($this->requestLogger->getData(), JSON_INVALID_UTF8_IGNORE)
))
);
$connection->close();
}
}

View File

@@ -0,0 +1,63 @@
<?php
namespace App\Client\Http\Controllers;
use App\Http\Controllers\Controller;
use Exception;
use App\WebSockets\Socket;
use GuzzleHttp\Psr7\Response;
use Illuminate\Support\Collection;
use Ratchet\ConnectionInterface;
use function GuzzleHttp\Psr7\str;
use Psr\Http\Message\RequestInterface;
class PushLogsToDashboardController extends Controller
{
public function onOpen(ConnectionInterface $connection, RequestInterface $request = null)
{
$connection->contentLength = $this->findContentLength($request->getHeaders());
$connection->requestBuffer = (string) $request->getBody();
$this->checkContentLength($connection);
}
public function onMessage(ConnectionInterface $from, $msg)
{
$from->requestBuffer .= $msg;
$this->checkContentLength($from);
}
protected function findContentLength(array $headers): int
{
return Collection::make($headers)->first(function ($values, $header) {
return strtolower($header) === 'content-length';
})[0] ?? 0;
}
protected function checkContentLength(ConnectionInterface $connection)
{
if (strlen($connection->requestBuffer) === $connection->contentLength) {
try {
/*
* This is the post payload from our PHPUnit tests.
* Send it to the connected connections.
*/
foreach (Socket::$connections as $webSocketConnection) {
$webSocketConnection->send($connection->requestBuffer);
}
$connection->send(str(new Response(200)));
} catch (Exception $e) {
$connection->send(str(new Response(500, [], $e->getMessage())));
}
$connection->close();
unset($connection->requestBuffer);
unset($connection->contentLength);
}
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace App\Client\Http\Controllers;
use App\Client\Http\HttpClient;
use App\Http\Controllers\Controller;
use App\Http\QueryParameters;
use App\Logger\RequestLogger;
use GuzzleHttp\Psr7\Response;
use Ratchet\ConnectionInterface;
use function GuzzleHttp\Psr7\str;
use Psr\Http\Message\RequestInterface;
class ReplayLogController extends Controller
{
/** @var RequestLogger */
protected $requestLogger;
/** @var HttpClient */
protected $httpClient;
public function __construct(RequestLogger $requestLogger, HttpClient $httpClient)
{
$this->requestLogger = $requestLogger;
$this->httpClient = $httpClient;
}
public function onOpen(ConnectionInterface $connection, RequestInterface $request = null)
{
$loggedRequest = $this->requestLogger->findLoggedRequest(QueryParameters::create($request)->get('log'));
if (is_null($loggedRequest)) {
$connection->send(
str(new Response(
404,
['Content-Type' => 'application/json'],
))
);
$connection->close();
return;
}
$requestData = $loggedRequest->getRequestData();
/** @var HttpClient $tunnel */
$this->httpClient->performRequest($requestData);
$connection->send(
str(new Response(
200,
['Content-Type' => 'application/json'],
''
))
);
$connection->close();
}
}